diff --git a/DEPS b/DEPS
index 9c8a3c4..5c264987 100644
--- a/DEPS
+++ b/DEPS
@@ -133,11 +133,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '159a9595963bd08326e9088db12fd701f78f1eae',
+  'skia_revision': 'e6a83e16086095ffcc14a2b9a37a1b6392100a55',
   # 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': '692f7eeca7782bb3736d793fbac2e2c945a238c2',
+  'v8_revision': '8733ff5449b43d9eaf4a6dcaf2344032cd61a149',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -145,15 +145,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '821337639b3f134f7530a06215f2741b788d064a',
+  'angle_revision': '3fb957a00ba37257efd6b635ce58e57127253557',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '5b09dd109681fcf147ff6fbf7ed449e38fec31b9',
+  'swiftshader_revision': '2e2385837e9b46547d8fb944882f93ac9088c40b',
   # 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': '015ab4328738d5a62036f06b67af8983c9b3d8b4',
+  'pdfium_revision': '0eaf1902e2845134ed8e765f0421d2621a44e856',
   # 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.
@@ -172,7 +172,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling googletest
   # and whatever else without interference from each other.
-  'googletest_revision': 'a53e931dcd00c2556ee181d832e699c9f3c29036',
+  'googletest_revision': '9997a830ee5589c2da79198bc3b60d1c47e50118',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling lighttpd
   # and whatever else without interference from each other.
@@ -200,11 +200,11 @@
   # 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': 'f88be3bdf9ca3a367b8fc922c53beb0c3aea9a5d',
+  'catapult_revision': 'c86b091be0e05ddefb1fbf321985cc31f453f2b3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
-  'libfuzzer_revision': 'ff7e2bdf49a44e8d713c9d0cda5c0bd8d981dad3',
+  'libfuzzer_revision': 'e9b95bcfe2f5472fac2e516a0040aedf2140dd62',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-node-modules
   # and whatever else without interference from each other.
@@ -276,7 +276,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.
-  'quiche_revision': 'de80b29a460deb934a6f53db6e567c403ac17664',
+  'quiche_revision': 'c703612c85f2b72c2c4e91956f48a27eb7f4833f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -476,7 +476,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'be3dc47ac8992eaa26be283c0fa0356c085b1e8a',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '3afe64494ecf45b4165d3bacabe9dea35cb001c5',
       'condition': 'checkout_ios',
   },
 
@@ -806,7 +806,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'cf5b7a9b39d66020c7d0eff2a06f3fbb2119d969',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '00aa8077cada1617a2b7b9b9cc441d1503216225',
       'condition': 'checkout_linux',
   },
 
@@ -831,7 +831,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'f61af56950276661d14c5c3387a706692487ecab',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b3aca437d08fb4a2c6f656ad0a0afae3d0396cec',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1355,7 +1355,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6f0b34abee8dba611c253738d955c59f703c147a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '756391a4c28ffced9d774d02ef240bb642afcd9e',
+    Var('webrtc_git') + '/src.git' + '@' + '2bb3e4315d8732b612dcac5f42b34c3f4261ef08',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1396,7 +1396,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@86162ce37530a5a0c02a925887f924ee975a5400',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@831c8b54505e241951b29542732d287326faa4cb',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 86afc51..bd3de32 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -3342,7 +3342,7 @@
   for f in input_api.AffectedFiles():
     # Check both added and modified files (but not deleted files).
     if f.Action() in ('A', 'M'):
-      size = input_api.os_path.getsize(f.LocalPath())
+      size = input_api.os_path.getsize(f.AbsoluteLocalPath())
       if size > TOO_LARGE_FILE_SIZE_LIMIT:
         too_large_files.append("%s: %d bytes" % (f.LocalPath(), size))
 
diff --git a/android_webview/common/aw_content_client.cc b/android_webview/common/aw_content_client.cc
index 7424d08..aa1abe4 100644
--- a/android_webview/common/aw_content_client.cc
+++ b/android_webview/common/aw_content_client.cc
@@ -54,6 +54,10 @@
       resource_id);
 }
 
+bool AwContentClient::IsDataResourceGzipped(int resource_id) const {
+  return ui::ResourceBundle::GetSharedInstance().IsGzipped(resource_id);
+}
+
 bool AwContentClient::CanSendWhileSwappedOut(const IPC::Message* message) {
   // For legacy API support we perform a few browser -> renderer synchronous IPC
   // messages that block the browser. However, the synchronous IPC replies might
diff --git a/android_webview/common/aw_content_client.h b/android_webview/common/aw_content_client.h
index f46f756a..6f3a0a6e 100644
--- a/android_webview/common/aw_content_client.h
+++ b/android_webview/common/aw_content_client.h
@@ -24,6 +24,7 @@
       int resource_id,
       ui::ScaleFactor scale_factor) const override;
   base::RefCountedMemory* GetDataResourceBytes(int resource_id) const override;
+  bool IsDataResourceGzipped(int resource_id) const override;
   bool CanSendWhileSwappedOut(const IPC::Message* message) override;
   void SetGpuInfo(const gpu::GPUInfo& gpu_info) override;
   bool UsingSynchronousCompositing() override;
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 48f67db5..cd765da5 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -44,11 +44,11 @@
     "frame/non_client_frame_view_ash.h",
     "frame/wide_frame_view.h",
     "magnifier/magnification_controller.h",
+    "multi_user/multi_user_window_manager_delegate_classic.h",
 
     # TODO: move MultiUserWindowManager (and delegate) to sources:
     # https://crbug.com/756085
-    "multi_user/multi_user_window_manager.h",
-    "multi_user/multi_user_window_manager_delegate_classic.h",
+    "multi_user/multi_user_window_manager_impl.h",
     "new_window_controller.h",
     "root_window_controller.h",
     "screenshot_delegate.h",
@@ -491,7 +491,7 @@
     "multi_device_setup/multi_device_notification_presenter.h",
     "multi_profile_uma.cc",
     "multi_profile_uma.h",
-    "multi_user/multi_user_window_manager.cc",
+    "multi_user/multi_user_window_manager_impl.cc",
     "multi_user/user_switch_animator.cc",
     "multi_user/user_switch_animator.h",
     "network_connect_delegate_mus.cc",
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 3f5b6242..40fdaba 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -819,7 +819,6 @@
 }
 
 void AppListControllerImpl::StartSearch(const base::string16& raw_query) {
-  last_raw_query_ = raw_query;
   if (client_) {
     base::string16 query;
     base::TrimWhitespace(raw_query, base::TRIM_ALL, &query);
@@ -860,13 +859,13 @@
     base::RecordAction(base::UserMetricsAction("AppList_OpenSearchResult"));
 
     UMA_HISTOGRAM_COUNTS_100(app_list::kSearchQueryLength,
-                             last_raw_query_.size());
+                             GetLastQueryLength());
     if (IsTabletMode()) {
       UMA_HISTOGRAM_COUNTS_100(app_list::kSearchQueryLengthInTablet,
-                               last_raw_query_.size());
+                               GetLastQueryLength());
     } else {
       UMA_HISTOGRAM_COUNTS_100(app_list::kSearchQueryLengthInClamshell,
-                               last_raw_query_.size());
+                               GetLastQueryLength());
     }
 
     if (result->distance_from_origin() >= 0) {
@@ -905,8 +904,7 @@
     app_list::SearchResultLaunchLocation launch_location,
     int suggestion_index) {
   app_list::RecordSearchLaunchIndexAndQueryLength(
-      launch_location, static_cast<int>(last_raw_query_.size()),
-      suggestion_index);
+      launch_location, GetLastQueryLength(), suggestion_index);
 }
 
 void AppListControllerImpl::LogSearchAbandonHistogram() {
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index 3df2c53..02a9ac2 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -338,8 +338,6 @@
   // Record the app launch for AppListAppLaunchedV2 metric.
   void RecordAppLaunched(mojom::AppListLaunchedFrom launched_from);
 
-  base::string16 last_raw_query_;
-
   mojom::AppListClientPtr client_;
 
   std::unique_ptr<app_list::AppListModel> model_;
diff --git a/ash/app_list/app_list_controller_impl_unittest.cc b/ash/app_list/app_list_controller_impl_unittest.cc
index 9b6ed35..ed15ca0 100644
--- a/ash/app_list/app_list_controller_impl_unittest.cc
+++ b/ash/app_list/app_list_controller_impl_unittest.cc
@@ -81,6 +81,11 @@
   return GetAppListView()->get_fullscreen_widget_for_test()->GetNativeView();
 }
 
+void SetSearchText(AppListControllerImpl* controller, const std::string& text) {
+  controller->GetSearchModel()->search_box()->Update(base::ASCIIToUTF16(text),
+                                                     false);
+}
+
 }  // namespace
 
 class AppListControllerImplTest : public AshTestBase {
@@ -271,7 +276,7 @@
 TEST_F(AppListControllerImplMetricsTest, LogSingleResultListClick) {
   histogram_tester_.ExpectTotalCount(kAppListResultLaunchIndexAndQueryLength,
                                      0);
-  controller_->StartSearch(base::string16());
+  SetSearchText(controller_, "");
   controller_->LogResultLaunchHistogram(SearchResultLaunchLocation::kResultList,
                                         4);
   histogram_tester_.ExpectUniqueSample(kAppListResultLaunchIndexAndQueryLength,
@@ -280,7 +285,7 @@
 
 TEST_F(AppListControllerImplMetricsTest, LogSingleTileListClick) {
   histogram_tester_.ExpectTotalCount(kAppListTileLaunchIndexAndQueryLength, 0);
-  controller_->StartSearch(base::ASCIIToUTF16("aaaa"));
+  SetSearchText(controller_, "aaaa");
   controller_->LogResultLaunchHistogram(SearchResultLaunchLocation::kTileList,
                                         4);
   histogram_tester_.ExpectUniqueSample(kAppListTileLaunchIndexAndQueryLength,
@@ -291,10 +296,9 @@
   histogram_tester_.ExpectTotalCount(kAppListResultLaunchIndexAndQueryLength,
                                      0);
   for (int query_length = 0; query_length < 11; ++query_length) {
-    const base::string16 query =
-        base::ASCIIToUTF16(std::string(query_length, 'a'));
+    const std::string query(query_length, 'a');
     for (int click_index = 0; click_index < 7; ++click_index) {
-      controller_->StartSearch(query);
+      SetSearchText(controller_, query);
       controller_->LogResultLaunchHistogram(
           SearchResultLaunchLocation::kResultList, click_index);
     }
@@ -313,7 +317,7 @@
 
 TEST_F(AppListControllerImplMetricsTest, LogManyClicksInOneBucket) {
   histogram_tester_.ExpectTotalCount(kAppListTileLaunchIndexAndQueryLength, 0);
-  controller_->StartSearch(base::ASCIIToUTF16("aaaa"));
+  SetSearchText(controller_, "aaaa");
   for (int i = 0; i < 50; ++i)
     controller_->LogResultLaunchHistogram(SearchResultLaunchLocation::kTileList,
                                           4);
diff --git a/ash/assistant/assistant_ui_controller.cc b/ash/assistant/assistant_ui_controller.cc
index 8bc6168..270b84b 100644
--- a/ash/assistant/assistant_ui_controller.cc
+++ b/ash/assistant/assistant_ui_controller.cc
@@ -12,7 +12,7 @@
 #include "ash/assistant/util/assistant_util.h"
 #include "ash/assistant/util/deep_link_util.h"
 #include "ash/assistant/util/histogram_util.h"
-#include "ash/multi_user/multi_user_window_manager.h"
+#include "ash/multi_user/multi_user_window_manager_impl.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
@@ -324,7 +324,7 @@
 
       // We also want to associate the window for Assistant UI with the active
       // user so that we don't leak across user sessions.
-      auto* window_manager = MultiUserWindowManager::Get();
+      auto* window_manager = MultiUserWindowManagerImpl::Get();
       if (window_manager) {
         const mojom::UserSession* user_session =
             Shell::Get()->session_controller()->GetUserSession(0);
diff --git a/ash/assistant/ui/assistant_container_view.cc b/ash/assistant/ui/assistant_container_view.cc
index 4f1044c..d874a12 100644
--- a/ash/assistant/ui/assistant_container_view.cc
+++ b/ash/assistant/ui/assistant_container_view.cc
@@ -16,6 +16,7 @@
 #include "ash/assistant/ui/assistant_ui_constants.h"
 #include "ash/assistant/ui/assistant_view_delegate.h"
 #include "ash/assistant/ui/assistant_web_view.h"
+#include "ash/assistant/util/assistant_util.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/ash/assistant/ui/assistant_container_view_animator.cc b/ash/assistant/ui/assistant_container_view_animator.cc
index d6d70869..1399338f 100644
--- a/ash/assistant/ui/assistant_container_view_animator.cc
+++ b/ash/assistant/ui/assistant_container_view_animator.cc
@@ -15,9 +15,11 @@
     AssistantContainerView* assistant_container_view)
     : delegate_(delegate), assistant_container_view_(assistant_container_view) {
   static_cast<views::View*>(assistant_container_view_)->AddObserver(this);
+  delegate_->AddUiModelObserver(this);
 }
 
 AssistantContainerViewAnimator::~AssistantContainerViewAnimator() {
+  delegate_->RemoveUiModelObserver(this);
   static_cast<views::View*>(assistant_container_view_)->RemoveObserver(this);
 }
 
@@ -33,6 +35,12 @@
 
 void AssistantContainerViewAnimator::Init() {}
 
+void AssistantContainerViewAnimator::OnUiVisibilityChanged(
+    AssistantVisibility new_visibility,
+    AssistantVisibility old_visibility,
+    base::Optional<AssistantEntryPoint> entry_point,
+    base::Optional<AssistantExitPoint> exit_point) {}
+
 void AssistantContainerViewAnimator::OnBoundsChanged() {}
 
 void AssistantContainerViewAnimator::OnPreferredSizeChanged() {
diff --git a/ash/assistant/ui/assistant_container_view_animator.h b/ash/assistant/ui/assistant_container_view_animator.h
index 7f34020b..8a08158 100644
--- a/ash/assistant/ui/assistant_container_view_animator.h
+++ b/ash/assistant/ui/assistant_container_view_animator.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "ash/assistant/model/assistant_ui_model_observer.h"
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "ui/gfx/geometry/size.h"
@@ -24,7 +25,8 @@
 // The AssistantContainerViewAnimator is the class responsible for smoothly
 // animating bound changes for the AssistantContainerView.
 class COMPONENT_EXPORT(ASSISTANT_UI) AssistantContainerViewAnimator
-    : public views::ViewObserver {
+    : public views::ViewObserver,
+      public AssistantUiModelObserver {
  public:
   ~AssistantContainerViewAnimator() override;
 
@@ -37,6 +39,13 @@
   // AssistantContainerViewAnimator an opportunity to perform initialization.
   virtual void Init();
 
+  // AssistantUiModelObserver:
+  void OnUiVisibilityChanged(
+      AssistantVisibility new_visibility,
+      AssistantVisibility old_visibility,
+      base::Optional<AssistantEntryPoint> entry_point,
+      base::Optional<AssistantExitPoint> exit_point) override;
+
  protected:
   AssistantContainerViewAnimator(
       AssistantViewDelegate* delegate,
diff --git a/ash/assistant/ui/assistant_container_view_animator_legacy_impl.cc b/ash/assistant/ui/assistant_container_view_animator_legacy_impl.cc
index 00cb5f7..981b151a 100644
--- a/ash/assistant/ui/assistant_container_view_animator_legacy_impl.cc
+++ b/ash/assistant/ui/assistant_container_view_animator_legacy_impl.cc
@@ -9,6 +9,8 @@
 #include "ash/assistant/ui/assistant_container_view.h"
 #include "ash/assistant/ui/assistant_ui_constants.h"
 #include "ash/assistant/ui/assistant_view_delegate.h"
+#include "ash/assistant/util/animation_util.h"
+#include "ash/assistant/util/assistant_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "ui/gfx/animation/slide_animation.h"
 #include "ui/views/animation/ink_drop_painted_layer_delegates.h"
@@ -20,6 +22,7 @@
 
 // Animation.
 constexpr int kAnimationDurationMs = 250;
+constexpr int kFadeInAnimationDelayMs = 17;
 
 // Helpers ---------------------------------------------------------------------
 
@@ -102,6 +105,41 @@
   assistant_container_view_->SizeToContents();
 }
 
+void AssistantContainerViewAnimatorLegacyImpl::OnUiVisibilityChanged(
+    AssistantVisibility new_visibility,
+    AssistantVisibility old_visibility,
+    base::Optional<AssistantEntryPoint> entry_point,
+    base::Optional<AssistantExitPoint> exit_point) {
+  if (!assistant::util::IsStartingSession(new_visibility, old_visibility))
+    return;
+
+  // Start the container open animation on height growth and fade-in when
+  // Assistant starts a new session.
+  using assistant::util::CreateLayerAnimationSequence;
+  using assistant::util::CreateOpacityElement;
+  animation_.reset();
+
+  // Animate the fade in with a delay.
+  assistant_container_view_->GetNonClientViewLayer()->SetOpacity(0.f);
+  assistant_container_view_->GetNonClientViewLayer()
+      ->GetAnimator()
+      ->StartAnimation(CreateLayerAnimationSequence(
+          ui::LayerAnimationElement::CreatePauseElement(
+              ui::LayerAnimationElement::AnimatableProperty::OPACITY,
+              base::TimeDelta::FromMilliseconds(kFadeInAnimationDelayMs)),
+          CreateOpacityElement(
+              1.f, base::TimeDelta::FromMilliseconds(kAnimationDurationMs),
+              gfx::Tween::Type::FAST_OUT_SLOW_IN_2)));
+
+  // Set the initial animation value of bound with height equals to 0.
+  gfx::Rect current_bounds = assistant_container_view_->bounds();
+  assistant_container_view_->SetBounds(
+      current_bounds.x(), current_bounds.y() + current_bounds.height(),
+      current_bounds.width(), 0);
+  // Animate the height growth.
+  OnPreferredSizeChanged();
+}
+
 void AssistantContainerViewAnimatorLegacyImpl::AnimationProgressed(
     const gfx::Animation* animation) {
   if (!assistant_container_view_->GetWidget())
diff --git a/ash/assistant/ui/assistant_container_view_animator_legacy_impl.h b/ash/assistant/ui/assistant_container_view_animator_legacy_impl.h
index d399053..ab6a74aa 100644
--- a/ash/assistant/ui/assistant_container_view_animator_legacy_impl.h
+++ b/ash/assistant/ui/assistant_container_view_animator_legacy_impl.h
@@ -42,6 +42,11 @@
   void Init() override;
   void OnBoundsChanged() override;
   void OnPreferredSizeChanged() override;
+  void OnUiVisibilityChanged(
+      AssistantVisibility new_visibility,
+      AssistantVisibility old_visibility,
+      base::Optional<AssistantEntryPoint> entry_point,
+      base::Optional<AssistantExitPoint> exit_point) override;
 
   // gfx::AnimationDelegate:
   void AnimationProgressed(const gfx::Animation* animation) override;
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 16f3e7b..82054d0 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -1108,6 +1108,11 @@
   // prevent lock screen from grabbing focus and hiding the OOBE dialog.
   GetWidget()->widget_delegate()->SetCanActivate(!oobe_dialog_visible_);
 
+  // Block login screen events, to prevent actions on user pods shown in the
+  // background (e.g. hover over user name, or clicking a pod) from having
+  // effect.
+  set_can_process_events_within_subtree(!oobe_dialog_visible_);
+
   if (state == mojom::OobeDialogState::HIDDEN && primary_big_view_)
     primary_big_view_->RequestFocus();
 }
diff --git a/ash/login/ui/lock_contents_view_unittest.cc b/ash/login/ui/lock_contents_view_unittest.cc
index 9e3e2a2f..ec213b4 100644
--- a/ash/login/ui/lock_contents_view_unittest.cc
+++ b/ash/login/ui/lock_contents_view_unittest.cc
@@ -2372,4 +2372,51 @@
   Shell::Get()->login_screen_controller()->FlushForTesting();
 }
 
+TEST_F(LockContentsViewUnitTest, LoginNotReactingOnEventsWithOobeDialogShown) {
+  auto* contents = new LockContentsView(
+      mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLogin,
+      DataDispatcher(),
+      std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher()));
+  SetUserCount(3);
+  SetWidget(CreateWidgetWithContent(contents));
+
+  LockContentsView::TestApi lock_contents(contents);
+  ScrollableUsersListView::TestApi users_list(lock_contents.users_list());
+  const auto* const list_user_view = users_list.user_views()[0];
+  LoginBigUserView* auth_view = lock_contents.primary_big_view();
+
+  AccountId auth_view_user =
+      auth_view->GetCurrentUser()->basic_user_info->account_id;
+  AccountId list_user =
+      list_user_view->current_user()->basic_user_info->account_id;
+
+  Shell::Get()->login_screen_controller()->NotifyOobeDialogState(
+      mojom::OobeDialogState::GAIA_SIGNIN);
+
+  // Send event to swap users.
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  generator->MoveMouseTo(list_user_view->GetBoundsInScreen().CenterPoint());
+  generator->ClickLeftButton();
+
+  // User info is not swapped.
+  EXPECT_EQ(auth_view_user,
+            auth_view->GetCurrentUser()->basic_user_info->account_id);
+  EXPECT_EQ(list_user,
+            list_user_view->current_user()->basic_user_info->account_id);
+
+  // Hide OOBE dialog.
+  Shell::Get()->login_screen_controller()->NotifyOobeDialogState(
+      mojom::OobeDialogState::HIDDEN);
+
+  // Attempt swap again.
+  generator->MoveMouseTo(list_user_view->GetBoundsInScreen().CenterPoint());
+  generator->ClickLeftButton();
+
+  // User info should be now swapped.
+  EXPECT_EQ(list_user,
+            auth_view->GetCurrentUser()->basic_user_info->account_id);
+  EXPECT_EQ(auth_view_user,
+            list_user_view->current_user()->basic_user_info->account_id);
+}
+
 }  // namespace ash
diff --git a/ash/multi_user/multi_user_window_manager.cc b/ash/multi_user/multi_user_window_manager_impl.cc
similarity index 85%
rename from ash/multi_user/multi_user_window_manager.cc
rename to ash/multi_user/multi_user_window_manager_impl.cc
index 5459676..a1fa38b 100644
--- a/ash/multi_user/multi_user_window_manager.cc
+++ b/ash/multi_user/multi_user_window_manager_impl.cc
@@ -2,13 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/multi_user/multi_user_window_manager.h"
+#include "ash/multi_user/multi_user_window_manager_impl.h"
 
 #include <set>
 #include <vector>
 
 #include "ash/media/media_controller.h"
-#include "ash/multi_user/multi_user_window_manager.h"
 #include "ash/multi_user/multi_user_window_manager_delegate_classic.h"
 #include "ash/multi_user/user_switch_animator.h"
 #include "ash/public/cpp/shell_window_ids.h"
@@ -42,7 +41,7 @@
 constexpr base::TimeDelta kTeleportAnimationTime =
     base::TimeDelta::FromMilliseconds(300);
 
-MultiUserWindowManager* g_instance = nullptr;
+MultiUserWindowManagerImpl* g_instance = nullptr;
 
 bool HasSystemModalTransientChildWindow(aura::Window* window) {
   if (window == nullptr)
@@ -116,7 +115,7 @@
   DISALLOW_COPY_AND_ASSIGN(AnimationSetter);
 };
 
-MultiUserWindowManager::WindowEntry::WindowEntry(
+MultiUserWindowManagerImpl::WindowEntry::WindowEntry(
     const AccountId& account_id,
     base::Optional<ws::Id> window_id)
     : owner_(account_id),
@@ -124,9 +123,9 @@
       window_id_(std::move(window_id)),
       from_window_service_(window_id.has_value()) {}
 
-MultiUserWindowManager::WindowEntry::~WindowEntry() = default;
+MultiUserWindowManagerImpl::WindowEntry::~WindowEntry() = default;
 
-MultiUserWindowManager::MultiUserWindowManager(
+MultiUserWindowManagerImpl::MultiUserWindowManagerImpl(
     mojom::MultiUserWindowManagerClient* client,
     MultiUserWindowManagerDelegateClassic* classic_delegate,
     const AccountId& account_id)
@@ -138,7 +137,7 @@
   Shell::Get()->session_controller()->AddObserver(this);
 }
 
-MultiUserWindowManager::~MultiUserWindowManager() {
+MultiUserWindowManagerImpl::~MultiUserWindowManagerImpl() {
   // When the MultiUserWindowManager gets destroyed, ash::Shell is mostly gone.
   // As such we should not try to finalize any outstanding user animations.
   // Note that the destruction of the object can be done later.
@@ -160,11 +159,11 @@
 }
 
 // static
-MultiUserWindowManager* MultiUserWindowManager::Get() {
+MultiUserWindowManagerImpl* MultiUserWindowManagerImpl::Get() {
   return g_instance;
 }
 
-void MultiUserWindowManager::SetClient(
+void MultiUserWindowManagerImpl::SetClient(
     mojom::MultiUserWindowManagerClient* client) {
   client_ = client;
 
@@ -174,10 +173,11 @@
     pair.second->reset_window_id();
 }
 
-void MultiUserWindowManager::SetWindowOwner(aura::Window* window,
-                                            const AccountId& account_id,
-                                            bool show_for_current_user,
-                                            base::Optional<ws::Id> window_id) {
+void MultiUserWindowManagerImpl::SetWindowOwner(
+    aura::Window* window,
+    const AccountId& account_id,
+    bool show_for_current_user,
+    base::Optional<ws::Id> window_id) {
   // Make sure the window is valid and there was no owner yet.
   DCHECK(window);
   DCHECK(account_id.is_valid());
@@ -215,14 +215,15 @@
     SetWindowVisibility(window, false);
 }
 
-const AccountId& MultiUserWindowManager::GetWindowOwner(
+const AccountId& MultiUserWindowManagerImpl::GetWindowOwner(
     aura::Window* window) const {
   WindowToEntryMap::const_iterator it = window_to_entry_.find(window);
   return it != window_to_entry_.end() ? it->second->owner() : EmptyAccountId();
 }
 
-void MultiUserWindowManager::ShowWindowForUser(aura::Window* window,
-                                               const AccountId& account_id) {
+void MultiUserWindowManagerImpl::ShowWindowForUser(
+    aura::Window* window,
+    const AccountId& account_id) {
   const AccountId previous_owner(GetUserPresentingWindow(window));
   if (!ShowWindowForUserIntern(window, account_id))
     return;
@@ -236,7 +237,7 @@
   Shell::Get()->session_controller()->SwitchActiveUser(account_id);
 }
 
-bool MultiUserWindowManager::AreWindowsSharedAmongUsers() const {
+bool MultiUserWindowManagerImpl::AreWindowsSharedAmongUsers() const {
   for (auto& window_pair : window_to_entry_) {
     if (window_pair.second->owner() != window_pair.second->show_for_user())
       return true;
@@ -244,14 +245,14 @@
   return false;
 }
 
-bool MultiUserWindowManager::IsWindowOnDesktopOfUser(
+bool MultiUserWindowManagerImpl::IsWindowOnDesktopOfUser(
     aura::Window* window,
     const AccountId& account_id) const {
   const AccountId& presenting_user = GetUserPresentingWindow(window);
   return (!presenting_user.is_valid()) || presenting_user == account_id;
 }
 
-const AccountId& MultiUserWindowManager::GetUserPresentingWindow(
+const AccountId& MultiUserWindowManagerImpl::GetUserPresentingWindow(
     aura::Window* window) const {
   WindowToEntryMap::const_iterator it = window_to_entry_.find(window);
   // If the window is not owned by anyone it is shown on all desktops and we
@@ -262,14 +263,14 @@
   return it->second->show_for_user();
 }
 
-void MultiUserWindowManager::OnActiveUserSessionChanged(
+void MultiUserWindowManagerImpl::OnActiveUserSessionChanged(
     const AccountId& account_id) {
-  // MultiUserWindowManager is created with an account before the change has
-  // potentially made it to SessionController. This means MultiUserWindowManager
-  // may be notified of a switch to the current user. Ignore this. Ignoring this
-  // is especially important in tests, which may be impacted by running the
-  // animation (when the animation closes, observers are notified, which may
-  // have side effects in downstream code).
+  // MultiUserWindowManagerImpl is created with an account before the change has
+  // potentially made it to SessionController. This means
+  // MultiUserWindowManagerImpl may be notified of a switch to the current user.
+  // Ignore this. Ignoring this is especially important in tests, which may be
+  // impacted by running the animation (when the animation closes, observers are
+  // notified, which may have side effects in downstream code).
   if (account_id == current_account_id_)
     return;
 
@@ -290,11 +291,11 @@
 
   // Call RequestCaptureState here instead of having MediaClient observe
   // ActiveUserChanged because it must happen after
-  // MultiUserWindowManager is notified.
+  // MultiUserWindowManagerImpl is notified.
   Shell::Get()->media_controller()->RequestCaptureState();
 }
 
-void MultiUserWindowManager::OnWindowDestroyed(aura::Window* window) {
+void MultiUserWindowManagerImpl::OnWindowDestroyed(aura::Window* window) {
   if (GetWindowOwner(window).empty()) {
     // This must be a window in the transient chain - remove it and its
     // children from the owner.
@@ -305,8 +306,9 @@
   window_to_entry_.erase(window);
 }
 
-void MultiUserWindowManager::OnWindowVisibilityChanging(aura::Window* window,
-                                                        bool visible) {
+void MultiUserWindowManagerImpl::OnWindowVisibilityChanging(
+    aura::Window* window,
+    bool visible) {
   // This command gets called first and immediately when show or hide gets
   // called. We remember here the desired state for restoration IF we were
   // not ourselves issuing the call.
@@ -329,8 +331,8 @@
   }
 }
 
-void MultiUserWindowManager::OnWindowVisibilityChanged(aura::Window* window,
-                                                       bool visible) {
+void MultiUserWindowManagerImpl::OnWindowVisibilityChanged(aura::Window* window,
+                                                           bool visible) {
   if (suppress_visibility_changes_)
     return;
 
@@ -345,7 +347,7 @@
     SetWindowVisibility(window, false);
 }
 
-void MultiUserWindowManager::OnTransientChildAdded(
+void MultiUserWindowManagerImpl::OnTransientChildAdded(
     aura::Window* window,
     aura::Window* transient_window) {
   if (!GetWindowOwner(window).empty()) {
@@ -360,7 +362,7 @@
   AddTransientOwnerRecursive(transient_window, owned_parent);
 }
 
-void MultiUserWindowManager::OnTransientChildRemoved(
+void MultiUserWindowManagerImpl::OnTransientChildRemoved(
     aura::Window* window,
     aura::Window* transient_window) {
   // Remove the transient child if the window itself is owned, or one of the
@@ -371,25 +373,25 @@
   }
 }
 
-void MultiUserWindowManager::OnTabletModeStarted() {
+void MultiUserWindowManagerImpl::OnTabletModeStarted() {
   for (auto& entry : window_to_entry_)
     Shell::Get()->tablet_mode_controller()->AddWindow(entry.first);
 }
 
-void MultiUserWindowManager::SetAnimationSpeedForTest(
-    MultiUserWindowManager::AnimationSpeed speed) {
+void MultiUserWindowManagerImpl::SetAnimationSpeedForTest(
+    MultiUserWindowManagerImpl::AnimationSpeed speed) {
   animation_speed_ = speed;
 }
 
-bool MultiUserWindowManager::IsAnimationRunningForTest() {
+bool MultiUserWindowManagerImpl::IsAnimationRunningForTest() {
   return animation_ && !animation_->IsAnimationFinished();
 }
 
-const AccountId& MultiUserWindowManager::GetCurrentUserForTest() const {
+const AccountId& MultiUserWindowManagerImpl::GetCurrentUserForTest() const {
   return current_account_id_;
 }
 
-bool MultiUserWindowManager::ShowWindowForUserIntern(
+bool MultiUserWindowManagerImpl::ShowWindowForUserIntern(
     aura::Window* window,
     const AccountId& account_id) {
   // If there is either no owner, or the owner is the current user, no action
@@ -429,7 +431,7 @@
   return true;
 }
 
-void MultiUserWindowManager::SetWindowVisibility(
+void MultiUserWindowManagerImpl::SetWindowVisibility(
     aura::Window* window,
     bool visible,
     base::TimeDelta animation_time) {
@@ -466,7 +468,7 @@
     SetWindowVisible(window, false, animation_time);
 }
 
-void MultiUserWindowManager::ShowWithTransientChildrenRecursive(
+void MultiUserWindowManagerImpl::ShowWithTransientChildrenRecursive(
     aura::Window* window,
     base::TimeDelta animation_time) {
   for (aura::Window* transient_child : ::wm::GetTransientChildren(window))
@@ -479,7 +481,7 @@
     SetWindowVisible(window, true, animation_time);
 }
 
-aura::Window* MultiUserWindowManager::GetOwningWindowInTransientChain(
+aura::Window* MultiUserWindowManagerImpl::GetOwningWindowInTransientChain(
     aura::Window* window) const {
   if (!GetWindowOwner(window).empty())
     return nullptr;
@@ -492,7 +494,7 @@
   return nullptr;
 }
 
-void MultiUserWindowManager::AddTransientOwnerRecursive(
+void MultiUserWindowManagerImpl::AddTransientOwnerRecursive(
     aura::Window* window,
     aura::Window* owned_parent) {
   // First add all child windows.
@@ -519,7 +521,7 @@
     SetWindowVisibility(window, false, kAnimationTime);
 }
 
-void MultiUserWindowManager::RemoveTransientOwnerRecursive(
+void MultiUserWindowManagerImpl::RemoveTransientOwnerRecursive(
     aura::Window* window) {
   // First remove all child windows.
   for (aura::Window* transient_child : ::wm::GetTransientChildren(window))
@@ -546,9 +548,10 @@
   }
 }
 
-void MultiUserWindowManager::SetWindowVisible(aura::Window* window,
-                                              bool visible,
-                                              base::TimeDelta animation_time) {
+void MultiUserWindowManagerImpl::SetWindowVisible(
+    aura::Window* window,
+    bool visible,
+    base::TimeDelta animation_time) {
   // The TabletModeWindowManager will not handle invisible windows since they
   // are not user activatable. Since invisible windows are not being tracked,
   // we tell it to maximize / track this window now before it gets shown, to
@@ -564,7 +567,7 @@
     window->Hide();
 }
 
-base::TimeDelta MultiUserWindowManager::GetAdjustedAnimationTime(
+base::TimeDelta MultiUserWindowManagerImpl::GetAdjustedAnimationTime(
     base::TimeDelta default_time) const {
   return animation_speed_ == ANIMATION_SPEED_NORMAL
              ? default_time
diff --git a/ash/multi_user/multi_user_window_manager.h b/ash/multi_user/multi_user_window_manager_impl.h
similarity index 94%
rename from ash/multi_user/multi_user_window_manager.h
rename to ash/multi_user/multi_user_window_manager_impl.h
index bddde6d..79d47c5 100644
--- a/ash/multi_user/multi_user_window_manager.h
+++ b/ash/multi_user/multi_user_window_manager_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_H_
-#define ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_H_
+#ifndef ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_IMPL_H_
+#define ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_IMPL_H_
 
 #include <map>
 #include <memory>
@@ -51,10 +51,11 @@
 //   visibility changes from the owning user. This way the visibility can be
 //   changed back to its requested state upon showing by us - or when the window
 //   gets detached from its current owning parent.
-class ASH_EXPORT MultiUserWindowManager : public SessionObserver,
-                                          public aura::WindowObserver,
-                                          public ::wm::TransientWindowObserver,
-                                          public TabletModeObserver {
+class ASH_EXPORT MultiUserWindowManagerImpl
+    : public SessionObserver,
+      public aura::WindowObserver,
+      public ::wm::TransientWindowObserver,
+      public TabletModeObserver {
  public:
   // The speed which should be used to perform animations.
   enum AnimationSpeed {
@@ -63,13 +64,13 @@
     ANIMATION_SPEED_DISABLED  // Unit tests which do not require animations.
   };
 
-  MultiUserWindowManager(
+  MultiUserWindowManagerImpl(
       mojom::MultiUserWindowManagerClient* client,
       MultiUserWindowManagerDelegateClassic* classic_delegate,
       const AccountId& account_id);
-  ~MultiUserWindowManager() override;
+  ~MultiUserWindowManagerImpl() override;
 
-  static MultiUserWindowManager* Get();
+  static MultiUserWindowManagerImpl* Get();
 
   // Resets the client. This is called when running in mash. In single-process
   // mash, the browser creates this class (with no client) and
@@ -267,9 +268,9 @@
   // The animation between users.
   std::unique_ptr<UserSwitchAnimator> animation_;
 
-  DISALLOW_COPY_AND_ASSIGN(MultiUserWindowManager);
+  DISALLOW_COPY_AND_ASSIGN(MultiUserWindowManagerImpl);
 };
 
 }  // namespace ash
 
-#endif  // ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_H_
+#endif  // ASH_MULTI_USER_MULTI_USER_WINDOW_MANAGER_IMPL_H_
diff --git a/ash/multi_user/user_switch_animator.cc b/ash/multi_user/user_switch_animator.cc
index b109388..c4a35f0b 100644
--- a/ash/multi_user/user_switch_animator.cc
+++ b/ash/multi_user/user_switch_animator.cc
@@ -4,7 +4,7 @@
 
 #include "ash/multi_user/user_switch_animator.h"
 
-#include "ash/multi_user/multi_user_window_manager.h"
+#include "ash/multi_user/multi_user_window_manager_impl.h"
 #include "ash/shell.h"
 #include "ash/wallpaper/wallpaper_controller.h"
 #include "ash/wm/mru_window_tracker.h"
@@ -80,7 +80,7 @@
 }  // namespace
 
 UserSwitchAnimator::UserSwitchAnimator(
-    MultiUserWindowManager* owner,
+    MultiUserWindowManagerImpl* owner,
     mojom::WallpaperUserInfoPtr wallpaper_user_info,
     base::TimeDelta animation_speed)
     : owner_(owner),
@@ -231,7 +231,7 @@
           // different than that of the for_show_account_id) should retrun to
           // their
           // original owners' desktops.
-          MultiUserWindowManager::WindowToEntryMap::const_iterator itr =
+          MultiUserWindowManagerImpl::WindowToEntryMap::const_iterator itr =
               owner_->window_to_entry().find(window);
           DCHECK(itr != owner_->window_to_entry().end());
           if (show_for_account_id != itr->second->owner() &&
diff --git a/ash/multi_user/user_switch_animator.h b/ash/multi_user/user_switch_animator.h
index 2f7ba50b5..78978e6 100644
--- a/ash/multi_user/user_switch_animator.h
+++ b/ash/multi_user/user_switch_animator.h
@@ -19,7 +19,7 @@
 
 namespace ash {
 
-class MultiUserWindowManager;
+class MultiUserWindowManagerImpl;
 
 // A class which performs transitions animations between users. Upon creation,
 // the animation gets started and upon destruction the animation gets finished
@@ -38,7 +38,7 @@
 
   // Creates a UserSwitchAnimator to animate between the current user and
   // |user_info|.
-  UserSwitchAnimator(MultiUserWindowManager* owner,
+  UserSwitchAnimator(MultiUserWindowManagerImpl* owner,
                      mojom::WallpaperUserInfoPtr user_info,
                      base::TimeDelta animation_speed);
   ~UserSwitchAnimator();
@@ -99,7 +99,7 @@
   void BuildUserToWindowsListMap();
 
   // The owning window manager.
-  MultiUserWindowManager* owner_;
+  MultiUserWindowManagerImpl* owner_;
 
   // Contains the wallpaper configuration for the user switching to. This is
   // passed to the WallpaperController at the right time.
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index 3d189d9b..155e8ad 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -73,7 +73,7 @@
                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kNotificationStackingBarRedesign{
-    "NotificationStackingBarRedesign", base::FEATURE_DISABLED_BY_DEFAULT};
+    "NotificationStackingBarRedesign", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kSystemTrayFeaturePodsPagination{
     "SystemTrayFeaturePodsPagination", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ash/system/message_center/arc/mock_arc_notification_item.cc b/ash/system/message_center/arc/mock_arc_notification_item.cc
index 9ac9d22..b855547 100644
--- a/ash/system/message_center/arc/mock_arc_notification_item.cc
+++ b/ash/system/message_center/arc/mock_arc_notification_item.cc
@@ -31,7 +31,7 @@
   count_close_++;
 
   if (close_callback_)
-    base::ResetAndReturn(&close_callback_).Run();
+    std::move(close_callback_).Run();
 }
 
 const gfx::ImageSkia& MockArcNotificationItem::GetSnapshot() const {
diff --git a/ash/system/message_center/unified_message_center_view_unittest.cc b/ash/system/message_center/unified_message_center_view_unittest.cc
index 0bf99aa9..8421fb23 100644
--- a/ash/system/message_center/unified_message_center_view_unittest.cc
+++ b/ash/system/message_center/unified_message_center_view_unittest.cc
@@ -72,6 +72,10 @@
   void SetUp() override {
     AshTestBase::SetUp();
     model_ = std::make_unique<UnifiedSystemTrayModel>();
+
+    scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
+    scoped_feature_list_->InitAndDisableFeature(
+        features::kNotificationStackingBarRedesign);
   }
 
   void TearDown() override {
@@ -184,7 +188,8 @@
   }
 
   void EnableNotificationStackingBarRedesign() {
-    scoped_feature_list_.InitAndEnableFeature(
+    scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
+    scoped_feature_list_->InitAndEnableFeature(
         features::kNotificationStackingBarRedesign);
   }
 
@@ -197,7 +202,7 @@
   UnifiedSystemTrayModel* model() { return model_.get(); }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
+  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
   int id_ = 0;
   int size_changed_count_ = 0;
 
diff --git a/ash/system/message_center/unified_message_list_view_unittest.cc b/ash/system/message_center/unified_message_list_view_unittest.cc
index 1d7c582..ee8d8a3 100644
--- a/ash/system/message_center/unified_message_list_view_unittest.cc
+++ b/ash/system/message_center/unified_message_list_view_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/message_center/unified_message_list_view.h"
 
+#include "ash/public/cpp/ash_features.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/unified_system_tray_model.h"
 #include "ash/test/ash_test_base.h"
@@ -86,6 +87,8 @@
   void SetUp() override {
     AshTestBase::SetUp();
     model_ = std::make_unique<UnifiedSystemTrayModel>();
+    scoped_feature_list_.InitAndDisableFeature(
+        features::kNotificationStackingBarRedesign);
   }
 
   void TearDown() override {
@@ -161,6 +164,7 @@
   int size_changed_count() const { return size_changed_count_; }
 
  private:
+  base::test::ScopedFeatureList scoped_feature_list_;
   int id_ = 0;
   int size_changed_count_ = 0;
 
diff --git a/ash/wm/desks/desk.cc b/ash/wm/desks/desk.cc
index 9801e491..aff481d2 100644
--- a/ash/wm/desks/desk.cc
+++ b/ash/wm/desks/desk.cc
@@ -27,7 +27,8 @@
   void OnWindowAdded(aura::Window* new_window) override {
     // TODO(afakhry): Overview mode creates a new widget for each window under
     // the same parent for the CaptionContainerView. We will be notified with
-    // this window addition here. Ignore this window.
+    // this window addition here. Consider ignoring these windows if they cause
+    // problems.
     owner_->AddWindowToDesk(new_window);
   }
 
@@ -88,14 +89,14 @@
   }
 }
 
-void Desk::Activate() {
-  is_active_ = true;
-
+void Desk::Activate(bool update_window_activation) {
   // Show the associated containers on all roots.
   for (aura::Window* root : Shell::GetAllRootWindows())
     root->GetChildById(container_id_)->Show();
 
-  if (windows_.empty())
+  is_active_ = true;
+
+  if (!update_window_activation || windows_.empty())
     return;
 
   // Activate the window on this desk that was most recently used right before
@@ -109,23 +110,26 @@
   }
 }
 
-void Desk::Deactivate() {
+void Desk::Deactivate(bool update_window_activation) {
   auto* active_window = wm::GetActiveWindow();
 
   // Hide the associated containers on all roots.
   for (aura::Window* root : Shell::GetAllRootWindows())
     root->GetChildById(container_id_)->Hide();
 
-  // Deactivate the active window (if any) after this desk's associated
+  is_active_ = false;
+
+  if (!update_window_activation)
+    return;
+
+  // Deactivate the active window (if it belongs to this desk; active window may
+  // be on a different container, or one of the widgets created by overview mode
+  // which are not considered desk windows) after this desk's associated
   // containers have been hidden. This is to prevent the focus controller from
   // activating another window on the same desk when the active window loses
   // focus.
-  if (active_window) {
-    DCHECK(windows_.contains(active_window));
+  if (active_window && windows_.contains(active_window))
     wm::DeactivateWindow(active_window);
-  }
-
-  is_active_ = false;
 }
 
 void Desk::MoveWindowsToDesk(Desk* target_desk) {
@@ -162,7 +166,7 @@
   return root->GetChildById(container_id_);
 }
 
-void Desk::OnWindowDestroyed(aura::Window* window) {
+void Desk::OnWindowDestroying(aura::Window* window) {
   const size_t count = windows_.erase(window);
   DCHECK(count);
 }
diff --git a/ash/wm/desks/desk.h b/ash/wm/desks/desk.h
index 869fbaa..71ad2d09 100644
--- a/ash/wm/desks/desk.h
+++ b/ash/wm/desks/desk.h
@@ -44,13 +44,15 @@
 
   // Activates this desk. All windows on this desk (if any) will become visible
   // (by means of showing this desk's associated containers on all root
-  // windows), and the most recently used one of them will be activated.
-  void Activate();
+  // windows). If |update_window_activation| is true, the most recently
+  // used one of them will be activated.
+  void Activate(bool update_window_activation);
 
   // Deactivates this desk. All windows on this desk (if any) will become hidden
   // (by means of hiding this desk's associated containers on all root windows),
-  // and the currently active window on this desk will be deactivated.
-  void Deactivate();
+  // If |update_window_activation| is true, the currently active window
+  // on this desk will be deactivated.
+  void Deactivate(bool update_window_activation);
 
   // Moves the windows on this desk to |target_desk|.
   void MoveWindowsToDesk(Desk* target_desk);
@@ -58,7 +60,7 @@
   aura::Window* GetDeskContainerForRoot(aura::Window* root) const;
 
   // aura::WindowObserver:
-  void OnWindowDestroyed(aura::Window* window) override;
+  void OnWindowDestroying(aura::Window* window) override;
 
  private:
   // The associated container ID with this desk.
diff --git a/ash/wm/desks/desk_mini_view.cc b/ash/wm/desks/desk_mini_view.cc
index d75a884..b305284 100644
--- a/ash/wm/desks/desk_mini_view.cc
+++ b/ash/wm/desks/desk_mini_view.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "ash/public/cpp/ash_features.h"
 #include "ash/wm/desks/close_desk_button.h"
 #include "ash/wm/desks/desk.h"
 #include "ash/wm/desks/desks_controller.h"
@@ -24,6 +25,12 @@
 
 constexpr gfx::Size kCloseButtonSize{24, 24};
 
+constexpr int kPreviewCornerRadius = 2;
+
+constexpr SkColor kActiveColor = SkColorSetARGB(0xEE, 0xFF, 0xFF, 0xFF);
+
+constexpr SkColor kInactiveColor = SkColorSetARGB(0x50, 0xFF, 0xFF, 0xFF);
+
 // The desk preview bounds are proportional to the bounds of the display on
 // which it resides, but always has a fixed height `kDeskPreviewHeight`.
 gfx::Rect GetDeskPreviewBounds(aura::Window* root_window) {
@@ -42,11 +49,13 @@
   explicit DeskPreviewView(DeskMiniView* mini_view) : mini_view_(mini_view) {
     // For now use a solid color layer.
     SetPaintToLayer(ui::LAYER_SOLID_COLOR);
-    layer()->SetColor(SK_ColorDKGRAY);
+    if (features::ShouldUseShaderRoundedCorner()) {
+      layer()->SetRoundedCornerRadius(
+          {kPreviewCornerRadius, kPreviewCornerRadius, kPreviewCornerRadius,
+           kPreviewCornerRadius});
+    }
 
-    // TODO(afakhry):
-    // - Ability to mark this preview as active.
-    // - Actually mirror the contents of the corresponding desk.
+    // TODO(afakhry): Mirror the contents of the corresponding desk.
   }
 
   ~DeskPreviewView() override = default;
@@ -66,6 +75,7 @@
       default:
         break;
     }
+    views::View::OnMouseEvent(event);
   }
 
  private:
@@ -105,6 +115,8 @@
   SetFocusPainter(nullptr);
   SetInkDropMode(InkDropMode::OFF);
 
+  UpdateActivationState();
+
   SchedulePaint();
 }
 
@@ -116,11 +128,17 @@
 
 void DeskMiniView::OnHoverStateMayHaveChanged() {
   // TODO(afakhry): In tablet mode, discuss showing the close button on long
-  // press.
+  // press. Also, don't show the close button when hovered while window drag is
+  // in progress.
   close_desk_button_->SetVisible(DesksController::Get()->CanRemoveDesks() &&
                                  IsMouseHovered());
 }
 
+void DeskMiniView::UpdateActivationState() {
+  desk_preview_->layer()->SetColor(desk_->is_active() ? kActiveColor
+                                                      : kInactiveColor);
+}
+
 const char* DeskMiniView::GetClassName() const {
   return "DeskMiniView";
 }
@@ -165,6 +183,9 @@
   if (sender != close_desk_button_)
     return;
 
+  // Hide the close button so it can no longer be pressed.
+  close_desk_button_->SetVisible(false);
+
   // This mini_view can no longer be pressed.
   listener_ = nullptr;
 
@@ -187,6 +208,7 @@
     default:
       break;
   }
+  views::Button::OnMouseEvent(event);
 }
 
 }  // namespace ash
diff --git a/ash/wm/desks/desk_mini_view.h b/ash/wm/desks/desk_mini_view.h
index 8fbccbb1..eacd6d3 100644
--- a/ash/wm/desks/desk_mini_view.h
+++ b/ash/wm/desks/desk_mini_view.h
@@ -42,6 +42,10 @@
   // view is mouse hovered.
   void OnHoverStateMayHaveChanged();
 
+  // Updates the border color of the DeskPreviewView based on the activation
+  // state of the corresponding desk.
+  void UpdateActivationState();
+
   // views::Button:
   const char* GetClassName() const override;
   void Layout() override;
diff --git a/ash/wm/desks/desks_bar_view.cc b/ash/wm/desks/desks_bar_view.cc
index cadc918..5d3d5306 100644
--- a/ash/wm/desks/desks_bar_view.cc
+++ b/ash/wm/desks/desks_bar_view.cc
@@ -9,10 +9,12 @@
 #include <utility>
 
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/wm/desks/desk_mini_view.h"
 #include "ash/wm/desks/desk_mini_view_animations.h"
 #include "ash/wm/desks/new_desk_button.h"
+#include "ash/wm/overview/overview_controller.h"
 #include "base/stl_util.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/aura/window.h"
@@ -139,13 +141,21 @@
 void DesksBarView::ButtonPressed(views::Button* sender,
                                  const ui::Event& event) {
   auto* controller = DesksController::Get();
-  if (sender == new_desk_button_ && controller->CanCreateDesks()) {
-    controller->NewDesk();
-  } else {
-    // TODO(afakhry): Handle mini_view presses.
+  if (sender == new_desk_button_) {
+    if (controller->CanCreateDesks()) {
+      controller->NewDesk();
+      UpdateNewDeskButtonState();
+    }
+    return;
   }
 
-  UpdateNewDeskButtonState();
+  for (auto& mini_view : mini_views_) {
+    if (mini_view.get() == sender) {
+      controller->ActivateDesk(mini_view->desk());
+      Shell::Get()->overview_controller()->OnSelectionEnded();
+      return;
+    }
+  }
 }
 
 void DesksBarView::OnDeskAdded(const Desk* desk) {
@@ -186,6 +196,15 @@
                                      begin_x - GetFirstMiniViewXOffset());
 }
 
+void DesksBarView::OnDeskActivationChanged(const Desk* activated,
+                                           const Desk* deactivated) {
+  for (auto& mini_view : mini_views_) {
+    const Desk* desk = mini_view->desk();
+    if (desk == activated || desk == deactivated)
+      mini_view->UpdateActivationState();
+  }
+}
+
 void DesksBarView::UpdateNewDeskButtonState() {
   new_desk_button_->SetEnabled(DesksController::Get()->CanCreateDesks());
 }
diff --git a/ash/wm/desks/desks_bar_view.h b/ash/wm/desks/desks_bar_view.h
index 7866b7d..57fab14 100644
--- a/ash/wm/desks/desks_bar_view.h
+++ b/ash/wm/desks/desks_bar_view.h
@@ -61,6 +61,8 @@
   // DesksController::Observer:
   void OnDeskAdded(const Desk* desk) override;
   void OnDeskRemoved(const Desk* desk) override;
+  void OnDeskActivationChanged(const Desk* activated,
+                               const Desk* deactivated) override;
 
  private:
   // This is called on initialization or when a new desk is created to create
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index 5c731d9e..84bec2d 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -10,10 +10,50 @@
 #include "ash/shell.h"
 #include "ash/wm/desks/desk.h"
 #include "ash/wm/desks/desks_util.h"
+#include "ash/wm/mru_window_tracker.h"
+#include "ash/wm/overview/overview_controller.h"
+#include "ash/wm/window_util.h"
+#include "base/auto_reset.h"
 #include "base/logging.h"
 
 namespace ash {
 
+namespace {
+
+// Appends the given |windows| to the end of the currently active overview mode
+// session such that the most-recently used window is added first. If
+// |should_animate| is true, the windows will animate to their positions in the
+// overview grid.
+void AppendWindowsToOverview(const base::flat_set<aura::Window*>& windows,
+                             bool should_animate) {
+  DCHECK(Shell::Get()->overview_controller()->IsSelecting());
+
+  auto* overview_session =
+      Shell::Get()->overview_controller()->overview_session();
+  for (auto* window :
+       Shell::Get()->mru_window_tracker()->BuildMruWindowList()) {
+    if (!windows.contains(window) || wm::ShouldExcludeForOverview(window))
+      continue;
+
+    overview_session->AppendItem(window, /*reposition=*/true, should_animate);
+  }
+}
+
+// Removes the given |windows| from the currently active overview mode session.
+void RemoveWindowsFromOverview(const base::flat_set<aura::Window*>& windows) {
+  DCHECK(Shell::Get()->overview_controller()->IsSelecting());
+
+  auto* overview_session =
+      Shell::Get()->overview_controller()->overview_session();
+  for (auto* window : windows) {
+    auto* item = overview_session->GetOverviewItemForWindow(window);
+    if (item)
+      overview_session->RemoveItem(item);
+  }
+}
+
+}  // namespace
+
 DesksController::DesksController() {
   for (int id : desks_util::GetDesksContainersIds())
     available_container_ids_.push(id);
@@ -21,7 +61,7 @@
   // There's always one default desk.
   NewDesk();
   active_desk_ = desks_.back().get();
-  active_desk_->Activate();
+  active_desk_->Activate(/*update_window_activation=*/true);
 }
 
 DesksController::~DesksController() = default;
@@ -52,6 +92,8 @@
   DCHECK(CanCreateDesks());
   DCHECK(!available_container_ids_.empty());
 
+  base::AutoReset<bool> in_progress(&are_desks_being_modified_, true);
+
   desks_.emplace_back(std::make_unique<Desk>(available_container_ids_.front()));
   available_container_ids_.pop();
 
@@ -62,6 +104,8 @@
 void DesksController::RemoveDesk(const Desk* desk) {
   DCHECK(CanRemoveDesks());
 
+  base::AutoReset<bool> in_progress(&are_desks_being_modified_, true);
+
   auto iter = std::find_if(
       desks_.begin(), desks_.end(),
       [desk](const std::unique_ptr<Desk>& d) { return d.get() == desk; });
@@ -74,11 +118,22 @@
 
   DCHECK(!desks_.empty());
 
+  const bool is_selecting = Shell::Get()->overview_controller()->IsSelecting();
+  const base::flat_set<aura::Window*> removed_desk_windows =
+      removed_desk->windows();
+
   // - Move windows in removed desk (if any) to the currently active desk.
   // - If the active desk is the one being removed, activate the desk to its
   //   left, if no desk to the left, activate one on the right.
   if (removed_desk.get() != active_desk_) {
     removed_desk->MoveWindowsToDesk(active_desk_);
+
+    // If overview mode is active, we add the windows of the removed desk to the
+    // overview grid in the order of their MRU. Note that this can only be done
+    // after the windows have moved to the active desk above, so that building
+    // the window MRU list should contain those windows.
+    if (is_selecting)
+      AppendWindowsToOverview(removed_desk_windows, /*should_animate=*/true);
   } else {
     Desk* target_desk = nullptr;
     if (iter_after == desks_.begin()) {
@@ -90,8 +145,26 @@
     }
 
     DCHECK(target_desk);
+
+    // The removed desk is the active desk, so temporarily remove its windows
+    // from the overview grid which will result in removing the
+    // "OverviewModeLabel" widgets created by overview mode for these windows.
+    // This way the removed desk tracks only real windows, which are now ready
+    // to be moved to the target desk.
+    if (is_selecting)
+      RemoveWindowsFromOverview(removed_desk_windows);
+
+    // If overview mode is active, change desk activation without changing
+    // window activation. Activation should remain on the dummy
+    // "OverviewModeFocusedWidget" while overview mode is active.
     removed_desk->MoveWindowsToDesk(target_desk);
-    ActivateDesk(target_desk);
+    ActivateDeskInternal(target_desk,
+                         /*update_window_activation=*/!is_selecting);
+
+    // Now that the windows from the removed and target desks merged, add them
+    // all without animation to the grid in the order of their MRU.
+    if (is_selecting)
+      AppendWindowsToOverview(target_desk->windows(), /*should_animate=*/false);
   }
 
   for (auto& observer : observers_)
@@ -103,23 +176,7 @@
 }
 
 void DesksController::ActivateDesk(const Desk* desk) {
-  DCHECK(HasDesk(desk));
-
-  if (desk == active_desk_)
-    return;
-
-  // Mark the new desk as active first, so that deactivating windows on the
-  // `old_active` desk do not activate other windows on the same desk. See
-  // `ash::IsWindowConsideredVisibleForActivation()`.
-  Desk* old_active = active_desk_;
-  active_desk_ = const_cast<Desk*>(desk);
-
-  // There should always be an active desk at any time.
-  DCHECK(old_active);
-  old_active->Deactivate();
-  active_desk_->Activate();
-
-  // TODO(afakhry): Do desk activation animation.
+  ActivateDeskInternal(desk, /*update_window_activation=*/true);
 }
 
 void DesksController::OnRootWindowAdded(aura::Window* root_window) {
@@ -139,4 +196,30 @@
   return iter != desks_.end();
 }
 
+void DesksController::ActivateDeskInternal(const Desk* desk,
+                                           bool update_window_activation) {
+  DCHECK(HasDesk(desk));
+
+  if (desk == active_desk_)
+    return;
+
+  base::AutoReset<bool> in_progress(&are_desks_being_modified_, true);
+
+  // Mark the new desk as active first, so that deactivating windows on the
+  // `old_active` desk do not activate other windows on the same desk. See
+  // `ash::IsWindowConsideredVisibleForActivation()`.
+  Desk* old_active = active_desk_;
+  active_desk_ = const_cast<Desk*>(desk);
+
+  // There should always be an active desk at any time.
+  DCHECK(old_active);
+  old_active->Deactivate(update_window_activation);
+  active_desk_->Activate(update_window_activation);
+
+  for (auto& observer : observers_)
+    observer.OnDeskActivationChanged(active_desk_, old_active);
+
+  // TODO(afakhry): Do desk activation animation.
+}
+
 }  // namespace ash
diff --git a/ash/wm/desks/desks_controller.h b/ash/wm/desks/desks_controller.h
index 11efe97..9fa3b8d 100644
--- a/ash/wm/desks/desks_controller.h
+++ b/ash/wm/desks/desks_controller.h
@@ -36,6 +36,11 @@
     // observers have been notified with this.
     virtual void OnDeskRemoved(const Desk* desk) = 0;
 
+    // Called when the |activated| desk gains activation from the |deactivated|
+    // desk.
+    virtual void OnDeskActivationChanged(const Desk* activated,
+                                         const Desk* deactivated) = 0;
+
    protected:
     virtual ~Observer() = default;
   };
@@ -51,6 +56,8 @@
 
   const Desk* active_desk() const { return active_desk_; }
 
+  bool are_desks_being_modified() const { return are_desks_being_modified_; }
+
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
@@ -69,7 +76,9 @@
   void RemoveDesk(const Desk* desk);
 
   // Activates the given |desk| and deactivates the currently active one. |desk|
-  // has to be an existing desk.
+  // has to be an existing desk. The active window on the currently active desk
+  // will be deactivated, and the most-recently used window from the
+  // newly-activated desk will be activated.
   void ActivateDesk(const Desk* desk);
 
   // Called explicitly by the RootWindowController when a root window has been
@@ -80,10 +89,26 @@
  private:
   bool HasDesk(const Desk* desk) const;
 
+  // Activates the given |desk| and deactivates the currently active one. |desk|
+  // has to be an existing desk. If |update_window_activation| is true,
+  // the active desk on the deactivated desk will be deactivated, and the most-
+  // recently used window on the newly-activated desk will be deactivated. This
+  // parameter is almost always true except when the active desk is being
+  // removed while in overview mode. In that case, windows from the active desk
+  // will move to another desk and remain in the overview grid, and no
+  // activation or deactivation should be done in order to keep overview mode
+  // active.
+  void ActivateDeskInternal(const Desk* desk, bool update_window_activation);
+
   std::vector<std::unique_ptr<Desk>> desks_;
 
   Desk* active_desk_ = nullptr;
 
+  // True when desks addition, removal, or activation change are in progress.
+  // This can be checked when overview mode is active to avoid exiting overview
+  // mode as a result of desks modifications.
+  bool are_desks_being_modified_ = false;
+
   // A free list of desk container IDs to be used for newly-created desks. New
   // desks pops from this queue and removed desks's associated container IDs are
   // re-pushed on this queue.
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 2afaeb63..c7ea3a4c 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -31,6 +31,30 @@
   return DesksController::Get()->active_desk()->windows().contains(window);
 }
 
+OverviewGrid* GetOverviewGridForRoot(aura::Window* root) {
+  DCHECK(root->IsRootWindow());
+
+  auto* overview_controller = Shell::Get()->overview_controller();
+  DCHECK(overview_controller->IsSelecting());
+
+  return overview_controller->overview_session()->GetGridWithRootWindow(root);
+}
+
+void CloseDeskFromMiniView(const DeskMiniView* desk_mini_view,
+                           ui::test::EventGenerator* event_generator) {
+  DCHECK(desk_mini_view);
+
+  // Move to the center of the mini view so that the close button shows up.
+  const gfx::Point mini_view_center =
+      desk_mini_view->GetBoundsInScreen().CenterPoint();
+  event_generator->MoveMouseTo(mini_view_center);
+  EXPECT_TRUE(desk_mini_view->close_desk_button()->visible());
+  // Move to the center of the close button and click.
+  event_generator->MoveMouseTo(
+      desk_mini_view->close_desk_button()->GetBoundsInScreen().CenterPoint());
+  event_generator->ClickLeftButton();
+}
+
 // Defines an observer to test DesksController notifications.
 class TestObserver : public DesksController::Observer {
  public:
@@ -40,8 +64,18 @@
   const std::vector<const Desk*>& desks() const { return desks_; }
 
   // DesksController::Observer:
-  void OnDeskAdded(const Desk* desk) override { desks_.emplace_back(desk); }
-  void OnDeskRemoved(const Desk* desk) override { base::Erase(desks_, desk); }
+  void OnDeskAdded(const Desk* desk) override {
+    desks_.emplace_back(desk);
+    EXPECT_TRUE(DesksController::Get()->are_desks_being_modified());
+  }
+  void OnDeskRemoved(const Desk* desk) override {
+    base::Erase(desks_, desk);
+    EXPECT_TRUE(DesksController::Get()->are_desks_being_modified());
+  }
+  void OnDeskActivationChanged(const Desk* activated,
+                               const Desk* deactivated) override {
+    EXPECT_TRUE(DesksController::Get()->are_desks_being_modified());
+  }
 
  private:
   std::vector<const Desk*> desks_;
@@ -109,8 +143,7 @@
   EXPECT_TRUE(overview_controller->IsSelecting());
 
   const auto* overview_grid =
-      overview_controller->overview_session()->GetGridWithRootWindow(
-          Shell::GetPrimaryRootWindow());
+      GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
 
   // Initially the grid is not offset down when there are no desk mini_views
   // once animations are added.
@@ -201,7 +234,9 @@
   const Desk* desk_2 = controller->desks()[1].get();
   const Desk* desk_3 = controller->desks()[2].get();
   const Desk* desk_4 = controller->desks()[3].get();
+  EXPECT_FALSE(controller->are_desks_being_modified());
   controller->ActivateDesk(desk_2);
+  EXPECT_FALSE(controller->are_desks_being_modified());
   EXPECT_EQ(desk_2, controller->active_desk());
   EXPECT_FALSE(desk_1->is_active());
   EXPECT_TRUE(desk_2->is_active());
@@ -214,7 +249,9 @@
 
   // Remove the active desk, which is in the middle, activation should move to
   // the left, so desk 1 should be activated.
+  EXPECT_FALSE(controller->are_desks_being_modified());
   controller->RemoveDesk(desk_2);
+  EXPECT_FALSE(controller->are_desks_being_modified());
   ASSERT_EQ(3u, controller->desks().size());
   EXPECT_EQ(desk_1, controller->active_desk());
   EXPECT_TRUE(desk_1->is_active());
@@ -226,7 +263,9 @@
 
   // Remove the active desk, it's the first one on the left, so desk_3 (on the
   // right) will be activated.
+  EXPECT_FALSE(controller->are_desks_being_modified());
   controller->RemoveDesk(desk_1);
+  EXPECT_FALSE(controller->are_desks_being_modified());
   ASSERT_EQ(2u, controller->desks().size());
   EXPECT_EQ(desk_3, controller->active_desk());
   EXPECT_TRUE(desk_3->is_active());
@@ -363,6 +402,186 @@
   EXPECT_TRUE(wm::CanActivateWindow(win4.get()));
 }
 
+TEST_F(DesksTest, ActivateDeskFromOverview) {
+  auto* controller = DesksController::Get();
+
+  // Create three desks other than the default initial desk.
+  controller->NewDesk();
+  controller->NewDesk();
+  controller->NewDesk();
+  ASSERT_EQ(4u, controller->desks().size());
+
+  // Create two windows on desk_1.
+  auto win0 = CreateTestWindow(gfx::Rect(0, 0, 250, 100));
+  auto win1 = CreateTestWindow(gfx::Rect(50, 50, 200, 200));
+  wm::ActivateWindow(win1.get());
+  EXPECT_EQ(win1.get(), wm::GetActiveWindow());
+
+  // Enter overview mode, and expect the desk bar is shown with exactly four
+  // desks mini views, and there are exactly two windows in the overview mode
+  // grid.
+  auto* overview_controller = Shell::Get()->overview_controller();
+  overview_controller->ToggleOverview();
+  EXPECT_TRUE(overview_controller->IsSelecting());
+  const auto* overview_grid =
+      GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
+  const auto* desks_bar_view = overview_grid->GetDesksBarViewForTesting();
+  ASSERT_TRUE(desks_bar_view);
+  ASSERT_EQ(4u, desks_bar_view->mini_views().size());
+  EXPECT_EQ(2u, overview_grid->window_list().size());
+
+  // Activate desk_4 (last one on the right) by clicking on its mini view.
+  const Desk* desk_4 = controller->desks()[3].get();
+  EXPECT_FALSE(desk_4->is_active());
+  const auto* mini_view = desks_bar_view->mini_views().back().get();
+  EXPECT_EQ(desk_4, mini_view->desk());
+  EXPECT_FALSE(mini_view->close_desk_button()->visible());
+  const gfx::Point mini_view_center =
+      mini_view->GetBoundsInScreen().CenterPoint();
+  auto* event_generator = GetEventGenerator();
+  event_generator->MoveMouseTo(mini_view_center);
+  event_generator->ClickLeftButton();
+
+  // Expect that desk_4 is now active, and overview mode exited.
+  EXPECT_TRUE(desk_4->is_active());
+  EXPECT_FALSE(overview_controller->IsSelecting());
+  // Exiting overview mode should not restore focus to a window on a
+  // now-inactive desk. Run a loop since the overview session is destroyed async
+  // and until that happens, focus will be on the dummy
+  // "OverviewModeFocusedWidget".
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(nullptr, wm::GetActiveWindow());
+
+  // Create one window in desk_4 and enter overview mode. Expect the grid is
+  // showing exactly one window.
+  auto win2 = CreateTestWindow(gfx::Rect(50, 50, 200, 200));
+  wm::ActivateWindow(win2.get());
+  overview_controller->ToggleOverview();
+  EXPECT_TRUE(overview_controller->IsSelecting());
+  overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
+  EXPECT_EQ(1u, overview_grid->window_list().size());
+
+  // When exiting overview mode without changing desks, the focus should be
+  // restored to the same window.
+  overview_controller->ToggleOverview();
+  EXPECT_FALSE(overview_controller->IsSelecting());
+  // Run a loop since the overview session is destroyed async and until that
+  // happens, focus will be on the dummy "OverviewModeFocusedWidget".
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(win2.get(), wm::GetActiveWindow());
+}
+
+TEST_F(DesksTest, RemoveInactiveDeskFromOverview) {
+  auto* controller = DesksController::Get();
+
+  // Create three desks other than the default initial desk.
+  controller->NewDesk();
+  controller->NewDesk();
+  controller->NewDesk();
+  ASSERT_EQ(4u, controller->desks().size());
+
+  // Create two windows on desk_1.
+  auto win0 = CreateTestWindow(gfx::Rect(0, 0, 250, 100));
+  auto win1 = CreateTestWindow(gfx::Rect(50, 50, 200, 200));
+  wm::ActivateWindow(win0.get());
+  EXPECT_EQ(win0.get(), wm::GetActiveWindow());
+
+  // Active desk_4 and enter overview mode. Expect that the grid is currently
+  // empty.
+  const Desk* desk_4 = controller->desks()[3].get();
+  controller->ActivateDesk(desk_4);
+  auto* overview_controller = Shell::Get()->overview_controller();
+  overview_controller->ToggleOverview();
+  EXPECT_TRUE(overview_controller->IsSelecting());
+  const auto* overview_grid =
+      GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
+  EXPECT_TRUE(overview_grid->window_list().empty());
+
+  // Remove desk_1 using the close button on its mini view. desk_1 is currently
+  // inactive. Its windows should be moved to desk_4 and added to the overview
+  // grid in the MRU order (win0, and win1).
+  const auto* desks_bar_view = overview_grid->GetDesksBarViewForTesting();
+  ASSERT_TRUE(desks_bar_view);
+  ASSERT_EQ(4u, desks_bar_view->mini_views().size());
+  const Desk* desk_1 = controller->desks()[0].get();
+  const auto* mini_view = desks_bar_view->mini_views().front().get();
+  EXPECT_EQ(desk_1, mini_view->desk());
+  CloseDeskFromMiniView(mini_view, GetEventGenerator());
+
+  ASSERT_EQ(3u, desks_bar_view->mini_views().size());
+  EXPECT_TRUE(overview_controller->IsSelecting());
+  ASSERT_EQ(2u, overview_grid->window_list().size());
+  EXPECT_TRUE(overview_grid->GetOverviewItemContaining(win0.get()));
+  EXPECT_TRUE(overview_grid->GetOverviewItemContaining(win1.get()));
+  EXPECT_EQ(overview_grid->GetOverviewItemContaining(win0.get()),
+            overview_grid->window_list()[0].get());
+  EXPECT_EQ(overview_grid->GetOverviewItemContaining(win1.get()),
+            overview_grid->window_list()[1].get());
+
+  // Make sure overview mode remains active.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(overview_controller->IsSelecting());
+}
+
+TEST_F(DesksTest, RemoveActiveDeskFromOverview) {
+  auto* controller = DesksController::Get();
+
+  // Create one desk other than the default initial desk.
+  controller->NewDesk();
+  ASSERT_EQ(2u, controller->desks().size());
+
+  // Create two windows on desk_1.
+  auto win0 = CreateTestWindow(gfx::Rect(0, 0, 250, 100));
+  auto win1 = CreateTestWindow(gfx::Rect(50, 50, 200, 200));
+  wm::ActivateWindow(win0.get());
+  EXPECT_EQ(win0.get(), wm::GetActiveWindow());
+
+  // Activate desk_2 and create one more window.
+  const Desk* desk_2 = controller->desks()[1].get();
+  controller->ActivateDesk(desk_2);
+  auto win2 = CreateTestWindow(gfx::Rect(50, 50, 200, 200));
+  wm::ActivateWindow(win2.get());
+  EXPECT_EQ(win2.get(), wm::GetActiveWindow());
+
+  // Enter overview mode, and remove desk_2 from its mini-view close button.
+  auto* overview_controller = Shell::Get()->overview_controller();
+  overview_controller->ToggleOverview();
+  EXPECT_TRUE(overview_controller->IsSelecting());
+  const auto* overview_grid =
+      GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
+  EXPECT_EQ(1u, overview_grid->window_list().size());
+  const auto* desks_bar_view = overview_grid->GetDesksBarViewForTesting();
+  ASSERT_TRUE(desks_bar_view);
+  ASSERT_EQ(2u, desks_bar_view->mini_views().size());
+  const auto* mini_view = desks_bar_view->mini_views().back().get();
+  EXPECT_EQ(desk_2, mini_view->desk());
+  CloseDeskFromMiniView(mini_view, GetEventGenerator());
+
+  // desk_1 will become active, and windows from desk_2 and desk_1 will merge
+  // and added in the overview grid in the order of MRU.
+  ASSERT_EQ(1u, controller->desks().size());
+  ASSERT_EQ(1u, desks_bar_view->mini_views().size());
+  const Desk* desk_1 = controller->desks()[0].get();
+  EXPECT_TRUE(desk_1->is_active());
+  EXPECT_TRUE(overview_controller->IsSelecting());
+  EXPECT_EQ(3u, overview_grid->window_list().size());
+  EXPECT_TRUE(overview_grid->GetOverviewItemContaining(win0.get()));
+  EXPECT_TRUE(overview_grid->GetOverviewItemContaining(win1.get()));
+  EXPECT_TRUE(overview_grid->GetOverviewItemContaining(win2.get()));
+
+  // The MRU order is {win2, win0, win1}.
+  EXPECT_EQ(overview_grid->GetOverviewItemContaining(win2.get()),
+            overview_grid->window_list()[0].get());
+  EXPECT_EQ(overview_grid->GetOverviewItemContaining(win0.get()),
+            overview_grid->window_list()[1].get());
+  EXPECT_EQ(overview_grid->GetOverviewItemContaining(win1.get()),
+            overview_grid->window_list()[2].get());
+
+  // Make sure overview mode remains active.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(overview_controller->IsSelecting());
+}
+
 // TODO(afakhry): Add more tests:
 // - Multi displays.
 // - Always on top windows are not tracked by any desk.
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index a2cfc5f2..dc8d593 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -590,6 +590,13 @@
     PositionWindows(animate, ignored_items);
 }
 
+void OverviewGrid::AppendItem(aura::Window* window,
+                              bool reposition,
+                              bool animate) {
+  AddItem(window, reposition, animate, /*ignored_items=*/{},
+          window_list_.size());
+}
+
 void OverviewGrid::RemoveItem(OverviewItem* overview_item) {
   auto* window = overview_item->GetWindow();
   // Use reverse iterator to be efficiently when removing all.
diff --git a/ash/wm/overview/overview_grid.h b/ash/wm/overview/overview_grid.h
index aba2e45..e2957a3e 100644
--- a/ash/wm/overview/overview_grid.h
+++ b/ash/wm/overview/overview_grid.h
@@ -107,6 +107,9 @@
                const base::flat_set<OverviewItem*>& ignored_items,
                size_t index);
 
+  // Similar to the above function, but adds the window to the end of the grid.
+  void AppendItem(aura::Window* window, bool reposition, bool animate);
+
   // Removes |overview_item| from the grid. |overview_item| cannot already be
   // absent from the grid. No items are repositioned.
   //
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 69b753d0..2a98604a 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -11,12 +11,15 @@
 #include "ash/accessibility/accessibility_controller.h"
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/metrics/user_metrics_recorder.h"
+#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/screen_util.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/wm/desks/desk.h"
+#include "ash/wm/desks/desks_controller.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_delegate.h"
@@ -465,6 +468,23 @@
   ::wm::ActivateWindow(GetOverviewFocusWindow());
 }
 
+void OverviewSession::AppendItem(aura::Window* window,
+                                 bool reposition,
+                                 bool animate) {
+  // Early exit if a grid already contains |window|.
+  OverviewGrid* grid = GetGridWithRootWindow(window->GetRootWindow());
+  if (!grid || grid->GetOverviewItemContaining(window))
+    return;
+
+  grid->AppendItem(window, reposition, animate);
+  ++num_items_;
+
+  // Transfer focus from |window| to |overview_focus_widget_| to match the
+  // behavior of entering overview mode in the beginning.
+  DCHECK(overview_focus_widget_);
+  ::wm::ActivateWindow(GetOverviewFocusWindow());
+}
+
 void OverviewSession::RemoveItem(OverviewItem* overview_item) {
   if (overview_item->GetWindow()->HasObserver(this)) {
     overview_item->GetWindow()->RemoveObserver(this);
@@ -602,7 +622,7 @@
     if (item)
       return item;
   }
-  NOTREACHED();
+
   return nullptr;
 }
 
@@ -669,6 +689,15 @@
   if (ignore_activations_ || gained_active == GetOverviewFocusWindow())
     return;
 
+  if (features::IsVirtualDesksEnabled() &&
+      DesksController::Get()->are_desks_being_modified()) {
+    // Activating a desk from its mini view will activate its most-recently used
+    // window, but this should not result in ending overview mode now.
+    // DesksBarView will end it explicitly. This will become significant when
+    // the desk activation animation is added.
+    return;
+  }
+
   if (!gained_active) {
     // Cancel overview session and do not restore focus when active window is
     // set to nullptr. This happens when removing a display.
@@ -760,6 +789,14 @@
     return;
   }
 
+  // Removing a desk while in overview mode results in reparenting the windows
+  // of that desk to the associated container of another desk. This is a window
+  // hierarchy change that shouldn't result in exiting overview mode.
+  if (features::IsVirtualDesksEnabled() &&
+      DesksController::Get()->are_desks_being_modified()) {
+    return;
+  }
+
   aura::Window* new_window = params.target;
   wm::WindowState* state = wm::GetWindowState(new_window);
   if (!state->IsUserPositionable() || state->IsPip())
@@ -930,6 +967,12 @@
   if (!restore_focus_window_)
     return;
 
+  if (features::IsVirtualDesksEnabled()) {
+    // Do not restore focus to a window that exists on an inactive desk.
+    focus &= DesksController::Get()->active_desk()->windows().contains(
+        restore_focus_window_);
+  }
+
   // Ensure the window is still in the window hierarchy and not in the middle
   // of teardown.
   if (focus && restore_focus_window_->GetRootWindow()) {
diff --git a/ash/wm/overview/overview_session.h b/ash/wm/overview/overview_session.h
index 3f0b7685..a96327ca 100644
--- a/ash/wm/overview/overview_session.h
+++ b/ash/wm/overview/overview_session.h
@@ -148,7 +148,10 @@
                bool reposition,
                bool animate,
                const base::flat_set<OverviewItem*>& ignored_items = {},
-               size_t index = 0u);
+               size_t index = 0);
+
+  // Similar to the above function, but adds the window at the end of the grid.
+  void AppendItem(aura::Window* window, bool reposition, bool animate);
 
   // Removes |overview_item| from the corresponding grid. No items are
   // repositioned.
@@ -191,7 +194,8 @@
   // Returns true if |window| is currently showing in overview.
   bool IsWindowInOverview(const aura::Window* window);
 
-  // Returns the overview item for |window|.
+  // Returns the overview item for |window|, or nullptr if |window| doesn't have
+  // a corresponding item in overview mode.
   OverviewItem* GetOverviewItemForWindow(const aura::Window* window);
 
   // Set the window grid that's displaying in |root_window| not animate when
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.h b/ash/wm/tablet_mode/tablet_mode_controller.h
index 506ff2ae..ac41bc8 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.h
+++ b/ash/wm/tablet_mode/tablet_mode_controller.h
@@ -103,7 +103,7 @@
 
   // Add a special window to the TabletModeWindowManager for tracking. This is
   // only required for special windows which are handled by other window
-  // managers like the |MultiUserWindowManager|.
+  // managers like the |MultiUserWindowManagerImpl|.
   // If the tablet mode is not enabled no action will be performed.
   void AddWindow(aura::Window* window);
 
diff --git a/ash/ws/multi_user_window_manager_bridge.cc b/ash/ws/multi_user_window_manager_bridge.cc
index 42c13fa..d0f49e1 100644
--- a/ash/ws/multi_user_window_manager_bridge.cc
+++ b/ash/ws/multi_user_window_manager_bridge.cc
@@ -4,7 +4,7 @@
 
 #include "ash/ws/multi_user_window_manager_bridge.h"
 
-#include "ash/multi_user/multi_user_window_manager.h"
+#include "ash/multi_user/multi_user_window_manager_impl.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "services/ws/window_tree.h"
@@ -23,8 +23,8 @@
 
 MultiUserWindowManagerBridge::~MultiUserWindowManagerBridge() {
   // We may get here after MultiUserWindowManager has been destroyed.
-  if (ash::MultiUserWindowManager::Get())
-    ash::MultiUserWindowManager::Get()->SetClient(nullptr);
+  if (ash::MultiUserWindowManagerImpl::Get())
+    ash::MultiUserWindowManagerImpl::Get()->SetClient(nullptr);
 }
 
 void MultiUserWindowManagerBridge::SetClient(
@@ -36,11 +36,12 @@
     // from being created (because multiple clients ask for
     // ash::mojom::MultiUserWindowManager). This code is assuming only a single
     // client is used at a time.
-    multi_user_window_manager_ = std::make_unique<ash::MultiUserWindowManager>(
-        client_.get(), nullptr,
-        Shell::Get()->session_controller()->GetActiveAccountId());
-  } else if (ash::MultiUserWindowManager::Get()) {
-    ash::MultiUserWindowManager::Get()->SetClient(client_.get());
+    multi_user_window_manager_ =
+        std::make_unique<ash::MultiUserWindowManagerImpl>(
+            client_.get(), nullptr,
+            Shell::Get()->session_controller()->GetActiveAccountId());
+  } else if (ash::MultiUserWindowManagerImpl::Get()) {
+    ash::MultiUserWindowManagerImpl::Get()->SetClient(client_.get());
   }
 }
 
@@ -51,12 +52,12 @@
   // here with no ash::MultiUserWindowManager. This should only be possible in
   // tests. None-the-less this needs to be fixed for the multi-process case.
   // http://crbug.com/875111.
-  if (!ash::MultiUserWindowManager::Get())
+  if (!ash::MultiUserWindowManagerImpl::Get())
     return;
 
   aura::Window* window = window_tree_->GetWindowByTransportId(window_id);
   if (window && window_tree_->IsTopLevel(window)) {
-    ash::MultiUserWindowManager::Get()->SetWindowOwner(
+    ash::MultiUserWindowManagerImpl::Get()->SetWindowOwner(
         window, account_id, show_for_current_user, {window_id});
   } else {
     DVLOG(1) << "SetWindowOwner passed invalid window, id=" << window_id;
@@ -70,14 +71,16 @@
   // here with no ash::MultiUserWindowManager. This should only be possible in
   // tests. None-the-less this needs to be fixed for the multi-process case.
   // http://crbug.com/875111.
-  if (!ash::MultiUserWindowManager::Get())
+  if (!ash::MultiUserWindowManagerImpl::Get())
     return;
 
   aura::Window* window = window_tree_->GetWindowByTransportId(window_id);
-  if (window && window_tree_->IsTopLevel(window))
-    ash::MultiUserWindowManager::Get()->ShowWindowForUser(window, account_id);
-  else
+  if (window && window_tree_->IsTopLevel(window)) {
+    ash::MultiUserWindowManagerImpl::Get()->ShowWindowForUser(window,
+                                                              account_id);
+  } else {
     DVLOG(1) << "ShowWindowForUser passed invalid window, id=" << window_id;
+  }
 }
 
 }  // namespace ash
diff --git a/ash/ws/multi_user_window_manager_bridge.h b/ash/ws/multi_user_window_manager_bridge.h
index 1766966..d3d35d0f6 100644
--- a/ash/ws/multi_user_window_manager_bridge.h
+++ b/ash/ws/multi_user_window_manager_bridge.h
@@ -20,7 +20,7 @@
 
 namespace ash {
 
-class MultiUserWindowManager;
+class MultiUserWindowManagerImpl;
 
 // Responsible for forwarding calls to MultiUserWindowManager. In multi-process
 // mash mode *this* owns the MultiUserWindowManager. In all other cases chrome
@@ -46,7 +46,7 @@
   mojo::AssociatedBinding<mojom::MultiUserWindowManager> binding_;
   mojom::MultiUserWindowManagerClientAssociatedPtr client_;
   // Only valid in MultiUserWindowManager.
-  std::unique_ptr<ash::MultiUserWindowManager> multi_user_window_manager_;
+  std::unique_ptr<ash::MultiUserWindowManagerImpl> multi_user_window_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(MultiUserWindowManagerBridge);
 };
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 9b8ab65..8e9076ba 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -991,6 +991,8 @@
     "trace_event/memory_usage_estimator.h",
     "trace_event/process_memory_dump.cc",
     "trace_event/process_memory_dump.h",
+    "trace_event/thread_instruction_count.cc",
+    "trace_event/thread_instruction_count.h",
     "trace_event/trace_arguments.cc",
     "trace_event/trace_arguments.h",
     "trace_event/trace_buffer.cc",
diff --git a/base/base_switches.cc b/base/base_switches.cc
index 1567a3ea..e3fef39 100644
--- a/base/base_switches.cc
+++ b/base/base_switches.cc
@@ -139,4 +139,12 @@
 const char kOrderfileMemoryOptimization[] = "orderfile-memory-optimization";
 #endif
 
+#if defined(OS_LINUX)
+// Controls whether or not retired instruction counts are surfaced for threads
+// in trace events on Linux.
+//
+// This flag requires the BPF sandbox to be disabled.
+const char kEnableThreadInstructionCount[] = "enable-thread-instruction-count";
+#endif
+
 }  // namespace switches
diff --git a/base/base_switches.h b/base/base_switches.h
index 49650dc..62397a9c 100644
--- a/base/base_switches.h
+++ b/base/base_switches.h
@@ -49,6 +49,10 @@
 extern const char kOrderfileMemoryOptimization[];
 #endif
 
+#if defined(OS_LINUX)
+extern const char kEnableThreadInstructionCount[];
+#endif
+
 }  // namespace switches
 
 #endif  // BASE_BASE_SWITCHES_H_
diff --git a/base/trace_event/thread_instruction_count.cc b/base/trace_event/thread_instruction_count.cc
new file mode 100644
index 0000000..2b04b99
--- /dev/null
+++ b/base/trace_event/thread_instruction_count.cc
@@ -0,0 +1,115 @@
+// 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/trace_event/thread_instruction_count.h"
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/threading/thread_local_storage.h"
+#include "build/build_config.h"
+
+#if defined(OS_LINUX)
+#include <linux/perf_event.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#endif  // defined(OS_LINUX)
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+#if defined(OS_LINUX)
+
+// Special constants used for counter FD states.
+constexpr int kPerfFdDisabled = -2;
+constexpr int kPerfFdOpenFailed = -1;
+constexpr int kPerfFdUninitialized = 0;
+
+ThreadLocalStorage::Slot& InstructionCounterFdSlot() {
+  static NoDestructor<ThreadLocalStorage::Slot> fd_slot([](void* fd_ptr) {
+    int fd = reinterpret_cast<intptr_t>(fd_ptr);
+    if (fd > kPerfFdUninitialized)
+      close(fd);
+  });
+  return *fd_slot;
+}
+
+// Opens a new file descriptor that emits the value of
+// PERF_COUNT_HW_INSTRUCTIONS in userspace (excluding kernel and hypervisor
+// instructions) for the given |thread_id|, or 0 for the calling thread.
+//
+// Returns kPerfFdOpenFailed if opening the file descriptor failed, or
+// kPerfFdDisabled if performance counters are disabled in the calling process.
+int OpenInstructionCounterFdForThread(int thread_id) {
+  // This switch is only propagated for processes that are unaffected by the
+  // BPF sandbox, such as the browser process or renderers with --no-sandbox.
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+  if (!command_line.HasSwitch(switches::kEnableThreadInstructionCount))
+    return kPerfFdDisabled;
+
+  struct perf_event_attr pe = {0};
+  pe.type = PERF_TYPE_HARDWARE;
+  pe.size = sizeof(struct perf_event_attr);
+  pe.config = PERF_COUNT_HW_INSTRUCTIONS;
+  pe.exclude_kernel = 1;
+  pe.exclude_hv = 1;
+
+  int fd = syscall(__NR_perf_event_open, &pe, thread_id, /* cpu */ -1,
+                   /* group_fd */ -1, /* flags */ 0);
+  if (fd < 0) {
+    LOG(ERROR) << "perf_event_open failed, omitting instruction counters";
+    return kPerfFdOpenFailed;
+  }
+  return fd;
+}
+
+// Retrieves the active perf counter FD for the current thread, performing
+// lazy-initialization if necessary.
+int InstructionCounterFdForCurrentThread() {
+  auto& slot = InstructionCounterFdSlot();
+  int fd = reinterpret_cast<intptr_t>(slot.Get());
+  if (fd == kPerfFdUninitialized) {
+    fd = OpenInstructionCounterFdForThread(0);
+    slot.Set(reinterpret_cast<void*>(fd));
+  }
+  return fd;
+}
+
+#endif  // defined(OS_LINUX)
+
+}  // namespace
+
+bool ThreadInstructionCount::IsSupported() {
+#if defined(OS_LINUX)
+  // If we can't initialize the counter FD, mark as disabled.
+  int counter_fd = InstructionCounterFdForCurrentThread();
+  if (counter_fd <= 0)
+    return false;
+
+  return true;
+#endif  // defined(OS_LINUX)
+  return false;
+}
+
+ThreadInstructionCount ThreadInstructionCount::Now() {
+  DCHECK(IsSupported());
+#if defined(OS_LINUX)
+  int fd = InstructionCounterFdForCurrentThread();
+  if (fd <= 0)
+    return ThreadInstructionCount();
+
+  uint64_t instructions = 0;
+  ssize_t bytes_read = read(fd, &instructions, sizeof(instructions));
+  DCHECK_EQ(bytes_read, static_cast<ssize_t>(sizeof(instructions)));
+  return ThreadInstructionCount(instructions);
+#endif  // defined(OS_LINUX)
+  return ThreadInstructionCount();
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/thread_instruction_count.h b/base/trace_event/thread_instruction_count.h
new file mode 100644
index 0000000..fda4d36
--- /dev/null
+++ b/base/trace_event/thread_instruction_count.h
@@ -0,0 +1,57 @@
+// 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 BASE_TRACE_EVENT_THREAD_INSTRUCTION_COUNT_H_
+#define BASE_TRACE_EVENT_THREAD_INSTRUCTION_COUNT_H_
+
+#include <stdint.h>
+
+namespace base {
+namespace trace_event {
+
+// Represents the number of instructions that were retired between two samples
+// of a thread's performance counters.
+class ThreadInstructionDelta {
+ public:
+  constexpr ThreadInstructionDelta() : delta_(0) {}
+  explicit constexpr ThreadInstructionDelta(int64_t delta) : delta_(delta) {}
+
+  constexpr int64_t ToInternalValue() const { return delta_; }
+
+ private:
+  int64_t delta_;
+};
+
+// Uses the system's performance counters in order to measure the number of
+// instructions that have been retired on the current thread.
+class ThreadInstructionCount {
+ public:
+  // Returns true if the platform supports hardware retired instruction
+  // counters.
+  static bool IsSupported();
+
+  // Returns the number of retired instructions relative to some epoch count,
+  // or -1 if getting the current instruction count failed / is disabled.
+  static ThreadInstructionCount Now();
+
+  constexpr ThreadInstructionCount() : value_(-1) {}
+  explicit constexpr ThreadInstructionCount(int64_t value) : value_(value) {}
+
+  constexpr bool is_null() const { return value_ == -1; }
+
+  constexpr ThreadInstructionDelta operator-(
+      ThreadInstructionCount other) const {
+    return ThreadInstructionDelta(value_ - other.value_);
+  }
+
+  constexpr int64_t ToInternalValue() const { return value_; }
+
+ private:
+  int64_t value_;
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_THREAD_INSTRUCTION_COUNT_H_
diff --git a/build/check_gn_headers_whitelist.txt b/build/check_gn_headers_whitelist.txt
index 40a73d0..d6f01d6 100644
--- a/build/check_gn_headers_whitelist.txt
+++ b/build/check_gn_headers_whitelist.txt
@@ -111,7 +111,6 @@
 components/user_manager/user.h
 components/user_manager/user_image/user_image.h
 components/user_manager/user_manager.h
-components/viz/display_compositor/display_provider.h
 components/viz/viz_export.h
 components/wifi/wifi_export.h
 components/wifi/wifi_service.h
diff --git a/build/config/c++/c++.gni b/build/config/c++/c++.gni
index 40177f9..4deaf03 100644
--- a/build/config/c++/c++.gni
+++ b/build/config/c++/c++.gni
@@ -11,7 +11,7 @@
   # Don't check in changes that set this to false for more platforms; doing so
   # is not supported.
   use_custom_libcxx =
-      is_fuchsia || is_android || is_mac || (is_ios && !use_xcode_clang) ||
+      is_fuchsia || is_android || is_mac || is_ios ||
       (is_linux &&
        (!is_chromeos || default_toolchain != "//build/toolchain/cros:target"))
 
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 645bf391..b51701c 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1434,7 +1434,7 @@
     }
   }
 
-  if (enable_gwp_asan) {
+  if (enable_gwp_asan_malloc) {
     public_deps += [ "//components/gwp_asan/client" ]
   }
 
@@ -1478,7 +1478,7 @@
     public_deps += [ "//components/nacl/renderer/plugin:nacl_trusted_plugin" ]
   }
 
-  if (enable_gwp_asan) {
+  if (enable_gwp_asan_malloc) {
     public_deps += [ "//components/gwp_asan/client" ]
   }
 }
@@ -1824,7 +1824,7 @@
       deps += [ "//third_party/gvr-android-sdk:gvr_shim" ]
     }
 
-    if (enable_gwp_asan) {
+    if (enable_gwp_asan_malloc) {
       deps += [ "//components/gwp_asan/client" ]
     }
   }
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 247cf36..8a553a2 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -849,6 +849,8 @@
     "//content/test/data/android/permission_navigation.html",
     "//content/test/data/android/quota_permissions.html",
     "//content/test/data/android/webshare.html",
+    "//content/test/data/android/webshare-apk.html",
+    "//content/test/data/android/webshare-dex.html",
     "//content/test/data/media/bear.webm",
     "//content/test/data/media/getusermedia.html",
     "//content/test/data/media/session/",
diff --git a/chrome/android/java/res/layout/suspended_tab.xml b/chrome/android/java/res/layout/suspended_tab.xml
index 7876aca..ec3a69d 100644
--- a/chrome/android/java/res/layout/suspended_tab.xml
+++ b/chrome/android/java/res/layout/suspended_tab.xml
@@ -44,21 +44,23 @@
             android:layout_gravity="start"
             android:text="@string/usage_stats_site_paused" />
 
-        <TextView
+        <org.chromium.ui.widget.TextViewWithLeading
             android:id="@+id/suspended_tab_explanation"
             android:layout_width="wrap_content"
             android:layout_height="0dp"
             android:layout_weight="1"
             android:paddingBottom="16dp"
             android:textAppearance="@style/TextAppearance.BlackBody"
-            android:layout_gravity="start" />
+            android:layout_gravity="start"
+            app:leading="@dimen/text_size_large_leading" />
 
         <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/suspended_tab_settings_button"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:minHeight="48dp"
             android:layout_gravity="end"
-            android:gravity="center_horizontal"
+            android:gravity="center"
             android:text="@string/preferences"
             style="@style/TextButton" />
     </LinearLayout>
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/WebShareTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/WebShareTest.java
index e805e01..8f6924c8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/WebShareTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/WebShareTest.java
@@ -42,6 +42,8 @@
     public final NativeLibraryTestRule mNativeLibraryTestRule = new NativeLibraryTestRule();
 
     private static final String TEST_FILE = "/content/test/data/android/webshare.html";
+    private static final String TEST_FILE_APK = "/content/test/data/android/webshare-apk.html";
+    private static final String TEST_FILE_DEX = "/content/test/data/android/webshare-dex.html";
 
     private EmbeddedTestServer mTestServer;
 
@@ -154,6 +156,36 @@
     }
 
     /**
+     * Verify WebShare fails if share of .apk is called from a user gesture.
+     * @throws Exception
+     */
+    @Test
+    @MediumTest
+    @Feature({"WebShare"})
+    public void testWebShareApk() throws Exception {
+        mActivityTestRule.loadUrl(mTestServer.getURL(TEST_FILE_APK));
+        // Click (instead of directly calling the JavaScript function) to simulate a user gesture.
+        TouchCommon.singleClickView(mTab.getView());
+        Assert.assertEquals(
+                "Fail: NotAllowedError: Permission denied", mUpdateWaiter.waitForUpdate());
+    }
+
+    /**
+     * Verify WebShare fails if share of .dex is called from a user gesture.
+     * @throws Exception
+     */
+    @Test
+    @MediumTest
+    @Feature({"WebShare"})
+    public void testWebShareDex() throws Exception {
+        mActivityTestRule.loadUrl(mTestServer.getURL(TEST_FILE_DEX));
+        // Click (instead of directly calling the JavaScript function) to simulate a user gesture.
+        TouchCommon.singleClickView(mTab.getView());
+        Assert.assertEquals(
+                "Fail: NotAllowedError: Permission denied", mUpdateWaiter.waitForUpdate());
+    }
+
+    /**
      * Verify WebShare succeeds if share is called from a user gesture, and app chosen.
      * @throws Exception
      */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserControllerInputTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserControllerInputTest.java
index 01a56d0..f0b9bd3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserControllerInputTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserControllerInputTest.java
@@ -11,7 +11,6 @@
 import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM_OR_STANDALONE;
 
 import android.graphics.PointF;
-import android.os.Build;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
 import android.support.v7.widget.RecyclerView;
@@ -208,13 +207,9 @@
             return coord.getScrollXPixInt();
         };
 
-        // Scrolling can be inconsistent on older/slower devices. So, try each direction up to
-        // 3 times to try to work around flakiness. Only enable this on problematic devices (
-        // currently first generation Pixel devices).
-        int numAttempts = 1;
-        if (Build.DEVICE.equals("marlin") || Build.DEVICE.equals("sailfish")) {
-            numAttempts = 3;
-        }
+        // Scrolling can be inconsistent. So, try each direction up to 3 times to try to work around
+        // flakiness.
+        int numAttempts = 3;
         final int diffMultiplier = 2;
 
         int startPoint;
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index 900bc6d6..384429e 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -400,7 +400,7 @@
     deps += [ "//services/ws/public/mojom:constants" ]
   }
 
-  if (enable_gwp_asan) {
+  if (enable_gwp_asan_malloc) {
     deps += [ "//components/gwp_asan/client" ]
   }
 }
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index 4362562..887c8fd 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -166,7 +166,7 @@
 #include "chrome/child/pdf_child_init.h"
 #endif
 
-#if BUILDFLAG(ENABLE_GWP_ASAN)
+#if BUILDFLAG(ENABLE_GWP_ASAN_MALLOC)
 #include "components/gwp_asan/client/gwp_asan.h"  // nogncheck
 #endif
 
@@ -552,7 +552,7 @@
 #endif
 
 void ChromeMainDelegate::PostFieldTrialInitialization() {
-#if BUILDFLAG(ENABLE_GWP_ASAN)
+#if BUILDFLAG(ENABLE_GWP_ASAN_MALLOC)
   version_info::Channel channel = chrome::GetChannel();
   bool is_canary_dev = (channel == version_info::Channel::CANARY ||
                         channel == version_info::Channel::DEV);
diff --git a/chrome/app/onboarding_welcome_strings.grdp b/chrome/app/onboarding_welcome_strings.grdp
index bba51be..491695f 100644
--- a/chrome/app/onboarding_welcome_strings.grdp
+++ b/chrome/app/onboarding_welcome_strings.grdp
@@ -22,9 +22,6 @@
   <message name="IDS_ONBOARDING_WELCOME_BOOKMARKS_REMOVED" desc="String read for accessibility to inform the user that several bookmarks were removed.">
     Bookmarks removed
   </message>
-  <message name="IDS_ONBOARDING_WELCOME_BOOKMARK_REPLACED" desc="String read for accessibility to inform the user a bookmark was replaced.">
-    Bookmark replaced
-  </message>
   <message name="IDS_ONBOARDING_DEFAULT_BROWSER_CHANGED" desc="text notifying users that their default browser is successfully changed to Chrome">
     Chrome is your default browser
   </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 5db3001..fcea933 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3552,7 +3552,7 @@
       "//chrome/chrome_watcher:client",
       "//chrome/common:metrics_constants_util_win",
       "//chrome/common:version_header",
-      "//chrome/credential_provider/common:common_constants",
+      "//chrome/credential_provider/gaiacp:common",
       "//chrome/install_static:install_static_util",
       "//chrome/notification_helper:constants",
       "//chrome/services/util_win/public/mojom",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 77dcd148..7349f03 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2325,14 +2325,6 @@
      kOsAll,
      FEATURE_VALUE_TYPE(
          autofill::features::kAutofillLocalCardMigrationUsesStrikeSystemV2)},
-    {"enable-autofill-save-card-dialog-unlabeled-expiration-date",
-     flag_descriptions::
-         kEnableAutofillSaveCardDialogUnlabeledExpirationDateName,
-     flag_descriptions::
-         kEnableAutofillSaveCardDialogUnlabeledExpirationDateDescription,
-     kOsDesktop,
-     FEATURE_VALUE_TYPE(
-         autofill::features::kAutofillSaveCardDialogUnlabeledExpirationDate)},
     {"enable-autofill-save-card-improved-user-consent",
      flag_descriptions::kEnableAutofillSaveCardImprovedUserConsentName,
      flag_descriptions::kEnableAutofillSaveCardImprovedUserConsentDescription,
diff --git a/chrome/browser/banners/app_banner_settings_helper.cc b/chrome/browser/banners/app_banner_settings_helper.cc
index cbcd13c..4c0ccbe9 100644
--- a/chrome/browser/banners/app_banner_settings_helper.cc
+++ b/chrome/browser/banners/app_banner_settings_helper.cc
@@ -88,20 +88,48 @@
   return dict;
 }
 
-base::Value* GetAppDict(base::DictionaryValue* origin_dict,
-                        const std::string& key_name) {
-  base::Value* app_dict =
-      origin_dict->FindKeyOfType(key_name, base::Value::Type::DICTIONARY);
-  if (!app_dict) {
-    // Don't allow more than kMaxAppsPerSite dictionaries.
-    if (origin_dict->size() < kMaxAppsPerSite) {
-      app_dict = origin_dict->SetKey(
-          key_name, base::Value(base::Value::Type::DICTIONARY));
+class AppPrefs {
+ public:
+  AppPrefs(content::WebContents* web_contents,
+           const GURL& origin,
+           const std::string& package_name_or_start_url)
+      : origin_(origin) {
+    Profile* profile =
+        Profile::FromBrowserContext(web_contents->GetBrowserContext());
+    if (profile->IsOffTheRecord() || !origin.is_valid())
+      return;
+
+    settings_ = HostContentSettingsMapFactory::GetForProfile(profile);
+    origin_dict_ = GetOriginAppBannerData(settings_, origin);
+    dict_ = origin_dict_->FindKeyOfType(package_name_or_start_url,
+                                        base::Value::Type::DICTIONARY);
+    if (!dict_) {
+      // Don't allow more than kMaxAppsPerSite dictionaries.
+      if (origin_dict_->size() < kMaxAppsPerSite) {
+        dict_ =
+            origin_dict_->SetKey(package_name_or_start_url,
+                                 base::Value(base::Value::Type::DICTIONARY));
+      }
     }
   }
 
-  return app_dict;
-}
+  HostContentSettingsMap* settings() { return settings_; }
+  base::Value* dict() { return dict_; }
+
+  void Save() {
+    DCHECK(dict_);
+    dict_ = nullptr;
+    settings_->SetWebsiteSettingDefaultScope(
+        origin_, GURL(), CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(),
+        std::move(origin_dict_));
+  }
+
+ private:
+  const GURL& origin_;
+  HostContentSettingsMap* settings_ = nullptr;
+  std::unique_ptr<base::DictionaryValue> origin_dict_;
+  base::Value* dict_ = nullptr;
+};
 
 // Queries variations for the number of days which dismissing and ignoring the
 // banner should prevent a banner from showing.
@@ -193,24 +221,12 @@
 base::Optional<NextInstallTextAnimation> NextInstallTextAnimation::Get(
     content::WebContents* web_contents,
     const GURL& scope) {
-  const NextInstallTextAnimation kNever = {base::Time::Max(),
-                                           base::TimeDelta::Max()};
+  AppPrefs app_prefs(web_contents, scope, scope.spec());
+  if (!app_prefs.dict())
+    return NextInstallTextAnimation{base::Time::Max(), base::TimeDelta::Max()};
 
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents->GetBrowserContext());
-  if (profile->IsOffTheRecord() || !scope.is_valid())
-    return kNever;
-
-  HostContentSettingsMap* settings =
-      HostContentSettingsMapFactory::GetForProfile(profile);
-  std::unique_ptr<base::DictionaryValue> origin_dict =
-      GetOriginAppBannerData(settings, scope);
-
-  base::Value* app_dict = GetAppDict(origin_dict.get(), scope.spec());
-  if (!app_dict)
-    return kNever;
-
-  const base::Value* next_dict = app_dict->FindKey(kNextInstallTextAnimation);
+  const base::Value* next_dict =
+      app_prefs.dict()->FindKey(kNextInstallTextAnimation);
   if (!next_dict || !next_dict->is_dict())
     return base::nullopt;
 
@@ -230,29 +246,16 @@
 
 void NextInstallTextAnimation::RecordToPrefs(content::WebContents* web_contents,
                                              const GURL& scope) const {
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents->GetBrowserContext());
-  if (profile->IsOffTheRecord() || !scope.is_valid())
-    return;
-
-  HostContentSettingsMap* settings =
-      HostContentSettingsMapFactory::GetForProfile(profile);
-  std::unique_ptr<base::DictionaryValue> origin_dict =
-      GetOriginAppBannerData(settings, scope);
-
-  base::Value* app_dict = GetAppDict(origin_dict.get(), scope.spec());
-  if (!app_dict)
+  AppPrefs app_prefs(web_contents, scope, scope.spec());
+  if (!app_prefs.dict())
     return;
 
   base::Value next_dict(base::Value::Type::DICTIONARY);
   next_dict.SetKey(kLastShownKey,
                    SerializeTimeDelta(last_shown.ToDeltaSinceWindowsEpoch()));
   next_dict.SetKey(kDelayKey, SerializeTimeDelta(delay));
-  app_dict->SetKey(kNextInstallTextAnimation, std::move(next_dict));
-
-  settings->SetWebsiteSettingDefaultScope(
-      scope, GURL(), CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(),
-      std::move(origin_dict));
+  app_prefs.dict()->SetKey(kNextInstallTextAnimation, std::move(next_dict));
+  app_prefs.Save();
 }
 
 }  // namespace
@@ -317,21 +320,8 @@
     const std::string& package_name_or_start_url,
     AppBannerEvent event,
     base::Time time) {
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents->GetBrowserContext());
-  if (profile->IsOffTheRecord() || package_name_or_start_url.empty())
-    return;
-
-  HostContentSettingsMap* settings =
-      HostContentSettingsMapFactory::GetForProfile(profile);
-  std::unique_ptr<base::DictionaryValue> origin_dict =
-      GetOriginAppBannerData(settings, origin_url);
-  if (!origin_dict)
-    return;
-
-  base::Value* app_dict =
-      GetAppDict(origin_dict.get(), package_name_or_start_url);
-  if (!app_dict)
+  AppPrefs app_prefs(web_contents, origin_url, package_name_or_start_url);
+  if (!app_prefs.dict())
     return;
 
   // Dates are stored in their raw form (i.e. not local dates) to be resilient
@@ -340,15 +330,13 @@
 
   if (event == APP_BANNER_EVENT_COULD_SHOW) {
     // Do not overwrite a could show event, as this is used for metrics.
-    if (app_dict->FindKeyOfType(event_key, base::Value::Type::DOUBLE))
+    if (app_prefs.dict()->FindKeyOfType(event_key, base::Value::Type::DOUBLE))
       return;
   }
-  app_dict->SetKey(event_key,
-                   base::Value(static_cast<double>(time.ToInternalValue())));
+  app_prefs.dict()->SetKey(
+      event_key, base::Value(static_cast<double>(time.ToInternalValue())));
 
-  settings->SetWebsiteSettingDefaultScope(
-      origin_url, GURL(), CONTENT_SETTINGS_TYPE_APP_BANNER, std::string(),
-      std::move(origin_dict));
+  app_prefs.Save();
 
   // App banner content settings are lossy, meaning they will not cause the
   // prefs to become dirty. This is fine for most events, as if they are lost it
@@ -356,7 +344,7 @@
   // DID_ADD_TO_HOMESCREEN event should always be recorded to prevent
   // spamminess.
   if (event == APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN)
-    settings->FlushLossyWebsiteSettings();
+    app_prefs.settings()->FlushLossyWebsiteSettings();
 }
 
 bool AppBannerSettingsHelper::HasBeenInstalled(
@@ -403,22 +391,11 @@
     AppBannerEvent event) {
   DCHECK(event < APP_BANNER_EVENT_NUM_EVENTS);
 
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents->GetBrowserContext());
-  HostContentSettingsMap* settings =
-      HostContentSettingsMapFactory::GetForProfile(profile);
-  std::unique_ptr<base::DictionaryValue> origin_dict =
-      GetOriginAppBannerData(settings, origin_url);
-
-  if (!origin_dict)
+  AppPrefs app_prefs(web_contents, origin_url, package_name_or_start_url);
+  if (!app_prefs.dict())
     return base::Time();
 
-  base::Value* app_dict =
-      GetAppDict(origin_dict.get(), package_name_or_start_url);
-  if (!app_dict)
-    return base::Time();
-
-  base::Value* internal_time = app_dict->FindKeyOfType(
+  base::Value* internal_time = app_prefs.dict()->FindKeyOfType(
       kBannerEventKeys[event], base::Value::Type::DOUBLE);
   if (!internal_time)
     return base::Time();
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 9df8b7c2..c45606c 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -342,6 +342,7 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/mojom/connector.mojom.h"
 #include "services/service_manager/sandbox/sandbox_type.h"
+#include "services/service_manager/sandbox/switches.h"
 #include "services/viz/public/interfaces/constants.mojom.h"
 #include "storage/browser/fileapi/external_mount_points.h"
 #include "third_party/blink/public/common/features.h"
@@ -2281,6 +2282,14 @@
   StackSamplingConfiguration::Get()->AppendCommandLineSwitchForChildProcess(
       process_type,
       command_line);
+
+#if defined(OS_LINUX)
+  // Processes may only query perf_event_open with the BPF sandbox disabled.
+  if (browser_command_line.HasSwitch(switches::kEnableThreadInstructionCount) &&
+      command_line->HasSwitch(service_manager::switches::kNoSandbox)) {
+    command_line->AppendSwitch(switches::kEnableThreadInstructionCount);
+  }
+#endif
 }
 
 void ChromeContentBrowserClient::AdjustUtilityServiceProcessCommandLine(
diff --git a/chrome/browser/chromeos/drive/drive_integration_service_browsertest.cc b/chrome/browser/chromeos/drive/drive_integration_service_browsertest.cc
index c710b4d..ddf98e3e 100644
--- a/chrome/browser/chromeos/drive/drive_integration_service_browsertest.cc
+++ b/chrome/browser/chromeos/drive/drive_integration_service_browsertest.cc
@@ -4,9 +4,11 @@
 
 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
 
+#include "base/command_line.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chromeos/constants/chromeos_switches.h"
 #include "components/drive/drive_pref_names.h"
 #include "components/prefs/pref_service.h"
 
@@ -45,4 +47,24 @@
   EXPECT_FALSE(integration_service->is_enabled());
 }
 
+class DriveIntegrationServiceWithGaiaDisabledBrowserTest
+    : public DriveIntegrationServiceBrowserTest {
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(chromeos::switches::kDisableGaiaServices);
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(DriveIntegrationServiceWithGaiaDisabledBrowserTest,
+                       DriveDisabled) {
+  // First make sure the pref is set to its default value which would normally
+  // permit drive.
+  browser()->profile()->GetPrefs()->SetBoolean(prefs::kDisableDrive, false);
+
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::FindForProfile(
+          browser()->profile());
+
+  ASSERT_TRUE(integration_service);
+  EXPECT_FALSE(integration_service->is_enabled());
+}
 }  // namespace drive
diff --git a/chrome/browser/chromeos/profiles/profile_util.cc b/chrome/browser/chromeos/profiles/profile_util.cc
index e4b11061..21b6ded 100644
--- a/chrome/browser/chromeos/profiles/profile_util.cc
+++ b/chrome/browser/chromeos/profiles/profile_util.cc
@@ -7,6 +7,7 @@
 #include "base/files/file_path.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "components/user_manager/user.h"
 
@@ -14,6 +15,10 @@
 
 bool IsProfileAssociatedWithGaiaAccount(Profile* profile) {
   // TODO(crbug.com/942937): This code can likely be simplified.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          chromeos::switches::kDisableGaiaServices)) {
+    return false;
+  }
   if (!chromeos::LoginState::IsInitialized())
     return false;
   if (!chromeos::LoginState::Get()->IsUserGaiaAuthenticated())
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index a69f6028..2122104 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -320,7 +320,7 @@
   (*s_whitelist)[ash::prefs::kAccessibilityAutoclickEnabled] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[ash::prefs::kAccessibilityAutoclickDelayMs] =
-      settings_api::PrefType::PREF_TYPE_BOOLEAN;
+      settings_api::PrefType::PREF_TYPE_NUMBER;
   (*s_whitelist)[ash::prefs::kAccessibilityAutoclickEventType] =
       settings_api::PrefType::PREF_TYPE_NUMBER;
   (*s_whitelist)[ash::prefs::kAccessibilityAutoclickRevertToLeftClick] =
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
index 183514c..f3e167e 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
@@ -365,16 +365,7 @@
   GURL url = extension->GetResourceURL(
       "a.html?" + base::NumberToString(embedded_test_server()->port()));
 
-  // Register an observer for the navigation in the subframe, so the test
-  // can wait until it is fully complete. Otherwise the context menu
-  // navigation is non-deterministic on which process it will get associated
-  // with, leading to test flakiness.
-  content::TestNavigationManager nav_manager(
-      tab, embedded_test_server()->GetURL(
-               "/extensions/api_test/webnavigation/userAction/subframe.html"));
   ui_test_utils::NavigateToURL(browser(), url);
-  nav_manager.WaitForNavigationFinished();
-  EXPECT_TRUE(nav_manager.was_successful());
 
   // This corresponds to "Open link in new tab".
   content::ContextMenuParams params;
diff --git a/chrome/browser/extensions/extension_install_prompt.cc b/chrome/browser/extensions/extension_install_prompt.cc
index 5d8e6a30dc..f1ed057 100644
--- a/chrome/browser/extensions/extension_install_prompt.cc
+++ b/chrome/browser/extensions/extension_install_prompt.cc
@@ -68,33 +68,6 @@
       .GetBitmap();
 }
 
-// If auto confirm is enabled then posts a task to proceed with or cancel the
-// install and returns true. Otherwise returns false.
-bool AutoConfirmPrompt(ExtensionInstallPrompt::DoneCallback* callback) {
-  switch (extensions::ScopedTestDialogAutoConfirm::GetAutoConfirmValue()) {
-    case extensions::ScopedTestDialogAutoConfirm::NONE:
-      return false;
-    // We use PostTask instead of calling the callback directly here, because in
-    // the real implementations it's highly likely the message loop will be
-    // pumping a few times before the user clicks accept or cancel.
-    case extensions::ScopedTestDialogAutoConfirm::ACCEPT:
-    case extensions::ScopedTestDialogAutoConfirm::ACCEPT_AND_OPTION:
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(base::ResetAndReturn(callback),
-                                    ExtensionInstallPrompt::Result::ACCEPTED));
-      return true;
-    case extensions::ScopedTestDialogAutoConfirm::CANCEL:
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE,
-          base::BindOnce(base::ResetAndReturn(callback),
-                         ExtensionInstallPrompt::Result::USER_CANCELED));
-      return true;
-  }
-
-  NOTREACHED();
-  return false;
-}
-
 }  // namespace
 
 ExtensionInstallPrompt::Prompt::InstallPromptPermissions::
@@ -685,7 +658,8 @@
   g_last_prompt_type_for_tests = prompt_->type();
   did_call_show_dialog_ = true;
 
-  if (AutoConfirmPrompt(&done_callback_))
+  // If true, auto confirm is enabled and already handled the result.
+  if (AutoConfirmPromptIfEnabled())
     return;
 
   if (show_dialog_callback_.is_null())
@@ -696,3 +670,28 @@
   std::move(show_dialog_callback_)
       .Run(show_params_.get(), cb, std::move(prompt_));
 }
+
+bool ExtensionInstallPrompt::AutoConfirmPromptIfEnabled() {
+  switch (extensions::ScopedTestDialogAutoConfirm::GetAutoConfirmValue()) {
+    case extensions::ScopedTestDialogAutoConfirm::NONE:
+      return false;
+    // We use PostTask instead of calling the callback directly here, because in
+    // the real implementations it's highly likely the message loop will be
+    // pumping a few times before the user clicks accept or cancel.
+    case extensions::ScopedTestDialogAutoConfirm::ACCEPT:
+    case extensions::ScopedTestDialogAutoConfirm::ACCEPT_AND_OPTION:
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(done_callback_),
+                                    ExtensionInstallPrompt::Result::ACCEPTED));
+      return true;
+    case extensions::ScopedTestDialogAutoConfirm::CANCEL:
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE,
+          base::BindOnce(std::move(done_callback_),
+                         ExtensionInstallPrompt::Result::USER_CANCELED));
+      return true;
+  }
+
+  NOTREACHED();
+  return false;
+}
diff --git a/chrome/browser/extensions/extension_install_prompt.h b/chrome/browser/extensions/extension_install_prompt.h
index c10dc09..44c0b01 100644
--- a/chrome/browser/extensions/extension_install_prompt.h
+++ b/chrome/browser/extensions/extension_install_prompt.h
@@ -326,6 +326,10 @@
   // Shows the actual UI (the icon should already be loaded).
   void ShowConfirmation();
 
+  // If auto confirm is enabled then posts a task to proceed with or cancel the
+  // install and returns true. Otherwise returns false.
+  bool AutoConfirmPromptIfEnabled();
+
   Profile* profile_;
 
   base::ThreadChecker ui_thread_checker_;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 41c0aff..67b0397b 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -827,11 +827,6 @@
     "expiry_milestone": 78
   },
   {
-    "name": "enable-autofill-save-card-dialog-unlabeled-expiration-date",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "enable-autofill-save-card-improved-user-consent",
     "owners": [ "chrome-autofill@google.com" ],
     "expiry_milestone": 73
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index dae21f7..8fe50b4 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -469,12 +469,6 @@
     "If enabled, the Autofill Dropdown will be built natively using Views, "
     "rather than painted directly to a canvas.";
 
-const char kEnableAutofillSaveCardDialogUnlabeledExpirationDateName[] =
-    "Show unlabeled expiration dates on the save card dialog";
-const char kEnableAutofillSaveCardDialogUnlabeledExpirationDateDescription[] =
-    "If enabled, expiration dates on the save card dialog (both local and "
-    "upstream) are shown without an 'Exp:' label.";
-
 const char kEnableAutofillSaveCardImprovedUserConsentName[] =
     "Use updated UI for credit card save bubbles";
 const char kEnableAutofillSaveCardImprovedUserConsentDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 92ed467f..6866933 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -316,10 +316,6 @@
 extern const char kEnableAutofillNativeDropdownViewsName[];
 extern const char kEnableAutofillNativeDropdownViewsDescription[];
 
-extern const char kEnableAutofillSaveCardDialogUnlabeledExpirationDateName[];
-extern const char
-    kEnableAutofillSaveCardDialogUnlabeledExpirationDateDescription[];
-
 extern const char kEnableAutofillSendExperimentIdsInPaymentsRPCsName[];
 extern const char kEnableAutofillSendExperimentIdsInPaymentsRPCsDescription[];
 
diff --git a/chrome/browser/history/history_browsertest.cc b/chrome/browser/history/history_browsertest.cc
index 0cc842b..84f439d 100644
--- a/chrome/browser/history/history_browsertest.cc
+++ b/chrome/browser/history/history_browsertest.cc
@@ -620,6 +620,67 @@
   EXPECT_EQ(title, row1.title());
 }
 
+// Ensure that commits unrelated to the pending entry do not cause incorrect
+// updates to history.
+IN_PROC_BROWSER_TEST_F(HistoryBrowserTest, BeforeUnloadCommitDuringPending) {
+  // Use the default embedded_test_server() for this test because replaceState
+  // requires a real, non-file URL.
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url1(embedded_test_server()->GetURL("foo.com", "/title3.html"));
+  ui_test_utils::NavigateToURL(browser(), url1);
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  base::string16 title1 = web_contents->GetTitle();
+
+  // Create a beforeunload handler that does a replaceState during navigation,
+  // unrelated to the destination URL (similar to Twitter).
+  ASSERT_TRUE(content::ExecuteScript(web_contents,
+                                     "window.onbeforeunload = function() {"
+                                     "history.replaceState({},'','test.html');"
+                                     "};"));
+  GURL url2(embedded_test_server()->GetURL("foo.com", "/test.html"));
+
+  // Start a cross-site navigation to trigger the beforeunload, but don't let
+  // the new URL commit yet.
+  GURL url3(embedded_test_server()->GetURL("bar.com", "/title2.html"));
+  content::TestNavigationManager manager(web_contents, url3);
+  web_contents->GetController().LoadURL(
+      url3, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
+  EXPECT_TRUE(manager.WaitForRequestStart());
+
+  // The beforeunload commit should happen before request start, which should
+  // result in two history entries, with the newest in index 0. urls[0] was
+  // incorrectly url3 in https://crbug.com/956208.
+  {
+    std::vector<GURL> urls(GetHistoryContents());
+    ASSERT_EQ(2u, urls.size());
+    EXPECT_EQ(url2, urls[0]);
+    EXPECT_EQ(url1, urls[1]);
+  }
+
+  // After the pending navigation commits and the new title arrives, there
+  // should be another row with the new URL and title.
+  manager.WaitForNavigationFinished();
+  content::WaitForLoadStop(web_contents);
+  base::string16 title3 = web_contents->GetTitle();
+  EXPECT_NE(title1, title3);
+  {
+    std::vector<GURL> urls(GetHistoryContents());
+    ASSERT_EQ(3u, urls.size());
+    EXPECT_EQ(url3, urls[0]);
+    history::URLRow row0 = LookUpURLInHistory(urls[0]);
+    EXPECT_EQ(title3, row0.title());
+
+    EXPECT_EQ(url2, urls[1]);
+    history::URLRow row1 = LookUpURLInHistory(urls[1]);
+    EXPECT_EQ(title1, row1.title());
+
+    EXPECT_EQ(url1, urls[2]);
+    history::URLRow row2 = LookUpURLInHistory(urls[2]);
+    EXPECT_EQ(title1, row2.title());
+  }
+}
+
 // Verify that submitting form adds target page to history list.
 IN_PROC_BROWSER_TEST_F(HistoryBrowserTest, SubmitFormAddsTargetPage) {
   GURL form = ui_test_utils::GetTestUrl(
diff --git a/chrome/browser/history/history_tab_helper.cc b/chrome/browser/history/history_tab_helper.cc
index 1e74c53f..009e29d 100644
--- a/chrome/browser/history/history_tab_helper.cc
+++ b/chrome/browser/history/history_tab_helper.cc
@@ -142,10 +142,9 @@
   // the WebContents' URL getter does.
   NavigationEntry* last_committed =
       web_contents()->GetController().GetLastCommittedEntry();
-  const history::HistoryAddPageArgs& add_page_args =
-      CreateHistoryAddPageArgs(
-          web_contents()->GetURL(), last_committed->GetTimestamp(),
-          last_committed->GetUniqueID(), navigation_handle);
+  const history::HistoryAddPageArgs& add_page_args = CreateHistoryAddPageArgs(
+      web_contents()->GetLastCommittedURL(), last_committed->GetTimestamp(),
+      last_committed->GetUniqueID(), navigation_handle);
 
   prerender::PrerenderManager* prerender_manager =
       prerender::PrerenderManagerFactory::GetForBrowserContext(
@@ -232,8 +231,8 @@
     NavigationEntry* entry = tab->GetController().GetLastCommittedEntry();
     history::ContextID context_id = history::ContextIDForWebContents(tab);
     if (entry) {
-      hs->UpdateWithPageEndTime(context_id, entry->GetUniqueID(), tab->GetURL(),
-                                base::Time::Now());
+      hs->UpdateWithPageEndTime(context_id, entry->GetUniqueID(),
+                                tab->GetLastCommittedURL(), base::Time::Now());
     }
     hs->ClearCachedDataForContextID(context_id);
   }
diff --git a/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.css b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.css
index 7cfda15..8eb71dfc 100644
--- a/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.css
+++ b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.css
@@ -3,7 +3,7 @@
  * found in the LICENSE file. */
 
 #arc-detailed-view-overlay {
-  background-color: rgb(255, 255, 192);
+  background-color: rgb(255, 255, 255);
   border: 1px solid #aaa;
   display: none;
   margin: 0;
@@ -44,6 +44,10 @@
   display: block;
 }
 
+#arc-graphics-tracing-control-buttons {
+  float: right;
+}
+
 .arc-cpu-view-title {
   border-bottom: 1px solid #888;
   font-size: 14px;
@@ -51,8 +55,11 @@
 }
 
 .arc-events-band {
+  border-color: #aaa;
+  border-style: solid;
+  border-width: 1px;
   display: block;
-  margin: 0;
+  padding: 2px;
 }
 
 .arc-events-cpu-detailed-band {
diff --git a/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.html b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.html
index b42be2a..32824ef 100644
--- a/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.html
+++ b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.html
@@ -18,8 +18,13 @@
     <div id="arc-graphics-tracing-control">
       <h3>ARC graphics tracing</h3>
       Use <b>Ctrl+Shift+G</b> in active Android app to start/stop tracing.
-      <input type="checkbox" id="arc-graphics-tracing-stop-on-jank">
+      <input type="checkbox" id="arc-graphics-tracing-stop-on-jank" checked>
       Stop on jank. Status: <i id="arc-graphics-tracing-status">Idle</i>
+      <div id="arc-graphics-tracing-control-buttons">
+        <button type="button" id="arc-graphics-tracing-save" disabled>Save
+        </button>
+        <button type="button" id="arc-graphics-tracing-load">Load</button>
+      </div>
     </div>
     <hr>
     <div id='arc-event-bands'></div>
diff --git a/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.js b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.js
index 4cf592f4..de992026 100644
--- a/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.js
+++ b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.js
@@ -91,7 +91,7 @@
 
   // Service events.
   // kTimeMark
-  10000: {color: '#fff', name: 'Time mark', width: 0.75},
+  10000: {color: '#888', name: 'Time mark', width: 0.75},
 };
 
 /**
@@ -145,6 +145,12 @@
 };
 
 /**
+ * @type {Object}.
+ * Currently loaded model.
+ */
+var activeModel = null;
+
+/**
  * @type {DetailedInfoView}.
  * Currently active detailed view.
  */
@@ -201,6 +207,29 @@
       discardDetailedInfo();
     }
   };
+
+  $('arc-graphics-tracing-save').onclick = function(event) {
+    var linkElement = document.createElement('a');
+    var file = new Blob([JSON.stringify(activeModel)], {type: 'text/plain'});
+    linkElement.href = URL.createObjectURL(file);
+    linkElement.download = 'tracing_model.json';
+    linkElement.click();
+  };
+
+  $('arc-graphics-tracing-load').onclick = function(event) {
+    var fileElement = document.createElement('input');
+    fileElement.type = 'file';
+
+    fileElement.onchange = function(event) {
+      var reader = new FileReader();
+      reader.onload = function(response) {
+        chrome.send('loadFromText', [response.target.result]);
+      };
+      reader.readAsText(event.target.files[0]);
+    };
+
+    fileElement.click();
+  };
 }
 
 /** Factory class for SVG elements. */
@@ -259,12 +288,15 @@
   }
 
   // Creates text element in the |svg| with provided attributes.
-  static addText(svg, x, y, fontSize, textContent) {
+  static addText(svg, x, y, fontSize, textContent, anchor) {
     var text = document.createElementNS(svgNS, 'text');
     text.setAttributeNS(null, 'x', x);
     text.setAttributeNS(null, 'y', y);
     text.setAttributeNS(null, 'fill', 'black');
     text.setAttributeNS(null, 'font-size', fontSize);
+    if (anchor) {
+      text.setAttributeNS(null, 'text-anchor', anchor);
+    }
     text.appendChild(document.createTextNode(textContent));
     svg.appendChild(text);
     return text;
@@ -396,7 +428,8 @@
    * @param {number} padding to separate from the next band or chart.
    */
   addBand(eventBand, height, padding) {
-    var currentColor = bandColor;
+    var currentColor = unusedColor;
+    var addToBand = false;
     var x = this.bandOffsetX;
     var eventIndex = eventBand.getFirstAfter(this.minTimestamp);
     while (eventIndex >= 0) {
@@ -405,20 +438,26 @@
         break;
       }
       var nextX = this.timestampToOffset(event[1]) + this.bandOffsetX;
-      SVG.addRect(
-          this.svg, x, this.nextYOffset, nextX - x, height, currentColor);
+      if (addToBand) {
+        SVG.addRect(
+            this.svg, x, this.nextYOffset, nextX - x, height, currentColor);
+      }
       if (eventBand.isEndOfSequence(eventIndex)) {
-        currentColor = bandColor;
+        currentColor = unusedColor;
+        addToBand = false;
       } else {
         currentColor = eventAttributes[event[0]].color;
+        addToBand = true;
       }
       x = nextX;
       eventIndex = eventBand.getNextEvent(eventIndex, 1 /* direction */);
     }
-    SVG.addRect(
-        this.svg, x, this.nextYOffset,
-        this.timestampToOffset(this.maxTimestamp) - x + this.bandOffsetX,
-        height, currentColor);
+    if (addToBand) {
+      SVG.addRect(
+          this.svg, x, this.nextYOffset,
+          this.timestampToOffset(this.maxTimestamp) - x + this.bandOffsetX,
+          height, currentColor);
+    }
 
     this.bands.push({
       band: eventBand,
@@ -430,6 +469,18 @@
   }
 
   /**
+   * This adds horizontal separator at |nextYOffset|.
+   *
+   * @param {number} padding to separate from the next band or chart.
+   */
+  addBandSeparator(padding) {
+    SVG.addLine(
+        this.svg, 0, this.nextYOffset, this.width, this.nextYOffset, '#888',
+        0.25);
+    this.updateHeight_(0 /* height */, padding);
+  }
+
+  /**
    * This adds new chart. Height of svg container is automatically adjusted to
    * fit the new content. This creates empty chart and one or more calls
    * |addChartSources| are expected to add actual content to the chart.
@@ -438,10 +489,6 @@
    * @param {number} padding to separate from the next band or chart.
    */
   addChart(height, padding) {
-    SVG.addRect(
-        this.svg, 0, this.nextYOffset,
-        this.timestampToOffset(this.maxTimestamp), height, bandColor);
-
     this.charts.push({
       sourcesWithBounds: [],
       top: this.nextYOffset,
@@ -628,25 +675,31 @@
     // Clear previous content.
     this.tooltip.textContent = '';
 
-    if (event.offsetX < this.bandOffsetX) {
+    var svgStyle = window.getComputedStyle(this.svg, null);
+    var paddingLeft = parseFloat(svgStyle.getPropertyValue('padding-left'));
+    var paddingTop = parseFloat(svgStyle.getPropertyValue('padding-top'));
+    var eventX = event.offsetX - paddingLeft;
+    var eventY = event.offsetY - paddingTop;
+
+    if (eventX < this.bandOffsetX) {
       this.tooltip.classList.remove('active');
       return;
     }
 
+    var eventTimestamp = this.offsetToTime(eventX - this.bandOffsetX);
+
     // Find band for this mouse event.
     for (var i = 0; i < this.bands.length; ++i) {
-      if (this.bands[i].top <= event.offsetY &&
-          this.bands[i].bottom > event.offsetY) {
-        this.updateToolTipForBand_(event, this.bands[i].band);
+      if (this.bands[i].top <= eventY && this.bands[i].bottom > eventY) {
+        this.updateToolTipForBand_(event, eventTimestamp, this.bands[i].band);
         return;
       }
     }
 
     // Find chart for this mouse event.
     for (var i = 0; i < this.charts.length; ++i) {
-      if (this.charts[i].top <= event.offsetY &&
-          this.charts[i].bottom > event.offsetY) {
-        this.updateToolTipForChart_(event, this.charts[i]);
+      if (this.charts[i].top <= eventY && this.charts[i].bottom > eventY) {
+        this.updateToolTipForChart_(event, eventTimestamp, this.charts[i]);
         return;
       }
     }
@@ -671,13 +724,49 @@
     return this.vsyncEvents.events[vsyncEventIndex][1];
   }
 
+
+  /**
+   * Adds time information for |eventTimestamp| to the tooltip. Global time is
+   * added first and VSYNC relative time is added next in case VSYNC event could
+   * be found.
+   *
+   * @param {Object} svg tooltip container.
+   * @param {number} eventTimestamp timestamp of the event.
+   * @returns {number} vertical position of the next element.
+   */
+  addTimeInfoToTooltip_(svg, eventTimestamp) {
+    var verticalGap = 5;
+    var horizontalGap = 10;
+    var fontSize = 12;
+    var lineHeight = 16;
+
+    var yOffset = verticalGap + lineHeight;
+
+    var vsyncTimestamp = this.getVSyncTimestamp_(eventTimestamp);
+
+    SVG.addText(
+        svg, horizontalGap, yOffset, fontSize,
+        timestempToMsText(eventTimestamp) + ' ms');
+    yOffset += lineHeight;
+    if (vsyncTimestamp) {
+      SVG.addText(
+          svg, horizontalGap, yOffset, fontSize,
+          '+' + timestempToMsText(eventTimestamp - vsyncTimestamp) +
+              ' since last vsync ms');
+      yOffset += lineHeight;
+    }
+
+    return yOffset;
+  }
+
   /**
    * Creates and shows tooltip for event band for the position under |event|.
    *
    * @param {Object} mouse event.
+   * @param (number} eventTimestamp timestamp of event.
    * @param {Object} active event band.
    */
-  updateToolTipForBand_(event, eventBand) {
+  updateToolTipForBand_(event, eventTimestamp, eventBand) {
     var horizontalGap = 10;
     var eventIconOffset = 24;
     var eventIconRadius = 4;
@@ -688,14 +777,11 @@
     var fontSize = 12;
     var width = 220;
 
-    var offsetX = event.offsetX - this.bandOffsetX;
     var svg = document.createElementNS(svgNS, 'svg');
     svg.setAttributeNS(
         'http://www.w3.org/2000/xmlns/', 'xmlns:xlink',
         'http://www.w3.org/1999/xlink');
     this.tooltip.appendChild(svg);
-    var yOffset = verticalGap + lineHeight;
-    var eventTimestamp = this.offsetToTime(offsetX);
 
     // Find the event under the cursor. |index| points to the current event
     // and |nextIndex| points to the next event.
@@ -715,10 +801,13 @@
       // Show the global event info.
       var globalEventType = globalEvent[0];
       var globalEventTimestamp = globalEvent[1];
-      // -1 to prevent VSYNC detects itself. In last case, previous VSYNC would
-      // be chosen.
-      var vsyncTimestamp = this.getVSyncTimestamp_(globalEventTimestamp - 1);
+      if (globalEventType == 400 /* kVsync */) {
+        // -1 to prevent VSYNC detects itself. In last case, previous VSYNC
+        // would be chosen.
+        globalEventTimestamp -= 1;
+      }
 
+      var yOffset = this.addTimeInfoToTooltip_(svg, globalEventTimestamp);
 
       var attributes = eventAttributes[globalEventType];
       SVG.addText(svg, horizontalGap, yOffset, fontSize, attributes.name);
@@ -730,20 +819,9 @@
             globalEvent[2]);
         yOffset += lineHeight;
       }
-
-      SVG.addText(
-          svg, horizontalGap, yOffset, fontSize,
-          timestempToMsText(globalEventTimestamp) + ' chart time ms');
-      yOffset += lineHeight;
-      if (vsyncTimestamp) {
-        SVG.addText(
-            svg, horizontalGap, yOffset, fontSize,
-            '+' + timestempToMsText(globalEventTimestamp - vsyncTimestamp) +
-                ' since last vsync ms');
-        yOffset += lineHeight;
-      }
     } else if (index < 0 || eventBand.isEndOfSequence(index)) {
       // In case cursor points to idle event, show its interval.
+      var yOffset = verticalGap + lineHeight;
       var startIdle = index < 0 ? 0 : eventBand.events[index][1];
       var endIdle =
           nextIndex < 0 ? this.maxTimestamp : eventBand.events[nextIndex][1];
@@ -764,19 +842,7 @@
       }
 
       var sequenceTimestamp = eventBand.events[index][1];
-      var vsyncTimestamp = this.getVSyncTimestamp_(sequenceTimestamp);
-
-      SVG.addText(
-          svg, horizontalGap, yOffset, fontSize,
-          timestempToMsText(sequenceTimestamp) + ' chart time ms');
-      yOffset += lineHeight;
-      if (vsyncTimestamp) {
-        SVG.addText(
-            svg, horizontalGap, yOffset, fontSize,
-            '+' + timestempToMsText(sequenceTimestamp - vsyncTimestamp) +
-                ' since last vsync ms');
-        yOffset += lineHeight;
-      }
+      var yOffset = this.addTimeInfoToTooltip_(svg, sequenceTimestamp);
 
       var lastTimestamp = sequenceTimestamp;
       // Scan for the entries to show.
@@ -826,28 +892,27 @@
    * Creates and show tooltip for event chart for the position under |event|.
    *
    * @param {Object} mouse event.
+   * @param (number} eventTimestamp timestamp of event.
    * @param {Object} active event chart.
    */
-  updateToolTipForChart_(event, chart) {
+  updateToolTipForChart_(event, eventTimestamp, chart) {
     var horizontalGap = 10;
     var iconRadius = 4;
-    var valueOffset = 20;
+    var iconOffset = 24;
+    var valueOffset = 32;
     var verticalGap = 5;
     var lineHeight = 16;
     var fontSize = 12;
     var width = 150;
+    var iconRadius = 4;
 
     var svg = document.createElementNS(svgNS, 'svg');
     svg.setAttributeNS(
         'http://www.w3.org/2000/xmlns/', 'xmlns:xlink',
         'http://www.w3.org/1999/xlink');
     this.tooltip.appendChild(svg);
-    var yOffset = verticalGap + lineHeight;
-    var eventTimestamp = this.offsetToTime(event.offsetX);
-    SVG.addText(
-        svg, horizontalGap, yOffset, fontSize,
-        timestempToMsText(eventTimestamp) + ' ms');
-    yOffset += lineHeight;
+
+    var yOffset = this.addTimeInfoToTooltip_(svg, eventTimestamp);
 
     for (var i = 0; i < chart.sourcesWithBounds.length; ++i) {
       var sourceWithBounds = chart.sourcesWithBounds[i];
@@ -867,7 +932,7 @@
           (eventTimestamp - eventBefore[1]) / (eventAfter[1] - eventBefore[1]);
       var value = factor * eventAfter[2] + (1.0 - factor) * eventBefore[2];
       SVG.addCircle(
-          svg, horizontalGap, yOffset - iconRadius, iconRadius, 1,
+          svg, iconOffset, yOffset - iconRadius, iconRadius, 1,
           sourceWithBounds.attributes.color, 'black');
       var text = (value * sourceWithBounds.attributes.scale).toFixed(1) + ' ' +
           sourceWithBounds.attributes.name;
@@ -930,7 +995,8 @@
     this.overlay.textContent = '';
 
     // UI constants to render.
-    var columnWidth = 140;
+    var columnNameWidth = 130;
+    var columnUsageWidth = 40;
     var scrollBarWidth = 3;
     var zoomFactor = 4.0;
     var cpuBandHeight = 14;
@@ -938,12 +1004,14 @@
     var padding = 2;
     var fontSize = 12;
     var processInfoPadding = 2;
-    var threadInfoPadding = 6;
+    var threadInfoPadding = 12;
+    var cpuUsagePadding = 2;
+    var columnsWidth = columnNameWidth + columnUsageWidth;
 
     // Use minimum 80% of inner width or 600 pixels to display detailed view
     // zoomed |zoomFactor| times.
     var availableWidthPixels =
-        window.innerWidth * 0.8 - columnWidth - scrollBarWidth;
+        window.innerWidth * 0.8 - columnsWidth - scrollBarWidth;
     availableWidthPixels = Math.max(availableWidthPixels, 600);
     var availableForHalfBandMcs = Math.floor(
         overviewBand.offsetToTime(availableWidthPixels) / (2.0 * zoomFactor));
@@ -1025,9 +1093,9 @@
     var bands = new EventBands(
         title, 'arc-events-cpu-detailed-band',
         overviewBand.resolution / zoomFactor, minTimestamp, maxTimestamp);
-    bands.setBandOffsetX(columnWidth);
+    bands.setBandOffsetX(columnsWidth);
     var bandsWidth = bands.timestampToOffset(maxTimestamp);
-    var totalWidth = bandsWidth + columnWidth;
+    var totalWidth = bandsWidth + columnsWidth;
     bands.setWidth(totalWidth);
 
     for (i = 0; i < pids.length; i++) {
@@ -1039,33 +1107,53 @@
       } else {
         processName = 'Others';
       }
-      bands.nextYOffset += (processInfoHeight + padding);
       var processCpuUsage = 100.0 * threadsPerPid[pid].totalTime / duration;
-      var processInfo = processName + ' <' + pid +
-          '>, cpu usage: ' + processCpuUsage.toFixed(2) + '%.';
+      var processInfo = processName + ' <' + pid + '>';
+      var processInfoTextLine = bands.nextYOffset + processInfoHeight - padding;
       SVG.addText(
-          bands.svg, processInfoPadding, bands.nextYOffset - 2 * padding,
-          fontSize, processInfo);
-      bands.svg.setAttribute('height', bands.nextYOffset + 'px');
+          bands.svg, processInfoPadding, processInfoTextLine, fontSize,
+          processInfo);
+      SVG.addText(
+          bands.svg, columnsWidth - cpuUsagePadding, processInfoTextLine,
+          fontSize, processCpuUsage.toFixed(2), 'end' /* anchor */);
 
       // Sort threads per time usage.
       threads.sort(function(a, b) {
         return eventsPerTid[b.tid].totalTime - eventsPerTid[a.tid].totalTime;
       });
 
+      // In case we have only one main thread add CPU info to process.
+      if (threads.length == 1 && threads[0].tid == pid) {
+        bands.addBand(
+            new Events(eventsPerTid[pid].events, 0, 1), cpuBandHeight, padding);
+        bands.addBandSeparator(2 /* padding */);
+        continue;
+      }
+
+      bands.nextYOffset += (processInfoHeight + padding);
+
       for (j = 0; j < threads.length; j++) {
         var tid = threads[j].tid;
         bands.addBand(
             new Events(eventsPerTid[tid].events, 0, 1), cpuBandHeight, padding);
         var threadName = overviewBand.model.system.threads[tid].name;
         var threadCpuUsage = 100.0 * threads[j].totalTime / duration;
-        var threadInfo = threadName + ' ' + threadCpuUsage.toFixed(2) + '%';
         SVG.addText(
             bands.svg, threadInfoPadding, bands.nextYOffset - padding, fontSize,
-            threadInfo);
+            threadName);
+        SVG.addText(
+            bands.svg, columnsWidth - cpuUsagePadding,
+            bands.nextYOffset - 2 * padding, fontSize,
+            threadCpuUsage.toFixed(2), 'end' /* anchor */);
       }
+      bands.addBandSeparator(2 /* padding */);
     }
 
+    var vsyncEvents = new Events(
+        overviewBand.model.android.global_events, 400 /* kVsync */,
+        400 /* kVsync */);
+    bands.setVSync(vsyncEvents);
+
     // Add center and boundary lines.
     var kTimeMark = 10000;
     var timeEvents = [
@@ -1074,6 +1162,13 @@
     ];
     bands.addGlobal(new Events(timeEvents, kTimeMark, kTimeMark));
 
+    SVG.addLine(
+        bands.svg, columnNameWidth, 0, columnNameWidth, bands.height, '#888',
+        0.25);
+
+    SVG.addLine(
+        bands.svg, columnsWidth, 0, columnsWidth, bands.height, '#888', 0.25);
+
     // Mark zoomed interval in overview.
     var overviewX = overviewBand.timestampToOffset(minTimestamp);
     var overviewWidth =
@@ -1088,7 +1183,7 @@
     // Align position in overview and middle line here if possible.
     var left = Math.max(
         Math.min(
-            Math.round(event.clientX - columnWidth - bandsWidth * 0.5),
+            Math.round(event.clientX - columnsWidth - bandsWidth * 0.5),
             window.innerWidth - totalWidth),
         0);
     this.overlay.style.left = left + 'px';
@@ -1313,6 +1408,7 @@
 function setGraphicBuffersModel(model) {
   // Clear previous content.
   $('arc-event-bands').textContent = '';
+  activeModel = model;
 
   // Microseconds per pixel.
   var resolution = 100.0;
@@ -1325,11 +1421,15 @@
   var innerLastBandPadding = 12;
   var chartHeight = 48;
 
+  var vsyncEvents = new Events(
+      model.android.global_events, 400 /* kVsync */, 400 /* kVsync */);
+
   var cpusTitle = new EventBandTitle(parent, 'CPUs', 'arc-events-band-title');
   var cpusBands = new CpuEventBands(
       cpusTitle, 'arc-events-band', resolution, 0, model.duration);
   cpusBands.setWidth(cpusBands.timestampToOffset(model.duration));
   cpusBands.setModel(model);
+  cpusBands.setVSync(vsyncEvents);
 
   var memoryTitle =
       new EventBandTitle(parent, 'Memory', 'arc-events-band-title');
@@ -1345,9 +1445,7 @@
     new Events(model.system.memory, 2 /* kSwapRead */, 2 /* kSwapRead */),
     new Events(model.system.memory, 3 /* kSwapWrite */, 3 /* kSwapWrite */)
   ]);
-
-  var vsyncEvents = new Events(
-      model.android.global_events, 400 /* kVsync */, 400 /* kVsync */);
+  memoryBands.setVSync(vsyncEvents);
 
   var chromeTitle =
       new EventBandTitle(parent, 'Chrome graphics', 'arc-events-band-title');
@@ -1360,6 +1458,7 @@
         topBandPadding);
   }
 
+  chromeBands.setVSync(vsyncEvents);
   chromeBands.addGlobal(new Events(
       model.chrome.global_events, 505 /* kChromeOSJank */,
       505 /* kChromeOSJank */));
@@ -1395,8 +1494,6 @@
         activityTitle, 'arc-events-band', resolution, 0, model.duration);
     activityBands.setWidth(activityBands.timestampToOffset(model.duration));
     for (j = 0; j < view.buffers.length; j++) {
-      var androidBand =
-          new Events(activityTitle, 'arc-events-band', model.duration, 14);
       // Android buffer events.
       activityBands.addBand(
           new Events(view.buffers[j], 100, 199), innerBandHeight,
@@ -1404,8 +1501,13 @@
       // exo events.
       activityBands.addBand(
           new Events(view.buffers[j], 200, 299), innerBandHeight,
-          innerLastBandPadding);
+          innerBandPadding /* padding */);
       // Chrome buffer events are not displayed at this time.
+
+      // Add separator between buffers.
+      if (j != view.buffers.length - 1) {
+        activityBands.addBandSeparator(innerBandPadding);
+      }
     }
     // Add vsync events
     activityBands.setVSync(vsyncEvents);
@@ -1415,4 +1517,6 @@
     activityBands.addGlobal(new Events(
         view.global_events, 600 /* kCustomEvent */, 600 /* kCustomEvent */));
   }
+
+  $('arc-graphics-tracing-save').disabled = false;
 }
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/BUILD.gn b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/BUILD.gn
index 068db0a..d903215 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/BUILD.gn
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/BUILD.gn
@@ -14,8 +14,14 @@
   deps = [
     ":google_app_proxy",
     "../:navigation_behavior",
-    "../shared:app_chooser",
+    "../shared:bookmark_proxy",
+    "../shared:module_metrics_proxy",
     "../shared:nux_types",
+    "../shared:step_indicator",
+    "//third_party/polymer/v1_0/components-chromium/iron-a11y-announcer:iron-a11y-announcer-extracted",
+    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:i18n_behavior",
+    "//ui/webui/resources/js:util",
   ]
 }
 
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
index 2cae5b1..dba91a4 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
@@ -1,13 +1,26 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
+<link rel="import" href="chrome://resources/html/cr.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/util.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-announcer/iron-a11y-announcer.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="../navigation_behavior.html">
-<link rel="import" href="../shared/app_chooser.html">
+<link rel="import" href="../shared/animations_css.html">
+<link rel="import" href="../shared/bookmark_proxy.html">
+<link rel="import" href="../shared/chooser_shared_css.html">
+<link rel="import" href="../shared/i18n_setup.html">
+<link rel="import" href="../shared/module_metrics_proxy.html">
+<link rel="import" href="../shared/step_indicator.html">
 <link rel="import" href="google_app_proxy.html">
 
 <dom-module id="nux-google-apps">
   <template>
-    <style>
+    <style include="animations chooser-shared-css paper-button-style">
       .apps-ask {
         text-align: center;
       }
@@ -32,12 +45,168 @@
         margin-bottom: 48px;
         outline: none;
       }
+
+      #appChooser {
+        display: block;
+        white-space: nowrap;
+      }
+
+      .button-bar {
+        margin-top: 4rem;
+      }
+
+      .option {
+        -webkit-appearance: none;
+        align-items: center;
+        border-radius: 8px;
+        box-sizing: border-box;
+        display: inline-flex;
+        font-family: inherit;
+        height: 7.5rem;
+        justify-content: center;
+        outline: 0;
+        position: relative;
+        transition-duration: 500ms;
+        transition-property: box-shadow;
+        vertical-align: bottom;
+        width: 6.25rem;
+      }
+
+      .option:not(:first-of-type) {
+        margin-inline-start: 1.5rem;
+      }
+
+      .option[active] {
+        border: 1px solid var(--cr-checked-color);
+        color: var(--cr-checked-color);
+        font-weight: 500;
+      }
+
+      .option.keyboard-focused:focus {
+        outline: var(--navi-keyboard-focus-color) solid 3px;
+      }
+
+      .option-name {
+        flex-grow: 0;
+        line-height: 1.25rem;
+        text-align: center;
+        white-space: normal;
+      }
+
+      .option-icon {
+        background-position: center;
+        background-repeat: no-repeat;
+        background-size: contain;
+        height: 2rem;
+        margin: auto;
+        width: 2rem;
+      }
+
+      .option-icon-shadow {
+        background-color: var(--navi-option-icon-shadow-color);
+        border-radius: 50%;
+        display: flex;
+        height: 3rem;
+        margin-bottom: .25rem;
+        width: 3rem;
+      }
+
+      .option iron-icon {
+        --iron-icon-fill-color: var(--cr-card-background-color);
+        background: var(--navi-check-icon-color);
+        border-radius: 50%;
+        display: none;
+        height: .75rem;
+        margin: 0;
+        position: absolute;
+        right: .375rem;
+        top: .375rem;
+        width: .75rem;
+      }
+
+      :host-context([dir=rtl]) .option iron-icon {
+        left: .375rem;
+        right: unset;
+      }
+
+      .option.keyboard-focused:focus iron-icon[icon='cr:check'],
+      .option:hover iron-icon[icon='cr:check'],
+      .option[active] iron-icon[icon='cr:check'] {
+        display: block;
+      }
+
+      .option[active] iron-icon[icon='cr:check'] {
+        background: var(--cr-checked-color);
+      }
+
+      /* App Icons */
+      .gmail {
+        content: -webkit-image-set(
+            url(chrome://welcome/images/gmail_1x.png) 1x,
+            url(chrome://welcome/images/gmail_2x.png) 2x);
+      }
+
+      .youtube {
+        content: -webkit-image-set(
+            url(chrome://welcome/images/youtube_1x.png) 1x,
+            url(chrome://welcome/images/youtube_2x.png) 2x);
+      }
+
+      .maps {
+        content: -webkit-image-set(
+            url(chrome://welcome/images/maps_1x.png) 1x,
+            url(chrome://welcome/images/maps_2x.png) 2x);
+      }
+
+      .translate {
+        content: -webkit-image-set(
+            url(chrome://welcome/images/translate_1x.png) 1x,
+            url(chrome://welcome/images/translate_2x.png) 2x);
+      }
+
+      .news {
+        content: -webkit-image-set(
+            url(chrome://welcome/images/news_1x.png) 1x,
+            url(chrome://welcome/images/news_2x.png) 2x);
+      }
+
+      .web-store {
+        content: -webkit-image-set(
+            url(chrome://welcome/images/chrome_store_1x.png) 1x,
+            url(chrome://welcome/images/chrome_store_2x.png) 2x);
+      }
     </style>
     <div class="apps-ask">
       <div class="chrome-logo" alt=""></div>
       <h1 tabindex="-1">$i18n{googleAppsDescription}</h1>
-      <app-chooser id="appChooser" indicator-model="[[indicatorModel]]">
-      </app-chooser>
+      <div id="appChooser">
+        <div class="slide-in">
+          <template is="dom-repeat" items="[[appList_]]">
+            <button active$="[[item.selected]]"
+                aria-pressed$="[[getAriaPressed_(item.selected)]]"
+                on-click="onAppClick_" on-pointerdown="onAppPointerDown_"
+                on-keyup="onAppKeyUp_" class="option">
+              <div class="option-icon-shadow">
+                <div class$="[[item.icon]] option-icon"></div>
+              </div>
+              <div class="option-name">[[item.name]]</div>
+              <iron-icon icon="cr:check"></iron-icon>
+            </button>
+          </template>
+        </div>
+
+        <div class="button-bar">
+          <paper-button id="noThanksButton" on-click="onNoThanksClicked_">
+            $i18n{skip}
+          </paper-button>
+          <step-indicator model="[[indicatorModel]]"></step-indicator>
+          <paper-button class="action-button" disabled$="[[!hasAppsSelected_]]"
+              on-click="onGetStartedClicked_">
+            $i18n{next}
+            <iron-icon icon="cr:chevron-right"></iron-icon>
+          </paper-button>
+        </div>
+      </div>
     </div>
   </template>
   <script src="nux_google_apps.js"></script>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.js b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.js
index 13ee8c7b..2ed67e53 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.js
@@ -2,32 +2,294 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+cr.exportPath('nux');
+
+/**
+ * @typedef {{
+ *   id: number,
+ *   name: string,
+ *   icon: string,
+ *   url: string,
+ *   bookmarkId: ?string,
+ *   selected: boolean,
+ * }}
+ */
+nux.AppItem;
+
+/**
+ * @typedef {{
+ *   item: !nux.AppItem,
+ *   set: function(string, boolean):void
+ * }}
+ */
+nux.AppItemModel;
+
+const KEYBOARD_FOCUSED = 'keyboard-focused';
+
 Polymer({
   is: 'nux-google-apps',
 
-  behaviors: [welcome.NavigationBehavior],
+  behaviors: [welcome.NavigationBehavior, I18nBehavior],
 
   properties: {
     /** @type {nux.stepIndicatorModel} */
     indicatorModel: Object,
+
+    /**
+     * @type {!Array<!nux.AppItem>}
+     * @private
+     */
+    appList_: Array,
+
+    hasAppsSelected_: {
+      type: Boolean,
+      notify: true,
+      value: true,
+    },
   },
 
+  /** @private {nux.AppProxy} */
+  appProxy_: null,
+
+  /** @private {?nux.ModuleMetricsManager} */
+  metricsManager_: null,
+
+  /** @private */
+  finalized_: false,
+
+  /** @private {nux.BookmarkProxy} */
+  bookmarkProxy_: null,
+
+  /** @private {nux.BookmarkBarManager} */
+  bookmarkBarManager_: null,
+
+  /** @private {boolean} */
+  wasBookmarkBarShownOnInit_: false,
+
   /** @override */
   ready: function() {
-    this.$.appChooser.appProxy = nux.GoogleAppProxyImpl.getInstance();
-    this.$.appChooser.metricsManager = new nux.ModuleMetricsManager(
+    this.appProxy_ = nux.GoogleAppProxyImpl.getInstance();
+    this.metricsManager_ = new nux.ModuleMetricsManager(
         nux.GoogleAppsMetricsProxyImpl.getInstance());
+    this.bookmarkProxy_ = nux.BookmarkProxyImpl.getInstance();
+    this.bookmarkBarManager_ = nux.BookmarkBarManager.getInstance();
+  },
+
+  /** @override */
+  attached: function() {
+    Polymer.RenderStatus.afterNextRender(this, () => {
+      Polymer.IronA11yAnnouncer.requestAvailability();
+    });
   },
 
   onRouteEnter: function() {
-    this.$.appChooser.onRouteEnter();
+    this.finalized_ = false;
+    this.metricsManager_.recordPageInitialized();
+    this.populateAllBookmarks_();
   },
 
   onRouteExit: function() {
-    this.$.appChooser.onRouteExit();
+    if (this.finalized_) {
+      return;
+    }
+    this.cleanUp_();
+    this.metricsManager_.recordBrowserBackOrForward();
   },
 
   onRouteUnload: function() {
-    this.$.appChooser.onRouteUnload();
+    if (this.finalized_) {
+      return;
+    }
+    this.cleanUp_();
+    this.metricsManager_.recordNavigatedAway();
   },
+
+  /**
+   * @param {EventTarget} element
+   * @param {number} direction
+   * @private
+   */
+  changeFocus_: function(element, direction) {
+    if (isRTL()) {
+      direction *= -1;  // Reverse direction if RTL.
+    }
+
+    const buttons = this.root.querySelectorAll('button');
+    const targetIndex = Array.prototype.indexOf.call(buttons, element);
+
+    const oldFocus = buttons[targetIndex];
+    if (!oldFocus) {
+      return;
+    }
+
+    const newFocus = buttons[targetIndex + direction];
+
+    // New target and we're changing direction.
+    if (newFocus && direction) {
+      newFocus.classList.add(KEYBOARD_FOCUSED);
+      oldFocus.classList.remove(KEYBOARD_FOCUSED);
+      newFocus.focus();
+    } else {
+      oldFocus.classList.add(KEYBOARD_FOCUSED);
+    }
+  },
+
+  /**
+   * Called when bookmarks should be removed for all selected apps.
+   * @private
+   */
+  cleanUp_: function() {
+    this.finalized_ = true;
+
+    if (!this.appList_) {
+      return;
+    }  // No apps to remove.
+
+    let removedBookmarks = false;
+    this.appList_.forEach(app => {
+      if (app.selected && app.bookmarkId) {
+        // Don't call |updateBookmark_| b/c we want to save the selection in the
+        // event of a browser back/forward.
+        this.bookmarkProxy_.removeBookmark(app.bookmarkId);
+        app.bookmarkId = null;
+        removedBookmarks = true;
+      }
+    });
+    // Only update and announce if we removed bookmarks.
+    if (removedBookmarks) {
+      this.bookmarkBarManager_.setShown(this.wasBookmarkBarShownOnInit_);
+      this.fire('iron-announce', {text: this.i18n('bookmarksRemoved')});
+    }
+  },
+
+  /**
+   * Handle toggling the apps selected.
+   * @param {!{model: !nux.AppItemModel}} e
+   * @private
+   */
+  onAppClick_: function(e) {
+    const item = e.model.item;
+
+    e.model.set('item.selected', !item.selected);
+
+    this.updateBookmark_(item);
+    this.updateHasAppsSelected_();
+
+    this.metricsManager_.recordClickedOption();
+
+    // Announcements should NOT be in |updateBookmark_| because there should be
+    // a different utterance when all app bookmarks are added/removed.
+    const i18nKey = item.selected ? 'bookmarkAdded' : 'bookmarkRemoved';
+    this.fire('iron-announce', {text: this.i18n(i18nKey)});
+  },
+
+  /**
+   * @param {!Event} e
+   * @private
+   */
+  onAppKeyUp_: function(e) {
+    if (e.key == 'ArrowRight') {
+      this.changeFocus_(e.currentTarget, 1);
+    } else if (e.key == 'ArrowLeft') {
+      this.changeFocus_(e.currentTarget, -1);
+    } else {
+      e.currentTarget.classList.add(KEYBOARD_FOCUSED);
+    }
+  },
+
+  /**
+   * @param {!Event} e
+   * @private
+   */
+  onAppPointerDown_: function(e) {
+    e.currentTarget.classList.remove(KEYBOARD_FOCUSED);
+  },
+
+  /** @private */
+  onGetStartedClicked_: function() {
+    this.finalized_ = true;
+    this.appList_.forEach(app => {
+      if (app.selected) {
+        this.appProxy_.recordProviderSelected(app.id);
+      }
+    });
+    this.metricsManager_.recordGetStarted();
+    welcome.navigateToNextStep();
+  },
+
+  /** @private */
+  onNoThanksClicked_: function() {
+    this.cleanUp_();
+    this.metricsManager_.recordNoThanks();
+    welcome.navigateToNextStep();
+  },
+
+  /**
+   * Called when bookmarks should be created for all selected apps.
+   * @private
+   */
+  populateAllBookmarks_: function() {
+    this.wasBookmarkBarShownOnInit_ = this.bookmarkBarManager_.getShown();
+
+    if (this.appList_) {
+      this.appList_.forEach(app => this.updateBookmark_(app));
+    } else {
+      this.appProxy_.getAppList().then(list => {
+        this.appList_ = /** @type(!Array<!nux.AppItem>) */ (list);
+        this.appList_.forEach((app, index) => {
+          // Default select first few items.
+          app.selected = index < 3;
+          this.updateBookmark_(app);
+        });
+        this.updateHasAppsSelected_();
+        this.fire('iron-announce', {text: this.i18n('bookmarksAdded')});
+      });
+    }
+  },
+
+  /**
+   * @param {!nux.AppItem} item
+   * @private
+   */
+  updateBookmark_: function(item) {
+    if (item.selected && !item.bookmarkId) {
+      this.bookmarkBarManager_.setShown(true);
+      this.bookmarkProxy_.addBookmark(
+          {
+            title: item.name,
+            url: item.url,
+            parentId: '1',
+          },
+          result => {
+            item.bookmarkId = result.id;
+          });
+      // Cache bookmark icon.
+      this.appProxy_.cacheBookmarkIcon(item.id);
+    } else if (!item.selected && item.bookmarkId) {
+      this.bookmarkProxy_.removeBookmark(item.bookmarkId);
+      item.bookmarkId = null;
+    }
+  },
+
+  /**
+   * Updates the value of hasAppsSelected_.
+   * @private
+   */
+  updateHasAppsSelected_: function() {
+    this.hasAppsSelected_ =
+        this.appList_ && this.appList_.some(a => a.selected);
+    if (!this.hasAppsSelected_) {
+      this.bookmarkBarManager_.setShown(this.wasBookmarkBarShownOnInit_);
+    }
+  },
+
+  /**
+   * Converts a boolean to a string because aria-pressed needs a string value.
+   * @param {boolean} value
+   * @return {string}
+   * @private
+   */
+  getAriaPressed_: function(value) {
+    return value ? 'true' : 'false';
+  }
 });
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd b/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd
index c9ecd0b..0eca6cf 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd
+++ b/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd
@@ -151,14 +151,6 @@
                  type="chrome_html"
                  compress="gzip"
                  preprocess="true"/>
-      <structure name="IDR_NUX_SHARED_APP_CHOOSER_HTML"
-                 file="shared/app_chooser.html"
-                 compress="gzip"
-                 type="chrome_html" />
-      <structure name="IDR_NUX_SHARED_APP_CHOOSER_JS"
-                 file="shared/app_chooser.js"
-                 compress="gzip"
-                 type="chrome_html" />
       <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ACTION_LINK_STYLE_JS"
                  file="shared/action_link_style.js"
                  compress="gzip"
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/BUILD.gn b/chrome/browser/resources/welcome/onboarding_welcome/shared/BUILD.gn
index f78a437..b3cb8800 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/shared/BUILD.gn
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/BUILD.gn
@@ -12,18 +12,6 @@
   ]
 }
 
-js_library("app_chooser") {
-  deps = [
-    ":bookmark_proxy",
-    ":module_metrics_proxy",
-    ":step_indicator",
-    "//third_party/polymer/v1_0/components-chromium/iron-a11y-announcer:iron-a11y-announcer-extracted",
-    "//ui/webui/resources/js:cr",
-    "//ui/webui/resources/js:i18n_behavior",
-    "//ui/webui/resources/js:util",
-  ]
-}
-
 js_library("bookmark_proxy") {
   deps = [
     "//ui/webui/resources/js:cr",
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.html b/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.html
deleted file mode 100644
index 398b4ec..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.html
+++ /dev/null
@@ -1,206 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/util.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-announcer/iron-a11y-announcer.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="../navigation_behavior.html">
-<link rel="import" href="animations_css.html">
-<link rel="import" href="bookmark_proxy.html">
-<link rel="import" href="chooser_shared_css.html">
-<link rel="import" href="i18n_setup.html">
-<link rel="import" href="module_metrics_proxy.html">
-<link rel="import" href="step_indicator.html">
-
-<dom-module id="app-chooser">
-  <template>
-    <style include="animations chooser-shared-css paper-button-style">
-      :host {
-        display: block;
-        white-space: nowrap;
-      }
-
-      .button-bar {
-        margin-top: 4rem;
-      }
-
-      .option {
-        -webkit-appearance: none;
-        align-items: center;
-        border-radius: 8px;
-        box-sizing: border-box;
-        display: inline-flex;
-        font-family: inherit;
-        height: 7.5rem;
-        justify-content: center;
-        outline: 0;
-        position: relative;
-        transition-duration: 500ms;
-        transition-property: box-shadow;
-        vertical-align: bottom;
-        width: 6.25rem;
-      }
-
-      .option:not(:first-of-type) {
-        margin-inline-start: 1.5rem;
-      }
-
-      .option[active] {
-        border: 1px solid var(--cr-checked-color);
-        color: var(--cr-checked-color);
-        font-weight: 500;
-      }
-
-      .option.keyboard-focused:focus {
-        outline: var(--navi-keyboard-focus-color) solid 3px;
-      }
-
-      .option-name {
-        flex-grow: 0;
-        line-height: 1.25rem;
-        text-align: center;
-        white-space: normal;
-      }
-
-      .option-icon {
-        background-position: center;
-        background-repeat: no-repeat;
-        background-size: contain;
-        height: 2rem;
-        margin: auto;
-        width: 2rem;
-      }
-
-      .option-icon-shadow {
-        background-color: var(--navi-option-icon-shadow-color);
-        border-radius: 50%;
-        display: flex;
-        height: 3rem;
-        margin-bottom: .25rem;
-        width: 3rem;
-      }
-
-      .option iron-icon {
-        --iron-icon-fill-color: var(--cr-card-background-color);
-        background: var(--navi-check-icon-color);
-        border-radius: 50%;
-        display: none;
-        height: .75rem;
-        margin: 0;
-        position: absolute;
-        right: .375rem;
-        top: .375rem;
-        width: .75rem;
-      }
-
-      :host-context([dir=rtl]) .option iron-icon {
-        left: .375rem;
-        right: unset;
-      }
-
-      .option.keyboard-focused:focus iron-icon[icon='cr:check'],
-      .option:hover iron-icon[icon='cr:check'],
-      .option[active] iron-icon[icon='cr:check'] {
-        display: block;
-      }
-
-      .option[active] iron-icon[icon='cr:check'] {
-        background: var(--cr-checked-color);
-      }
-
-      /* App Icons */
-      .gmail {
-        content: -webkit-image-set(
-            url(chrome://welcome/images/gmail_1x.png) 1x,
-            url(chrome://welcome/images/gmail_2x.png) 2x);
-      }
-
-      .yahoo {
-        background-image: -webkit-image-set(
-            url(chrome://welcome/images/yahoo_1x.png) 1x,
-            url(chrome://welcome/images/yahoo_2x.png) 2x);
-      }
-
-      .aol {
-        background-image: -webkit-image-set(
-            url(chrome://welcome/images/aol_1x.png) 1x,
-            url(chrome://welcome/images/aol_2x.png) 2x);
-      }
-
-      .icloud {
-        background-image: -webkit-image-set(
-            url(chrome://welcome/images/icloud_1x.png) 1x,
-            url(chrome://welcome/images/icloud_2x.png) 2x);
-      }
-
-      .outlook {
-        background-image: -webkit-image-set(
-            url(chrome://welcome/images/outlook_1x.png) 1x,
-            url(chrome://welcome/images/outlook_2x.png) 2x);
-      }
-
-      .youtube {
-        content: -webkit-image-set(
-            url(chrome://welcome/images/youtube_1x.png) 1x,
-            url(chrome://welcome/images/youtube_2x.png) 2x);
-      }
-
-      .maps {
-        content: -webkit-image-set(
-            url(chrome://welcome/images/maps_1x.png) 1x,
-            url(chrome://welcome/images/maps_2x.png) 2x);
-      }
-
-      .translate {
-        content: -webkit-image-set(
-            url(chrome://welcome/images/translate_1x.png) 1x,
-            url(chrome://welcome/images/translate_2x.png) 2x);
-      }
-
-      .news {
-        content: -webkit-image-set(
-            url(chrome://welcome/images/news_1x.png) 1x,
-            url(chrome://welcome/images/news_2x.png) 2x);
-      }
-
-      .web-store {
-        content: -webkit-image-set(
-            url(chrome://welcome/images/chrome_store_1x.png) 1x,
-            url(chrome://welcome/images/chrome_store_2x.png) 2x);
-      }
-    </style>
-
-    <div class="slide-in">
-      <template is="dom-repeat" items="[[appList_]]">
-        <button active$="[[item.selected]]"
-            aria-pressed$="[[getAriaPressed_(item.selected)]]"
-            on-click="onAppClick_" on-pointerdown="onAppPointerDown_"
-            on-keyup="onAppKeyUp_" class="option">
-          <div class="option-icon-shadow">
-            <div class$="[[item.icon]] option-icon"></div>
-          </div>
-          <div class="option-name">[[item.name]]</div>
-          <iron-icon icon="cr:check"></iron-icon>
-        </button>
-      </template>
-    </div>
-
-    <div class="button-bar">
-      <paper-button id="noThanksButton" on-click="onNoThanksClicked_">
-        $i18n{skip}
-      </paper-button>
-      <step-indicator model="[[indicatorModel]]"></step-indicator>
-      <paper-button class="action-button" disabled$="[[!hasAppsSelected_]]"
-          on-click="onGetStartedClicked_">
-        $i18n{next}
-        <iron-icon icon="cr:chevron-right"></iron-icon>
-      </paper-button>
-    </div>
-  </template>
-  <script src="app_chooser.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.js b/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.js
deleted file mode 100644
index 72d3853..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.js
+++ /dev/null
@@ -1,325 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-cr.exportPath('nux');
-
-/**
- * @typedef {{
- *   id: number,
- *   name: string,
- *   icon: string,
- *   url: string,
- *   bookmarkId: ?string,
- *   selected: boolean,
- * }}
- */
-nux.AppItem;
-
-/**
- * @typedef {{
- *   item: !nux.AppItem,
- *   set: function(string, boolean):void
- * }}
- */
-nux.AppItemModel;
-
-const KEYBOARD_FOCUSED = 'keyboard-focused';
-
-Polymer({
-  is: 'app-chooser',
-
-  behaviors: [I18nBehavior],
-
-  properties: {
-    /** @type {nux.stepIndicatorModel} */
-    indicatorModel: Object,
-
-    singleSelect: {
-      type: Boolean,
-      value: false,
-    },
-
-    /**
-     * @type {!Array<!nux.AppItem>}
-     * @private
-     */
-    appList_: Array,
-
-    hasAppsSelected_: {
-      type: Boolean,
-      notify: true,
-      value: true,
-    },
-  },
-
-  /**
-   * Should be set in parent element's |ready| method.
-   * @type {nux.AppProxy}
-   */
-  appProxy: null,
-
-  /**
-   * Should be set in parent element's |ready| method.
-   * @type {?nux.ModuleMetricsManager}
-   */
-  metricsManager: null,
-
-  /** @private */
-  finalized_: false,
-
-  /** @private {nux.BookmarkProxy} */
-  bookmarkProxy_: null,
-
-  /** @private {nux.BookmarkBarManager} */
-  bookmarkBarManager_: null,
-
-  /** @private {boolean} */
-  wasBookmarkBarShownOnInit_: false,
-
-  /** @override */
-  attached: function() {
-    Polymer.RenderStatus.afterNextRender(this, () => {
-      Polymer.IronA11yAnnouncer.requestAvailability();
-    });
-  },
-
-  /** @override */
-  ready: function() {
-    this.bookmarkProxy_ = nux.BookmarkProxyImpl.getInstance();
-    this.bookmarkBarManager_ = nux.BookmarkBarManager.getInstance();
-  },
-
-  onRouteEnter: function() {
-    this.finalized_ = false;
-    this.metricsManager.recordPageInitialized();
-    this.populateAllBookmarks_();
-  },
-
-  onRouteExit: function() {
-    if (this.finalized_) {
-      return;
-    }
-    this.cleanUp_();
-    this.metricsManager.recordBrowserBackOrForward();
-  },
-
-  onRouteUnload: function() {
-    if (this.finalized_) {
-      return;
-    }
-    this.cleanUp_();
-    this.metricsManager.recordNavigatedAway();
-  },
-
-  /**
-   * @param {EventTarget} element
-   * @param {number} direction
-   * @private
-   */
-  changeFocus_: function(element, direction) {
-    if (isRTL()) {
-      direction *= -1;  // Reverse direction if RTL.
-    }
-
-    const buttons = this.root.querySelectorAll('button');
-    const targetIndex = Array.prototype.indexOf.call(buttons, element);
-
-    const oldFocus = buttons[targetIndex];
-    if (!oldFocus) {
-      return;
-    }
-
-    const newFocus = buttons[targetIndex + direction];
-
-    // New target and we're changing direction.
-    if (newFocus && direction) {
-      newFocus.classList.add(KEYBOARD_FOCUSED);
-      oldFocus.classList.remove(KEYBOARD_FOCUSED);
-      newFocus.focus();
-    } else {
-      oldFocus.classList.add(KEYBOARD_FOCUSED);
-    }
-  },
-
-  /**
-   * Called when bookmarks should be removed for all selected apps.
-   * @private
-   */
-  cleanUp_: function() {
-    this.finalized_ = true;
-
-    if (!this.appList_) {
-      return;
-    }  // No apps to remove.
-
-    let removedBookmarks = false;
-    this.appList_.forEach(app => {
-      if (app.selected && app.bookmarkId) {
-        // Don't call |updateBookmark_| b/c we want to save the selection in the
-        // event of a browser back/forward.
-        this.bookmarkProxy_.removeBookmark(app.bookmarkId);
-        app.bookmarkId = null;
-        removedBookmarks = true;
-      }
-    });
-    // Only update and announce if we removed bookmarks.
-    if (removedBookmarks) {
-      this.bookmarkBarManager_.setShown(this.wasBookmarkBarShownOnInit_);
-      this.fire('iron-announce', {text: this.i18n('bookmarksRemoved')});
-    }
-  },
-
-  /**
-   * Handle toggling the apps selected.
-   * @param {!{model: !nux.AppItemModel}} e
-   * @private
-   */
-  onAppClick_: function(e) {
-    const item = e.model.item;
-    let prevItemIndex = -1;
-
-    if (this.singleSelect) {
-      this.appList_.forEach((app, index) => {
-        if (app.selected) {
-          prevItemIndex = index;
-        }
-      });
-    }
-
-    e.model.set('item.selected', !item.selected);
-
-    if (this.singleSelect && item.selected && prevItemIndex > -1) {
-      this.set(`appList_.${prevItemIndex}.selected`, false);
-      this.updateBookmark_(this.appList_[prevItemIndex]);
-    }
-
-    this.updateBookmark_(item);
-    this.updateHasAppsSelected_();
-
-    this.metricsManager.recordClickedOption();
-
-    // Announcements should NOT be in |updateBookmark_| because there should be
-    // a different utterance when all app bookmarks are added/removed.
-    let i18nKey = 'bookmarkRemoved';
-    if (item.selected) {
-      i18nKey = prevItemIndex > -1 ? 'bookmarkReplaced' : 'bookmarkAdded';
-    }
-    this.fire('iron-announce', {text: this.i18n(i18nKey)});
-  },
-
-  /**
-   * @param {!Event} e
-   * @private
-   */
-  onAppKeyUp_: function(e) {
-    if (e.key == 'ArrowRight') {
-      this.changeFocus_(e.currentTarget, 1);
-    } else if (e.key == 'ArrowLeft') {
-      this.changeFocus_(e.currentTarget, -1);
-    } else {
-      this.changeFocus_(e.currentTarget, 0);
-    }
-  },
-
-  /**
-   * @param {!Event} e
-   * @private
-   */
-  onAppPointerDown_: function(e) {
-    e.currentTarget.classList.remove(KEYBOARD_FOCUSED);
-  },
-
-  /** @private */
-  onGetStartedClicked_: function() {
-    this.finalized_ = true;
-    this.appList_.forEach(app => {
-      if (app.selected) {
-        this.appProxy.recordProviderSelected(app.id);
-      }
-    });
-    this.metricsManager.recordGetStarted();
-    welcome.navigateToNextStep();
-  },
-
-  /** @private */
-  onNoThanksClicked_: function() {
-    this.cleanUp_();
-    this.metricsManager.recordNoThanks();
-    welcome.navigateToNextStep();
-  },
-
-  /**
-   * Called when bookmarks should be created for all selected apps.
-   * @private
-   */
-  populateAllBookmarks_: function() {
-    this.wasBookmarkBarShownOnInit_ = this.bookmarkBarManager_.getShown();
-
-    if (this.appList_) {
-      this.appList_.forEach(app => this.updateBookmark_(app));
-    } else {
-      this.appProxy.getAppList().then(list => {
-        this.appList_ = /** @type(!Array<!nux.AppItem>) */ (list);
-        this.appList_.forEach((app, index) => {
-          if (this.singleSelect) {
-            // Default select the first item.
-            app.selected = index == 0;
-          } else {
-            // Default select first few items.
-            app.selected = index < 3;
-          }
-          this.updateBookmark_(app);
-        });
-        this.updateHasAppsSelected_();
-        this.fire('iron-announce', {text: this.i18n('bookmarksAdded')});
-      });
-    }
-  },
-
-  /**
-   * @param {!nux.AppItem} item
-   * @private
-   */
-  updateBookmark_: function(item) {
-    if (item.selected && !item.bookmarkId) {
-      this.bookmarkBarManager_.setShown(true);
-      this.bookmarkProxy_.addBookmark(
-          {
-            title: item.name,
-            url: item.url,
-            parentId: '1',
-          },
-          result => {
-            item.bookmarkId = result.id;
-          });
-      // Cache bookmark icon.
-      this.appProxy.cacheBookmarkIcon(item.id);
-    } else if (!item.selected && item.bookmarkId) {
-      this.bookmarkProxy_.removeBookmark(item.bookmarkId);
-      item.bookmarkId = null;
-    }
-  },
-
-  /**
-   * Updates the value of hasAppsSelected_.
-   * @private
-   */
-  updateHasAppsSelected_: function() {
-    this.hasAppsSelected_ =
-        this.appList_ && this.appList_.some(a => a.selected);
-    if (!this.hasAppsSelected_) {
-      this.bookmarkBarManager_.setShown(this.wasBookmarkBarShownOnInit_);
-    }
-  },
-
-  /**
-   * Converts a boolean to a string because aria-pressed needs a string value.
-   * @param {boolean} value
-   * @return {string}
-   * @private
-   */
-  getAriaPressed_: function(value) {
-    return value ? 'true' : 'false';
-  }
-});
diff --git a/chrome/browser/search/search.cc b/chrome/browser/search/search.cc
index a2b2d6dc..7828c41 100644
--- a/chrome/browser/search/search.cc
+++ b/chrome/browser/search/search.cc
@@ -32,10 +32,6 @@
 #include "chrome/browser/supervised_user/supervised_user_url_filter.h"
 #endif
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/login/signin/merge_session_throttling_utils.h"
-#endif  // defined(OS_CHROMEOS)
-
 #if !defined(OS_ANDROID)
 #include "chrome/browser/search/instant_service.h"
 #include "chrome/browser/search/instant_service_factory.h"
@@ -167,19 +163,6 @@
   return false;
 }
 
-bool ShouldDelayRemoteNTP(const GURL& search_provider_url, Profile* profile) {
-#if defined(OS_CHROMEOS)
-  // On Chrome OS, if the session hasn't merged yet, we need to avoid loading
-  // the remote NTP because that will trigger showing the merge session throttle
-  // interstitial page, which can show for 5+ seconds. crbug.com/591530.
-  if (merge_session_throttling_utils::ShouldDelayUrl(search_provider_url) &&
-      merge_session_throttling_utils::IsSessionRestorePending(profile)) {
-    return true;
-  }
-#endif  // defined(OS_CHROMEOS)
-  return false;
-}
-
 // Used to look up the URL to use for the New Tab page. Also tracks how we
 // arrived at that URL so it can be logged with UMA.
 struct NewTabURLDetails {
@@ -205,9 +188,6 @@
         TemplateURLRef::SearchTermsArgs(base::string16()),
         UIThreadSearchTermsData(profile)));
 
-    if (ShouldDelayRemoteNTP(search_provider_url, profile))
-      return NewTabURLDetails(local_url, NEW_TAB_URL_VALID);
-
     if (!search_provider_url.is_valid())
       return NewTabURLDetails(local_url, NEW_TAB_URL_NOT_SET);
     if (!search_provider_url.SchemeIsCryptographic())
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 9482832..a635b49c 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2883,6 +2883,8 @@
       "views/tabs/stacked_tab_strip_layout.h",
       "views/tabs/tab.cc",
       "views/tabs/tab.h",
+      "views/tabs/tab_animation_state.cc",
+      "views/tabs/tab_animation_state.h",
       "views/tabs/tab_close_button.cc",
       "views/tabs/tab_close_button.h",
       "views/tabs/tab_controller.h",
diff --git a/chrome/browser/ui/ash/launcher/DEPS b/chrome/browser/ui/ash/launcher/DEPS
index b870e12..8d4d790 100644
--- a/chrome/browser/ui/ash/launcher/DEPS
+++ b/chrome/browser/ui/ash/launcher/DEPS
@@ -9,7 +9,7 @@
   ],
   # https://crbug.com/875111
   "chrome_launcher_controller_unittest\.cc": [
-    "+ash/multi_user/multi_user_window_manager.h",
+    "+ash/multi_user/multi_user_window_manager_impl.h",
   ],
   # https://crbug.com/887156
   "crostini_app_window_shelf_controller\.cc": [
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
index 873e3cdb..e4efc9d 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -15,7 +15,7 @@
 #include <vector>
 
 #include "ash/display/display_configuration_controller.h"
-#include "ash/multi_user/multi_user_window_manager.h"
+#include "ash/multi_user/multi_user_window_manager_impl.h"
 #include "ash/public/cpp/app_list/internal_app_id_constants.h"
 #include "ash/public/cpp/shelf_item.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
@@ -1306,9 +1306,10 @@
   // Switch to another user.
   void SwitchActiveUser(const AccountId& account_id) {
     GetFakeUserManager()->SwitchActiveUser(account_id);
-    ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest(
-        ash::MultiUserWindowManager::ANIMATION_SPEED_DISABLED);
-    ash::MultiUserWindowManager::Get()->OnActiveUserSessionChanged(account_id);
+    ash::MultiUserWindowManagerImpl::Get()->SetAnimationSpeedForTest(
+        ash::MultiUserWindowManagerImpl::ANIMATION_SPEED_DISABLED);
+    ash::MultiUserWindowManagerImpl::Get()->OnActiveUserSessionChanged(
+        account_id);
     // Call FlushBindings() to ensure ash has completed processing of the
     // switch.
     FlushBindings();
diff --git a/chrome/browser/ui/ash/multi_user/DEPS b/chrome/browser/ui/ash/multi_user/DEPS
index b95647d..cfd51c2 100644
--- a/chrome/browser/ui/ash/multi_user/DEPS
+++ b/chrome/browser/ui/ash/multi_user/DEPS
@@ -1,7 +1,7 @@
 specific_include_rules = {
   # https://crbug.com/875111
   "multi_user_window_manager_client_impl\.*": [
-    "+ash/multi_user/multi_user_window_manager.h",
+    "+ash/multi_user/multi_user_window_manager_impl.h",
     "+ash/multi_user/multi_user_window_manager_delegate_classic.h",
   ],
   # https://crbug.com/875111
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.cc
index f9d4e6d..18848c1 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.cc
@@ -7,7 +7,7 @@
 #include <set>
 #include <vector>
 
-#include "ash/multi_user/multi_user_window_manager.h"
+#include "ash/multi_user/multi_user_window_manager_impl.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/browser_process.h"
@@ -165,8 +165,8 @@
   }
   if (!features::IsMultiProcessMash()) {
     ash_multi_user_window_manager_ =
-        std::make_unique<ash::MultiUserWindowManager>(client, this,
-                                                      current_account_id);
+        std::make_unique<ash::MultiUserWindowManagerImpl>(client, this,
+                                                          current_account_id);
   }
 }
 
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.h b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.h
index ec5d4fb3..75fdc90 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.h
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.h
@@ -25,7 +25,7 @@
 class Browser;
 
 namespace ash {
-class MultiUserWindowManager;
+class MultiUserWindowManagerImpl;
 class MultiUserWindowManagerClientImplTest;
 }  // namespace ash
 
@@ -172,7 +172,8 @@
 
   // This is *only* created in classic and single-process mash cases. In
   // multi-process mash Ash creates the MultiUserWindowManager.
-  std::unique_ptr<ash::MultiUserWindowManager> ash_multi_user_window_manager_;
+  std::unique_ptr<ash::MultiUserWindowManagerImpl>
+      ash_multi_user_window_manager_;
 
   // Only used for windows created for the window-service. For example,
   // Browser windows when running in mash.
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl_unittest.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl_unittest.cc
index 280f81d..a3906346 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl_unittest.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl_unittest.cc
@@ -9,7 +9,7 @@
 
 #include "ash/display/screen_orientation_controller.h"
 #include "ash/display/screen_orientation_controller_test_api.h"
-#include "ash/multi_user/multi_user_window_manager.h"
+#include "ash/multi_user/multi_user_window_manager_impl.h"
 #include "ash/multi_user/user_switch_animator.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
@@ -145,7 +145,7 @@
     // switch.
     aura::test::WaitForAllChangesToComplete();
     fake_user_manager_->SwitchActiveUser(id);
-    ash::MultiUserWindowManager::Get()->OnActiveUserSessionChanged(id);
+    ash::MultiUserWindowManagerImpl::Get()->OnActiveUserSessionChanged(id);
     aura::test::WaitForAllChangesToComplete();
     MultiUserWindowManagerClientImplTestHelper::FlushBindings();
   }
@@ -156,9 +156,11 @@
   // Switch the user and wait until the animation is finished.
   void SwitchUserAndWaitForAnimation(const AccountId& account_id) {
     EnsureTestUser(account_id);
-    ash::MultiUserWindowManager::Get()->OnActiveUserSessionChanged(account_id);
+    ash::MultiUserWindowManagerImpl::Get()->OnActiveUserSessionChanged(
+        account_id);
     base::TimeTicks now = base::TimeTicks::Now();
-    while (ash::MultiUserWindowManager::Get()->IsAnimationRunningForTest()) {
+    while (
+        ash::MultiUserWindowManagerImpl::Get()->IsAnimationRunningForTest()) {
       // This should never take longer then a second.
       ASSERT_GE(1000, (base::TimeTicks::Now() - now).InMilliseconds());
       base::RunLoop().RunUntilIdle();
@@ -228,8 +230,8 @@
 
   void ShowWindowForUserNoUserTransition(aura::Window* window,
                                          const AccountId& account_id) {
-    ash::MultiUserWindowManager::Get()->ShowWindowForUserIntern(window,
-                                                                account_id);
+    ash::MultiUserWindowManagerImpl::Get()->ShowWindowForUserIntern(window,
+                                                                    account_id);
   }
 
   // The FakeChromeUserManager does not automatically call the window
@@ -250,13 +252,13 @@
 
   // Call next animation step.
   void AdvanceUserTransitionAnimation() {
-    ash::MultiUserWindowManager::Get()
+    ash::MultiUserWindowManagerImpl::Get()
         ->animation_->AdvanceUserTransitionAnimation();
   }
 
   // Return the user id of the wallpaper which is currently set.
   const std::string& GetWallpaperUserIdForTest() {
-    return ash::MultiUserWindowManager::Get()
+    return ash::MultiUserWindowManagerImpl::Get()
         ->animation_->wallpaper_user_id_for_test();
   }
 
@@ -327,8 +329,8 @@
   multi_user_window_manager_client_ =
       new MultiUserWindowManagerClientImpl(AccountId::FromUserEmail("A"));
   multi_user_window_manager_client_->Init();
-  ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest(
-      ash::MultiUserWindowManager::ANIMATION_SPEED_DISABLED);
+  ash::MultiUserWindowManagerImpl::Get()->SetAnimationSpeedForTest(
+      ash::MultiUserWindowManagerImpl::ANIMATION_SPEED_DISABLED);
   ::MultiUserWindowManagerClient::SetInstanceForTest(
       multi_user_window_manager_client_);
   wallpaper_controller_client_ = std::make_unique<WallpaperControllerClient>();
@@ -999,8 +1001,8 @@
   const AccountId account_id_C(AccountId::FromUserEmail("C"));
 
   // Turn the use of delays and animation on.
-  ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest(
-      ash::MultiUserWindowManager::ANIMATION_SPEED_FAST);
+  ash::MultiUserWindowManagerImpl::Get()->SetAnimationSpeedForTest(
+      ash::MultiUserWindowManagerImpl::ANIMATION_SPEED_FAST);
   // Set some owners and make sure we got what we asked for.
   multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A);
   multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_B);
@@ -1038,8 +1040,8 @@
   const AccountId account_id_B(AccountId::FromUserEmail("B"));
 
   // Turn the use of delays and animation on.
-  ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest(
-      ash::MultiUserWindowManager::ANIMATION_SPEED_FAST);
+  ash::MultiUserWindowManagerImpl::Get()->SetAnimationSpeedForTest(
+      ash::MultiUserWindowManagerImpl::ANIMATION_SPEED_FAST);
   // Set some owners and make sure we got what we asked for.
   multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A);
   multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_B);
@@ -1059,8 +1061,8 @@
   const AccountId account_id_C(AccountId::FromUserEmail("C"));
 
   // Turn the use of delays and animation on.
-  ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest(
-      ash::MultiUserWindowManager::ANIMATION_SPEED_FAST);
+  ash::MultiUserWindowManagerImpl::Get()->SetAnimationSpeedForTest(
+      ash::MultiUserWindowManagerImpl::ANIMATION_SPEED_FAST);
   // Set some owners and make sure we got what we asked for.
   multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A);
   multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_B);
@@ -1117,8 +1119,8 @@
   const AccountId account_id_C(AccountId::FromUserEmail("C"));
 
   // Turn the use of delays and animation on.
-  ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest(
-      ash::MultiUserWindowManager::ANIMATION_SPEED_FAST);
+  ash::MultiUserWindowManagerImpl::Get()->SetAnimationSpeedForTest(
+      ash::MultiUserWindowManagerImpl::ANIMATION_SPEED_FAST);
   // Set some owners and make sure we got what we asked for.
   multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A);
   wm::GetWindowState(window(0))->Maximize();
@@ -1162,8 +1164,8 @@
   const AccountId account_id_C(AccountId::FromUserEmail("C"));
 
   // Turn the use of delays and animation on.
-  ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest(
-      ash::MultiUserWindowManager::ANIMATION_SPEED_FAST);
+  ash::MultiUserWindowManagerImpl::Get()->SetAnimationSpeedForTest(
+      ash::MultiUserWindowManagerImpl::ANIMATION_SPEED_FAST);
   // Set some owners and make sure we got what we asked for.
   multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A);
   multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_B);
@@ -1209,8 +1211,8 @@
   const AccountId account_id_C(AccountId::FromUserEmail("C"));
 
   // Turn the use of delays and animation on.
-  ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest(
-      ash::MultiUserWindowManager::ANIMATION_SPEED_FAST);
+  ash::MultiUserWindowManagerImpl::Get()->SetAnimationSpeedForTest(
+      ash::MultiUserWindowManagerImpl::ANIMATION_SPEED_FAST);
   // Set some owners and make sure we got what we asked for.
   multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A);
   wm::GetWindowState(window(0))->Maximize();
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index 36b1bfe..35a5f01 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -333,70 +333,18 @@
                                         std::unique_ptr<WebContents> contents,
                                         int add_types,
                                         const TabGroupData* group) {
-  // TODO(erikchne): Change this to a CHECK. https://crbug.com/851400.
   DCHECK(!reentrancy_guard_);
   base::AutoReset<bool> resetter(&reentrancy_guard_, true);
 
-  delegate()->WillAddWebContents(contents.get());
-
-  bool active = (add_types & ADD_ACTIVE) != 0;
-  bool pin = (add_types & ADD_PINNED) != 0;
-  if (group)
-    pin = IsGroupPinned(group);
-  index = ConstrainInsertionIndex(index, pin);
-
-  // Have to get the active contents before we monkey with the contents
-  // otherwise we run into problems when we try to change the active contents
-  // since the old contents and the new contents will be the same...
-  WebContents* active_contents = GetActiveWebContents();
-  WebContents* raw_contents = contents.get();
-  std::unique_ptr<WebContentsData> data =
-      std::make_unique<WebContentsData>(std::move(contents));
-  data->set_pinned(pin);
-  if ((add_types & ADD_INHERIT_OPENER) && active_contents) {
-    if (active) {
-      // Forget any existing relationships, we don't want to make things too
-      // confusing by having multiple openers active at the same time.
-      ForgetAllOpeners();
-    }
-    data->set_opener(active_contents);
-  }
-
-  // TODO(gbillock): Ask the modal dialog manager whether the WebContents should
-  // be blocked, or just let the modal dialog manager make the blocking call
-  // directly and not use this at all.
-  const web_modal::WebContentsModalDialogManager* manager =
-      web_modal::WebContentsModalDialogManager::FromWebContents(raw_contents);
-  if (manager)
-    data->set_blocked(manager->IsDialogActive());
-
-  TabStripSelectionChange selection(GetActiveWebContents(), selection_model_);
-
-  contents_data_.insert(contents_data_.begin() + index, std::move(data));
-
-  selection_model_.IncrementFrom(index);
-
-  if (active) {
-    ui::ListSelectionModel new_model = selection_model_;
-    new_model.SetSelectedIndex(index);
-    selection = SetSelection(std::move(new_model),
-                             TabStripModelObserver::CHANGE_REASON_NONE,
-                             /*triggered_by_other_operation=*/true);
-  }
-
-  if (group)
-    contents_data_[index]->set_group(group);
-
-  TabStripModelChange change(
-      TabStripModelChange::kInserted,
-      TabStripModelChange::CreateInsertDelta(raw_contents, index));
-  for (auto& observer : observers_)
-    observer.OnTabStripModelChanged(this, change, selection);
+  InsertWebContentsAtImpl(index, std::move(contents), add_types, group);
 }
 
 std::unique_ptr<content::WebContents> TabStripModel::ReplaceWebContentsAt(
     int index,
     std::unique_ptr<WebContents> new_contents) {
+  DCHECK(!reentrancy_guard_);
+  base::AutoReset<bool> resetter(&reentrancy_guard_, true);
+
   delegate()->WillAddWebContents(new_contents.get());
 
   DCHECK(ContainsIndex(index));
@@ -427,7 +375,6 @@
 
 std::unique_ptr<content::WebContents> TabStripModel::DetachWebContentsAt(
     int index) {
-  // TODO(erikchne): Change this to a CHECK. https://crbug.com/851400.
   DCHECK(!reentrancy_guard_);
   base::AutoReset<bool> resetter(&reentrancy_guard_, true);
 
@@ -550,6 +497,9 @@
 }
 
 void TabStripModel::ActivateTabAt(int index, UserGestureDetails user_gesture) {
+  DCHECK(!reentrancy_guard_);
+  base::AutoReset<bool> resetter(&reentrancy_guard_, true);
+
   DCHECK(ContainsIndex(index));
   TRACE_EVENT0("ui", "TabStripModel::ActivateTabAt");
 
@@ -585,6 +535,9 @@
 void TabStripModel::MoveWebContentsAt(int index,
                                       int to_position,
                                       bool select_after_move) {
+  DCHECK(!reentrancy_guard_);
+  base::AutoReset<bool> resetter(&reentrancy_guard_, true);
+
   DCHECK(ContainsIndex(index));
 
   // Ensure pinned and non-pinned tabs do not mix.
@@ -599,6 +552,9 @@
 }
 
 void TabStripModel::MoveSelectedTabsTo(int index) {
+  DCHECK(!reentrancy_guard_);
+  base::AutoReset<bool> resetter(&reentrancy_guard_, true);
+
   int total_pinned_count = IndexOfFirstNonPinnedTab();
   int selected_pinned_count = 0;
   int selected_count =
@@ -666,6 +622,9 @@
 }
 
 void TabStripModel::CloseAllTabs() {
+  DCHECK(!reentrancy_guard_);
+  base::AutoReset<bool> resetter(&reentrancy_guard_, true);
+
   // Set state so that observers can adjust their behavior to suit this
   // specific condition when CloseWebContentsAt causes a flurry of
   // Close/Detach/Select notifications to be sent.
@@ -762,25 +721,10 @@
 }
 
 void TabStripModel::SetTabPinned(int index, bool pinned) {
-  DCHECK(ContainsIndex(index));
-  if (contents_data_[index]->pinned() == pinned)
-    return;
+  DCHECK(!reentrancy_guard_);
+  base::AutoReset<bool> resetter(&reentrancy_guard_, true);
 
-  // The tab's position may have to change as the pinned tab state is changing.
-  int non_pinned_tab_index = IndexOfFirstNonPinnedTab();
-  contents_data_[index]->set_pinned(pinned);
-  if (pinned && index != non_pinned_tab_index) {
-    MoveWebContentsAtImpl(index, non_pinned_tab_index, false);
-    index = non_pinned_tab_index;
-  } else if (!pinned && index + 1 != non_pinned_tab_index) {
-    MoveWebContentsAtImpl(index, non_pinned_tab_index - 1, false);
-    index = non_pinned_tab_index - 1;
-  }
-
-  for (auto& observer : observers_) {
-    observer.TabPinnedStateChanged(this, contents_data_[index]->web_contents(),
-                                   index);
-  }
+  SetTabPinnedImpl(index, pinned);
 }
 
 bool TabStripModel::IsTabPinned(int index) const {
@@ -893,6 +837,9 @@
                                    ui::PageTransition transition,
                                    int add_types,
                                    const TabGroupData* group) {
+  DCHECK(!reentrancy_guard_);
+  base::AutoReset<bool> resetter(&reentrancy_guard_, true);
+
   // If the newly-opened tab is part of the same task as the parent tab, we want
   // to inherit the parent's opener attribute, so that if this tab is then
   // closed we'll jump back to the parent tab.
@@ -928,9 +875,9 @@
     inherit_opener = true;
   }
   WebContents* raw_contents = contents.get();
-  InsertWebContentsAt(index, std::move(contents),
-                      add_types | (inherit_opener ? ADD_INHERIT_OPENER : 0),
-                      group);
+  InsertWebContentsAtImpl(index, std::move(contents),
+                          add_types | (inherit_opener ? ADD_INHERIT_OPENER : 0),
+                          group);
   // Reset the index, just in case insert ended up moving it on us.
   index = GetIndexOfWebContents(raw_contents);
 
@@ -960,6 +907,9 @@
 }
 
 void TabStripModel::CloseSelectedTabs() {
+  DCHECK(!reentrancy_guard_);
+  base::AutoReset<bool> resetter(&reentrancy_guard_, true);
+
   InternalCloseTabs(
       GetWebContentsesByIndices(selection_model_.selected_indices()),
       CLOSE_CREATE_HISTORICAL_TAB | CLOSE_USER_GESTURE);
@@ -990,6 +940,9 @@
 }
 
 void TabStripModel::AddToNewGroup(const std::vector<int>& indices) {
+  DCHECK(!reentrancy_guard_);
+  base::AutoReset<bool> resetter(&reentrancy_guard_, true);
+
   group_data_.push_back(std::make_unique<TabGroupData>());
   TabGroupData* new_group = group_data_.back().get();
 
@@ -1016,6 +969,9 @@
 
 void TabStripModel::AddToExistingGroup(const std::vector<int>& indices,
                                        const TabGroupData* group) {
+  DCHECK(!reentrancy_guard_);
+  base::AutoReset<bool> resetter(&reentrancy_guard_, true);
+
   int destination_index = -1;
   bool pin = false;
   for (int i = contents_data_.size() - 1; i >= 0; i--) {
@@ -1038,6 +994,9 @@
 }
 
 void TabStripModel::RemoveFromGroup(const std::vector<int>& indices) {
+  DCHECK(!reentrancy_guard_);
+  base::AutoReset<bool> resetter(&reentrancy_guard_, true);
+
   // Remove each tab from the group it's in, if any. Go from right to left
   // since tabs may move to the right.
   for (int i = indices.size() - 1; i >= 0; i--) {
@@ -1184,6 +1143,9 @@
     }
 
     case CommandCloseTab: {
+      DCHECK(!reentrancy_guard_);
+      base::AutoReset<bool> resetter(&reentrancy_guard_, true);
+
       base::RecordAction(UserMetricsAction("TabContextMenu_CloseTab"));
       InternalCloseTabs(
           GetWebContentsesByIndices(GetIndicesForCommand(context_index)),
@@ -1192,6 +1154,9 @@
     }
 
     case CommandCloseOtherTabs: {
+      DCHECK(!reentrancy_guard_);
+      base::AutoReset<bool> resetter(&reentrancy_guard_, true);
+
       base::RecordAction(UserMetricsAction("TabContextMenu_CloseOtherTabs"));
       InternalCloseTabs(GetWebContentsesByIndices(GetIndicesClosedByCommand(
                             context_index, command_id)),
@@ -1200,6 +1165,9 @@
     }
 
     case CommandCloseTabsToRight: {
+      DCHECK(!reentrancy_guard_);
+      base::AutoReset<bool> resetter(&reentrancy_guard_, true);
+
       base::RecordAction(UserMetricsAction("TabContextMenu_CloseTabsToRight"));
       InternalCloseTabs(GetWebContentsesByIndices(GetIndicesClosedByCommand(
                             context_index, command_id)),
@@ -1221,17 +1189,20 @@
     }
 
     case CommandTogglePinned: {
+      DCHECK(!reentrancy_guard_);
+      base::AutoReset<bool> resetter(&reentrancy_guard_, true);
+
       base::RecordAction(UserMetricsAction("TabContextMenu_TogglePinned"));
       std::vector<int> indices = GetIndicesForCommand(context_index);
       bool pin = WillContextMenuPin(context_index);
       if (pin) {
         for (size_t i = 0; i < indices.size(); ++i)
-          SetTabPinned(indices[i], true);
+          SetTabPinnedImpl(indices[i], true);
       } else {
         // Unpin from the back so that the order is maintained (unpinning can
         // trigger moving a tab).
         for (size_t i = indices.size(); i > 0; --i)
-          SetTabPinned(indices[i - 1], false);
+          SetTabPinnedImpl(indices[i - 1], false);
       }
       break;
     }
@@ -1448,13 +1419,71 @@
   return items;
 }
 
+void TabStripModel::InsertWebContentsAtImpl(
+    int index,
+    std::unique_ptr<content::WebContents> contents,
+    int add_types,
+    const TabGroupData* group) {
+  delegate()->WillAddWebContents(contents.get());
+
+  bool active = (add_types & ADD_ACTIVE) != 0;
+  bool pin = (add_types & ADD_PINNED) != 0;
+  if (group)
+    pin = IsGroupPinned(group);
+  index = ConstrainInsertionIndex(index, pin);
+
+  // Have to get the active contents before we monkey with the contents
+  // otherwise we run into problems when we try to change the active contents
+  // since the old contents and the new contents will be the same...
+  WebContents* active_contents = GetActiveWebContents();
+  WebContents* raw_contents = contents.get();
+  std::unique_ptr<WebContentsData> data =
+      std::make_unique<WebContentsData>(std::move(contents));
+  data->set_pinned(pin);
+  if ((add_types & ADD_INHERIT_OPENER) && active_contents) {
+    if (active) {
+      // Forget any existing relationships, we don't want to make things too
+      // confusing by having multiple openers active at the same time.
+      ForgetAllOpeners();
+    }
+    data->set_opener(active_contents);
+  }
+
+  // TODO(gbillock): Ask the modal dialog manager whether the WebContents should
+  // be blocked, or just let the modal dialog manager make the blocking call
+  // directly and not use this at all.
+  const web_modal::WebContentsModalDialogManager* manager =
+      web_modal::WebContentsModalDialogManager::FromWebContents(raw_contents);
+  if (manager)
+    data->set_blocked(manager->IsDialogActive());
+
+  TabStripSelectionChange selection(GetActiveWebContents(), selection_model_);
+
+  contents_data_.insert(contents_data_.begin() + index, std::move(data));
+
+  selection_model_.IncrementFrom(index);
+
+  if (active) {
+    ui::ListSelectionModel new_model = selection_model_;
+    new_model.SetSelectedIndex(index);
+    selection = SetSelection(std::move(new_model),
+                             TabStripModelObserver::CHANGE_REASON_NONE,
+                             /*triggered_by_other_operation=*/true);
+  }
+
+  if (group)
+    contents_data_[index]->set_group(group);
+
+  TabStripModelChange change(
+      TabStripModelChange::kInserted,
+      TabStripModelChange::CreateInsertDelta(raw_contents, index));
+  for (auto& observer : observers_)
+    observer.OnTabStripModelChanged(this, change, selection);
+}
+
 bool TabStripModel::InternalCloseTabs(
     base::span<content::WebContents* const> items,
     uint32_t close_types) {
-  // TODO(erikchne): Change this to a CHECK. https://crbug.com/851400.
-  DCHECK(!reentrancy_guard_);
-  base::AutoReset<bool> resetter(&reentrancy_guard_, true);
-
   if (items.empty())
     return true;
 
@@ -1659,8 +1688,8 @@
   size_t tab_index = start;
   while (tab_index < end &&
          selection_model_.selected_indices()[start] < index) {
-    MoveWebContentsAt(selection_model_.selected_indices()[start],
-                      target_index - 1, false);
+    MoveWebContentsAtImpl(selection_model_.selected_indices()[start],
+                          target_index - 1, false);
     tab_index++;
   }
 
@@ -1668,8 +1697,8 @@
   // selection.
   while (tab_index < end) {
     if (selection_model_.selected_indices()[tab_index] != target_index) {
-      MoveWebContentsAt(selection_model_.selected_indices()[tab_index],
-                        target_index, false);
+      MoveWebContentsAtImpl(selection_model_.selected_indices()[tab_index],
+                            target_index, false);
     }
     tab_index++;
     target_index++;
@@ -1710,7 +1739,8 @@
   // TODO(crbug.com/940677): Ideally the delta of type kGroupChanged below would
   // be batched with the move deltas resulting from MoveWebContentsAt, but that
   // is not possible right now.
-  MoveWebContentsAt(index, new_index, false);
+  if (index != new_index)
+    MoveWebContentsAtImpl(index, new_index, false);
   WebContentsData* contents_data = contents_data_[new_index].get();
   contents_data->set_group(new_group);
 
@@ -1752,6 +1782,28 @@
   return group;
 }
 
+void TabStripModel::SetTabPinnedImpl(int index, bool pinned) {
+  DCHECK(ContainsIndex(index));
+  if (contents_data_[index]->pinned() == pinned)
+    return;
+
+  // The tab's position may have to change as the pinned tab state is changing.
+  int non_pinned_tab_index = IndexOfFirstNonPinnedTab();
+  contents_data_[index]->set_pinned(pinned);
+  if (pinned && index != non_pinned_tab_index) {
+    MoveWebContentsAtImpl(index, non_pinned_tab_index, false);
+    index = non_pinned_tab_index;
+  } else if (!pinned && index + 1 != non_pinned_tab_index) {
+    MoveWebContentsAtImpl(index, non_pinned_tab_index - 1, false);
+    index = non_pinned_tab_index - 1;
+  }
+
+  for (auto& observer : observers_) {
+    observer.TabPinnedStateChanged(this, contents_data_[index]->web_contents(),
+                                   index);
+  }
+}
+
 std::vector<int> TabStripModel::SetTabsPinned(const std::vector<int>& indices,
                                               bool pinned) {
   std::vector<int> new_indices;
@@ -1760,7 +1812,7 @@
       if (IsTabPinned(indices[i])) {
         new_indices.push_back(indices[i]);
       } else {
-        SetTabPinned(indices[i], true);
+        SetTabPinnedImpl(indices[i], true);
         new_indices.push_back(IndexOfFirstNonPinnedTab() - 1);
       }
     }
@@ -1769,7 +1821,7 @@
       if (!IsTabPinned(indices[i])) {
         new_indices.push_back(indices[i]);
       } else {
-        SetTabPinned(indices[i], false);
+        SetTabPinnedImpl(indices[i], false);
         new_indices.push_back(IndexOfFirstNonPinnedTab());
       }
     }
diff --git a/chrome/browser/ui/tabs/tab_strip_model.h b/chrome/browser/ui/tabs/tab_strip_model.h
index e6235b31..a7205f7 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.h
+++ b/chrome/browser/ui/tabs/tab_strip_model.h
@@ -520,6 +520,21 @@
   // something related to their current activity.
   bool IsNewTabAtEndOfTabStrip(content::WebContents* contents) const;
 
+  // Adds the specified WebContents at the specified location.
+  // |add_types| is a bitmask of AddTabTypes; see it for details.
+  //
+  // All append/insert methods end up in this method.
+  //
+  // NOTE: adding a tab using this method does NOT query the order controller,
+  // as such the ADD_FORCE_INDEX AddTabTypes is meaningless here.  The only time
+  // the |index| is changed is if using the index would result in breaking the
+  // constraint that all pinned tabs occur before non-pinned tabs.
+  // See also AddWebContents.
+  void InsertWebContentsAtImpl(int index,
+                               std::unique_ptr<content::WebContents> contents,
+                               int add_types,
+                               const TabGroupData* group);
+
   // Closes the WebContentses at the specified indices. This causes the
   // WebContentses to be destroyed, but it may not happen immediately. If
   // the page in question has an unload event the WebContents will not be
@@ -598,6 +613,9 @@
   // no tabs. Returns that group.
   const TabGroupData* UngroupTab(int index);
 
+  // Changes the pinned state of the tab at |index|.
+  void SetTabPinnedImpl(int index, bool pinned);
+
   // Ensures all tabs indicated by |indices| are pinned, moving them in the
   // process if necessary. Returns the new locations of all of those tabs.
   std::vector<int> SetTabsPinned(const std::vector<int>& indices, bool pinned);
diff --git a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
index c3610d1..cf2fd02 100644
--- a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
@@ -2203,6 +2203,78 @@
   strip.CloseAllTabs();
 }
 
+TEST_F(TabStripModelTest, MoveTabNext) {
+  TestTabStripModelDelegate delegate;
+  TabStripModel strip(&delegate, profile());
+  ASSERT_NO_FATAL_FAILURE(PrepareTabstripForSelectionTest(&strip, 6, 3, "3"));
+  EXPECT_EQ("0p 1p 2p 3 4 5", GetTabStripStateString(strip));
+
+  strip.MoveTabNext();
+  EXPECT_EQ("0p 1p 2p 4 3 5", GetTabStripStateString(strip));
+
+  strip.MoveTabNext();
+  EXPECT_EQ("0p 1p 2p 4 5 3", GetTabStripStateString(strip));
+
+  strip.MoveTabNext();
+  EXPECT_EQ("0p 1p 2p 4 5 3", GetTabStripStateString(strip));
+
+  strip.CloseAllTabs();
+}
+
+TEST_F(TabStripModelTest, MoveTabNext_Pinned) {
+  TestTabStripModelDelegate delegate;
+  TabStripModel strip(&delegate, profile());
+  ASSERT_NO_FATAL_FAILURE(PrepareTabstripForSelectionTest(&strip, 6, 3, "0"));
+  EXPECT_EQ("0p 1p 2p 3 4 5", GetTabStripStateString(strip));
+
+  strip.MoveTabNext();
+  EXPECT_EQ("1p 0p 2p 3 4 5", GetTabStripStateString(strip));
+
+  strip.MoveTabNext();
+  EXPECT_EQ("1p 2p 0p 3 4 5", GetTabStripStateString(strip));
+
+  strip.MoveTabNext();
+  EXPECT_EQ("1p 2p 0p 3 4 5", GetTabStripStateString(strip));
+
+  strip.CloseAllTabs();
+}
+
+TEST_F(TabStripModelTest, MoveTabPrevious) {
+  TestTabStripModelDelegate delegate;
+  TabStripModel strip(&delegate, profile());
+  ASSERT_NO_FATAL_FAILURE(PrepareTabstripForSelectionTest(&strip, 6, 3, "5"));
+  EXPECT_EQ("0p 1p 2p 3 4 5", GetTabStripStateString(strip));
+
+  strip.MoveTabPrevious();
+  EXPECT_EQ("0p 1p 2p 3 5 4", GetTabStripStateString(strip));
+
+  strip.MoveTabPrevious();
+  EXPECT_EQ("0p 1p 2p 5 3 4", GetTabStripStateString(strip));
+
+  strip.MoveTabPrevious();
+  EXPECT_EQ("0p 1p 2p 5 3 4", GetTabStripStateString(strip));
+
+  strip.CloseAllTabs();
+}
+
+TEST_F(TabStripModelTest, MoveTabPrevious_Pinned) {
+  TestTabStripModelDelegate delegate;
+  TabStripModel strip(&delegate, profile());
+  ASSERT_NO_FATAL_FAILURE(PrepareTabstripForSelectionTest(&strip, 6, 3, "2"));
+  EXPECT_EQ("0p 1p 2p 3 4 5", GetTabStripStateString(strip));
+
+  strip.MoveTabPrevious();
+  EXPECT_EQ("0p 2p 1p 3 4 5", GetTabStripStateString(strip));
+
+  strip.MoveTabPrevious();
+  EXPECT_EQ("2p 0p 1p 3 4 5", GetTabStripStateString(strip));
+
+  strip.MoveTabPrevious();
+  EXPECT_EQ("2p 0p 1p 3 4 5", GetTabStripStateString(strip));
+
+  strip.CloseAllTabs();
+}
+
 TEST_F(TabStripModelTest, MoveSelectedTabsTo) {
   struct TestData {
     // Number of tabs the tab strip should have.
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views.cc b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views.cc
index 84808f6..e8fdfef 100644
--- a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views.cc
+++ b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views.cc
@@ -196,9 +196,7 @@
     box_layout->SetFlexForView(spacer, /*flex=*/1);
 
     auto* expiration_date_label = new views::Label(
-        card.AbbreviatedExpirationDateForDisplay(
-            !features::
-                IsAutofillSaveCardDialogUnlabeledExpirationDateEnabled()),
+        card.AbbreviatedExpirationDateForDisplay(false),
         CONTEXT_BODY_TEXT_LARGE, ChromeTextStyle::STYLE_SECONDARY);
     expiration_date_label->set_id(DialogViewId::EXPIRATION_DATE_LABEL);
     description_view->AddChildView(expiration_date_label);
diff --git a/chrome/browser/ui/views/passwords/password_pending_view.cc b/chrome/browser/ui/views/passwords/password_pending_view.cc
index a2a0b92..b341405 100644
--- a/chrome/browser/ui/views/passwords/password_pending_view.cc
+++ b/chrome/browser/ui/views/passwords/password_pending_view.cc
@@ -389,12 +389,16 @@
 }
 
 void PasswordPendingView::UpdateUsernameAndPasswordInModel() {
-  DCHECK(username_dropdown_ && password_dropdown_);
+  if (!username_dropdown_ && !password_dropdown_)
+    return;
   base::string16 new_username = model()->pending_password().username_value;
   base::string16 new_password = model()->pending_password().password_value;
-  new_username = username_dropdown_->GetText();
-  base::TrimString(new_username, base::ASCIIToUTF16(" "), &new_username);
-  new_password = password_dropdown_->GetText();
+  if (username_dropdown_) {
+    new_username = username_dropdown_->GetText();
+    base::TrimString(new_username, base::ASCIIToUTF16(" "), &new_username);
+  }
+  if (password_dropdown_)
+    new_password = password_dropdown_->GetText();
   model()->OnCredentialEdited(std::move(new_username), std::move(new_password));
 }
 
diff --git a/chrome/browser/ui/views/tabs/tab_animation_state.cc b/chrome/browser/ui/views/tabs/tab_animation_state.cc
new file mode 100644
index 0000000..76646dc
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/tab_animation_state.cc
@@ -0,0 +1,72 @@
+// 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/ui/views/tabs/tab_animation_state.h"
+
+#include "chrome/browser/ui/views/tabs/tab_strip_layout.h"
+#include "ui/gfx/animation/tween.h"
+
+TabAnimationState TabAnimationState::ForIdealTabState(TabOpenness open,
+                                                      TabPinnedness pinned,
+                                                      TabActiveness active,
+                                                      int tab_index_offset) {
+  return TabAnimationState(open == TabOpenness::kOpen ? 1 : 0,
+                           pinned == TabPinnedness::kPinned ? 1 : 0,
+                           active == TabActiveness::kActive ? 1 : 0,
+                           tab_index_offset);
+}
+
+TabAnimationState TabAnimationState::Interpolate(float value,
+                                                 TabAnimationState origin,
+                                                 TabAnimationState target) {
+  return TabAnimationState(
+      gfx::Tween::FloatValueBetween(value, origin.normalized_leading_edge_x_,
+                                    target.normalized_leading_edge_x_),
+      gfx::Tween::FloatValueBetween(value, origin.openness_, target.openness_),
+      gfx::Tween::FloatValueBetween(value, origin.pinnedness_,
+                                    target.pinnedness_),
+      gfx::Tween::FloatValueBetween(value, origin.activeness_,
+                                    target.activeness_));
+}
+
+float TabAnimationState::GetMinimumWidth(TabSizeInfo tab_size_info) const {
+  const float min_width = gfx::Tween::FloatValueBetween(
+      activeness_, tab_size_info.min_inactive_width,
+      tab_size_info.min_active_width);
+  return TransformForPinnednessAndOpenness(tab_size_info, min_width);
+}
+
+float TabAnimationState::GetLayoutCrossoverWidth(
+    TabSizeInfo tab_size_info) const {
+  return TransformForPinnednessAndOpenness(tab_size_info,
+                                           tab_size_info.min_active_width);
+}
+
+float TabAnimationState::GetPreferredWidth(TabSizeInfo tab_size_info) const {
+  return TransformForPinnednessAndOpenness(tab_size_info,
+                                           tab_size_info.standard_size.width());
+}
+
+float TabAnimationState::TransformForPinnednessAndOpenness(
+    TabSizeInfo tab_size_info,
+    float width) const {
+  const float pinned_width = gfx::Tween::FloatValueBetween(
+      pinnedness_, width, tab_size_info.pinned_tab_width);
+  return gfx::Tween::FloatValueBetween(openness_, tab_size_info.tab_overlap,
+                                       pinned_width);
+}
+
+int TabAnimationState::GetLeadingEdgeOffset(std::vector<int> tab_widths,
+                                            int my_index) const {
+  // TODO(949660): Implement this to handle animated tab translations. Sum
+  // widths from my_index to my_index +
+  // round_towards_zero(normalized_leading_edge_x), inclusive. Add the
+  // fractional part for the width of the last tab.
+  // A different approach that doesn't stretch/compress space based on
+  // tab widths might be needed. Though maybe not, since very few animations
+  // will actually translate tabs across a mixture of pinned and unpinned
+  // tabs.
+  NOTIMPLEMENTED();
+  return 0;
+}
diff --git a/chrome/browser/ui/views/tabs/tab_animation_state.h b/chrome/browser/ui/views/tabs/tab_animation_state.h
new file mode 100644
index 0000000..dbba925
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/tab_animation_state.h
@@ -0,0 +1,85 @@
+// 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_UI_VIEWS_TABS_TAB_ANIMATION_STATE_H_
+#define CHROME_BROWSER_UI_VIEWS_TABS_TAB_ANIMATION_STATE_H_
+
+#include <vector>
+
+struct TabSizeInfo;
+
+// Contains the data necessary to determine the bounds of a tab even while
+// it's in the middle of animating between states.  Immutable.
+class TabAnimationState {
+ public:
+  enum class TabOpenness { kOpen, kClosed };
+
+  enum class TabPinnedness { kPinned, kUnpinned };
+
+  enum class TabActiveness { kActive, kInactive };
+
+  // Returns the TabAnimationState that expresses the provided
+  // ideal tab state. These correspond to the endpoints of animations.
+  // |open| controls whether the returned TabAnimationState is fully open or
+  // closed. |pinned| and |active| are analogous. |tab_index_offset| is the
+  // distance, in tab indices, away from its current model position the tab
+  // should be drawn at. It may be negative.
+  static TabAnimationState ForIdealTabState(TabOpenness open,
+                                            TabPinnedness pinned,
+                                            TabActiveness active,
+                                            int tab_index_offset);
+
+  // Interpolates from |origin| to |target| by |value|.
+  // |value| should be in the [0, 1] interval, where 0
+  // corresponds to |origin| and 1 to |target|.
+  static TabAnimationState Interpolate(float value,
+                                       TabAnimationState origin,
+                                       TabAnimationState target);
+
+  // The smallest width this tab should ever have.
+  float GetMinimumWidth(TabSizeInfo tab_size_info) const;
+
+  // The width this tab should have at the crossover point between the
+  // tabstrip's two layout domains.  Above this width, inactive tabs have the
+  // same width as active tabs.  Below this width, inactive tabs are smaller
+  // than active tabs.
+  float GetLayoutCrossoverWidth(TabSizeInfo tab_size_info) const;
+
+  // The width this tab would like to have, if space is available.
+  float GetPreferredWidth(TabSizeInfo tab_size_info) const;
+
+  int GetLeadingEdgeOffset(std::vector<int> tab_widths, int my_index) const;
+
+ private:
+  TabAnimationState(float openness,
+                    float pinnedness,
+                    float activeness,
+                    float normalized_leading_edge_x)
+      : openness_(openness),
+        pinnedness_(pinnedness),
+        activeness_(activeness),
+        normalized_leading_edge_x_(normalized_leading_edge_x) {}
+
+  // All widths are affected by pinnedness and activeness in the same way.
+  float TransformForPinnednessAndOpenness(TabSizeInfo tab_size_info,
+                                          float width) const;
+
+  // The degree to which the tab is open. 1 if it is, 0 if it is not, and in
+  // between if it's in the process of animating between open and closed.
+  const float openness_;
+
+  // The degree to which the tab is pinned. 1 if it is, 0 if it is not, and in
+  // between if it's in the process of animating between pinned and unpinned.
+  const float pinnedness_;
+
+  // The degree to which the tab is active. 1 if it is, 0 if it is not, and in
+  // between if it's in the process of animating between active and inactive.
+  const float activeness_;
+
+  // The offset, in number of tab slots, of the tab's bounds relative to the
+  // space dedicated for it in the tabstrip.
+  const float normalized_leading_edge_x_;
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_TABS_TAB_ANIMATION_STATE_H_
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
index dafe0ee..4e49a8ed 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
@@ -237,7 +237,7 @@
   layout->SetCollapseMargins(true);
 
   constexpr int kOuterMargin = 12;
-  constexpr int kLineSpacing = 8;
+  constexpr int kLineSpacing = 0;
   title_label_->SetProperty(
       views::kMarginsKey,
       new gfx::Insets(kOuterMargin, kOuterMargin, kLineSpacing, kOuterMargin));
diff --git a/chrome/browser/ui/views/tabs/tab_strip_layout.cc b/chrome/browser/ui/views/tabs/tab_strip_layout.cc
index 15814b69..f537194 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_layout.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_layout.cc
@@ -9,109 +9,194 @@
 #include <algorithm>
 
 #include "base/logging.h"
+#include "base/numerics/ranges.h"
+#include "chrome/browser/ui/views/tabs/tab_animation_state.h"
+#include "ui/gfx/animation/tween.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace {
 
-// Calculates the size for normal tabs.
-// |is_active_tab_normal| is true if the active tab is a normal tab, if false
-// the active tab is not in the set of normal tabs.
-// |normal_width| is the available width for normal tabs.
-void CalculateNormalTabWidths(const TabSizeInfo& tab_size_info,
-                              bool is_active_tab_normal,
-                              int num_normal_tabs,
-                              int normal_width,
-                              int* active_width,
-                              int* inactive_width) {
-  DCHECK_NE(0, num_normal_tabs);
+// Inactive tabs have a smaller minimum width than the active tab. Layout has
+// different behavior when inactive tabs are smaller than the active tab
+// than it does when they are the same size.
+enum class LayoutDomain {
+  // There is not enough space for inactive tabs to match the active tab's
+  // width.
+  kInactiveWidthBelowActiveWidth,
+  // There is enough space for inactive tabs to match the active tab's width.
+  kInactiveWidthEqualsActiveWidth
+};
 
-  // Calculate the desired tab width by dividing the available space into equal
-  // portions, bounded by the standard width.
-  normal_width += tab_size_info.tab_overlap * (num_normal_tabs - 1);
-  int desired_tab_width = std::min(normal_width / num_normal_tabs,
-                                   tab_size_info.standard_size.width());
+// Determines the size of each tab given information on the overall amount
+// of space available relative to how much the tabs could use.
+class TabSizer {
+ public:
+  TabSizer(TabSizeInfo tab_size_info,
+           LayoutDomain domain,
+           float space_fraction_available)
+      : tab_size_info_(tab_size_info),
+        domain_(domain),
+        space_fraction_available_(space_fraction_available) {}
 
-  *active_width = std::max(desired_tab_width, tab_size_info.min_active_width);
+  int CalculateTabWidth(TabAnimationState tab) {
+    switch (domain_) {
+      case LayoutDomain::kInactiveWidthBelowActiveWidth:
+        return std::floor(gfx::Tween::FloatValueBetween(
+            space_fraction_available_, tab.GetMinimumWidth(tab_size_info_),
+            tab.GetLayoutCrossoverWidth(tab_size_info_)));
+      case LayoutDomain::kInactiveWidthEqualsActiveWidth:
+        return std::floor(gfx::Tween::FloatValueBetween(
+            space_fraction_available_,
+            tab.GetLayoutCrossoverWidth(tab_size_info_),
+            tab.GetPreferredWidth(tab_size_info_)));
+    }
+  }
 
-  // If |desired_tab_width| is smaller than the minimum active tab width, then
-  // we need to recalculate it having accounted for the active tab, since that
-  // may further shrink inactive tabs.
-  if ((*active_width > desired_tab_width) && is_active_tab_normal &&
-      (num_normal_tabs > 1))
-    desired_tab_width = (normal_width - *active_width) / (num_normal_tabs - 1);
+  // Returns true iff it's OK for this tab to be one pixel wider than
+  // CalculateTabWidth(|tab|).
+  bool TabAcceptsExtraSpace(TabAnimationState tab) {
+    if (space_fraction_available_ == 0.0f || space_fraction_available_ == 1.0f)
+      return false;
+    switch (domain_) {
+      case LayoutDomain::kInactiveWidthBelowActiveWidth:
+        return tab.GetMinimumWidth(tab_size_info_) <
+               tab.GetLayoutCrossoverWidth(tab_size_info_);
+      case LayoutDomain::kInactiveWidthEqualsActiveWidth:
+        return tab.GetLayoutCrossoverWidth(tab_size_info_) <
+               tab.GetPreferredWidth(tab_size_info_);
+    }
+  }
 
-  *inactive_width =
-      std::max(desired_tab_width, tab_size_info.min_inactive_width);
+  bool IsAlreadyPreferredWidth() {
+    return domain_ == LayoutDomain::kInactiveWidthEqualsActiveWidth &&
+           space_fraction_available_ == 1;
+  }
+
+ private:
+  const TabSizeInfo tab_size_info_;
+  const LayoutDomain domain_;
+
+  // The proportion of space requirements we can fulfill within the layout
+  // domain we're in.
+  const float space_fraction_available_;
+};
+
+// Solve layout constraints to determine how much space is available for tabs
+// to use relative to how much they want to use.
+TabSizer CalculateSpaceFractionAvailable(
+    const TabSizeInfo& tab_size_info,
+    const std::vector<TabAnimationState>& tabs,
+    int width) {
+  float minimum_width = 0;
+  float crossover_width = 0;
+  float preferred_width = 0;
+  for (TabAnimationState tab : tabs) {
+    // Add the tab's width, less the width of its trailing foot (which would
+    // be double counting).
+    minimum_width +=
+        tab.GetMinimumWidth(tab_size_info) - tab_size_info.tab_overlap;
+    crossover_width +=
+        tab.GetLayoutCrossoverWidth(tab_size_info) - tab_size_info.tab_overlap;
+    preferred_width +=
+        tab.GetPreferredWidth(tab_size_info) - tab_size_info.tab_overlap;
+  }
+
+  // Add back the width of the trailing foot of the last tab.
+  minimum_width += tab_size_info.tab_overlap;
+  crossover_width += tab_size_info.tab_overlap;
+  preferred_width += tab_size_info.tab_overlap;
+
+  LayoutDomain domain;
+  float space_fraction_available;
+  if (width < crossover_width) {
+    domain = LayoutDomain::kInactiveWidthBelowActiveWidth;
+    space_fraction_available =
+        (width - minimum_width) / (crossover_width - minimum_width);
+  } else {
+    domain = LayoutDomain::kInactiveWidthEqualsActiveWidth;
+    space_fraction_available =
+        preferred_width == crossover_width
+            ? 1
+            : (width - crossover_width) / (preferred_width - crossover_width);
+  }
+
+  space_fraction_available =
+      base::ClampToRange(space_fraction_available, 0.0f, 1.0f);
+  return TabSizer(tab_size_info, domain, space_fraction_available);
+}
+
+// Because TabSizer::CalculateTabWidth() rounds down, the fractional part of tab
+// widths go unused.  Retroactively round up tab widths from left to right to
+// use up that width.
+void AllocateExtraSpace(std::vector<gfx::Rect>& bounds,
+                        const std::vector<TabAnimationState>& tabs,
+                        int width,
+                        TabSizer tab_sizer) {
+  // Don't expand tabs if they are already at their preferred width.
+  if (tab_sizer.IsAlreadyPreferredWidth())
+    return;
+
+  const int extra_space = width - bounds.back().right();
+  int allocated_extra_space = 0;
+  for (size_t i = 0; i < tabs.size(); i++) {
+    TabAnimationState tab = tabs[i];
+    bounds[i].set_x(bounds[i].x() + allocated_extra_space);
+    if (allocated_extra_space < extra_space &&
+        tab_sizer.TabAcceptsExtraSpace(tab)) {
+      allocated_extra_space++;
+      bounds[i].set_width(bounds[i].width() + 1);
+    }
+  }
 }
 
 }  // namespace
 
-int CalculateBoundsForPinnedTabs(const TabSizeInfo& tab_size_info,
-                                 int num_pinned_tabs,
-                                 int num_tabs,
-                                 std::vector<gfx::Rect>* tab_bounds) {
-  DCHECK_EQ(static_cast<size_t>(num_tabs), tab_bounds->size());
+std::vector<gfx::Rect> CalculateTabBounds(
+    const TabSizeInfo& tab_size_info,
+    const std::vector<TabAnimationState>& tabs,
+    int width,
+    int* active_width,
+    int* inactive_width) {
+  DCHECK_LE(tab_size_info.min_inactive_width, tab_size_info.min_active_width);
+  if (tabs.empty())
+    return std::vector<gfx::Rect>();
+
+  TabSizer tab_sizer =
+      CalculateSpaceFractionAvailable(tab_size_info, tabs, width);
+
   int next_x = 0;
-  const int tab_height = tab_size_info.standard_size.height();
-  for (int index = 0; index < num_pinned_tabs; ++index) {
-    (*tab_bounds)[index].SetRect(next_x, 0, tab_size_info.pinned_tab_width,
-                                 tab_height);
-    next_x += tab_size_info.pinned_tab_width - tab_size_info.tab_overlap;
+  std::vector<gfx::Rect> bounds;
+  for (TabAnimationState tab : tabs) {
+    const int tab_width = tab_sizer.CalculateTabWidth(tab);
+    bounds.push_back(
+        gfx::Rect(next_x, 0, tab_width, tab_size_info.standard_size.height()));
+    next_x += tab_width - tab_size_info.tab_overlap;
   }
-  return next_x;
+
+  AllocateExtraSpace(bounds, tabs, width, tab_sizer);
+
+  if (active_width != nullptr) {
+    *active_width =
+        tab_sizer.CalculateTabWidth(TabAnimationState::ForIdealTabState(
+            TabAnimationState::TabOpenness::kOpen,
+            TabAnimationState::TabPinnedness::kUnpinned,
+            TabAnimationState::TabActiveness::kActive, 0));
+  }
+
+  if (inactive_width != nullptr) {
+    *inactive_width =
+        tab_sizer.CalculateTabWidth(TabAnimationState::ForIdealTabState(
+            TabAnimationState::TabOpenness::kOpen,
+            TabAnimationState::TabPinnedness::kUnpinned,
+            TabAnimationState::TabActiveness::kInactive, 0));
+  }
+
+  return bounds;
 }
 
-std::vector<gfx::Rect> CalculateTabBounds(const TabSizeInfo& tab_size_info,
-                                          int num_pinned_tabs,
-                                          int num_tabs,
-                                          int active_index,
-                                          int width,
-                                          int* active_width,
-                                          int* inactive_width) {
-  DCHECK_NE(0, num_tabs);
-
-  std::vector<gfx::Rect> tab_bounds(num_tabs);
-
-  *active_width = *inactive_width = tab_size_info.standard_size.width();
-
-  int next_x = CalculateBoundsForPinnedTabs(tab_size_info, num_pinned_tabs,
-                                            num_tabs, &tab_bounds);
-  if (num_pinned_tabs == num_tabs)
-    return tab_bounds;
-  width -= next_x;
-
-  const bool is_active_tab_normal = active_index >= num_pinned_tabs;
-  const int num_normal_tabs = num_tabs - num_pinned_tabs;
-  CalculateNormalTabWidths(tab_size_info, is_active_tab_normal, num_normal_tabs,
-                           width, active_width, inactive_width);
-
-  // As CalculateNormalTabWidths() calculates sizes using ints there may be a
-  // bit of extra space (due to the available width not being an integer
-  // multiple of these sizes). Give the extra space to the first tabs, and only
-  // give extra space to the active tab if it is the same size as the inactive
-  // tabs (the active tab may already be bigger).
-  int extra_space = 0;
-  bool widen_active = false;
-  if (*inactive_width != tab_size_info.standard_size.width()) {
-    widen_active = *active_width == *inactive_width;
-    const int tab_width =
-        (*inactive_width - tab_size_info.tab_overlap) * (num_normal_tabs - 1) +
-        (is_active_tab_normal ? *active_width : *inactive_width);
-    extra_space = width - tab_width;
-  }
-
-  // Convert the widths to bounds.
-  const int tab_height = tab_size_info.standard_size.height();
-  for (int i = num_pinned_tabs; i < num_tabs; ++i) {
-    const bool is_active = i == active_index;
-    int width = is_active ? *active_width : *inactive_width;
-    if (extra_space > 0 && (!is_active || widen_active)) {
-      ++width;
-      --extra_space;
-    }
-    tab_bounds[i].SetRect(next_x, 0, width, tab_height);
-    next_x += tab_bounds[i].width() - tab_size_info.tab_overlap;
-  }
-
-  return tab_bounds;
+std::vector<gfx::Rect> CalculatePinnedTabBounds(
+    const TabSizeInfo& tab_size_info,
+    const std::vector<TabAnimationState>& pinned_tabs) {
+  // Pinned tabs are always the same size regardless of the available width.
+  return CalculateTabBounds(tab_size_info, pinned_tabs, 0, nullptr, nullptr);
 }
diff --git a/chrome/browser/ui/views/tabs/tab_strip_layout.h b/chrome/browser/ui/views/tabs/tab_strip_layout.h
index 3787305f..8641262 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_layout.h
+++ b/chrome/browser/ui/views/tabs/tab_strip_layout.h
@@ -13,6 +13,8 @@
 class Rect;
 }
 
+class TabAnimationState;
+
 struct TabSizeInfo {
   // The width of pinned tabs.
   int pinned_tab_width;
@@ -32,24 +34,19 @@
   int tab_overlap;
 };
 
-// Calculates the bounds of the pinned tabs. This assumes |tab_bounds| is the
-// same size as |num_tabs|. Returns the x-coordinate to use for the first
-// non-pinned tab, if any.
-int CalculateBoundsForPinnedTabs(const TabSizeInfo& tab_size_info,
-                                 int num_pinned_tabs,
-                                 int num_tabs,
-                                 std::vector<gfx::Rect>* tab_bounds);
-
 // Calculates and returns the bounds of the tabs. |width| is the available
 // width to use for tab layout. This never sizes the tabs smaller then the
 // minimum widths in TabSizeInfo, and as a result the calculated bounds may go
 // beyond |width|.
-std::vector<gfx::Rect> CalculateTabBounds(const TabSizeInfo& tab_size_info,
-                                          int num_pinned_tabs,
-                                          int num_tabs,
-                                          int active_index,
-                                          int width,
-                                          int* active_width,
-                                          int* inactive_width);
+std::vector<gfx::Rect> CalculateTabBounds(
+    const TabSizeInfo& tab_size_info,
+    const std::vector<TabAnimationState>& tabs,
+    int width,
+    int* active_width,
+    int* inactive_width);
+
+std::vector<gfx::Rect> CalculatePinnedTabBounds(
+    const TabSizeInfo& tab_size_info,
+    const std::vector<TabAnimationState>& pinned_tabs);
 
 #endif  // CHROME_BROWSER_UI_VIEWS_TABS_TAB_STRIP_LAYOUT_H_
diff --git a/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc b/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc
index c383f91..5d02fa1 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc
@@ -7,6 +7,7 @@
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/tabs/tab_style.h"
 #include "chrome/browser/ui/views/tabs/tab.h"
+#include "chrome/browser/ui/views/tabs/tab_animation_state.h"
 #include "chrome/browser/ui/views/tabs/tab_strip_layout.h"
 #include "chrome/browser/ui/views/tabs/tab_style_views.h"
 #include "ui/base/material_design/material_design_controller.h"
@@ -53,10 +54,22 @@
 void TabStripLayoutHelper::UpdateIdealBounds(views::ViewModelT<Tab>* tabs,
                                              int available_width,
                                              int active_tab_index) {
-  const std::vector<gfx::Rect> tab_bounds =
-      CalculateTabBounds(GetTabSizeInfo(), GetPinnedTabCount(tabs),
-                         tabs->view_size(), active_tab_index, available_width,
-                         &active_tab_width_, &inactive_tab_width_);
+  std::vector<TabAnimationState> ideal_animation_states;
+  const int num_pinned_tabs = GetPinnedTabCount(tabs);
+  for (int tab_index = 0; tab_index < tabs->view_size(); tab_index++) {
+    ideal_animation_states.push_back(TabAnimationState::ForIdealTabState(
+        TabAnimationState::TabOpenness::kOpen,
+        tab_index < num_pinned_tabs
+            ? TabAnimationState::TabPinnedness::kPinned
+            : TabAnimationState::TabPinnedness::kUnpinned,
+        tab_index == active_tab_index
+            ? TabAnimationState::TabActiveness::kActive
+            : TabAnimationState::TabActiveness::kInactive,
+        0));
+  }
+  const std::vector<gfx::Rect> tab_bounds = CalculateTabBounds(
+      GetTabSizeInfo(), ideal_animation_states, available_width,
+      &active_tab_width_, &inactive_tab_width_);
   DCHECK_EQ(static_cast<size_t>(tabs->view_size()), tab_bounds.size());
 
   for (size_t i = 0; i < tab_bounds.size(); ++i)
@@ -71,9 +84,17 @@
   first_non_pinned_tab_x_ = 0;
 
   if (pinned_tab_count > 0) {
-    std::vector<gfx::Rect> tab_bounds(tabs->view_size());
-    first_non_pinned_tab_x_ = CalculateBoundsForPinnedTabs(
-        GetTabSizeInfo(), pinned_tab_count, tabs->view_size(), &tab_bounds);
+    std::vector<TabAnimationState> ideal_animation_states;
+    for (int tab_index = 0; tab_index < pinned_tab_count; tab_index++) {
+      ideal_animation_states.push_back(TabAnimationState::ForIdealTabState(
+          TabAnimationState::TabOpenness::kOpen,
+          TabAnimationState::TabPinnedness::kPinned,
+          TabAnimationState::TabActiveness::kInactive, 0));
+    }
+
+    const std::vector<gfx::Rect> tab_bounds =
+        CalculatePinnedTabBounds(GetTabSizeInfo(), ideal_animation_states);
+
     for (int i = 0; i < pinned_tab_count; ++i)
       tabs->set_ideal_bounds(i, tab_bounds[i]);
   }
diff --git a/chrome/browser/ui/views/tabs/tab_strip_layout_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_layout_unittest.cc
index 6078668..4621102 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_layout_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_layout_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "chrome/browser/ui/views/tabs/tab_animation_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -54,21 +55,35 @@
 constexpr int kStandardHeight = 10;
 constexpr int kMinActiveWidth = 20;
 constexpr int kMinInactiveWidth = 14;
+constexpr int kPinnedWidth = 10;
+constexpr int kTabOverlap = 4;
 
 CalculateTabBoundsResult CalculateTabBounds(TestCase test_case) {
   TabSizeInfo size_info;
-  size_info.pinned_tab_width = 10;
+  size_info.pinned_tab_width = kPinnedWidth;
   size_info.min_active_width = kMinActiveWidth;
   size_info.min_inactive_width = kMinInactiveWidth;
   size_info.standard_size = gfx::Size(kStandardWidth, kStandardHeight);
-  size_info.tab_overlap = 4;
+  size_info.tab_overlap = kTabOverlap;
+
+  std::vector<TabAnimationState> ideal_animation_states;
+  for (int tab_index = 0; tab_index < test_case.num_tabs; tab_index++) {
+    ideal_animation_states.push_back(TabAnimationState::ForIdealTabState(
+        TabAnimationState::TabOpenness::kOpen,
+        tab_index < test_case.num_pinned_tabs
+            ? TabAnimationState::TabPinnedness::kPinned
+            : TabAnimationState::TabPinnedness::kUnpinned,
+        tab_index == test_case.active_index
+            ? TabAnimationState::TabActiveness::kActive
+            : TabAnimationState::TabActiveness::kInactive,
+        0));
+  }
 
   int active_width;
   int inactive_width;
   std::vector<gfx::Rect> tab_bounds = CalculateTabBounds(
-      size_info, test_case.num_pinned_tabs, test_case.num_tabs,
-      test_case.active_index, test_case.tabstrip_width, &active_width,
-      &inactive_width);
+      size_info, ideal_animation_states, test_case.tabstrip_width,
+      &active_width, &inactive_width);
 
   CalculateTabBoundsResult result;
   result.bounds = tab_bounds;
@@ -269,3 +284,20 @@
   auto result = CalculateTabBounds(test_case);
   EXPECT_EQ("10 14 14", TabWidthsAsString(result.bounds));
 }
+
+TEST(TabStripLayoutTest, ExactlyEnoughSpaceAllPinnedTabs) {
+  TestCase test_case;
+  test_case.num_tabs = 2;
+  test_case.num_pinned_tabs = 2;
+  test_case.tabstrip_width = 2 * kPinnedWidth - kTabOverlap;
+
+  // We want to check the case where the necessary strip width equals the
+  // available width.
+  auto result = CalculateTabBounds(test_case);
+
+  EXPECT_EQ("10 10", TabWidthsAsString(result.bounds));
+
+  // Validate that the tabstrip width is indeeed exactly enough to hold two
+  // pinned tabs.
+  EXPECT_EQ(test_case.tabstrip_width, result.bounds[1].right());
+}
diff --git a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
index 729c314..0c01b57 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
@@ -835,12 +835,14 @@
   // During mouse-based tab closure, inactive tabs shouldn't shrink
   // so that users can close tabs continuously without moving mouse.
   controller_->SelectTab(0, dummy_event_);
-  int last_inactive_width = tab_strip_->InactiveTabWidth();
-  while (tab_strip_->tab_count() > 0) {
+  // If there are only two tabs in the strip, then after closing one the
+  // remaining one will be active and there will be no inactive tabs,
+  // so we stop at 2.
+  while (tab_strip_->tab_count() > 2) {
+    const int last_inactive_width = tab_strip_->InactiveTabWidth();
     tab_strip_->CloseTab(tab_strip_->tab_at(controller_->GetActiveIndex()),
                          CLOSE_TAB_FROM_MOUSE);
     EXPECT_GE(tab_strip_->InactiveTabWidth(), last_inactive_width);
-    last_inactive_width = tab_strip_->InactiveTabWidth();
   }
 }
 
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 4e0e162..caa66b5 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
@@ -194,6 +194,16 @@
   return std::make_pair(std::move(*model), "Tracing model is ready");
 }
 
+std::pair<base::Value, std::string> LoadGraphicsModel(
+    const std::string& json_text) {
+  arc::ArcTracingGraphicsModel graphics_model;
+  if (!graphics_model.LoadFromJson(json_text))
+    return std::make_pair(base::Value(), "Failed to load tracing model");
+
+  std::unique_ptr<base::DictionaryValue> model = graphics_model.Serialize();
+  return std::make_pair(std::move(*model), "Tracing model is loaded");
+}
+
 }  // namespace
 
 ArcGraphicsTracingHandler::ArcGraphicsTracingHandler()
@@ -226,6 +236,10 @@
       "setStopOnJank",
       base::BindRepeating(&ArcGraphicsTracingHandler::HandleSetStopOnJank,
                           base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "loadFromText",
+      base::BindRepeating(&ArcGraphicsTracingHandler::HandleLoadFromText,
+                          base::Unretained(this)));
 }
 
 void ArcGraphicsTracingHandler::OnWindowActivated(ActivationReason reason,
@@ -433,4 +447,20 @@
   stop_on_jank_ = args->GetList()[0].GetBool();
 }
 
+void ArcGraphicsTracingHandler::HandleLoadFromText(
+    const base::ListValue* args) {
+  DCHECK_EQ(1U, args->GetSize());
+  if (!args->GetList()[0].is_string()) {
+    LOG(ERROR) << "Invalid input";
+    return;
+  }
+
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+      base::BindOnce(&LoadGraphicsModel,
+                     std::move(args->GetList()[0].GetString())),
+      base::BindOnce(&ArcGraphicsTracingHandler::OnGraphicsModelReady,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.h b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.h
index b2b7ab2..d986704 100644
--- a/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.h
+++ b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.h
@@ -75,6 +75,7 @@
   // Handlers for calls from JS.
   void HandleReady(const base::ListValue* args);
   void HandleSetStopOnJank(const base::ListValue* args);
+  void HandleLoadFromText(const base::ListValue* args);
 
   // Updates title and icon for the active ARC window.
   void UpdateActiveArcWindowInfo();
diff --git a/chrome/browser/ui/webui/welcome/welcome_ui.cc b/chrome/browser/ui/webui/welcome/welcome_ui.cc
index f2b76d3..02d470d 100644
--- a/chrome/browser/ui/webui/welcome/welcome_ui.cc
+++ b/chrome/browser/ui/webui/welcome/welcome_ui.cc
@@ -89,7 +89,6 @@
       {"bookmarksAdded", IDS_ONBOARDING_WELCOME_BOOKMARKS_ADDED},
       {"bookmarkRemoved", IDS_ONBOARDING_WELCOME_BOOKMARK_REMOVED},
       {"bookmarksRemoved", IDS_ONBOARDING_WELCOME_BOOKMARKS_REMOVED},
-      {"bookmarkReplaced", IDS_ONBOARDING_WELCOME_BOOKMARK_REPLACED},
       {"defaultBrowserChanged", IDS_ONBOARDING_DEFAULT_BROWSER_CHANGED},
       {"getStarted", IDS_ONBOARDING_WELCOME_GET_STARTED},
       {"headerText", IDS_WELCOME_HEADER},
diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc
index a7689095..09fdcac 100644
--- a/chrome/common/chrome_content_client.cc
+++ b/chrome/common/chrome_content_client.cc
@@ -676,6 +676,10 @@
       resource_id);
 }
 
+bool ChromeContentClient::IsDataResourceGzipped(int resource_id) const {
+  return ui::ResourceBundle::GetSharedInstance().IsGzipped(resource_id);
+}
+
 gfx::Image& ChromeContentClient::GetNativeImageNamed(int resource_id) const {
   return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
       resource_id);
diff --git a/chrome/common/chrome_content_client.h b/chrome/common/chrome_content_client.h
index 667a1f19..6f800f3a 100644
--- a/chrome/common/chrome_content_client.h
+++ b/chrome/common/chrome_content_client.h
@@ -92,6 +92,7 @@
       ui::ScaleFactor scale_factor) const override;
   base::RefCountedMemory* GetDataResourceBytes(
       int resource_id) const override;
+  bool IsDataResourceGzipped(int resource_id) const override;
   gfx::Image& GetNativeImageNamed(int resource_id) const override;
   base::DictionaryValue GetNetLogConstants() const override;
   std::string GetProcessTypeNameInEnglish(int type) override;
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 0a4bf71..9c248a1 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -189,9 +189,9 @@
 
 // Enables an uninstall option in the right-click menu of Crostini (Linux)
 // applications.
-// TODO(iby): Remove once remaining bugs fixed.
+// TODO(crbug.com/955797): Remove this flag entirely.
 const base::Feature kCrostiniAppUninstallGui{"CrostiniAppUninstallGui",
-                                             base::FEATURE_DISABLED_BY_DEFAULT};
+                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enable support for "Plugin VMs" on Chrome OS.
 const base::Feature kPluginVm{"PluginVm", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/credential_provider/setup/BUILD.gn b/chrome/credential_provider/setup/BUILD.gn
index 010504901..4c7aed8 100644
--- a/chrome/credential_provider/setup/BUILD.gn
+++ b/chrome/credential_provider/setup/BUILD.gn
@@ -27,6 +27,7 @@
     "//base:base",
   ]
   deps = [
+    ":version",
     "../gaiacp:common",
     "//chrome/installer/util:with_no_strings",
   ]
@@ -68,7 +69,6 @@
   deps = [
     ":common",
     ":setup_resources",
-    ":version",
     "../eventlog:gcp_eventlog_messages",
     "../gaiacp:common",
     "//chrome/common:version_header",
diff --git a/chrome/test/chromedriver/js/get_element_region.js b/chrome/test/chromedriver/js/get_element_region.js
index 126a753e..60896ed 100644
--- a/chrome/test/chromedriver/js/get_element_region.js
+++ b/chrome/test/chromedriver/js/get_element_region.js
@@ -12,11 +12,41 @@
   // SVG is one case that doesn't have a first client rect.
   var clientRects = element.getClientRects();
 
+  // Determines if region is partially in viewport, returning visible region
+  // if so. If not, returns null. If fully visible, returns original region.
+  function getVisibleSubregion(region) {
+    // Given two regions, determines if any intersection occurs.
+    // Overlapping edges are not considered intersections.
+    function getIntersectingSubregion(region1, region2) {
+      if (!(region2.right  <= region1.left   ||
+            region2.left   >= region1.right  ||
+            region2.top    >= region1.bottom ||
+            region2.bottom <= region1.top)) {
+        // Determines region of intersection.
+        // If region2 contains region1, returns region1.
+        // If region1 contains region2, returns region2.
+        return {
+          'left': Math.max(region1.left, region2.left),
+          'right': Math.min(region1.right, region2.right),
+          'bottom': Math.min(region1.bottom, region2.bottom),
+          'top': Math.max(region1.top, region2.top)
+        };
+      }
+      return null;
+    }
+    var viewport = new DOMRect(0, 0, window.innerWidth, window.innerHeight);
+    return getIntersectingSubregion(viewport, region);
+  }
+
+  var boundingRect = null;
+  var clientRect = null;
   // Element area of a map has same first ClientRect and BoundingClientRect
   // after blink roll at chromium commit position 290738 which includes blink
   // revision 180610. Thus handle area as a special case.
   if (clientRects.length == 0 || element.tagName.toLowerCase() == 'area') {
-    var box = element.getBoundingClientRect();
+    // Area clicking is technically not supported by W3C standard but is a
+    // desired feature. Returns region containing the area instead of subregion
+    // so that whole area is visible and always clicked correctly.
     if (element.tagName.toLowerCase() == 'area') {
       var coords = element.coords.split(',');
       if (element.shape.toLowerCase() == 'rect') {
@@ -68,27 +98,26 @@
       } else {
         throw new Error('shape=' + element.shape + ' is not supported');
       }
+    } else {
+      boundingRect = element.getBoundingClientRect();
+      clientRect = Object.assign({}, boundingRect);
     }
-    return {
-        'left': 0,
-        'top': 0,
-        'width': box.width,
-        'height': box.height
-    };
   } else {
-    var box = element.getBoundingClientRect();
-    var clientRect = clientRects[0];
+    boundingRect = element.getBoundingClientRect();
+    clientRect = clientRects[0];
     for (var i = 0; i < clientRects.length; i++) {
       if (clientRects[i].height != 0 && clientRects[i].width != 0) {
         clientRect = clientRects[i];
         break;
       }
     }
-    return {
-        'left': clientRect.left - box.left,
-        'top': clientRect.top - box.top,
-        'width': clientRect.right - clientRect.left,
-        'height': clientRect.bottom - clientRect.top
-    };
   }
+  var visiblePortion = getVisibleSubregion(clientRect) || clientRect;
+  // Returned region is relative to boundingRect's left,top.
+  return {
+    'left': visiblePortion.left - boundingRect.left,
+    'top': visiblePortion.top - boundingRect.top,
+    'height': visiblePortion.bottom - visiblePortion.top,
+    'width': visiblePortion.right - visiblePortion.left
+  };
 }
diff --git a/chrome/test/chromedriver/js/get_element_region_test.html b/chrome/test/chromedriver/js/get_element_region_test.html
index 1ee7f29..102dd228 100644
--- a/chrome/test/chromedriver/js/get_element_region_test.html
+++ b/chrome/test/chromedriver/js/get_element_region_test.html
@@ -53,6 +53,77 @@
   assertEquals(10, region.height);
 }
 
+function testPartialOutOfView() {
+  var region = getElementRegion(document.getElementById('partial-out-of-view'));
+  assertEquals(50, region.left);
+  assertEquals(0, region.top);
+  assertEquals(50, region.width);
+  assertEquals(100, region.height);
+}
+
+function testScrolledOutOfView() {
+  // Scroll to an offset to make element with id=a partially visible.
+  var elemRect = document.getElementById("a").getBoundingClientRect();
+  window.scrollTo(elemRect.left + 50, elemRect.top + 100);
+  var region = getElementRegion(document.getElementById("a"));
+  assertEquals(50, region.left);
+  assertEquals(100, region.top);
+  assertEquals(50, region.width);
+  assertEquals(100, region.height);
+  window.scrollTo(0, 0);
+}
+
+function testFullOutOfView() {
+  var region = getElementRegion(document.getElementById('out-of-view'));
+  assertEquals(0, region.left);
+  assertEquals(0, region.top);
+  assertEquals(100, region.width);
+  assertEquals(100, region.height);
+}
+
+function testFullOutOfViewRect() {
+  var region = getElementRegion(document.getElementById('out-of-view-rect'));
+  assertEquals(120, region.left);
+  assertEquals(100, region.top);
+  assertEquals(20, region.width);
+  assertEquals(50, region.height);
+}
+
+function testFullOutOfViewCircle() {
+  var region = getElementRegion(document.getElementById('out-of-view-circle'));
+  assertEquals(175, region.left);
+  assertEquals(165, region.top);
+  assertEquals(10, region.width);
+  assertEquals(10, region.height);
+}
+
+function testPartialOutOfViewRect() {
+  var region = getElementRegion(document.getElementById('partial-rect'));
+  assertEquals(120, region.left);
+  assertEquals(10, region.top);
+  assertEquals(10, region.width);
+  // Height should not be truncated, even if partially visible.
+  assertEquals(40, region.height);
+}
+
+function testPartialOutOfViewCircle() {
+  var region = getElementRegion(document.getElementById('partial-circle'));
+  assertEquals(30, region.left);
+  assertEquals(0, region.top);
+  // Area is treated as if it is fully in-view or fully out-of-view.
+  assertEquals(60, region.width);
+  assertEquals(60, region.height);
+}
+
+function testNegativeDimensions() {
+  var region = getElementRegion(document.getElementById('negative-dims'));
+  assertEquals(0, region.left);
+  assertEquals(0, region.top);
+  // Negative dimensions appear as 0, so region is 0x0.
+  assertEquals(0, region.width);
+  assertEquals(0, region.height);
+}
+
 function testAreaDefault() {
   try {
     getElementRegion(document.getElementById('default'));
@@ -83,6 +154,43 @@
       <area id="default" shape="default">
     </map>
   </div>
+  <div style="background-color:blue;width:100px;height:100px;position:absolute;top:200px;left:-50px"
+    id="partial-out-of-view">
+  </div>
+  <div style="background-color:green;width:100px;height:100px;position:absolute;top:100px;left:-200px"
+    id="out-of-view">
+  </div>
+  <div>
+    <img width="200" height="200" usemap="#outofviewmap" style="position:absolute;top:120vh;left:50px">
+    <map name="outofviewmap">
+      <area id="out-of-view-rect" shape="rect" coords="120,100,140,150">
+      <area id="out-of-view-circle" shape="circle" coords="180,170,5">
+    </map>
+  </div>
+  <div>
+    <img width="200" height="200" usemap="#partialmap" style="position:absolute;top:calc(100vh - 20px);left:50px">
+    <map name="partialmap">
+      <area id="partial-rect" shape="rect" coords="120,10,130,50">
+      <area id="partial-circle" shape="circle" coords="60,5,30">
+    </map>
+  </div>
+  <div style="background-color:blue;width:100px;height:100px;position:absolute;top:200px;left:-50px"
+    id="partial-out-of-view">
+  </div>
+  <div style="background-color:green;width:100px;height:100px;position:absolute;top:100px;left:-200px"
+    id="out-of-view">
+  </div>
+  <div style="background-color:yellow;width:-100px;height:-100px;position:absolute;top:200px;left:200px;"
+    id="negative-dims">
+  </div>
+  <!-- This element adds scroll to see how script handles viewport with scroll -->
+  <div style="background-color:red;width:50px;height:50px;position:absolute;top:200vh;left:200vw;">
+  </div>
+
+</div>
+</body>
+</html>
+
 </div>
 </body>
 </html>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/crossProcessHistory/test_crossProcessHistory.js b/chrome/test/data/extensions/api_test/webnavigation/crossProcessHistory/test_crossProcessHistory.js
index 2a52740..0f02f89 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/crossProcessHistory/test_crossProcessHistory.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/crossProcessHistory/test_crossProcessHistory.js
@@ -135,7 +135,7 @@
                      processId: 0,
                      tabId: 0,
                      timeStamp: 0,
-                     url: getURL('h.html') }},
+                     url: getURL('empty.html') }},
         { label: "a-onHistoryStateUpdated",
           event: "onHistoryStateUpdated",
           details: { frameId: 0,
@@ -181,8 +181,8 @@
                      timeStamp: 0,
                      url: URL_TEST + "5" }}],
         [ navigationOrder("a-"), navigationOrder("b-"),
-          [ "a-onCompleted", "b-onBeforeNavigate",
-            "a-onHistoryStateUpdated"] ]);
+          [ "b-onBeforeNavigate", "a-onHistoryStateUpdated",
+            "a-onCompleted"] ]);
 
       chrome.tabs.update(tab.id, {url: getURL('h.html?' + port)});
     },
diff --git a/chrome/test/data/webui/cr_elements/cr_slider_test.js b/chrome/test/data/webui/cr_elements/cr_slider_test.js
index a4291d5..eb7410f 100644
--- a/chrome/test/data/webui/cr_elements/cr_slider_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_slider_test.js
@@ -56,7 +56,7 @@
   }
 
   function pointerEvent(eventType, ratio) {
-    const rect = crSlider.$.barContainer.getBoundingClientRect();
+    const rect = crSlider.$.container.getBoundingClientRect();
     crSlider.dispatchEvent(new PointerEvent(eventType, {
       buttons: 1,
       pointerId: 1,
@@ -293,7 +293,7 @@
     assertEquals(1, crSlider.max);
   });
 
-  test('when drag ends, value updated before dragging-changed event', () => {
+  test('value updated before dragging-changed event handled', () => {
     const wait = new Promise(resolve => {
       crSlider.addEventListener('dragging-changed', e => {
         if (!e.detail.value) {
@@ -305,57 +305,48 @@
     pointerDown(0);
     pointerMove(.5);
     pointerUp();
-    return wait.then(() => {
-      assertEquals(50, crSlider.value);
-    });
+    return wait;
   });
 
-  test('smooth position transition only on pointerdown', () => {
+  test('smooth position transition only on pointerdown', async () => {
     const assertNoTransition = () => {
       const expected = 'all 0s ease 0s';
-      assertEquals(expected, getComputedStyle(crSlider.$.knob).transition);
+      assertEquals(
+          expected, getComputedStyle(crSlider.$.knobAndLabel).transition);
       assertEquals(expected, getComputedStyle(crSlider.$.bar).transition);
-      assertEquals(expected, getComputedStyle(crSlider.$.label).transition);
     };
     const assertTransition = () => {
       const getValue = propName => `${propName} 0.08s ease 0s`;
       assertEquals(
           getValue('margin-inline-start'),
-          getComputedStyle(crSlider.$.knob).transition);
+          getComputedStyle(crSlider.$.knobAndLabel).transition);
       assertEquals(
           getValue('width'), getComputedStyle(crSlider.$.bar).transition);
-      assertEquals(
-          getValue('margin-inline-start'),
-          getComputedStyle(crSlider.$.label).transition);
     };
 
     assertNoTransition();
     pointerDown(.5);
     assertTransition();
-    return test_util.eventToPromise('transitionend', crSlider.$.knob)
-        .then(() => {
-          assertNoTransition();
-          // Other operations that change the value do not have transitions.
-          pointerMove(0);
-          assertNoTransition();
-          assertEquals(0, crSlider.value);
-          pointerUp();
-          pressArrowRight();
-          assertNoTransition();
-          assertEquals(1, crSlider.value);
-          crSlider.value = 50;
-          assertNoTransition();
+    await test_util.eventToPromise('transitionend', crSlider.$.knobAndLabel);
+    assertNoTransition();
+    // Other operations that change the value do not have transitions.
+    pointerMove(0);
+    assertNoTransition();
+    assertEquals(0, crSlider.value);
+    pointerUp();
+    pressArrowRight();
+    assertNoTransition();
+    assertEquals(1, crSlider.value);
+    crSlider.value = 50;
+    assertNoTransition();
 
-          // Check that the slider is not stuck with a transition when the value
-          // does not change.
-          crSlider.value = 0;
-          pointerDown(0);
-          assertTransition();
-          return test_util.eventToPromise('transitionend', crSlider.$.knob);
-        })
-        .then(() => {
-          assertNoTransition();
-        });
+    // Check that the slider is not stuck with a transition when the value
+    // does not change.
+    crSlider.value = 0;
+    pointerDown(0);
+    assertTransition();
+    await test_util.eventToPromise('transitionend', crSlider.$.knobAndLabel);
+    assertNoTransition();
   });
 
   test('getRatio()', () => {
diff --git a/chrome/test/data/webui/settings/device_page_tests.js b/chrome/test/data/webui/settings/device_page_tests.js
index 7adcdbb..f016a29 100644
--- a/chrome/test/data/webui/settings/device_page_tests.js
+++ b/chrome/test/data/webui/settings/device_page_tests.js
@@ -844,7 +844,7 @@
             // Ensure that the zoom value remains unchanged while draggging.
             function pointerEvent(eventType, ratio) {
               const crSlider = displayPage.$.displaySizeSlider.$.slider;
-              const rect = crSlider.$.barContainer.getBoundingClientRect();
+              const rect = crSlider.$.container.getBoundingClientRect();
               crSlider.dispatchEvent(new PointerEvent(eventType, {
                 buttons: 1,
                 pointerId: 1,
diff --git a/chrome/test/data/webui/settings/settings_slider_tests.js b/chrome/test/data/webui/settings/settings_slider_tests.js
index 355b29a..dbf139fd 100644
--- a/chrome/test/data/webui/settings/settings_slider_tests.js
+++ b/chrome/test/data/webui/settings/settings_slider_tests.js
@@ -60,7 +60,7 @@
   }
 
   function pointerEvent(eventType, ratio) {
-    const rect = crSlider.$.barContainer.getBoundingClientRect();
+    const rect = crSlider.$.container.getBoundingClientRect();
     crSlider.dispatchEvent(new PointerEvent(eventType, {
       buttons: 1,
       pointerId: 1,
diff --git a/chrome/test/data/webui/welcome/app_chooser_test.js b/chrome/test/data/webui/welcome/app_chooser_test.js
index 43412f9d..3457da78 100644
--- a/chrome/test/data/webui/welcome/app_chooser_test.js
+++ b/chrome/test/data/webui/welcome/app_chooser_test.js
@@ -53,19 +53,17 @@
       testAppBrowserProxy = new TestNuxAppProxy();
       testAppMetricsProxy = new TestMetricsProxy();
       testBookmarkBrowserProxy = new TestBookmarkProxy();
+
+      nux.GoogleAppProxyImpl.instance_ = testAppBrowserProxy;
+      nux.GoogleAppsMetricsProxyImpl.instance_ = testAppMetricsProxy;
       nux.BookmarkProxyImpl.instance_ = testBookmarkBrowserProxy;
-      // Reset w/ new proxy for test.
       nux.BookmarkBarManager.instance_ = new nux.BookmarkBarManager();
 
       testAppBrowserProxy.setAppList(apps);
 
       PolymerTest.clearBody();
-      testElement = document.createElement('app-chooser');
+      testElement = document.createElement('nux-google-apps');
       document.body.appendChild(testElement);
-      testElement.singleSelect = false;  // All apps can be selected.
-      testElement.appProxy = testAppBrowserProxy;
-      testElement.metricsManager =
-          new nux.ModuleMetricsManager(testAppMetricsProxy);
       // Simulate nux-app's onRouteEnter call.
       testElement.onRouteEnter();
       await testAppMetricsProxy.whenCalled('recordPageShown');
diff --git a/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js b/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js
index 3ae01aba..78fdd00 100644
--- a/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js
+++ b/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js
@@ -33,7 +33,7 @@
 OnboardingWelcomeAppChooserTest = class extends OnboardingWelcomeBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://welcome/shared/app_chooser.html';
+    return 'chrome://welcome/google_apps/nux_google_apps.html';
   }
 
   /** @override */
diff --git a/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js b/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js
index f7f2fc0..ea89e4e 100644
--- a/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js
+++ b/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js
@@ -86,6 +86,7 @@
     case sessionTypes.IMMERSIVE:
       console.info('Requesting immersive VR session');
       navigator.xr.requestSession('immersive-vr').then( (session) => {
+        session.mode = 'immersive-vr';
         console.info('Immersive VR session request succeeded');
         sessionInfos[sessionTypes.IMMERSIVE].currentSession = session;
         onSessionStarted(session);
@@ -96,6 +97,7 @@
     case sessionTypes.AR:
       console.info('Requesting Immersive AR session');
       navigator.xr.requestSession('immersive-ar').then((session) => {
+        session.mode = 'immersive-ar';
         console.info('Immersive AR session request succeeded');
         sessionInfos[sessionTypes.AR].currentSession = session;
         onSessionStarted(session);
@@ -104,6 +106,7 @@
         console.info('Attempting to fall back to legacy AR mode');
         navigator.xr.requestSession('legacy-inline-ar').then(
             (session) => {
+          session.mode = 'legacy-inline-ar';
           session.updateRenderState({
               outputContext: webglCanvas.getContext('xrpresent')
           });
@@ -197,6 +200,7 @@
   let ctx = webglCanvas.getContext('xrpresent');
   navigator.xr.requestSession('inline')
   .then((session) => {
+    session.mode = 'inline';
     session.updateRenderState({
       outputContext: ctx
     });
diff --git a/chromecast/activity/BUILD.gn b/chromecast/activity/BUILD.gn
new file mode 100644
index 0000000..69b7534
--- /dev/null
+++ b/chromecast/activity/BUILD.gn
@@ -0,0 +1,15 @@
+# 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("//chromecast/chromecast.gni")
+
+cast_source_set("activity") {
+  sources = [
+    "queryable_data_host.h",
+  ]
+
+  deps = [
+    "//base",
+  ]
+}
diff --git a/chromecast/activity/queryable_data_host.h b/chromecast/activity/queryable_data_host.h
new file mode 100644
index 0000000..885830f
--- /dev/null
+++ b/chromecast/activity/queryable_data_host.h
@@ -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.
+
+#ifndef CHROMECAST_ACTIVITY_QUERYABLE_DATA_HOST_H_
+#define CHROMECAST_ACTIVITY_QUERYABLE_DATA_HOST_H_
+
+#include <string>
+
+#include "base/values.h"
+
+namespace chromecast {
+
+// Sends queryable data to a host where it will be consumed.
+// Either through to WebContents (non-fuchsia) or through FIDL to the
+// Cast Runner (Fuchsia).
+class QueryableDataHost {
+ public:
+  virtual ~QueryableDataHost() = default;
+
+  // Send the queryable data entry |key|:|value|
+  virtual void SendQueryableValue(const std::string& key,
+                                  const base::Value& value) = 0;
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_ACTIVITY_QUERYABLE_DATA_HOST_H_
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index 0523059..5bb6be4 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -110,6 +110,8 @@
     "media/media_caps_impl.h",
     "media/supported_codec_finder.cc",
     "media/supported_codec_finder.h",
+    "queryable_data_host_cast.cc",
+    "queryable_data_host_cast.h",
     "renderer_prelauncher.cc",
     "renderer_prelauncher.h",
     "service/cast_service_simple.cc",
@@ -145,6 +147,7 @@
     "//base:i18n",
     "//cc",
     "//chromecast:chromecast_buildflags",
+    "//chromecast/activity",
     "//chromecast/app:cast_crash_client",
     "//chromecast/app:chromecast_settings",
     "//chromecast/app:resources",
@@ -154,6 +157,7 @@
     "//chromecast/browser/bluetooth/public/mojom",
     "//chromecast/browser/general_audience_browsing/mojom",
     "//chromecast/common",
+    "//chromecast/common:queryable_data",
     "//chromecast/common/media",
     "//chromecast/common/mojom",
     "//chromecast/graphics",
diff --git a/chromecast/browser/DEPS b/chromecast/browser/DEPS
index 3334b5a..ddc5fd9 100644
--- a/chromecast/browser/DEPS
+++ b/chromecast/browser/DEPS
@@ -1,6 +1,8 @@
 include_rules = [
   "+cc/base/switches.h",
+  "+chromecast/activity",
   "+chromecast/common",
+  "+chromecast/common/mojom",
   "+chromecast/graphics",
   "+chromecast/app/grit/chromecast_settings.h",
   "+chromecast/app/resources/grit/shell_resources.h",
diff --git a/chromecast/browser/cast_overlay_manifests.cc b/chromecast/browser/cast_overlay_manifests.cc
index aa050547..3edf4831 100644
--- a/chromecast/browser/cast_overlay_manifests.cc
+++ b/chromecast/browser/cast_overlay_manifests.cc
@@ -12,6 +12,7 @@
 #include "chromecast/common/mojom/media_caps.mojom.h"
 #include "chromecast/common/mojom/media_playback_options.mojom.h"
 #include "chromecast/common/mojom/memory_pressure.mojom.h"
+#include "chromecast/common/mojom/queryable_data_store.mojom.h"
 #include "media/mojo/services/media_manifest.h"
 #include "services/service_manager/public/cpp/manifest_builder.h"
 
@@ -70,7 +71,8 @@
         .ExposeInterfaceFilterCapability_Deprecated(
             "navigation:frame", "browser",
             service_manager::Manifest::InterfaceList<
-                mojom::FeatureManager, mojom::MediaPlaybackOptions>())
+                mojom::FeatureManager, mojom::MediaPlaybackOptions,
+                mojom::QueryableDataStore>())
         .Build()
 #if defined(USE_INTERNAL_OVERLAY_MANIFESTS)
         .Amend(cast_content_renderer_internal_manifest_overlay::GetManifest())
diff --git a/chromecast/browser/cast_web_contents_impl.cc b/chromecast/browser/cast_web_contents_impl.cc
index 41ee8b4f..f50ac23 100644
--- a/chromecast/browser/cast_web_contents_impl.cc
+++ b/chromecast/browser/cast_web_contents_impl.cc
@@ -9,10 +9,13 @@
 #include "base/bind.h"
 #include "base/no_destructor.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "base/values.h"
 #include "chromecast/base/chromecast_switches.h"
 #include "chromecast/browser/cast_browser_process.h"
 #include "chromecast/browser/devtools/remote_debugging_server.h"
 #include "chromecast/common/mojom/media_playback_options.mojom.h"
+#include "chromecast/common/mojom/queryable_data_store.mojom.h"
+#include "chromecast/common/queryable_data.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
@@ -257,6 +260,15 @@
   render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
       &media_playback_options);
   media_playback_options->SetUseCmaRenderer(use_cma_renderer_);
+
+  // Send queryable values
+  chromecast::shell::mojom::QueryableDataStorePtr queryable_data_store_ptr;
+  render_frame_host->GetRemoteInterfaces()->GetInterface(
+      &queryable_data_store_ptr);
+  for (const auto& value : QueryableData::GetValues()) {
+    // base::Value is not copyable.
+    queryable_data_store_ptr->Set(value.first, value.second.Clone());
+  }
 }
 
 std::vector<chromecast::shell::mojom::FeaturePtr>
diff --git a/chromecast/browser/queryable_data_host_cast.cc b/chromecast/browser/queryable_data_host_cast.cc
new file mode 100644
index 0000000..53ff7e73
--- /dev/null
+++ b/chromecast/browser/queryable_data_host_cast.cc
@@ -0,0 +1,32 @@
+// 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 "chromecast/browser/queryable_data_host_cast.h"
+
+#include "chromecast/common/mojom/queryable_data_store.mojom.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+
+namespace chromecast {
+
+QueryableDataHostCast::QueryableDataHostCast(content::WebContents* web_contents)
+    : web_contents_(web_contents) {
+  DCHECK(web_contents_);
+}
+
+QueryableDataHostCast::~QueryableDataHostCast() {}
+
+void QueryableDataHostCast::SendQueryableValue(const std::string& key,
+                                               const base::Value& value) {
+  for (content::RenderFrameHost* render_frame_host :
+       web_contents_->GetAllFrames()) {
+    shell::mojom::QueryableDataStorePtr queryable_data_store_ptr;
+    render_frame_host->GetRemoteInterfaces()->GetInterface(
+        &queryable_data_store_ptr);
+    queryable_data_store_ptr->Set(key, value.Clone());
+  }
+}
+
+}  // namespace chromecast
diff --git a/chromecast/browser/queryable_data_host_cast.h b/chromecast/browser/queryable_data_host_cast.h
new file mode 100644
index 0000000..ae80c48b
--- /dev/null
+++ b/chromecast/browser/queryable_data_host_cast.h
@@ -0,0 +1,38 @@
+// 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_QUERYABLE_DATA_HOST_CAST_H_
+#define CHROMECAST_BROWSER_QUERYABLE_DATA_HOST_CAST_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/values.h"
+#include "chromecast/activity/queryable_data_host.h"
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace chromecast {
+
+// Sends queryable data to a non fuchsia Host.
+class QueryableDataHostCast : public QueryableDataHost {
+ public:
+  explicit QueryableDataHostCast(content::WebContents* web_contents);
+  ~QueryableDataHostCast() override;
+
+  // chromecast::QueryableDataHost implementation:
+  void SendQueryableValue(const std::string& key,
+                          const base::Value& value) override;
+
+ private:
+  content::WebContents* const web_contents_;
+
+  DISALLOW_COPY_AND_ASSIGN(QueryableDataHostCast);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_QUERYABLE_DATA_HOST_CAST_H_
diff --git a/chromecast/common/BUILD.gn b/chromecast/common/BUILD.gn
index 71ea0bb..c643929 100644
--- a/chromecast/common/BUILD.gn
+++ b/chromecast/common/BUILD.gn
@@ -5,6 +5,17 @@
 import("//chromecast/chromecast.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 
+cast_source_set("queryable_data") {
+  sources = [
+    "queryable_data.cc",
+    "queryable_data.h",
+  ]
+
+  deps = [
+    "//base",
+  ]
+}
+
 cast_source_set("common") {
   sources = [
     "cast_content_client.cc",
diff --git a/chromecast/common/mojom/BUILD.gn b/chromecast/common/mojom/BUILD.gn
index 6997473..0ad9f0e0 100644
--- a/chromecast/common/mojom/BUILD.gn
+++ b/chromecast/common/mojom/BUILD.gn
@@ -13,6 +13,7 @@
     "media_playback_options.mojom",
     "memory_pressure.mojom",
     "multiroom.mojom",
+    "queryable_data_store.mojom",
   ]
 
   public_deps = [
diff --git a/chromecast/common/mojom/queryable_data_store.mojom b/chromecast/common/mojom/queryable_data_store.mojom
new file mode 100644
index 0000000..a19c59e3
--- /dev/null
+++ b/chromecast/common/mojom/queryable_data_store.mojom
@@ -0,0 +1,15 @@
+// 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.
+
+module chromecast.shell.mojom;
+
+import "mojo/public/mojom/base/values.mojom";
+
+// This interface provides a method for browser to set renderer-frame queryable
+// values at start up. It exposes read-only key/value pairs of config data to
+// Cast apps through javascript. This is implemented by a single renderer.
+interface QueryableDataStore {
+  Set(string key, mojo_base.mojom.Value value);
+};
+
diff --git a/chromecast/common/queryable_data.cc b/chromecast/common/queryable_data.cc
new file mode 100644
index 0000000..00fdaaa1
--- /dev/null
+++ b/chromecast/common/queryable_data.cc
@@ -0,0 +1,49 @@
+// 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 "chromecast/common/queryable_data.h"
+
+#include <utility>
+
+namespace chromecast {
+
+namespace {
+QueryableData& GetQueryableData() {
+  static base::NoDestructor<QueryableData> queryable_data;
+  return *queryable_data;
+}
+}  // namespace
+
+// static
+void QueryableData::RegisterQueryableValue(const std::string& query_key,
+                                           base::Value initial_value) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(GetQueryableData().sequence_checker_);
+  GetQueryableData().queryable_values_[query_key] = std::move(initial_value);
+}
+
+// static
+const base::Value* QueryableData::Query(const std::string& query_key) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(GetQueryableData().sequence_checker_);
+  const QueryableData& data = GetQueryableData();
+
+  auto value = data.queryable_values_.find(query_key);
+  if (value == data.queryable_values_.end())
+    return nullptr;
+  return &value->second;
+}
+
+// static
+const QueryableData::ValueMap& QueryableData::GetValues() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(GetQueryableData().sequence_checker_);
+  const QueryableData& data = GetQueryableData();
+  return data.queryable_values_;
+}
+
+QueryableData::QueryableData() {}
+
+QueryableData::~QueryableData() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+}  // namespace chromecast
diff --git a/chromecast/common/queryable_data.h b/chromecast/common/queryable_data.h
new file mode 100644
index 0000000..a8a4d84
--- /dev/null
+++ b/chromecast/common/queryable_data.h
@@ -0,0 +1,55 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_COMMON_QUERYABLE_DATA_H_
+#define CHROMECAST_COMMON_QUERYABLE_DATA_H_
+
+#include <memory>
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/no_destructor.h"
+#include "base/sequence_checker.h"
+#include "base/values.h"
+
+namespace chromecast {
+
+// This class is for use by both browser- and renderer-side, and tracks data
+// that can be exposed directly to applications via JS APIs. The browser-side
+// code uses it to expose key/value to renderer. The ipc to renderer is done by
+// binding QueryableDataStorePointer on browser side to the implementation on
+// the renderer side and calling the Set method.
+// All methods should be accessed on the main thread.
+// TODO(mdellaquila): Change the class implementation so that an instance of it
+// is created for each RenderFrame
+class QueryableData {
+ public:
+  using ValueMap = base::flat_map<std::string, base::Value>;
+
+  // Stores a value for the current process. If the key already exists, the
+  // value is replaced.
+  static void RegisterQueryableValue(const std::string& query_key,
+                                     base::Value initial_value);
+
+  // Returns a pointer to the value for given key, if found, or null if not.
+  static const base::Value* Query(const std::string& query_key);
+
+  static const ValueMap& GetValues();
+
+ private:
+  friend class base::NoDestructor<QueryableData>;
+
+  QueryableData();
+  ~QueryableData();
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  ValueMap queryable_values_;
+
+  DISALLOW_COPY_AND_ASSIGN(QueryableData);
+};
+
+}  // namespace chromecast
+
+#endif  //  CHROMECAST_COMMON_QUERYABLE_DATA_H_
diff --git a/chromecast/fuchsia/BUILD.gn b/chromecast/fuchsia/BUILD.gn
new file mode 100644
index 0000000..ea408054
--- /dev/null
+++ b/chromecast/fuchsia/BUILD.gn
@@ -0,0 +1,17 @@
+# 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("//chromecast/chromecast.gni")
+
+cast_source_set("fuchsia") {
+  sources = [
+    "queryable_data_host_fuchsia.cc",
+    "queryable_data_host_fuchsia.h",
+  ]
+  deps = [
+    "//base",
+    "//chromecast/activity",
+    "//chromecast/common:queryable_data",
+  ]
+}
diff --git a/chromecast/fuchsia/DEPS b/chromecast/fuchsia/DEPS
new file mode 100644
index 0000000..81da176
--- /dev/null
+++ b/chromecast/fuchsia/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+chromecast/activity",
+  "+chromecast/common",
+]
diff --git a/chromecast/fuchsia/queryable_data_host_fuchsia.cc b/chromecast/fuchsia/queryable_data_host_fuchsia.cc
new file mode 100644
index 0000000..7936163
--- /dev/null
+++ b/chromecast/fuchsia/queryable_data_host_fuchsia.cc
@@ -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.
+
+#include "chromecast/fuchsia/queryable_data_host_fuchsia.h"
+
+#include "base/values.h"
+#include "chromecast/common/queryable_data.h"
+
+namespace chromecast {
+
+QueryableDataHostFuchsia::QueryableDataHostFuchsia() {}
+
+QueryableDataHostFuchsia::~QueryableDataHostFuchsia() {}
+
+void QueryableDataHostFuchsia::SendQueryableValue(const std::string& key,
+                                                  const base::Value& value) {
+  // TODO(elvin): async call to update queryable values when runner fidl exists
+  LOG(ERROR) << "Not Implemented: SendQueryableValue" << key << " " << value;
+}
+
+}  // namespace chromecast
diff --git a/chromecast/fuchsia/queryable_data_host_fuchsia.h b/chromecast/fuchsia/queryable_data_host_fuchsia.h
new file mode 100644
index 0000000..beb1749
--- /dev/null
+++ b/chromecast/fuchsia/queryable_data_host_fuchsia.h
@@ -0,0 +1,32 @@
+// 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_FUCHSIA_QUERYABLE_DATA_HOST_FUCHSIA_H_
+#define CHROMECAST_FUCHSIA_QUERYABLE_DATA_HOST_FUCHSIA_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/values.h"
+#include "chromecast/activity/queryable_data_host.h"
+
+namespace chromecast {
+
+// Sends queryable data through FIDL to the Cast Runner (Fuchsia).
+class QueryableDataHostFuchsia : public QueryableDataHost {
+ public:
+  QueryableDataHostFuchsia();
+  ~QueryableDataHostFuchsia() override;
+
+  // chromecast::QueryableDataHost implementation:
+  void SendQueryableValue(const std::string& key,
+                          const base::Value& value) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(QueryableDataHostFuchsia);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_FUCHSIA_QUERYABLE_DATA_HOST_FUCHSIA_H_
diff --git a/chromecast/media/cma/base/balanced_media_task_runner_factory.cc b/chromecast/media/cma/base/balanced_media_task_runner_factory.cc
index f2747c3..3c11403 100644
--- a/chromecast/media/cma/base/balanced_media_task_runner_factory.cc
+++ b/chromecast/media/cma/base/balanced_media_task_runner_factory.cc
@@ -5,9 +5,9 @@
 #include "chromecast/media/cma/base/balanced_media_task_runner_factory.h"
 
 #include <map>
+#include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/single_thread_task_runner.h"
@@ -135,7 +135,7 @@
       return;
     }
 
-    task = base::ResetAndReturn(&pending_task_);
+    task = std::move(pending_task_);
   }
   task_runner_->PostTask(from_here_, task);
 }
diff --git a/chromecast/media/cma/base/buffering_frame_provider.cc b/chromecast/media/cma/base/buffering_frame_provider.cc
index f456daff..4c6a6cd 100644
--- a/chromecast/media/cma/base/buffering_frame_provider.cc
+++ b/chromecast/media/cma/base/buffering_frame_provider.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "chromecast/media/cma/base/buffering_state.h"
 #include "chromecast/media/cma/base/decoder_buffer_base.h"
 #include "media/base/bind_to_current_loop.h"
@@ -134,10 +133,9 @@
   if (!buffer_with_config.buffer()->end_of_stream())
     total_buffer_size_ -= buffer_with_config.buffer()->data_size();
 
-  base::ResetAndReturn(&read_cb_).Run(
-      buffer_with_config.buffer(),
-      buffer_with_config.audio_config(),
-      buffer_with_config.video_config());
+  std::move(read_cb_).Run(buffer_with_config.buffer(),
+                          buffer_with_config.audio_config(),
+                          buffer_with_config.video_config());
 }
 
 }  // namespace media
diff --git a/chromecast/media/cma/base/demuxer_stream_adapter.cc b/chromecast/media/cma/base/demuxer_stream_adapter.cc
index 5b91da3..4ff68881 100644
--- a/chromecast/media/cma/base/demuxer_stream_adapter.cc
+++ b/chromecast/media/cma/base/demuxer_stream_adapter.cc
@@ -4,8 +4,9 @@
 
 #include "chromecast/media/cma/base/demuxer_stream_adapter.h"
 
+#include <utility>
+
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/single_thread_task_runner.h"
 #include "chromecast/media/cma/base/balanced_media_task_runner_factory.h"
@@ -122,7 +123,7 @@
   // Just discard the buffer in the flush stage.
   if (!flush_cb_.is_null()) {
     LOG(INFO) << "Flush done";
-    base::ResetAndReturn(&flush_cb_).Run();
+    std::move(flush_cb_).Run();
     return;
   }
 
diff --git a/chromecast/media/cma/pipeline/av_pipeline_impl.cc b/chromecast/media/cma/pipeline/av_pipeline_impl.cc
index 53dabb6..5680108e 100644
--- a/chromecast/media/cma/pipeline/av_pipeline_impl.cc
+++ b/chromecast/media/cma/pipeline/av_pipeline_impl.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/single_thread_task_runner.h"
@@ -159,7 +158,7 @@
   }
   DCHECK_EQ(state_, kFlushing);
   set_state(kFlushed);
-  base::ResetAndReturn(&flush_cb_).Run();
+  std::move(flush_cb_).Run();
 }
 
 void AvPipelineImpl::SetCdm(CastCdmContext* cast_cdm_context) {
@@ -343,7 +342,7 @@
     client_.playback_error_cb.Run(::media::PIPELINE_ERROR_COULD_NOT_RENDER);
 
   if (!flush_cb_.is_null())
-    base::ResetAndReturn(&flush_cb_).Run();
+    std::move(flush_cb_).Run();
 }
 
 void AvPipelineImpl::OnKeyStatusChanged(const std::string& key_id,
diff --git a/chromecast/renderer/BUILD.gn b/chromecast/renderer/BUILD.gn
index 676efb3..0f2ec3ec 100644
--- a/chromecast/renderer/BUILD.gn
+++ b/chromecast/renderer/BUILD.gn
@@ -32,6 +32,12 @@
     "cast_media_playback_options.h",
     "cast_url_loader_throttle_provider.cc",
     "cast_url_loader_throttle_provider.h",
+    "native_bindings_helper.cc",
+    "native_bindings_helper.h",
+    "queryable_data_bindings.cc",
+    "queryable_data_bindings.h",
+    "queryable_data_store.cc",
+    "queryable_data_store.h",
   ]
 
   public_deps = [
@@ -47,6 +53,7 @@
     "//chromecast:chromecast_buildflags",
     "//chromecast/base",
     "//chromecast/common",
+    "//chromecast/common:queryable_data",
     "//chromecast/common/media",
     "//chromecast/common/mojom",
     "//chromecast/media",
@@ -54,10 +61,12 @@
     "//content/public/common",
     "//content/public/renderer",
     "//crypto",
+    "//gin",
     "//ipc",
     "//media",
     "//services/network/public/cpp:cpp",
     "//services/service_manager/public/cpp",
+    "//v8",
   ]
 
   if (!is_fuchsia) {
diff --git a/chromecast/renderer/DEPS b/chromecast/renderer/DEPS
index 3aaee62cf..62159ea 100644
--- a/chromecast/renderer/DEPS
+++ b/chromecast/renderer/DEPS
@@ -9,6 +9,7 @@
   "+content/public/renderer",
   "+extensions/common",
   "+extensions/renderer",
+  "+gin",
   "+media/base",
   "+media/media_buildflags.h",
   "+media/renderers",
@@ -16,4 +17,5 @@
   "+services/network/public/cpp",
   "+services/service_manager/public",
   "+third_party/blink/public",
+  "+v8",
 ]
diff --git a/chromecast/renderer/cast_content_renderer_client.cc b/chromecast/renderer/cast_content_renderer_client.cc
index 2461c2e..453e1f0 100644
--- a/chromecast/renderer/cast_content_renderer_client.cc
+++ b/chromecast/renderer/cast_content_renderer_client.cc
@@ -17,6 +17,7 @@
 #include "chromecast/renderer/cast_url_loader_throttle_provider.h"
 #include "chromecast/renderer/media/key_systems_cast.h"
 #include "chromecast/renderer/media/media_caps_observer_impl.h"
+#include "chromecast/renderer/queryable_data_bindings.h"
 #include "components/network_hints/renderer/prescient_networking_dispatcher.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/service_names.mojom.h"
@@ -147,6 +148,7 @@
   DCHECK(render_frame);
   // Lifetime is tied to |render_frame| via content::RenderFrameObserver.
   new CastMediaPlaybackOptions(render_frame);
+  new QueryableDataBindings(render_frame);
 
   if (!app_media_capabilities_observer_binding_.is_bound()) {
     mojom::ApplicationMediaCapabilitiesObserverPtr observer;
diff --git a/chromecast/renderer/native_bindings_helper.cc b/chromecast/renderer/native_bindings_helper.cc
new file mode 100644
index 0000000..bcc46cd
--- /dev/null
+++ b/chromecast/renderer/native_bindings_helper.cc
@@ -0,0 +1,86 @@
+// 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 "chromecast/renderer/native_bindings_helper.h"
+
+#include "base/logging.h"
+#include "content/public/renderer/render_frame.h"
+#include "gin/converter.h"
+#include "third_party/blink/public/web/blink.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+
+namespace chromecast {
+
+namespace {
+const char kCastObjectName[] = "cast";
+const char kCastPlatformObjectKey[] = "__platform__";
+}  // namespace
+
+v8::Local<v8::Object> GetOrCreateCastPlatformObject(
+    v8::Isolate* isolate,
+    v8::Local<v8::Object> global) {
+  v8::Local<v8::Object> cast =
+      EnsureObjectExists(isolate, global, kCastObjectName);
+  return EnsureObjectExists(isolate, cast, kCastPlatformObjectKey);
+}
+
+v8::Local<v8::Object> EnsureObjectExists(v8::Isolate* isolate,
+                                         v8::Local<v8::Object> parent,
+                                         const std::string& key) {
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  v8::MaybeLocal<v8::Value> child =
+      parent->Get(context, gin::StringToV8(isolate, key));
+  v8::Local<v8::Value> child_local;
+  v8::Local<v8::Object> child_object;
+  if (child.ToLocal(&child_local) && child_local->IsObject() &&
+      child_local->ToObject(context).ToLocal(&child_object))
+    return child_object;
+
+  if (!child_local.IsEmpty() && !child_local->IsUndefined())
+    LOG(WARNING) << "Overwriting non-empty non-object with key " << key;
+
+  v8::Local<v8::Object> new_child_object = v8::Object::New(isolate);
+  v8::Maybe<bool> result =
+      parent->Set(context, gin::StringToSymbol(isolate, key), new_child_object);
+  if (result.IsNothing() || !result.FromJust())
+    LOG(ERROR) << "Failed to set new object with key " << key;
+
+  return new_child_object;
+}
+
+CastBinding::CastBinding(content::RenderFrame* render_frame)
+    : content::RenderFrameObserver(render_frame) {}
+
+CastBinding::~CastBinding() {}
+
+void CastBinding::DidClearWindowObject() {
+  TryInstall();
+}
+
+void CastBinding::OnDestruct() {
+  delete this;
+}
+
+void CastBinding::TryInstall() {
+  blink::WebLocalFrame* web_frame = render_frame()->GetWebFrame();
+  if (!web_frame)
+    return;
+
+  v8::Isolate* isolate = blink::MainThreadIsolate();
+  if (!isolate)
+    return;
+
+  v8::HandleScope handle_scope(isolate);
+  v8::Local<v8::Context> context = web_frame->MainWorldScriptContext();
+  if (context.IsEmpty())
+    return;
+
+  v8::Context::Scope context_scope(context);
+  v8::Local<v8::Object> global = context->Global();
+  v8::Local<v8::Object> cast_platform =
+      GetOrCreateCastPlatformObject(isolate, global);
+  Install(cast_platform, isolate);
+}
+
+}  // namespace chromecast
diff --git a/chromecast/renderer/native_bindings_helper.h b/chromecast/renderer/native_bindings_helper.h
new file mode 100644
index 0000000..96a9a524
--- /dev/null
+++ b/chromecast/renderer/native_bindings_helper.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 CHROMECAST_RENDERER_NATIVE_BINDINGS_HELPER_H_
+#define CHROMECAST_RENDERER_NATIVE_BINDINGS_HELPER_H_
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "content/public/renderer/render_frame_observer.h"
+#include "gin/function_template.h"
+#include "v8/include/v8.h"
+namespace content {
+class RenderFrame;
+}
+
+namespace chromecast {
+
+// Returns the reference to "cast.__platform__", creating it if necessary.
+v8::Local<v8::Object> GetOrCreateCastPlatformObject(
+    v8::Isolate* isolate,
+    v8::Local<v8::Object> global);
+
+// Returns parent[key], creating it as a v8::Object if necessary. This will
+// aggressively overwrite things that are not objects.
+v8::Local<v8::Object> EnsureObjectExists(v8::Isolate* isolate,
+                                         v8::Local<v8::Object> parent,
+                                         const std::string& key);
+
+// Binds |method| and |args| into a JS function named |method_name| which will
+// be attached to |parent_object|. Return the bound function to caller.
+template <typename Functor, typename... Args>
+v8::Local<v8::Function> InstallBinding(v8::Isolate* isolate,
+                                       v8::Local<v8::Object> parent_object,
+                                       std::string method_name,
+                                       Functor method,
+                                       Args&&... args) {
+  v8::Local<v8::FunctionTemplate> temp(gin::CreateFunctionTemplate(
+      isolate, base::BindRepeating(method, std::forward<Args>(args)...)));
+  v8::Local<v8::Function> func =
+      temp->GetFunction(isolate->GetCurrentContext()).ToLocalChecked();
+  v8::Maybe<bool> result = parent_object->Set(
+      isolate->GetCurrentContext(),
+      gin::StringToSymbol(isolate, std::move(method_name)), func);
+  if (result.IsNothing() || !result.FromJust())
+    LOG(ERROR) << "Failed to install binging for method " << method_name;
+
+  return func;
+}
+
+// Template for managing the lifetime of a cast_shell binding. Derive from
+// from CastBinding and the class will be destroyed when the frame is
+// destroyed.
+class CastBinding : public content::RenderFrameObserver {
+ public:
+  void TryInstall();
+
+ protected:
+  explicit CastBinding(content::RenderFrame* render_frame);
+  ~CastBinding() override;
+
+  // content::RenderFrameObserver implementation:
+  void DidClearWindowObject() final;
+  void OnDestruct() final;
+
+  // Adds function bindings to the cast.__platform__ object.
+  // The function can be called multiple times, sub classes should make sure
+  // the binding is updated in every call.
+  virtual void Install(v8::Local<v8::Object> cast_platform,
+                       v8::Isolate* isolate) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CastBinding);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_RENDERER_NATIVE_BINDINGS_HELPER_H_
diff --git a/chromecast/renderer/queryable_data_bindings.cc b/chromecast/renderer/queryable_data_bindings.cc
new file mode 100644
index 0000000..bd6f9ba
--- /dev/null
+++ b/chromecast/renderer/queryable_data_bindings.cc
@@ -0,0 +1,65 @@
+// 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 "chromecast/renderer/queryable_data_bindings.h"
+
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "chromecast/common/queryable_data.h"
+#include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/v8_value_converter.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/web/blink.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "v8/include/v8.h"
+
+namespace chromecast {
+
+namespace {
+
+const char kQueryPlatformValueMethodName[] = "queryPlatformValue";
+
+}  // namespace
+
+QueryableDataBindings::QueryableDataBindings(content::RenderFrame* frame)
+    : CastBinding(frame),
+      queryable_data_store_(std::make_unique<QueryableDataStore>(
+          base::ThreadTaskRunnerHandle::Get())) {
+  registry_.AddInterface<shell::mojom::QueryableDataStore>(
+      base::BindRepeating(&QueryableDataStore::BindQueryableDataStoreRequest,
+                          base::Unretained(queryable_data_store_.get())));
+}
+
+QueryableDataBindings::~QueryableDataBindings() {}
+
+void QueryableDataBindings::Install(v8::Local<v8::Object> cast_platform,
+                                    v8::Isolate* isolate) {
+  VLOG(1) << "Installing QueryableDataBindings";
+
+  InstallBinding(isolate, cast_platform, kQueryPlatformValueMethodName,
+                 &QueryableDataBindings::QueryPlatformValue,
+                 base::Unretained(this));
+}
+
+void QueryableDataBindings::OnInterfaceRequestForFrame(
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle* interface_pipe) {
+  registry_.TryBindInterface(interface_name, interface_pipe);
+}
+
+v8::Local<v8::Value> QueryableDataBindings::QueryPlatformValue(
+    const std::string& query_key) {
+  VLOG(1) << __FUNCTION__ << ": " << query_key;
+
+  v8::Isolate* isolate = blink::MainThreadIsolate();
+
+  const base::Value* query_value = QueryableData::Query(query_key);
+  if (!query_value)
+    return v8::Local<v8::Value>(v8::Undefined(isolate));
+
+  return content::V8ValueConverter::Create()->ToV8Value(
+      query_value, render_frame()->GetWebFrame()->MainWorldScriptContext());
+}
+
+}  // namespace chromecast
diff --git a/chromecast/renderer/queryable_data_bindings.h b/chromecast/renderer/queryable_data_bindings.h
new file mode 100644
index 0000000..6fad3247
--- /dev/null
+++ b/chromecast/renderer/queryable_data_bindings.h
@@ -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.
+
+#ifndef CHROMECAST_RENDERER_QUERYABLE_DATA_BINDINGS_H_
+#define CHROMECAST_RENDERER_QUERYABLE_DATA_BINDINGS_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "chromecast/renderer/native_bindings_helper.h"
+#include "chromecast/renderer/queryable_data_store.h"
+#include "content/public/renderer/render_frame_observer.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+
+namespace chromecast {
+
+class QueryableDataBindings : public CastBinding {
+ public:
+  explicit QueryableDataBindings(content::RenderFrame* frame);
+
+  // content::RenderFrameObserver implementation:
+  void OnInterfaceRequestForFrame(
+      const std::string& interface_name,
+      mojo::ScopedMessagePipeHandle* interface_pipe) override;
+
+ private:
+  friend class CastBinding;
+
+  ~QueryableDataBindings() override;
+
+  // CastBinding implementation:
+  void Install(v8::Local<v8::Object> cast_platform,
+               v8::Isolate* isolate) override;
+
+  // Binding methods
+  v8::Local<v8::Value> QueryPlatformValue(const std::string& query_key);
+
+  service_manager::BinderRegistry registry_;
+  const std::unique_ptr<QueryableDataStore> queryable_data_store_;
+
+  DISALLOW_COPY_AND_ASSIGN(QueryableDataBindings);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_RENDERER_QUERYABLE_DATA_BINDINGS_H_
diff --git a/chromecast/renderer/queryable_data_store.cc b/chromecast/renderer/queryable_data_store.cc
new file mode 100644
index 0000000..6cbd59b
--- /dev/null
+++ b/chromecast/renderer/queryable_data_store.cc
@@ -0,0 +1,33 @@
+// 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 "chromecast/renderer/queryable_data_store.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/task_runner.h"
+#include "base/values.h"
+#include "chromecast/common/queryable_data.h"
+#include "content/public/renderer/render_thread.h"
+
+namespace chromecast {
+
+QueryableDataStore::QueryableDataStore(
+    const scoped_refptr<base::TaskRunner> render_main_thread)
+    : render_main_thread_(std::move(render_main_thread)) {}
+
+QueryableDataStore::~QueryableDataStore() {}
+
+void QueryableDataStore::Set(const std::string& key, base::Value value) {
+  render_main_thread_->PostTask(
+      FROM_HERE, base::BindOnce(&QueryableData::RegisterQueryableValue, key,
+                                std::move(value)));
+}
+
+void QueryableDataStore::BindQueryableDataStoreRequest(
+    shell::mojom::QueryableDataStoreRequest request) {
+  queryable_data_bindings_.AddBinding(this, std::move(request));
+}
+
+}  // namespace chromecast
diff --git a/chromecast/renderer/queryable_data_store.h b/chromecast/renderer/queryable_data_store.h
new file mode 100644
index 0000000..d1c8ffb
--- /dev/null
+++ b/chromecast/renderer/queryable_data_store.h
@@ -0,0 +1,46 @@
+// 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_RENDERER_QUERYABLE_DATA_STORE_H_
+#define CHROMECAST_RENDERER_QUERYABLE_DATA_STORE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "chromecast/common/mojom/queryable_data_store.mojom.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+
+namespace base {
+class Value;
+class TaskRunner;
+}  // namespace base
+
+namespace chromecast {
+
+// This class is used to sync queryable data on the renderer thread based on
+// messages from the browser process.
+class QueryableDataStore : public shell::mojom::QueryableDataStore {
+ public:
+  explicit QueryableDataStore(
+      const scoped_refptr<base::TaskRunner> render_main_thread);
+  ~QueryableDataStore() override;
+
+  void BindQueryableDataStoreRequest(
+      shell::mojom::QueryableDataStoreRequest request);
+
+ private:
+  // shell::mojom::QueryableDataStore implementation:
+  void Set(const std::string& key, base::Value value) override;
+
+  const scoped_refptr<base::TaskRunner> render_main_thread_;
+
+  mojo::BindingSet<shell::mojom::QueryableDataStore> queryable_data_bindings_;
+
+  DISALLOW_COPY_AND_ASSIGN(QueryableDataStore);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_RENDERER_QUERYABLE_DATA_STORE_H_
diff --git a/chromeos/audio/cras_audio_handler.cc b/chromeos/audio/cras_audio_handler.cc
index a9c960c..11482ef 100644
--- a/chromeos/audio/cras_audio_handler.cc
+++ b/chromeos/audio/cras_audio_handler.cc
@@ -452,6 +452,13 @@
     NotifyActiveNodeChanged(is_input);
 }
 
+void CrasAudioHandler::SetHotwordModel(uint64_t node_id,
+                                       const std::string& hotword_model,
+                                       VoidCrasAudioHandlerCallback callback) {
+  CrasAudioClient::Get()->SetHotwordModel(node_id, hotword_model,
+                                          std::move(callback));
+}
+
 void CrasAudioHandler::SwapInternalSpeakerLeftRightChannel(bool swap) {
   for (const auto& item : audio_devices_) {
     const AudioDevice& device = item.second;
diff --git a/chromeos/audio/cras_audio_handler.h b/chromeos/audio/cras_audio_handler.h
index 9dbd708..78b4ddf 100644
--- a/chromeos/audio/cras_audio_handler.h
+++ b/chromeos/audio/cras_audio_handler.h
@@ -39,6 +39,11 @@
 
 class AudioDevicesPrefHandler;
 
+// Callback to handle response of methods without result.
+// |result| is true if the method call is successfully completed, otherwise
+// false.
+using VoidCrasAudioHandlerCallback = base::OnceCallback<void(bool result)>;
+
 // This class is not thread safe. The public functions should be called on
 // browser main thread.
 class COMPONENT_EXPORT(CHROMEOS_AUDIO) CrasAudioHandler
@@ -245,6 +250,15 @@
   // Returns whether the acive nodes were successfully set.
   bool SetActiveOutputNodes(const NodeIdList& node_ids);
 
+  // Sets |hotword_model| to the given |node_id|.
+  // |hotword_model| is expected to be in format <language>_<region> with lower
+  // cases. E.g., "en_us".
+  // The callback will receive a boolean which indicates if the hotword model is
+  // successfully set.
+  void SetHotwordModel(uint64_t node_id,
+                       const std::string& hotword_model,
+                       VoidCrasAudioHandlerCallback callback);
+
   // Swaps the left and right channel of the internal speaker.
   // Swap the left and right channel if |swap| is true; otherwise, swap the left
   // and right channel back to the normal mode.
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 56497ff..3a29482 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -20,7 +20,7 @@
 
 // Enables or disables Crostini Backup.
 const base::Feature kCrostiniBackup{"CrostiniBackup",
-                                    base::FEATURE_ENABLED_BY_DEFAULT};
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables or disables Crostini support for usb mounting.
 const base::Feature kCrostiniUsbSupport{"CrostiniUsbSupport",
diff --git a/chromeos/dbus/audio/cras_audio_client.cc b/chromeos/dbus/audio/cras_audio_client.cc
index 5566ff7..c1acc78 100644
--- a/chromeos/dbus/audio/cras_audio_client.cc
+++ b/chromeos/dbus/audio/cras_audio_client.cc
@@ -239,6 +239,20 @@
                             base::DoNothing());
   }
 
+  void SetHotwordModel(uint64_t node_id,
+                       const std::string& hotword_model,
+                       VoidDBusMethodCallback callback) override {
+    dbus::MethodCall method_call(cras::kCrasControlInterface,
+                                 cras::kSetHotwordModel);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendUint64(node_id);
+    writer.AppendString(hotword_model);
+    cras_proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&CrasAudioClientImpl::OnSetHotwordModel,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
   void AddActiveInputNode(uint64_t node_id) override {
     dbus::MethodCall method_call(cras::kCrasControlInterface,
                                  cras::kAddActiveInputNode);
@@ -544,6 +558,31 @@
     std::move(callback).Run(num_active_streams);
   }
 
+  void OnSetHotwordModel(VoidDBusMethodCallback callback,
+                         dbus::Response* response) {
+    if (!response) {
+      LOG(ERROR) << "Failed to call SetHotwordModel.";
+      std::move(callback).Run(false);
+      return;
+    }
+
+    dbus::MessageReader reader(response);
+    int32_t result;
+    if (!reader.PopInt32(&result)) {
+      LOG(ERROR) << "Failed to parse results from SetHotwordModel.";
+      std::move(callback).Run(false);
+      return;
+    }
+
+    if (result != 0) {
+      LOG(ERROR) << "Errors in SetHotwordModel.";
+      std::move(callback).Run(false);
+      return;
+    }
+
+    std::move(callback).Run(true);
+  }
+
   bool GetAudioNode(dbus::Response* response,
                     dbus::MessageReader* array_reader,
                     AudioNode* node) {
diff --git a/chromeos/dbus/audio/cras_audio_client.h b/chromeos/dbus/audio/cras_audio_client.h
index f2ae386..6572792 100644
--- a/chromeos/dbus/audio/cras_audio_client.h
+++ b/chromeos/dbus/audio/cras_audio_client.h
@@ -119,6 +119,15 @@
   // Sets the primary active input node to |node_id|.
   virtual void SetActiveInputNode(uint64_t node_id) = 0;
 
+  // Sets |hotword_model| for the given |node_id|.
+  // |hotword_model| is expected to be in format <language>_<region> with lower
+  // cases. E.g., "en_us".
+  // The callback will receive a boolean which indicates if the hotword model is
+  // successfully set.
+  virtual void SetHotwordModel(uint64_t node_id,
+                               const std::string& hotword_model,
+                               VoidDBusMethodCallback callback) = 0;
+
   // Adds input node |node_id| to the active input list. This is used to add
   // an additional active input node besides the one set by SetActiveInputNode.
   // Note that this action will not trigger an ActiveInputNodeChanged event and
diff --git a/chromeos/dbus/audio/fake_cras_audio_client.cc b/chromeos/dbus/audio/fake_cras_audio_client.cc
index 778f5d64..a2256d17 100644
--- a/chromeos/dbus/audio/fake_cras_audio_client.cc
+++ b/chromeos/dbus/audio/fake_cras_audio_client.cc
@@ -188,6 +188,10 @@
     observer.ActiveInputNodeChanged(node_id);
 }
 
+void FakeCrasAudioClient::SetHotwordModel(uint64_t node_id,
+                                          const std::string& hotword_model,
+                                          VoidDBusMethodCallback callback) {}
+
 void FakeCrasAudioClient::AddActiveInputNode(uint64_t node_id) {
   for (size_t i = 0; i < node_list_.size(); ++i) {
     if (node_list_[i].id == node_id)
diff --git a/chromeos/dbus/audio/fake_cras_audio_client.h b/chromeos/dbus/audio/fake_cras_audio_client.h
index 6210447f..1a06b9b8 100644
--- a/chromeos/dbus/audio/fake_cras_audio_client.h
+++ b/chromeos/dbus/audio/fake_cras_audio_client.h
@@ -41,6 +41,9 @@
   void SetInputMute(bool mute_on) override;
   void SetActiveOutputNode(uint64_t node_id) override;
   void SetActiveInputNode(uint64_t node_id) override;
+  void SetHotwordModel(uint64_t node_id,
+                       const std::string& hotword_model,
+                       VoidDBusMethodCallback callback) override;
   void AddActiveInputNode(uint64_t node_id) override;
   void RemoveActiveInputNode(uint64_t node_id) override;
   void AddActiveOutputNode(uint64_t node_id) override;
diff --git a/chromeos/dbus/pipe_reader.cc b/chromeos/dbus/pipe_reader.cc
index 2f6de4b..d210f16 100644
--- a/chromeos/dbus/pipe_reader.cc
+++ b/chromeos/dbus/pipe_reader.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/task_runner.h"
 #include "net/base/file_stream.h"
@@ -76,7 +75,7 @@
     // Clear members before calling the |callback|.
     data_.clear();
     data_stream_.reset();
-    base::ResetAndReturn(&callback_).Run(std::move(result));
+    std::move(callback_).Run(std::move(result));
     return;
   }
 
diff --git a/chromeos/dbus/shill/shill_client_helper.cc b/chromeos/dbus/shill/shill_client_helper.cc
index 410496c..d33bd0a 100644
--- a/chromeos/dbus/shill/shill_client_helper.cc
+++ b/chromeos/dbus/shill/shill_client_helper.cc
@@ -10,7 +10,6 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/device_event_log/device_event_log.h"
@@ -477,7 +476,7 @@
 void ShillClientHelper::Release() {
   --active_refs_;
   if (active_refs_ == 0 && !released_callback_.is_null())
-    base::ResetAndReturn(&released_callback_).Run(this);  // May delete this
+    std::move(released_callback_).Run(this);  // May delete this
 }
 
 void ShillClientHelper::OnSignalConnected(const std::string& interface,
diff --git a/chromeos/resources/multidevice_resources.grdp b/chromeos/resources/multidevice_resources.grdp
index b0289aa..1c21e3d 100644
--- a/chromeos/resources/multidevice_resources.grdp
+++ b/chromeos/resources/multidevice_resources.grdp
@@ -1,21 +1,41 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
   <!-- Mojo JS files. -->
+  <include name="IDR_MULTIDEVICE_MULTIDEVICE_TYPES_MOJOM_HTML"
+      file="${mojom_root}/chromeos/components/multidevice/mojom/multidevice_types.mojom.html"
+      use_base_dir="false"
+      type="BINDATA"
+      compress="gzip" />
   <include name="IDR_MULTIDEVICE_MULTIDEVICE_TYPES_MOJOM_LITE_JS"
       file="${mojom_root}/chromeos/components/multidevice/mojom/multidevice_types.mojom-lite.js"
       use_base_dir="false"
       type="BINDATA"
       compress="gzip" />
+  <include name="IDR_MULTIDEVICE_DEVICE_SYNC_MOJOM_HTML"
+      file="${mojom_root}/chromeos/services/device_sync/public/mojom/device_sync.mojom.html"
+      use_base_dir="false"
+      type="BINDATA"
+      compress="gzip" />
   <include name="IDR_MULTIDEVICE_DEVICE_SYNC_MOJOM_LITE_JS"
       file="${mojom_root}/chromeos/services/device_sync/public/mojom/device_sync.mojom-lite.js"
       use_base_dir="false"
       type="BINDATA"
       compress="gzip" />
+  <include name="IDR_MULTIDEVICE_MULTIDEVICE_SETUP_MOJOM_HTML"
+      file="${mojom_root}/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.html"
+      use_base_dir="false"
+      type="BINDATA"
+      compress="gzip" />
   <include name="IDR_MULTIDEVICE_MULTIDEVICE_SETUP_MOJOM_LITE_JS"
       file="${mojom_root}/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom-lite.js"
       use_base_dir="false"
       type="BINDATA"
       compress="gzip" />
+  <include name="IDR_MULTIDEVICE_MULTIDEVICE_SETUP_CONSTANTS_MOJOM_HTML"
+      file="${mojom_root}/chromeos/services/multidevice_setup/public/mojom/constants.mojom.html"
+      use_base_dir="false"
+      type="BINDATA"
+      compress="gzip" />
   <include name="IDR_MULTIDEVICE_MULTIDEVICE_SETUP_CONSTANTS_MOJOM_LITE_JS"
       file="${mojom_root}/chromeos/services/multidevice_setup/public/mojom/constants.mojom-lite.js"
       use_base_dir="false"
diff --git a/chromeos/tpm/tpm_token_info_getter_unittest.cc b/chromeos/tpm/tpm_token_info_getter_unittest.cc
index 6de30f50..fe0bda26 100644
--- a/chromeos/tpm/tpm_token_info_getter_unittest.cc
+++ b/chromeos/tpm/tpm_token_info_getter_unittest.cc
@@ -190,8 +190,7 @@
     // Called synchronously for convenience (to avoid using extra RunLoop in
     // tests). Unlike with other Cryptohome callbacks, TPMTokenInfoGetter does
     // not rely on this callback being called asynchronously.
-    base::ResetAndReturn(&pending_get_tpm_token_info_callback_)
-        .Run(tpm_token_info_);
+    std::move(pending_get_tpm_token_info_callback_).Run(tpm_token_info_);
   }
 
   AccountId account_id_;
diff --git a/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc b/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
index dbcdbdb71..8db36dd 100644
--- a/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
+++ b/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
@@ -4,8 +4,9 @@
 
 #include "components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h"
 
+#include <utility>
+
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/metrics/histogram_macros.h"
 #include "build/build_config.h"
 #include "components/arc/video_accelerator/arc_video_accelerator_util.h"
@@ -245,7 +246,7 @@
     pending_flush_callbacks_.pop();
   }
 
-  base::ResetAndReturn(&pending_reset_callback_)
+  std::move(pending_reset_callback_)
       .Run(mojom::VideoDecodeAccelerator::Result::SUCCESS);
   RunPendingRequests();
 }
@@ -263,7 +264,7 @@
     pending_flush_callbacks_.pop();
   }
   if (pending_reset_callback_) {
-    base::ResetAndReturn(&pending_reset_callback_)
+    std::move(pending_reset_callback_)
         .Run(mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE);
   }
 
diff --git a/components/autofill/core/browser/payments/autofill_credit_card_filling_infobar_delegate_mobile.cc b/components/autofill/core/browser/payments/autofill_credit_card_filling_infobar_delegate_mobile.cc
index aadf087d..87895b3 100644
--- a/components/autofill/core/browser/payments/autofill_credit_card_filling_infobar_delegate_mobile.cc
+++ b/components/autofill/core/browser/payments/autofill_credit_card_filling_infobar_delegate_mobile.cc
@@ -29,9 +29,7 @@
 #else
       card_label_(base::string16(kMidlineEllipsis) + card.LastFourDigits()),
 #endif
-      card_sub_label_(card.AbbreviatedExpirationDateForDisplay(
-          !features::
-              IsAutofillSaveCardDialogUnlabeledExpirationDateEnabled())) {
+      card_sub_label_(card.AbbreviatedExpirationDateForDisplay(false)) {
 }
 
 AutofillCreditCardFillingInfoBarDelegateMobile::
diff --git a/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.cc b/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.cc
index 00b04c8..24e2a81 100644
--- a/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.cc
+++ b/components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.cc
@@ -47,8 +47,7 @@
       had_user_interaction_(false),
       issuer_icon_id_(CreditCard::IconResourceId(card.network())),
       card_label_(card.NetworkAndLastFourDigits()),
-      card_sub_label_(card.AbbreviatedExpirationDateForDisplay(
-          !features::IsAutofillSaveCardDialogUnlabeledExpirationDateEnabled())),
+      card_sub_label_(card.AbbreviatedExpirationDateForDisplay(false)),
       card_last_four_digits_(card.LastFourDigits()),
       is_off_the_record_(is_off_the_record) {
   DCHECK_EQ(upload, !upload_save_card_prompt_callback_.is_null());
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index e200384..e0ba8bb3 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -147,10 +147,6 @@
 const base::Feature kAutofillSaveOnProbablySubmitted{
     "AutofillSaveOnProbablySubmitted", base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kAutofillSaveCardDialogUnlabeledExpirationDate{
-    "AutofillSaveCardDialogUnlabeledExpirationDate",
-    base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enables or Disables (mostly for hermetic testing) autofill server
 // communication. The URL of the autofill server can further be controlled via
 // the autofill-server-url param. The given URL should specify the complete
@@ -240,10 +236,5 @@
   return base::FeatureList::IsEnabled(kAutofillManualFallbackPhaseTwo);
 }
 
-bool IsAutofillSaveCardDialogUnlabeledExpirationDateEnabled() {
-  return base::FeatureList::IsEnabled(
-      kAutofillSaveCardDialogUnlabeledExpirationDate);
-}
-
 }  // namespace features
 }  // namespace autofill
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index 4ad9afb..6489b514 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -48,7 +48,6 @@
 extern const base::Feature kAutofillProfileServerValidation;
 extern const base::Feature kAutofillRestrictUnownedFieldsToFormlessCheckout;
 extern const base::Feature kAutofillRichMetadataQueries;
-extern const base::Feature kAutofillSaveCardDialogUnlabeledExpirationDate;
 extern const base::Feature kAutofillSaveOnProbablySubmitted;
 extern const base::Feature kAutofillServerCommunication;
 extern const base::Feature kAutofillSettingsCardTypeSplit;
@@ -84,10 +83,6 @@
 // enabled.
 bool IsAutofillManualFallbackEnabled();
 
-// Returns true if expiration dates on the save card dialog should be
-// unlabeled, i.e. not preceded by "Exp."
-bool IsAutofillSaveCardDialogUnlabeledExpirationDateEnabled();
-
 }  // namespace features
 }  // namespace autofill
 
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer.cc b/components/certificate_transparency/chrome_ct_policy_enforcer.cc
index 99a4405..a062412 100644
--- a/components/certificate_transparency/chrome_ct_policy_enforcer.cc
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer.cc
@@ -96,17 +96,17 @@
   return "unknown";
 }
 
-std::unique_ptr<base::Value> NetLogCertComplianceCheckResultCallback(
+base::Value NetLogCertComplianceCheckResultCallback(
     net::X509Certificate* cert,
     bool build_timely,
     CTPolicyCompliance compliance,
     net::NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->Set("certificate",
-            net::NetLogX509CertificateCallback(cert, capture_mode));
-  dict->SetBoolean("build_timely", build_timely);
-  dict->SetString("ct_compliance_status",
-                  CTPolicyComplianceToString(compliance));
+  base::DictionaryValue dict;
+  dict.SetKey("certificate",
+              net::NetLogX509CertificateCallback(cert, capture_mode));
+  dict.SetBoolean("build_timely", build_timely);
+  dict.SetString("ct_compliance_status",
+                 CTPolicyComplianceToString(compliance));
   return std::move(dict);
 }
 
diff --git a/components/certificate_transparency/single_tree_tracker.cc b/components/certificate_transparency/single_tree_tracker.cc
index 90794fb..09815d0f 100644
--- a/components/certificate_transparency/single_tree_tracker.cc
+++ b/components/certificate_transparency/single_tree_tracker.cc
@@ -138,17 +138,17 @@
   return sct_timestamp + kMaximumMergeDelay < sth_timestamp;
 }
 
-std::unique_ptr<base::Value> NetLogEntryAuditingEventCallback(
+base::Value NetLogEntryAuditingEventCallback(
     const SHA256HashValue* log_entry,
     base::StringPiece log_id,
     bool success,
     net::NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  base::DictionaryValue dict;
 
-  dict->SetString("log_entry",
-                  base::HexEncode(log_entry->data, crypto::kSHA256Length));
-  dict->SetString("log_id", base::HexEncode(log_id.data(), log_id.size()));
-  dict->SetBoolean("success", success);
+  dict.SetString("log_entry",
+                 base::HexEncode(log_entry->data, crypto::kSHA256Length));
+  dict.SetString("log_id", base::HexEncode(log_id.data(), log_id.size()));
+  dict.SetBoolean("success", success);
 
   return std::move(dict);
 }
diff --git a/components/content_capture/browser/content_capture_receiver_manager.cc b/components/content_capture/browser/content_capture_receiver_manager.cc
index 353ef88..98bcbe1 100644
--- a/components/content_capture/browser/content_capture_receiver_manager.cc
+++ b/components/content_capture/browser/content_capture_receiver_manager.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "components/content_capture/browser/content_capture_receiver.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
 
@@ -86,10 +87,12 @@
     content::NavigationHandle* navigation_handle) {
   auto* receiver =
       ContentCaptureReceiverForFrame(navigation_handle->GetRenderFrameHost());
-  if (ShouldCapture(navigation_handle->GetURL()))
-    receiver->StartCapture();
-  else
+  if (web_contents()->GetBrowserContext()->IsOffTheRecord() ||
+      !ShouldCapture(navigation_handle->GetURL())) {
     receiver->StopCapture();
+    return;
+  }
+  receiver->StartCapture();
 }
 
 void ContentCaptureReceiverManager::DidCaptureContent(
diff --git a/components/crash/android/crash_keys_android.cc b/components/crash/android/crash_keys_android.cc
index 1e85b5c..64e5b5bd 100644
--- a/components/crash/android/crash_keys_android.cc
+++ b/components/crash/android/crash_keys_android.cc
@@ -24,7 +24,7 @@
       {"dynamic_module_dex_name", JavaCrashKey::Tag::kArray},
   };
   static_assert(
-      base::size(crash_keys) == static_cast<size_t>(CrashKeyIndex::NUM_KEYS),
+      base::size(crash_keys) == static_cast<size_t>(CrashKeyIndex::NUM_ENTRIES),
       "crash_keys out of sync with index enum");
 
   return crash_keys[index];
diff --git a/components/crash/android/crash_keys_android.h b/components/crash/android/crash_keys_android.h
index 5e2838ee..afb1e35 100644
--- a/components/crash/android/crash_keys_android.h
+++ b/components/crash/android/crash_keys_android.h
@@ -17,7 +17,7 @@
   INSTALLED_MODULES,
   EMULATED_MODULES,
   DYNAMIC_MODULE_DEX_NAME,
-  NUM_KEYS
+  NUM_ENTRIES
 };
 
 // These methods are only exposed for testing -- normal usage should be from
diff --git a/components/crash/android/java/src/org/chromium/components/crash/CrashKeys.java b/components/crash/android/java/src/org/chromium/components/crash/CrashKeys.java
index 2eed3953..553e58db 100644
--- a/components/crash/android/java/src/org/chromium/components/crash/CrashKeys.java
+++ b/components/crash/android/java/src/org/chromium/components/crash/CrashKeys.java
@@ -34,7 +34,7 @@
     private static class Holder { static final CrashKeys INSTANCE = new CrashKeys(); }
 
     private CrashKeys() {
-        assert CrashKeyIndex.NUM_KEYS == KEYS.length;
+        assert CrashKeyIndex.NUM_ENTRIES == KEYS.length;
     }
 
     /**
diff --git a/components/crash/core/common/BUILD.gn b/components/crash/core/common/BUILD.gn
index 5097f18..8caf17a 100644
--- a/components/crash/core/common/BUILD.gn
+++ b/components/crash/core/common/BUILD.gn
@@ -128,7 +128,7 @@
       "//components/gwp_asan/buildflags",
     ]
 
-    if (enable_gwp_asan) {
+    if (enable_gwp_asan_malloc) {
       deps += [ "//components/gwp_asan/client" ]
     }
 
diff --git a/components/crash/core/common/objc_zombie.mm b/components/crash/core/common/objc_zombie.mm
index 4b2eb9a..f2dfff2 100644
--- a/components/crash/core/common/objc_zombie.mm
+++ b/components/crash/core/common/objc_zombie.mm
@@ -21,7 +21,7 @@
 #include "components/crash/core/common/crash_key.h"
 #include "components/gwp_asan/buildflags/buildflags.h"
 
-#if BUILDFLAG(ENABLE_GWP_ASAN)
+#if BUILDFLAG(ENABLE_GWP_ASAN_MALLOC)
 #include "components/gwp_asan/client/sampling_allocator_shims.h"  // nogncheck
 #endif
 
@@ -98,7 +98,7 @@
 
   // Use the original |-dealloc| if the object doesn't wish to be
   // zombied or GWP-ASan is the backing allocator.
-#if BUILDFLAG(ENABLE_GWP_ASAN)
+#if BUILDFLAG(ENABLE_GWP_ASAN_MALLOC)
   bool gwp_asan_allocation = gwp_asan::IsGwpAsanAllocation(self);
 #else
   bool gwp_asan_allocation = false;
diff --git a/components/cronet/ios/BUILD.gn b/components/cronet/ios/BUILD.gn
index a10f65a..501fd258 100644
--- a/components/cronet/ios/BUILD.gn
+++ b/components/cronet/ios/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/buildflag_header.gni")
-import("//build/config/c++/c++.gni")
 import("//build/config/ios/rules.gni")
 import("//build/config/mac/symbols.gni")
 import("//build/mac/tweak_info_plist.gni")
@@ -170,7 +169,6 @@
   public_deps = [
     "//components/grpc_support",
   ]
-
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
@@ -182,16 +180,6 @@
   deps = [
     ":cronet_static",
   ]
-
-  if (use_custom_libcxx) {
-    deps += [
-      # Add shared_library_deps to include custom libc++ into dependencies.
-      # They are by default only added to executable(), loadable_module(), and
-      # shared_library() targets, but cronet_static_complete library needs it as well to
-      # avoid linking with different versions of libc++.
-      "//build/config:shared_library_deps",
-    ]
-  }
 }
 
 # A static library which contains cronet and all dependendencies hidden inside.
diff --git a/components/cronet/native/url_request.cc b/components/cronet/native/url_request.cc
index a993f40..6237f025 100644
--- a/components/cronet/native/url_request.cc
+++ b/components/cronet/native/url_request.cc
@@ -755,7 +755,7 @@
     int64_t received_bytes_count) {
   DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
   base::AutoLock lock(url_request_->lock_);
-  DCHECK_EQ(url_request_->metrics_.get(), nullptr)
+  DCHECK_EQ(url_request_->metrics_, nullptr)
       << "Metrics collection should only happen once.";
   auto metrics = std::make_unique<Cronet_Metrics>();
   using native_metrics_util::ConvertTime;
diff --git a/components/cronet/stale_host_resolver_unittest.cc b/components/cronet/stale_host_resolver_unittest.cc
index 8287e14..9418875 100644
--- a/components/cronet/stale_host_resolver_unittest.cc
+++ b/components/cronet/stale_host_resolver_unittest.cc
@@ -9,7 +9,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
@@ -316,7 +315,7 @@
     resolve_complete_ = true;
 
     if (!resolve_closure_.is_null())
-      base::ResetAndReturn(&resolve_closure_).Run();
+      std::move(resolve_closure_).Run();
   }
 
   void AdvanceTickClock(base::TimeDelta delta) { tick_clock_.Advance(delta); }
diff --git a/components/cronet/tools/hide_symbols.py b/components/cronet/tools/hide_symbols.py
index f38bc81..9680cba 100755
--- a/components/cronet/tools/hide_symbols.py
+++ b/components/cronet/tools/hide_symbols.py
@@ -120,11 +120,6 @@
   ]
   subprocess.check_call(command)
 
-  ret = os.system('xcrun nm -u "' + options.output_obj + '" | grep ___cxa_pure_virtual')
-  if ret == 0:
-    print "ERROR: Found undefined libc++ symbols, is libc++ indcluded in dependencies?"
-    exit(2)
-
 
 if __name__ == "__main__":
   main()
diff --git a/components/dom_distiller/core/article_distillation_update.cc b/components/dom_distiller/core/article_distillation_update.cc
index c15ebb1..dab88db 100644
--- a/components/dom_distiller/core/article_distillation_update.cc
+++ b/components/dom_distiller/core/article_distillation_update.cc
@@ -9,7 +9,7 @@
 namespace dom_distiller {
 
 ArticleDistillationUpdate::ArticleDistillationUpdate(
-    const std::vector<scoped_refptr<RefCountedPageProto> >& pages,
+    const std::vector<scoped_refptr<RefCountedPageProto>>& pages,
     bool has_next_page,
     bool has_prev_page)
     : has_next_page_(has_next_page),
diff --git a/components/dom_distiller/core/article_distillation_update.h b/components/dom_distiller/core/article_distillation_update.h
index 0e4fcd0..be6dbbd 100644
--- a/components/dom_distiller/core/article_distillation_update.h
+++ b/components/dom_distiller/core/article_distillation_update.h
@@ -20,7 +20,7 @@
   typedef base::RefCountedData<DistilledPageProto> RefCountedPageProto;
 
   ArticleDistillationUpdate(
-      const std::vector<scoped_refptr<RefCountedPageProto> >& pages,
+      const std::vector<scoped_refptr<RefCountedPageProto>>& pages,
       bool has_next_page,
       bool has_prev_page);
   ArticleDistillationUpdate(const ArticleDistillationUpdate& other);
@@ -46,7 +46,7 @@
   bool has_next_page_;
   bool has_prev_page_;
   // Currently available pages.
-  std::vector<scoped_refptr<RefCountedPageProto> > pages_;
+  std::vector<scoped_refptr<RefCountedPageProto>> pages_;
 };
 
 }  // namespace dom_distiller
diff --git a/components/dom_distiller/core/article_entry.cc b/components/dom_distiller/core/article_entry.cc
index 076f519..d19d818 100644
--- a/components/dom_distiller/core/article_entry.cc
+++ b/components/dom_distiller/core/article_entry.cc
@@ -7,13 +7,15 @@
 #include "base/logging.h"
 #include "components/sync/model/sync_change.h"
 
-using sync_pb::EntitySpecifics;
 using sync_pb::ArticlePage;
 using sync_pb::ArticleSpecifics;
+using sync_pb::EntitySpecifics;
 
 namespace dom_distiller {
 
-bool IsEntryPageValid(const ArticleEntryPage& page) { return page.has_url(); }
+bool IsEntryPageValid(const ArticleEntryPage& page) {
+  return page.has_url();
+}
 
 bool IsEntryValid(const ArticleEntry& entry) {
   if (!entry.has_entry_id())
diff --git a/components/dom_distiller/core/article_entry_unittest.cc b/components/dom_distiller/core/article_entry_unittest.cc
index bd861b0b9..3d8ba0d7 100644
--- a/components/dom_distiller/core/article_entry_unittest.cc
+++ b/components/dom_distiller/core/article_entry_unittest.cc
@@ -7,12 +7,12 @@
 #include "components/sync/protocol/sync.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using sync_pb::EntitySpecifics;
 using sync_pb::ArticlePage;
 using sync_pb::ArticleSpecifics;
+using sync_pb::EntitySpecifics;
+using testing::AssertionFailure;
 using testing::AssertionResult;
 using testing::AssertionSuccess;
-using testing::AssertionFailure;
 
 namespace dom_distiller {
 
diff --git a/components/dom_distiller/core/distillable_page_detector.cc b/components/dom_distiller/core/distillable_page_detector.cc
index 1a9e5777..3091ca2 100644
--- a/components/dom_distiller/core/distillable_page_detector.cc
+++ b/components/dom_distiller/core/distillable_page_detector.cc
@@ -53,8 +53,7 @@
   }
 }
 
-DistillablePageDetector::~DistillablePageDetector() {
-}
+DistillablePageDetector::~DistillablePageDetector() {}
 
 bool DistillablePageDetector::Classify(
     const std::vector<double>& features) const {
diff --git a/components/dom_distiller/core/distillable_page_detector.h b/components/dom_distiller/core/distillable_page_detector.h
index 6fe2360..9d0fa34 100644
--- a/components/dom_distiller/core/distillable_page_detector.h
+++ b/components/dom_distiller/core/distillable_page_detector.h
@@ -30,13 +30,13 @@
 
   double Score(const std::vector<double>& features) const;
   double GetThreshold() const;
+
  private:
   std::unique_ptr<AdaBoostProto> proto_;
   double threshold_;
   DISALLOW_COPY_AND_ASSIGN(DistillablePageDetector);
 };
 
-
 }  // namespace dom_distiller
 
 #endif  // COMPONENTS_DOM_DISTILLER_CORE_DISTILLABLE_PAGE_DETECTOR_H_
diff --git a/components/dom_distiller/core/distillable_page_detector_unittest.cc b/components/dom_distiller/core/distillable_page_detector_unittest.cc
index b5afbd19..60a0a72 100644
--- a/components/dom_distiller/core/distillable_page_detector_unittest.cc
+++ b/components/dom_distiller/core/distillable_page_detector_unittest.cc
@@ -40,7 +40,7 @@
   AdaBoostProto proto_;
 };
 
-}
+}  // namespace
 
 TEST(DomDistillerDistillablePageDetectorTest, TestCalculateThreshold) {
   std::unique_ptr<DistillablePageDetector> detector =
@@ -48,10 +48,7 @@
 
   EXPECT_DOUBLE_EQ(1.5, detector->GetThreshold());
 
-  detector = Builder()
-                 .Stump(0, 1.0, -1.0)
-                 .Stump(0, 1.4, 2.0)
-                 .Build();
+  detector = Builder().Stump(0, 1.0, -1.0).Stump(0, 1.4, 2.0).Build();
   EXPECT_DOUBLE_EQ(0.5, detector->GetThreshold());
 
   detector = Builder()
@@ -106,6 +103,4 @@
   EXPECT_DOUBLE_EQ(0.0, detector->Score(features));
 }
 
-
-}
-
+}  // namespace dom_distiller
diff --git a/components/dom_distiller/core/distilled_content_store.cc b/components/dom_distiller/core/distilled_content_store.cc
index 3548fd82a..0a734c0 100644
--- a/components/dom_distiller/core/distilled_content_store.cc
+++ b/components/dom_distiller/core/distilled_content_store.cc
@@ -92,11 +92,9 @@
 }
 
 InMemoryContentStore::CacheDeletor::CacheDeletor(InMemoryContentStore* store)
-    : store_(store) {
-}
+    : store_(store) {}
 
-InMemoryContentStore::CacheDeletor::~CacheDeletor() {
-}
+InMemoryContentStore::CacheDeletor::~CacheDeletor() {}
 
 void InMemoryContentStore::CacheDeletor::operator()(
     DistilledArticleProto* proto) {
diff --git a/components/dom_distiller/core/distilled_content_store.h b/components/dom_distiller/core/distilled_content_store.h
index b3fe71a..cf7c1fa 100644
--- a/components/dom_distiller/core/distilled_content_store.h
+++ b/components/dom_distiller/core/distilled_content_store.h
@@ -87,6 +87,6 @@
   UrlMap url_to_id_;
 };
 
-}  // dom_distiller
+}  // namespace dom_distiller
 
 #endif  // COMPONENTS_DOM_DISTILLER_CORE_DOM_DISTILLER_CONTENT_CACHE_H_
diff --git a/components/dom_distiller/core/distilled_content_store_unittest.cc b/components/dom_distiller/core/distilled_content_store_unittest.cc
index f4713932..f223607 100644
--- a/components/dom_distiller/core/distilled_content_store_unittest.cc
+++ b/components/dom_distiller/core/distilled_content_store_unittest.cc
@@ -82,8 +82,7 @@
   const ArticleEntry entry = CreateEntry("test-id", "url1", "url2", "url3");
   const DistilledArticleProto stored_proto =
       CreateDistilledArticleForEntry(entry);
-  store_->SaveContent(entry,
-                      stored_proto,
+  store_->SaveContent(entry, stored_proto,
                       base::Bind(&InMemoryContentStoreTest::OnSaveCallback,
                                  base::Unretained(this)));
   base::RunLoop().RunUntilIdle();
@@ -120,8 +119,7 @@
   const ArticleEntry first_entry = CreateEntry("first", "url1", "url2", "url3");
   const DistilledArticleProto first_stored_proto =
       CreateDistilledArticleForEntry(first_entry);
-  store_->SaveContent(first_entry,
-                      first_stored_proto,
+  store_->SaveContent(first_entry, first_stored_proto,
                       base::Bind(&InMemoryContentStoreTest::OnSaveCallback,
                                  base::Unretained(this)));
   base::RunLoop().RunUntilIdle();
@@ -133,8 +131,7 @@
       CreateEntry("second", "url4", "url5", "url6");
   const DistilledArticleProto second_stored_proto =
       CreateDistilledArticleForEntry(second_entry);
-  store_->SaveContent(second_entry,
-                      second_stored_proto,
+  store_->SaveContent(second_entry, second_stored_proto,
                       base::Bind(&InMemoryContentStoreTest::OnSaveCallback,
                                  base::Unretained(this)));
   base::RunLoop().RunUntilIdle();
@@ -175,8 +172,7 @@
   const ArticleEntry first_entry = CreateEntry("first", "url1", "url2", "url3");
   const DistilledArticleProto first_stored_proto =
       CreateDistilledArticleForEntry(first_entry);
-  store_->SaveContent(first_entry,
-                      first_stored_proto,
+  store_->SaveContent(first_entry, first_stored_proto,
                       base::Bind(&InMemoryContentStoreTest::OnSaveCallback,
                                  base::Unretained(this)));
   base::RunLoop().RunUntilIdle();
@@ -188,8 +184,7 @@
       CreateEntry("second", "url4", "url5", "url6");
   const DistilledArticleProto second_stored_proto =
       CreateDistilledArticleForEntry(second_entry);
-  store_->SaveContent(second_entry,
-                      second_stored_proto,
+  store_->SaveContent(second_entry, second_stored_proto,
                       base::Bind(&InMemoryContentStoreTest::OnSaveCallback,
                                  base::Unretained(this)));
   base::RunLoop().RunUntilIdle();
@@ -200,8 +195,7 @@
   const ArticleEntry third_entry = CreateEntry("third", "url7", "url8", "url9");
   const DistilledArticleProto third_stored_proto =
       CreateDistilledArticleForEntry(third_entry);
-  store_->SaveContent(third_entry,
-                      third_stored_proto,
+  store_->SaveContent(third_entry, third_stored_proto,
                       base::Bind(&InMemoryContentStoreTest::OnSaveCallback,
                                  base::Unretained(this)));
   base::RunLoop().RunUntilIdle();
@@ -225,8 +219,7 @@
       CreateEntry("fourth", "url10", "url11", "url12");
   const DistilledArticleProto fourth_stored_proto =
       CreateDistilledArticleForEntry(fourth_entry);
-  store_->SaveContent(fourth_entry,
-                      fourth_stored_proto,
+  store_->SaveContent(fourth_entry, fourth_stored_proto,
                       base::Bind(&InMemoryContentStoreTest::OnSaveCallback,
                                  base::Unretained(this)));
   base::RunLoop().RunUntilIdle();
@@ -250,8 +243,7 @@
   const ArticleEntry entry = CreateEntry("test-id", "url1", "url2", "url3");
   const DistilledArticleProto stored_proto =
       CreateDistilledArticleForEntry(entry);
-  store_->SaveContent(entry,
-                      stored_proto,
+  store_->SaveContent(entry, stored_proto,
                       base::Bind(&InMemoryContentStoreTest::OnSaveCallback,
                                  base::Unretained(this)));
   base::RunLoop().RunUntilIdle();
@@ -293,8 +285,7 @@
   const ArticleEntry first_entry = CreateEntry("first", "url1", "url2", "url3");
   const DistilledArticleProto first_stored_proto =
       CreateDistilledArticleForEntry(first_entry);
-  store_->SaveContent(first_entry,
-                      first_stored_proto,
+  store_->SaveContent(first_entry, first_stored_proto,
                       base::Bind(&InMemoryContentStoreTest::OnSaveCallback,
                                  base::Unretained(this)));
   base::RunLoop().RunUntilIdle();
@@ -318,8 +309,7 @@
       CreateEntry("second", "url4", "url5", "url6");
   const DistilledArticleProto second_stored_proto =
       CreateDistilledArticleForEntry(second_entry);
-  store_->SaveContent(second_entry,
-                      second_stored_proto,
+  store_->SaveContent(second_entry, second_stored_proto,
                       base::Bind(&InMemoryContentStoreTest::OnSaveCallback,
                                  base::Unretained(this)));
   base::RunLoop().RunUntilIdle();
diff --git a/components/dom_distiller/core/distilled_page_prefs.cc b/components/dom_distiller/core/distilled_page_prefs.cc
index 45cf79fa..b5db0557 100644
--- a/components/dom_distiller/core/distilled_page_prefs.cc
+++ b/components/dom_distiller/core/distilled_page_prefs.cc
@@ -17,11 +17,9 @@
 namespace dom_distiller {
 
 DistilledPagePrefs::DistilledPagePrefs(PrefService* pref_service)
-    : pref_service_(pref_service), weak_ptr_factory_(this) {
-}
+    : pref_service_(pref_service), weak_ptr_factory_(this) {}
 
-DistilledPagePrefs::~DistilledPagePrefs() {
-}
+DistilledPagePrefs::~DistilledPagePrefs() {}
 
 // static
 void DistilledPagePrefs::RegisterProfilePrefs(
@@ -115,8 +113,7 @@
     observer.OnChangeTheme(new_theme);
 }
 
-void DistilledPagePrefs::NotifyOnChangeFontScaling(
-    float scaling) {
+void DistilledPagePrefs::NotifyOnChangeFontScaling(float scaling) {
   for (Observer& observer : observers_)
     observer.OnChangeFontScaling(scaling);
 }
diff --git a/components/dom_distiller/core/distilled_page_prefs.h b/components/dom_distiller/core/distilled_page_prefs.h
index 2b62554..f6a00b6 100644
--- a/components/dom_distiller/core/distilled_page_prefs.h
+++ b/components/dom_distiller/core/distilled_page_prefs.h
@@ -86,7 +86,6 @@
   // Notifies all Observers of new font scaling.
   void NotifyOnChangeFontScaling(float scaling);
 
-
   PrefService* pref_service_;
   base::ObserverList<Observer>::Unchecked observers_;
 
diff --git a/components/dom_distiller/core/distilled_page_prefs_android.cc b/components/dom_distiller/core/distilled_page_prefs_android.cc
index 173795ea..ebe2cab 100644
--- a/components/dom_distiller/core/distilled_page_prefs_android.cc
+++ b/components/dom_distiller/core/distilled_page_prefs_android.cc
@@ -18,11 +18,9 @@
     JNIEnv* env,
     jobject obj,
     DistilledPagePrefs* distilled_page_prefs_ptr)
-    : distilled_page_prefs_(distilled_page_prefs_ptr) {
-}
+    : distilled_page_prefs_(distilled_page_prefs_ptr) {}
 
-DistilledPagePrefsAndroid::~DistilledPagePrefsAndroid() {
-}
+DistilledPagePrefsAndroid::~DistilledPagePrefsAndroid() {}
 
 void DistilledPagePrefsAndroid::SetFontFamily(JNIEnv* env,
                                               const JavaParamRef<jobject>& obj,
@@ -34,7 +32,7 @@
 jint DistilledPagePrefsAndroid::GetFontFamily(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) {
-  return (int) distilled_page_prefs_->GetFontFamily();
+  return (int)distilled_page_prefs_->GetFontFamily();
 }
 
 void DistilledPagePrefsAndroid::SetTheme(JNIEnv* env,
@@ -46,7 +44,7 @@
 
 jint DistilledPagePrefsAndroid::GetTheme(JNIEnv* env,
                                          const JavaParamRef<jobject>& obj) {
-  return (int) distilled_page_prefs_->GetTheme();
+  return (int)distilled_page_prefs_->GetTheme();
 }
 
 void DistilledPagePrefsAndroid::SetFontScaling(JNIEnv* env,
@@ -115,8 +113,7 @@
                                                        (int)new_theme);
 }
 
-void DistilledPagePrefsObserverAndroid::OnChangeFontScaling(
-    float scaling) {
+void DistilledPagePrefsObserverAndroid::OnChangeFontScaling(float scaling) {
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_DistilledPagePrefsObserverWrapper_onChangeFontScaling(env, java_ref_,
                                                              scaling);
diff --git a/components/dom_distiller/core/distiller.cc b/components/dom_distiller/core/distiller.cc
index fe4fbea..af737ed 100644
--- a/components/dom_distiller/core/distiller.cc
+++ b/components/dom_distiller/core/distiller.cc
@@ -30,7 +30,7 @@
 namespace {
 // Maximum number of distilled pages in an article.
 const size_t kMaxPagesInArticle = 32;
-}
+}  // namespace
 
 namespace dom_distiller {
 
@@ -61,8 +61,7 @@
       dom_distiller_options_(dom_distiller_options),
       max_pages_in_article_(kMaxPagesInArticle),
       destruction_allowed_(true),
-      weak_factory_(this) {
-}
+      weak_factory_(this) {}
 
 DistillerImpl::~DistillerImpl() {
   DCHECK(destruction_allowed_);
@@ -104,8 +103,8 @@
          finished_pages_index_.find(page_num) != finished_pages_index_.end();
 }
 
-DistillerImpl::DistilledPageData* DistillerImpl::GetPageAtIndex(size_t index)
-    const {
+DistillerImpl::DistilledPageData* DistillerImpl::GetPageAtIndex(
+    size_t index) const {
   DCHECK_LT(index, pages_.size());
   DistilledPageData* page_data = pages_[index].get();
   DCHECK(page_data);
@@ -138,13 +137,13 @@
     seen_urls_.insert(url.spec());
     pages_.push_back(std::make_unique<DistilledPageData>());
     started_pages_index_[page_num] = pages_.size() - 1;
+
+    // TODO(gilmanmh): Investigate whether this needs to be
+    // base::BindRepeating() or if base::BindOnce() can be used instead.
     distiller_page_->DistillPage(
-        url,
-        dom_distiller_options_,
-        base::Bind(&DistillerImpl::OnPageDistillationFinished,
-                   weak_factory_.GetWeakPtr(),
-                   page_num,
-                   url));
+        url, dom_distiller_options_,
+        base::BindRepeating(&DistillerImpl::OnPageDistillationFinished,
+                            weak_factory_.GetWeakPtr(), page_num, url));
   }
 }
 
@@ -164,20 +163,17 @@
     if (distiller_result->statistics_info().has_word_count()) {
       UMA_HISTOGRAM_CUSTOM_COUNTS(
           "DomDistiller.Statistics.FirstPageWordCount",
-          distiller_result->statistics_info().word_count(),
-          1, 4000, 50);
+          distiller_result->statistics_info().word_count(), 1, 4000, 50);
     }
   }
 
   DCHECK(distiller_result);
-  DistilledPageData* page_data =
-      GetPageAtIndex(started_pages_index_[page_num]);
+  DistilledPageData* page_data = GetPageAtIndex(started_pages_index_[page_num]);
   page_data->distilled_page_proto =
       new base::RefCountedData<DistilledPageProto>();
   page_data->page_num = page_num;
   if (distiller_result->has_title()) {
-    page_data->distilled_page_proto->data.set_title(
-        distiller_result->title());
+    page_data->distilled_page_proto->data.set_title(distiller_result->title());
   }
   page_data->distilled_page_proto->data.set_url(page_url.spec());
   bool content_empty = true;
@@ -202,29 +198,25 @@
 
     if (distiller_timing_info.has_document_construction_time()) {
       timing_info.set_name("document_construction");
-      timing_info.set_time(
-          distiller_timing_info.document_construction_time());
+      timing_info.set_time(distiller_timing_info.document_construction_time());
       *page_data->distilled_page_proto->data.add_timing_info() = timing_info;
     }
 
     if (distiller_timing_info.has_article_processing_time()) {
       timing_info.set_name("article_processing");
-      timing_info.set_time(
-          distiller_timing_info.article_processing_time());
+      timing_info.set_time(distiller_timing_info.article_processing_time());
       *page_data->distilled_page_proto->data.add_timing_info() = timing_info;
     }
 
     if (distiller_timing_info.has_formatting_time()) {
       timing_info.set_name("formatting");
-      timing_info.set_time(
-          distiller_timing_info.formatting_time());
+      timing_info.set_time(distiller_timing_info.formatting_time());
       *page_data->distilled_page_proto->data.add_timing_info() = timing_info;
     }
 
     if (distiller_timing_info.has_total_time()) {
       timing_info.set_name("total");
-      timing_info.set_time(
-          distiller_timing_info.total_time());
+      timing_info.set_time(distiller_timing_info.total_time());
       *page_data->distilled_page_proto->data.add_timing_info() = timing_info;
     }
 
@@ -252,15 +244,14 @@
     const proto::PaginationInfo& pagination_info =
         distiller_result->pagination_info();
     // Skip the next page if the first page is empty.
-    if (pagination_info.has_next_page() &&
-        (page_num != 0 || !content_empty)) {
+    if (pagination_info.has_next_page() && (page_num != 0 || !content_empty)) {
       GURL next_page_url(pagination_info.next_page());
       if (next_page_url.is_valid()) {
         // The pages should be in same origin.
         DCHECK_EQ(next_page_url.GetOrigin(), page_url.GetOrigin());
         AddToDistillationQueue(page_num + 1, next_page_url);
-        page_data->distilled_page_proto->data.mutable_pagination_info()->
-            set_next_page(next_page_url.spec());
+        page_data->distilled_page_proto->data.mutable_pagination_info()
+            ->set_next_page(next_page_url.spec());
       }
     }
 
@@ -269,16 +260,16 @@
       if (prev_page_url.is_valid()) {
         DCHECK_EQ(prev_page_url.GetOrigin(), page_url.GetOrigin());
         AddToDistillationQueue(page_num - 1, prev_page_url);
-        page_data->distilled_page_proto->data.mutable_pagination_info()->
-            set_prev_page(prev_page_url.spec());
+        page_data->distilled_page_proto->data.mutable_pagination_info()
+            ->set_prev_page(prev_page_url.spec());
       }
     }
 
     if (pagination_info.has_canonical_page()) {
       GURL canonical_page_url(pagination_info.canonical_page());
       if (canonical_page_url.is_valid()) {
-        page_data->distilled_page_proto->data.mutable_pagination_info()->
-            set_canonical_page(canonical_page_url.spec());
+        page_data->distilled_page_proto->data.mutable_pagination_info()
+            ->set_canonical_page(canonical_page_url.spec());
       }
     }
   }
@@ -298,7 +289,8 @@
 void DistillerImpl::MaybeFetchImage(int page_num,
                                     const std::string& image_id,
                                     const std::string& image_url) {
-  if (!GURL(image_url).is_valid()) return;
+  if (!GURL(image_url).is_valid())
+    return;
   DCHECK(started_pages_index_.find(page_num) != started_pages_index_.end());
   DistilledPageData* page_data = GetPageAtIndex(started_pages_index_[page_num]);
 
@@ -314,13 +306,13 @@
       distiller_url_fetcher_factory_.CreateDistillerURLFetcher();
   page_data->image_fetchers_.push_back(base::WrapUnique(fetcher));
 
-  fetcher->FetchURL(image_url,
-                    base::Bind(&DistillerImpl::OnFetchImageDone,
-                               weak_factory_.GetWeakPtr(),
-                               page_num,
-                               base::Unretained(fetcher),
-                               image_id,
-                               image_url));
+  // TODO(gilmanmh): Investigate whether this needs to be base::BindRepeating()
+  // or if base::BindOnce() can be used instead.
+  fetcher->FetchURL(
+      image_url,
+      base::BindRepeating(&DistillerImpl::OnFetchImageDone,
+                          weak_factory_.GetWeakPtr(), page_num,
+                          base::Unretained(fetcher), image_id, image_url));
 }
 
 void DistillerImpl::OnFetchImageDone(int page_num,
@@ -380,7 +372,7 @@
     has_next_page = IsPageNumberInUse(next_page_num);
   }
 
-  std::vector<scoped_refptr<ArticleDistillationUpdate::RefCountedPageProto> >
+  std::vector<scoped_refptr<ArticleDistillationUpdate::RefCountedPageProto>>
       update_pages;
   for (auto it = finished_pages_index_.begin();
        it != finished_pages_index_.end(); ++it) {
diff --git a/components/dom_distiller/core/distiller.h b/components/dom_distiller/core/distiller.h
index 8595db0..cd346f2 100644
--- a/components/dom_distiller/core/distiller.h
+++ b/components/dom_distiller/core/distiller.h
@@ -100,7 +100,7 @@
     // Relative page number of the page.
     int page_num;
     std::vector<std::unique_ptr<DistillerURLFetcher>> image_fetchers_;
-    scoped_refptr<base::RefCountedData<DistilledPageProto> >
+    scoped_refptr<base::RefCountedData<DistilledPageProto>>
         distilled_page_proto;
 
    private:
diff --git a/components/dom_distiller/core/distiller_page.cc b/components/dom_distiller/core/distiller_page.cc
index 36eafa9..106b17f 100644
--- a/components/dom_distiller/core/distiller_page.cc
+++ b/components/dom_distiller/core/distiller_page.cc
@@ -59,14 +59,13 @@
   DCHECK_NE(std::string::npos, stringify_offset);
   DCHECK_EQ(std::string::npos,
             script.find(kStringifyPlaceholder, stringify_offset + 1));
-  script = script.replace(stringify_offset,
-                          strlen(kStringifyPlaceholder),
+  script = script.replace(stringify_offset, strlen(kStringifyPlaceholder),
                           stringify);
 
   return script;
 }
 
-}
+}  // namespace
 
 DistillerPageFactory::~DistillerPageFactory() {}
 
@@ -84,8 +83,8 @@
   ready_ = false;
   distiller_page_callback_ = callback;
   distillation_start_ = base::TimeTicks::Now();
-  DistillPageImpl(gurl, GetDistillerScriptWithOptions(options,
-                                                      StringifyOutput()));
+  DistillPageImpl(gurl,
+                  GetDistillerScriptWithOptions(options, StringifyOutput()));
 }
 
 void DistillerPage::OnDistillationDone(const GURL& page_url,
@@ -119,16 +118,14 @@
               base::TimeDelta::FromMillisecondsD(timing.markup_parsing_time()));
         }
         if (timing.has_document_construction_time()) {
-          UMA_HISTOGRAM_TIMES(
-              "DomDistiller.Time.DocumentConstruction",
-              base::TimeDelta::FromMillisecondsD(
-                  timing.document_construction_time()));
+          UMA_HISTOGRAM_TIMES("DomDistiller.Time.DocumentConstruction",
+                              base::TimeDelta::FromMillisecondsD(
+                                  timing.document_construction_time()));
         }
         if (timing.has_article_processing_time()) {
-          UMA_HISTOGRAM_TIMES(
-              "DomDistiller.Time.ArticleProcessing",
-              base::TimeDelta::FromMillisecondsD(
-                  timing.article_processing_time()));
+          UMA_HISTOGRAM_TIMES("DomDistiller.Time.ArticleProcessing",
+                              base::TimeDelta::FromMillisecondsD(
+                                  timing.article_processing_time()));
         }
         if (timing.has_formatting_time()) {
           UMA_HISTOGRAM_TIMES(
@@ -139,18 +136,16 @@
           UMA_HISTOGRAM_TIMES(
               "DomDistiller.Time.DistillationTotal",
               base::TimeDelta::FromMillisecondsD(timing.total_time()));
-          VLOG(1) << "DomDistiller.Time.DistillationTotal = " <<
-              base::TimeDelta::FromMillisecondsD(timing.total_time());
+          VLOG(1) << "DomDistiller.Time.DistillationTotal = "
+                  << base::TimeDelta::FromMillisecondsD(timing.total_time());
         }
       }
       if (distiller_result->has_statistics_info()) {
         const dom_distiller::proto::StatisticsInfo& statistics =
             distiller_result->statistics_info();
         if (statistics.has_word_count()) {
-          UMA_HISTOGRAM_CUSTOM_COUNTS(
-            "DomDistiller.Statistics.WordCount",
-            statistics.word_count(),
-            1, 4000, 50);
+          UMA_HISTOGRAM_CUSTOM_COUNTS("DomDistiller.Statistics.WordCount",
+                                      statistics.word_count(), 1, 4000, 50);
         }
       }
     }
diff --git a/components/dom_distiller/core/distiller_url_fetcher.cc b/components/dom_distiller/core/distiller_url_fetcher.cc
index c4450a2e..db83e91 100644
--- a/components/dom_distiller/core/distiller_url_fetcher.cc
+++ b/components/dom_distiller/core/distiller_url_fetcher.cc
@@ -18,8 +18,8 @@
 
 DistillerURLFetcherFactory::~DistillerURLFetcherFactory() {}
 
-DistillerURLFetcher*
-DistillerURLFetcherFactory::CreateDistillerURLFetcher() const {
+DistillerURLFetcher* DistillerURLFetcherFactory::CreateDistillerURLFetcher()
+    const {
   return new DistillerURLFetcher(url_loader_factory_);
 }
 
@@ -27,8 +27,7 @@
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
     : url_loader_factory_(url_loader_factory) {}
 
-DistillerURLFetcher::~DistillerURLFetcher() {
-}
+DistillerURLFetcher::~DistillerURLFetcher() {}
 
 void DistillerURLFetcher::FetchURL(const std::string& url,
                                    const URLFetcherCallback& callback) {
diff --git a/components/dom_distiller/core/dom_distiller_features.cc b/components/dom_distiller/core/dom_distiller_features.cc
index 8c01dd1..0107703 100644
--- a/components/dom_distiller/core/dom_distiller_features.cc
+++ b/components/dom_distiller/core/dom_distiller_features.cc
@@ -13,7 +13,7 @@
 
 bool IsEnableDomDistillerSet() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableDomDistiller);
+      switches::kEnableDomDistiller);
 }
 
 }  // namespace dom_distiller
diff --git a/components/dom_distiller/core/dom_distiller_model.cc b/components/dom_distiller/core/dom_distiller_model.cc
index 1fdc9aa..81d2667 100644
--- a/components/dom_distiller/core/dom_distiller_model.cc
+++ b/components/dom_distiller/core/dom_distiller_model.cc
@@ -14,8 +14,7 @@
 
 namespace dom_distiller {
 
-DomDistillerModel::DomDistillerModel()
-    : next_key_(1) {}
+DomDistillerModel::DomDistillerModel() : next_key_(1) {}
 
 DomDistillerModel::DomDistillerModel(
     const std::vector<ArticleEntry>& initial_data)
@@ -38,7 +37,7 @@
 }
 
 bool DomDistillerModel::GetEntryByUrl(const GURL& url,
-                                     ArticleEntry* entry) const {
+                                      ArticleEntry* entry) const {
   KeyType key = 0;
   if (!GetKeyByUrl(url, &key)) {
     return false;
@@ -122,16 +121,15 @@
        ++it) {
     if (entries_to_change.find(it->second.entry_id()) ==
         entries_to_change.end()) {
-      changes_missing->push_back(SyncChange(
-          FROM_HERE, SyncChange::ACTION_ADD, CreateLocalData(it->second)));
+      changes_missing->push_back(SyncChange(FROM_HERE, SyncChange::ACTION_ADD,
+                                            CreateLocalData(it->second)));
     }
   }
 }
 
-void DomDistillerModel::ApplyChangesToModel(
-    const SyncChangeList& changes,
-    SyncChangeList* changes_applied,
-    SyncChangeList* changes_missing) {
+void DomDistillerModel::ApplyChangesToModel(const SyncChangeList& changes,
+                                            SyncChangeList* changes_applied,
+                                            SyncChangeList* changes_missing) {
   DCHECK(changes_applied);
   DCHECK(changes_missing);
 
@@ -164,10 +162,9 @@
   }
 }
 
-void DomDistillerModel::ApplyChangeToModel(
-    const SyncChange& change,
-    SyncChangeList* changes_applied,
-    SyncChangeList* changes_missing) {
+void DomDistillerModel::ApplyChangeToModel(const SyncChange& change,
+                                           SyncChangeList* changes_applied,
+                                           SyncChangeList* changes_missing) {
   DCHECK(change.IsValid());
   DCHECK(changes_applied);
   DCHECK(changes_missing);
diff --git a/components/dom_distiller/core/dom_distiller_model_unittest.cc b/components/dom_distiller/core/dom_distiller_model_unittest.cc
index 10e4fb6..bdf51e0 100644
--- a/components/dom_distiller/core/dom_distiller_model_unittest.cc
+++ b/components/dom_distiller/core/dom_distiller_model_unittest.cc
@@ -116,8 +116,8 @@
   entry2.mutable_pages(0)->set_url("http://example.com/foo1");
   changes_to_apply.push_back(syncer::SyncChange(
       FROM_HERE, syncer::SyncChange::ACTION_UPDATE, CreateLocalData(entry2)));
-  model.ApplyChangesToModel(
-      changes_to_apply, &changes_applied, &changes_missing);
+  model.ApplyChangesToModel(changes_to_apply, &changes_applied,
+                            &changes_missing);
 
   EXPECT_TRUE(model.GetEntryById(entry1.entry_id(), &found_entry));
   ASSERT_TRUE(IsEntryValid(found_entry));
diff --git a/components/dom_distiller/core/dom_distiller_observer.h b/components/dom_distiller/core/dom_distiller_observer.h
index 695d9c3f..2c934149 100644
--- a/components/dom_distiller/core/dom_distiller_observer.h
+++ b/components/dom_distiller/core/dom_distiller_observer.h
@@ -18,11 +18,7 @@
  public:
   // An update to an article entry.
   struct ArticleUpdate {
-    enum UpdateType {
-      ADD,
-      UPDATE,
-      REMOVE
-    };
+    enum UpdateType { ADD, UPDATE, REMOVE };
     std::string entry_id;
     UpdateType update_type;
   };
diff --git a/components/dom_distiller/core/dom_distiller_request_view_base.cc b/components/dom_distiller/core/dom_distiller_request_view_base.cc
index bc71777..ad52193 100644
--- a/components/dom_distiller/core/dom_distiller_request_view_base.cc
+++ b/components/dom_distiller/core/dom_distiller_request_view_base.cc
@@ -24,11 +24,9 @@
     DistilledPagePrefs* distilled_page_prefs)
     : page_count_(0),
       distilled_page_prefs_(distilled_page_prefs),
-      is_error_page_(false) {
-}
+      is_error_page_(false) {}
 
-DomDistillerRequestViewBase::~DomDistillerRequestViewBase() {
-}
+DomDistillerRequestViewBase::~DomDistillerRequestViewBase() {}
 
 void DomDistillerRequestViewBase::FlagAsErrorPage() {
   // Viewer handle is not passed to this in the case of error pages
diff --git a/components/dom_distiller/core/dom_distiller_request_view_base_unittest.cc b/components/dom_distiller/core/dom_distiller_request_view_base_unittest.cc
index f7d8dd9a1..1f42423 100644
--- a/components/dom_distiller/core/dom_distiller_request_view_base_unittest.cc
+++ b/components/dom_distiller/core/dom_distiller_request_view_base_unittest.cc
@@ -65,7 +65,6 @@
                 Not(HasSubstr(has_special_chars)));
     handle.ClearJavaScriptBuffer();
   }
-
 }
 
 TEST_F(DomDistillerRequestViewTest, TestTitleNeverEmpty) {
diff --git a/components/dom_distiller/core/dom_distiller_service.cc b/components/dom_distiller/core/dom_distiller_service.cc
index f94b08f..0685d3a 100644
--- a/components/dom_distiller/core/dom_distiller_service.cc
+++ b/components/dom_distiller/core/dom_distiller_service.cc
@@ -53,8 +53,7 @@
       distiller_page_factory_(std::move(distiller_page_factory)),
       distilled_page_prefs_(std::move(distilled_page_prefs)) {}
 
-DomDistillerService::~DomDistillerService() {
-}
+DomDistillerService::~DomDistillerService() {}
 
 std::unique_ptr<DistillerPage> DomDistillerService::CreateDefaultDistillerPage(
     const gfx::Size& render_view_size) {
diff --git a/components/dom_distiller/core/dom_distiller_service_android.cc b/components/dom_distiller/core/dom_distiller_service_android.cc
index 81883a6f..37e7185 100644
--- a/components/dom_distiller/core/dom_distiller_service_android.cc
+++ b/components/dom_distiller/core/dom_distiller_service_android.cc
@@ -28,8 +28,7 @@
   java_ref_.Reset(env, local_java_ref.obj());
 }
 
-DomDistillerServiceAndroid::~DomDistillerServiceAndroid() {
-}
+DomDistillerServiceAndroid::~DomDistillerServiceAndroid() {}
 
 bool DomDistillerServiceAndroid::HasEntry(
     JNIEnv* env,
diff --git a/components/dom_distiller/core/dom_distiller_service_unittest.cc b/components/dom_distiller/core/dom_distiller_service_unittest.cc
index d781a985c..5a6c5ac 100644
--- a/components/dom_distiller/core/dom_distiller_service_unittest.cc
+++ b/components/dom_distiller/core/dom_distiller_service_unittest.cc
@@ -24,9 +24,9 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using leveldb_proto::test::FakeDB;
+using testing::_;
 using testing::Invoke;
 using testing::Return;
-using testing::_;
 
 namespace dom_distiller {
 namespace test {
@@ -83,8 +83,7 @@
   void SetUp() override {
     FakeDB<ArticleEntry>* fake_db = new FakeDB<ArticleEntry>(&db_model_);
     FakeDB<ArticleEntry>::EntryMap store_model;
-    store_ =
-        test::util::CreateStoreWithFakeDB(fake_db, store_model);
+    store_ = test::util::CreateStoreWithFakeDB(fake_db, store_model);
     distiller_factory_ = new MockDistillerFactory();
     distiller_page_factory_ = new MockDistillerPageFactory();
     service_.reset(new DomDistillerService(
diff --git a/components/dom_distiller/core/dom_distiller_store.cc b/components/dom_distiller/core/dom_distiller_store.cc
index 483b9c5..0f8ff5ea 100644
--- a/components/dom_distiller/core/dom_distiller_store.cc
+++ b/components/dom_distiller/core/dom_distiller_store.cc
@@ -231,7 +231,8 @@
 }
 
 SyncMergeResult DomDistillerStore::MergeDataWithModel(
-    const SyncDataList& data, SyncChangeList* changes_applied,
+    const SyncDataList& data,
+    SyncChangeList* changes_applied,
     SyncChangeList* changes_missing) {
   // TODO(cjhopman): This naive merge algorithm could cause flip-flopping
   // between database/sync of multiple clients.
diff --git a/components/dom_distiller/core/dom_distiller_store.h b/components/dom_distiller/core/dom_distiller_store.h
index 1b04a16a..f5dcfe7 100644
--- a/components/dom_distiller/core/dom_distiller_store.h
+++ b/components/dom_distiller/core/dom_distiller_store.h
@@ -101,7 +101,8 @@
                    syncer::SyncChange::SyncChangeType changeType);
 
   syncer::SyncMergeResult MergeDataWithModel(
-      const syncer::SyncDataList& data, syncer::SyncChangeList* changes_applied,
+      const syncer::SyncDataList& data,
+      syncer::SyncChangeList* changes_applied,
       syncer::SyncChangeList* changes_missing);
 
   // Convert a SyncDataList to a SyncChangeList of add or update changes based
diff --git a/components/dom_distiller/core/dom_distiller_store_unittest.cc b/components/dom_distiller/core/dom_distiller_store_unittest.cc
index 4e4812f..c68f6d3 100644
--- a/components/dom_distiller/core/dom_distiller_store_unittest.cc
+++ b/components/dom_distiller/core/dom_distiller_store_unittest.cc
@@ -34,11 +34,11 @@
 using syncer::SyncDataList;
 using syncer::SyncError;
 using syncer::SyncErrorFactory;
+using testing::_;
 using testing::AssertionFailure;
 using testing::AssertionResult;
 using testing::AssertionSuccess;
 using testing::SaveArgPointee;
-using testing::_;
 
 namespace dom_distiller {
 
@@ -182,12 +182,12 @@
   for (auto it = entries.begin(); it != entries.end(); ++it) {
     auto expected_it = expected_entries.find(it->entry_id());
     if (expected_it == expected_entries.end()) {
-      return AssertionFailure() << "Found unexpected entry with id <"
-                                << it->entry_id() << ">";
+      return AssertionFailure()
+             << "Found unexpected entry with id <" << it->entry_id() << ">";
     }
     if (!AreEntriesEqual(expected_it->second, *it)) {
-      return AssertionFailure() << "Mismatched entry with id <"
-                                << it->entry_id() << ">";
+      return AssertionFailure()
+             << "Mismatched entry with id <" << it->entry_id() << ">";
     }
     expected_entries.erase(expected_it);
   }
diff --git a/components/dom_distiller/core/dom_distiller_test_util.h b/components/dom_distiller/core/dom_distiller_test_util.h
index 8d22a25..93cdca9 100644
--- a/components/dom_distiller/core/dom_distiller_test_util.h
+++ b/components/dom_distiller/core/dom_distiller_test_util.h
@@ -38,7 +38,7 @@
 };
 
 testing::Matcher<const std::vector<DomDistillerObserver::ArticleUpdate>&>
-    HasExpectedUpdates(const std::vector<DomDistillerObserver::ArticleUpdate>&);
+HasExpectedUpdates(const std::vector<DomDistillerObserver::ArticleUpdate>&);
 
 // Creates a simple DomDistillerStore backed by |fake_db| and initialized
 // with |store_model|.
diff --git a/components/dom_distiller/core/experiments.cc b/components/dom_distiller/core/experiments.cc
index ba011f5..98cbc86 100644
--- a/components/dom_distiller/core/experiments.cc
+++ b/components/dom_distiller/core/experiments.cc
@@ -54,4 +54,4 @@
   }
   return DistillerHeuristicsType::ADABOOST_MODEL;
 }
-}
+}  // namespace dom_distiller
diff --git a/components/dom_distiller/core/experiments.h b/components/dom_distiller/core/experiments.h
index da2fab4..7612c7a 100644
--- a/components/dom_distiller/core/experiments.h
+++ b/components/dom_distiller/core/experiments.h
@@ -16,6 +16,6 @@
 };
 
 DistillerHeuristicsType GetDistillerHeuristicsType();
-}
+}  // namespace dom_distiller
 
 #endif  // COMPONENTS_DOM_DISTILLER_CORE_EXPERIMENTS_H_
diff --git a/components/dom_distiller/core/fake_distiller.cc b/components/dom_distiller/core/fake_distiller.cc
index 6b046b1..c1e97165 100644
--- a/components/dom_distiller/core/fake_distiller.cc
+++ b/components/dom_distiller/core/fake_distiller.cc
@@ -8,7 +8,6 @@
 
 #include "base/auto_reset.h"
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -21,8 +20,7 @@
 MockDistillerFactory::~MockDistillerFactory() {}
 
 FakeDistiller::FakeDistiller(bool execute_callback)
-    : execute_callback_(execute_callback),
-      destruction_allowed_(true) {
+    : execute_callback_(execute_callback), destruction_allowed_(true) {
   EXPECT_CALL(*this, Die()).Times(testing::AnyNumber());
 }
 
@@ -49,7 +47,7 @@
   article_callback_ = article_callback;
   page_callback_ = page_callback;
   if (!distillation_initiated_callback_.is_null()) {
-    base::ResetAndReturn(&distillation_initiated_callback_).Run();
+    std::move(distillation_initiated_callback_).Run();
   }
   if (execute_callback_) {
     std::unique_ptr<DistilledArticleProto> proto(new DistilledArticleProto);
diff --git a/components/dom_distiller/core/fake_distiller_page.cc b/components/dom_distiller/core/fake_distiller_page.cc
index 208d3bb..0d5a9e58 100644
--- a/components/dom_distiller/core/fake_distiller_page.cc
+++ b/components/dom_distiller/core/fake_distiller_page.cc
@@ -7,15 +7,11 @@
 namespace dom_distiller {
 namespace test {
 
-MockDistillerPageFactory::MockDistillerPageFactory() {
-}
-MockDistillerPageFactory::~MockDistillerPageFactory() {
-}
+MockDistillerPageFactory::MockDistillerPageFactory() {}
+MockDistillerPageFactory::~MockDistillerPageFactory() {}
 
-MockDistillerPage::MockDistillerPage() {
-}
-MockDistillerPage::~MockDistillerPage() {
-}
+MockDistillerPage::MockDistillerPage() {}
+MockDistillerPage::~MockDistillerPage() {}
 
 }  // namespace test
 }  // namespace dom_distiller
diff --git a/components/dom_distiller/core/page_features.cc b/components/dom_distiller/core/page_features.cc
index 6de750f..52b027d 100644
--- a/components/dom_distiller/core/page_features.cc
+++ b/components/dom_distiller/core/page_features.cc
@@ -188,15 +188,14 @@
                                   innerHTML);
 }
 
-std::vector<double> CalculateDerivedFeatures(
-    bool openGraph,
-    const GURL& url,
-    unsigned elementCount,
-    unsigned anchorCount,
-    unsigned formCount,
-    double mozScore,
-    double mozScoreAllSqrt,
-    double mozScoreAllLinear) {
+std::vector<double> CalculateDerivedFeatures(bool openGraph,
+                                             const GURL& url,
+                                             unsigned elementCount,
+                                             unsigned anchorCount,
+                                             unsigned formCount,
+                                             double mozScore,
+                                             double mozScoreAllSqrt,
+                                             double mozScoreAllLinear) {
   const std::string& path = url.path();
   std::vector<double> features;
   // 'opengraph', opengraph,
@@ -236,8 +235,7 @@
   // 'elementCount', numElements,
   features.push_back(elementCount);
   // 'anchorRatio', float(numAnchors) / max(1, numElements),
-  features.push_back(
-      double(anchorCount) / std::max<double>(1, elementCount));
+  features.push_back(double(anchorCount) / std::max<double>(1, elementCount));
   // 'mozScore'
   features.push_back(mozScore);
   // 'mozScoreAllSqrt'
diff --git a/components/dom_distiller/core/page_features.h b/components/dom_distiller/core/page_features.h
index a54d1cdf..5667d7f3 100644
--- a/components/dom_distiller/core/page_features.h
+++ b/components/dom_distiller/core/page_features.h
@@ -38,15 +38,14 @@
 std::vector<double> CalculateDerivedFeaturesFromJSON(
     const base::Value* stringified_json);
 
-std::vector<double> CalculateDerivedFeatures(
-    bool openGraph,
-    const GURL& url,
-    unsigned elementCount,
-    unsigned anchorCount,
-    unsigned formCount,
-    double mozScore,
-    double mozScoreAllSqrt,
-    double mozScoreAllLinear);
+std::vector<double> CalculateDerivedFeatures(bool openGraph,
+                                             const GURL& url,
+                                             unsigned elementCount,
+                                             unsigned anchorCount,
+                                             unsigned formCount,
+                                             double mozScore,
+                                             double mozScoreAllSqrt,
+                                             double mozScoreAllLinear);
 
 }  // namespace dom_distiller
 
diff --git a/components/dom_distiller/core/page_features_unittest.cc b/components/dom_distiller/core/page_features_unittest.cc
index 4efa8dd4..3b00131 100644
--- a/components/dom_distiller/core/page_features_unittest.cc
+++ b/components/dom_distiller/core/page_features_unittest.cc
@@ -101,15 +101,14 @@
 }
 
 std::vector<double> DeriveFromPath(const GURL& url) {
-  return CalculateDerivedFeatures(
-    false, // bool openGraph
-    url,   // const GURL& url
-    0,     // unsigned elementCount
-    0,     // unsigned anchorCount
-    0,     // unsigned formCount
-    0,     // double mozScore
-    0,     // double mozScoreAllSqrt
-    0      // double mozScoreAllLinear
+  return CalculateDerivedFeatures(false,  // bool openGraph
+                                  url,    // const GURL& url
+                                  0,      // unsigned elementCount
+                                  0,      // unsigned anchorCount
+                                  0,      // unsigned formCount
+                                  0,      // double mozScore
+                                  0,      // double mozScoreAllSqrt
+                                  0       // double mozScoreAllLinear
   );
 }
 
@@ -192,4 +191,4 @@
   EXPECT_EQ(0, lround(derived[13]));
   EXPECT_EQ(9, lround(derived[14]));
 }
-}
+}  // namespace dom_distiller
diff --git a/components/dom_distiller/core/task_tracker.cc b/components/dom_distiller/core/task_tracker.cc
index 95c8315..cdd047e 100644
--- a/components/dom_distiller/core/task_tracker.cc
+++ b/components/dom_distiller/core/task_tracker.cc
@@ -97,7 +97,9 @@
       &TaskTracker::RemoveViewer, weak_ptr_factory_.GetWeakPtr(), delegate)));
 }
 
-const std::string& TaskTracker::GetEntryId() const { return entry_.entry_id(); }
+const std::string& TaskTracker::GetEntryId() const {
+  return entry_.entry_id();
+}
 
 bool TaskTracker::HasEntryId(const std::string& entry_id) const {
   return entry_.entry_id() == entry_id;
@@ -132,7 +134,9 @@
   cancel_callback_.Run(this);
 }
 
-void TaskTracker::CancelSaveCallbacks() { ScheduleSaveCallbacks(false); }
+void TaskTracker::CancelSaveCallbacks() {
+  ScheduleSaveCallbacks(false);
+}
 
 void TaskTracker::ScheduleSaveCallbacks(bool distillation_succeeded) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -232,8 +236,7 @@
   if (!save_callbacks_.empty()) {
     for (size_t i = 0; i < save_callbacks_.size(); ++i) {
       DCHECK(!save_callbacks_[i].is_null());
-      save_callbacks_[i].Run(
-          entry_, distilled_article_.get(), success);
+      save_callbacks_[i].Run(entry_, distilled_article_.get(), success);
     }
 
     save_callbacks_.clear();
@@ -251,10 +254,9 @@
 void TaskTracker::AddDistilledContentToStore(
     const DistilledArticleProto& content) {
   if (content_store_) {
-    content_store_->SaveContent(
-        entry_, content, DistilledContentStore::SaveCallback());
+    content_store_->SaveContent(entry_, content,
+                                DistilledContentStore::SaveCallback());
   }
 }
 
-
 }  // namespace dom_distiller
diff --git a/components/dom_distiller/core/task_tracker_unittest.cc b/components/dom_distiller/core/task_tracker_unittest.cc
index bc799485..b31e3f5 100644
--- a/components/dom_distiller/core/task_tracker_unittest.cc
+++ b/components/dom_distiller/core/task_tracker_unittest.cc
@@ -15,8 +15,8 @@
 #include "components/dom_distiller/core/fake_distiller.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::Return;
 using testing::_;
+using testing::Return;
 
 namespace dom_distiller {
 namespace test {
@@ -235,8 +235,8 @@
   content_store.InjectContent(entry_with_blob, stored_distilled_article);
   TestCancelCallback cancel_callback;
 
-  TaskTracker task_tracker(
-      entry_with_blob, cancel_callback.GetCallback(), &content_store);
+  TaskTracker task_tracker(entry_with_blob, cancel_callback.GetCallback(),
+                           &content_store);
 
   FakeViewRequestDelegate viewer_delegate;
   std::unique_ptr<ViewerHandle> handle(
@@ -269,8 +269,8 @@
   InMemoryContentStore content_store(kDefaultMaxNumCachedEntries);
   content_store.InjectContent(entry_with_blob, stored_distilled_article);
   TestCancelCallback cancel_callback;
-  TaskTracker task_tracker(
-      entry_with_blob, cancel_callback.GetCallback(), &content_store);
+  TaskTracker task_tracker(entry_with_blob, cancel_callback.GetCallback(),
+                           &content_store);
 
   FakeViewRequestDelegate viewer_delegate;
   std::unique_ptr<ViewerHandle> handle(
@@ -311,8 +311,8 @@
       new DistilledArticleProto(CreateDistilledArticleForEntry(entry)));
 
   TestCancelCallback cancel_callback;
-  TaskTracker task_tracker(
-      GetDefaultEntry(), cancel_callback.GetCallback(), &content_store);
+  TaskTracker task_tracker(GetDefaultEntry(), cancel_callback.GetCallback(),
+                           &content_store);
 
   FakeViewRequestDelegate viewer_delegate;
   std::unique_ptr<ViewerHandle> handle(
@@ -345,8 +345,8 @@
   MockContentStore content_store;
 
   TestCancelCallback cancel_callback;
-  TaskTracker task_tracker(
-      GetDefaultEntry(), cancel_callback.GetCallback(), &content_store);
+  TaskTracker task_tracker(GetDefaultEntry(), cancel_callback.GetCallback(),
+                           &content_store);
 
   FakeViewRequestDelegate viewer_delegate;
   std::unique_ptr<ViewerHandle> handle(
@@ -386,8 +386,8 @@
 
   MockContentStore content_store;
   TestCancelCallback cancel_callback;
-  TaskTracker task_tracker(
-      GetDefaultEntry(), cancel_callback.GetCallback(), &content_store);
+  TaskTracker task_tracker(GetDefaultEntry(), cancel_callback.GetCallback(),
+                           &content_store);
 
   FakeViewRequestDelegate viewer_delegate;
   std::unique_ptr<ViewerHandle> handle(
diff --git a/components/dom_distiller/core/test_request_view_handle.h b/components/dom_distiller/core/test_request_view_handle.h
index 5271e59c..1a7cd93 100644
--- a/components/dom_distiller/core/test_request_view_handle.h
+++ b/components/dom_distiller/core/test_request_view_handle.h
@@ -28,8 +28,7 @@
 };
 
 TestRequestViewHandle::TestRequestViewHandle(DistilledPagePrefs* prefs)
-    : DomDistillerRequestViewBase(prefs) {
-}
+    : DomDistillerRequestViewBase(prefs) {}
 
 TestRequestViewHandle::~TestRequestViewHandle() {
   distilled_page_prefs_->RemoveObserver(this);
diff --git a/components/dom_distiller/core/url_utils_unittest.cc b/components/dom_distiller/core/url_utils_unittest.cc
index 5604417..ed10e23b 100644
--- a/components/dom_distiller/core/url_utils_unittest.cc
+++ b/components/dom_distiller/core/url_utils_unittest.cc
@@ -43,7 +43,8 @@
 
 std::string ThroughDistiller(const std::string& url) {
   return GetOriginalUrlFromDistillerUrl(
-      GetDistillerViewUrlFromUrl(kDomDistillerScheme, GURL(url), 123)).spec();
+             GetDistillerViewUrlFromUrl(kDomDistillerScheme, GURL(url), 123))
+      .spec();
 }
 
 std::string GetOriginalUrlFromDistillerUrl(const std::string& url) {
diff --git a/components/dom_distiller/core/viewer.cc b/components/dom_distiller/core/viewer.cc
index e5a285b..8c3f533 100644
--- a/components/dom_distiller/core/viewer.cc
+++ b/components/dom_distiller/core/viewer.cc
@@ -30,6 +30,7 @@
 #include "url/gurl.h"
 
 namespace dom_distiller {
+namespace viewer {
 
 namespace {
 
@@ -92,8 +93,8 @@
 void EnsureNonEmptyContent(std::string* content) {
   UMA_HISTOGRAM_BOOLEAN("DomDistiller.PageHasDistilledData", !content->empty());
   if (content->empty()) {
-    *content = l10n_util::GetStringUTF8(
-        IDS_DOM_DISTILLER_VIEWER_NO_DATA_CONTENT);
+    *content =
+        l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_VIEWER_NO_DATA_CONTENT);
   }
 }
 
@@ -121,41 +122,36 @@
   substitutions.push_back(
       l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_VIEWER_LOADING_TITLE));  // $1
 
-  substitutions.push_back(css.str());                                     // $2
+  substitutions.push_back(css.str());  // $2
   substitutions.push_back(GetThemeCssClass(theme) + " " +
-                          GetFontCssClass(font_family));                  // $3
+                          GetFontCssClass(font_family));  // $3
 
   substitutions.push_back(
       l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_VIEWER_LOADING_TITLE));  // $4
-  substitutions.push_back(
-      l10n_util::GetStringUTF8(
-          IDS_DOM_DISTILLER_JAVASCRIPT_DISABLED_CONTENT));                // $5
+  substitutions.push_back(l10n_util::GetStringUTF8(
+      IDS_DOM_DISTILLER_JAVASCRIPT_DISABLED_CONTENT));  // $5
 
-  substitutions.push_back(svg.str());                                     // $6
+  substitutions.push_back(svg.str());  // $6
 
-  substitutions.push_back(original_url);                                  // $7
-  substitutions.push_back(
-      l10n_util::GetStringUTF8(
-          IDS_DOM_DISTILLER_VIEWER_CLOSE_READER_VIEW));                   // $8
+  substitutions.push_back(original_url);  // $7
+  substitutions.push_back(l10n_util::GetStringUTF8(
+      IDS_DOM_DISTILLER_VIEWER_CLOSE_READER_VIEW));  // $8
 
   return base::ReplaceStringPlaceholders(html_template, substitutions, nullptr);
 }
 
 }  // namespace
 
-namespace viewer {
-
 const std::string GetUnsafeIncrementalDistilledPageJs(
     const DistilledPageProto* page_proto,
-    const bool is_last_page) {
+    bool is_last_page) {
   std::string output(page_proto->html());
   EnsureNonEmptyContent(&output);
   base::Value value(output);
   base::JSONWriter::Write(value, &output);
   std::string page_update("addToPage(");
   page_update += output + ");";
-  return page_update + GetToggleLoadingIndicatorJs(
-      is_last_page);
+  return page_update + GetToggleLoadingIndicatorJs(is_last_page);
 }
 
 const std::string GetErrorPageJs() {
@@ -187,16 +183,16 @@
   return "setTextDirection(" + output + ");";
 }
 
-const std::string GetToggleLoadingIndicatorJs(const bool is_last_page) {
+const std::string GetToggleLoadingIndicatorJs(bool is_last_page) {
   if (is_last_page)
     return "showLoadingIndicator(true);";
   return "showLoadingIndicator(false);";
 }
 
 const std::string GetUnsafeArticleTemplateHtml(
-    const std::string original_url,
-    const DistilledPagePrefs::Theme theme,
-    const DistilledPagePrefs::FontFamily font_family) {
+    const std::string& original_url,
+    DistilledPagePrefs::Theme theme,
+    DistilledPagePrefs::FontFamily font_family) {
   return ReplaceHtmlTemplateValues(original_url, theme, font_family);
 }
 
@@ -294,5 +290,4 @@
 }
 
 }  // namespace viewer
-
 }  // namespace dom_distiller
diff --git a/components/dom_distiller/core/viewer.h b/components/dom_distiller/core/viewer.h
index 950c9db..eef5b17 100644
--- a/components/dom_distiller/core/viewer.h
+++ b/components/dom_distiller/core/viewer.h
@@ -29,9 +29,9 @@
 // considered unsafe, so callers must ensure rendering it does not compromise
 // Chrome.
 const std::string GetUnsafeArticleTemplateHtml(
-    const std::string original_url,
-    const DistilledPagePrefs::Theme theme,
-    const DistilledPagePrefs::FontFamily font_family);
+    const std::string& original_url,
+    DistilledPagePrefs::Theme theme,
+    DistilledPagePrefs::FontFamily font_family);
 
 // Returns the JavaScript to place a full article's HTML on the page. The
 // returned HTML should be considered unsafe, so callers must ensure
@@ -45,7 +45,7 @@
 // article.
 const std::string GetUnsafeIncrementalDistilledPageJs(
     const DistilledPageProto* page_proto,
-    const bool is_last_page);
+    bool is_last_page);
 
 // Returns the JavaScript to set the title of the distilled article page.
 const std::string GetSetTitleJs(std::string title);
@@ -60,7 +60,7 @@
 // Returns a JavaScript blob for controlling the "in-progress" indicator when
 // viewing a partially-distilled page. |is_last_page| indicates whether this is
 // the last page of the article (i.e. loading indicator should be removed).
-const std::string GetToggleLoadingIndicatorJs(const bool is_last_page);
+const std::string GetToggleLoadingIndicatorJs(bool is_last_page);
 
 // Returns the default CSS to be used for a viewer.
 const std::string GetCss();
@@ -93,7 +93,6 @@
 const std::string GetDistilledPageFontScalingJs(float scaling);
 
 }  // namespace viewer
-
 }  // namespace dom_distiller
 
 #endif  // COMPONENTS_DOM_DISTILLER_CORE_VIEWER_H_
diff --git a/components/dom_distiller/core/viewer_unittest.cc b/components/dom_distiller/core/viewer_unittest.cc
index 5047213..1e69a43 100644
--- a/components/dom_distiller/core/viewer_unittest.cc
+++ b/components/dom_distiller/core/viewer_unittest.cc
@@ -80,8 +80,8 @@
   std::unique_ptr<ViewerHandle> CreateViewRequest(
       const std::string& path,
       ViewRequestDelegate* view_request_delegate) {
-    return viewer::CreateViewRequest(
-        service_.get(), path, view_request_delegate, gfx::Size());
+    return viewer::CreateViewRequest(service_.get(), path,
+                                     view_request_delegate, gfx::Size());
   }
 
   std::unique_ptr<TestDomDistillerService> service_;
diff --git a/components/domain_reliability/context_unittest.cc b/components/domain_reliability/context_unittest.cc
index b5b3c09..ad40e8d 100644
--- a/components/domain_reliability/context_unittest.cc
+++ b/components/domain_reliability/context_unittest.cc
@@ -12,7 +12,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/json/json_reader.h"
 #include "base/strings/string_piece.h"
 #include "components/domain_reliability/beacon.h"
@@ -160,7 +159,7 @@
 
   void CallUploadAllowedResultCallback(bool allowed) {
     DCHECK(!upload_allowed_result_callback_.is_null());
-    base::ResetAndReturn(&upload_allowed_result_callback_).Run(allowed);
+    std::move(upload_allowed_result_callback_).Run(allowed);
   }
 
   MockTime time_;
diff --git a/components/download/internal/background_service/controller_impl.cc b/components/download/internal/background_service/controller_impl.cc
index c93a1fdf..c16357d 100644
--- a/components/download/internal/background_service/controller_impl.cc
+++ b/components/download/internal/background_service/controller_impl.cc
@@ -9,7 +9,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/optional.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -388,8 +387,7 @@
     return;
 
   if (status != stats::ScheduledTaskStatus::CANCELLED_ON_STOP) {
-    base::ResetAndReturn(&task_finished_callbacks_[task_type])
-        .Run(needs_reschedule);
+    std::move(task_finished_callbacks_[task_type]).Run(needs_reschedule);
   }
   // TODO(dtrainor): It might be useful to log how many downloads we have
   // running when we're asked to stop processing.
@@ -1093,8 +1091,8 @@
   if (init_callback_.is_null())
     return;
 
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::ResetAndReturn(&init_callback_));
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                std::move(init_callback_));
 }
 
 void ControllerImpl::HandleStartDownloadResponse(
diff --git a/components/download/internal/common/download_item_impl_unittest.cc b/components/download/internal/common/download_item_impl_unittest.cc
index f5e31f5..f202fda 100644
--- a/components/download/internal/common/download_item_impl_unittest.cc
+++ b/components/download/internal/common/download_item_impl_unittest.cc
@@ -14,7 +14,6 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/callback_helpers.h"
 #include "base/containers/circular_deque.h"
 #include "base/containers/queue.h"
 #include "base/files/file_util.h"
@@ -849,10 +848,10 @@
     EXPECT_CALL(*mock_download_file_ref, RenameAndUniquify(_, _)).Times(i == 0);
 
     ASSERT_FALSE(callback.is_null());
-    base::ResetAndReturn(&callback).Run(
-        target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
-        DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, intermediate_path,
-        DOWNLOAD_INTERRUPT_REASON_NONE);
+    std::move(callback).Run(target_path,
+                            DownloadItem::TARGET_DISPOSITION_OVERWRITE,
+                            DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+                            intermediate_path, DOWNLOAD_INTERRUPT_REASON_NONE);
     task_environment_.RunUntilIdle();
 
     // Use a continuable interrupt.
diff --git a/components/drive/chromeos/change_list_loader.cc b/components/drive/chromeos/change_list_loader.cc
index e2801b7..1e8717e 100644
--- a/components/drive/chromeos/change_list_loader.cc
+++ b/components/drive/chromeos/change_list_loader.cc
@@ -13,7 +13,6 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback.h"
-#include "base/callback_helpers.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/strcat.h"
@@ -400,7 +399,10 @@
   // If there is pending update check, try to load the change from the server
   // again, because there may exist an update during the completed loading.
   if (pending_update_check_callback_) {
-    Load(base::ResetAndReturn(&pending_update_check_callback_));
+    auto cb = std::move(pending_update_check_callback_);
+    // TODO(dcheng): Rewrite this to use OnceCallback. Load() currently takes a
+    // callback by const ref, so std::move() won't do anything. :(
+    Load(cb);
   }
 }
 
diff --git a/components/gwp_asan/buildflags/BUILD.gn b/components/gwp_asan/buildflags/BUILD.gn
index 95e986c..4d1ebd2 100644
--- a/components/gwp_asan/buildflags/BUILD.gn
+++ b/components/gwp_asan/buildflags/BUILD.gn
@@ -7,5 +7,5 @@
 
 buildflag_header("buildflags") {
   header = "buildflags.h"
-  flags = [ "ENABLE_GWP_ASAN=$enable_gwp_asan" ]
+  flags = [ "ENABLE_GWP_ASAN_MALLOC=$enable_gwp_asan_malloc" ]
 }
diff --git a/components/gwp_asan/buildflags/buildflags.gni b/components/gwp_asan/buildflags/buildflags.gni
index 3cb25db9..6a97419 100644
--- a/components/gwp_asan/buildflags/buildflags.gni
+++ b/components/gwp_asan/buildflags/buildflags.gni
@@ -5,6 +5,6 @@
 import("//build/config/allocator.gni")
 
 declare_args() {
-  # Is the GWP-ASan client enabled for chrome/ on a given platform.
-  enable_gwp_asan = (is_win || is_mac) && use_allocator_shim
+  # Is GWP-ASan malloc() hooking enabled for chrome/ on a given platform.
+  enable_gwp_asan_malloc = (is_win || is_mac) && use_allocator_shim
 }
diff --git a/components/login/secure_module_util_chromeos.cc b/components/login/secure_module_util_chromeos.cc
index 58aa44b2..25fc3eb7 100644
--- a/components/login/secure_module_util_chromeos.cc
+++ b/components/login/secure_module_util_chromeos.cc
@@ -5,7 +5,6 @@
 #include "components/login/secure_module_util_chromeos.h"
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/task/post_task.h"
@@ -35,7 +34,7 @@
 
 void GetSecureModuleUsed(GetSecureModuleUsedCallback callback) {
   if (g_secure_module_used != SecureModuleUsed::UNQUERIED) {
-    base::ResetAndReturn(&callback).Run(g_secure_module_used);
+    std::move(callback).Run(g_secure_module_used);
     return;
   }
 
diff --git a/components/mirroring/browser/cast_remoting_sender_unittest.cc b/components/mirroring/browser/cast_remoting_sender_unittest.cc
index 01b9c3f7..5be55054 100644
--- a/components/mirroring/browser/cast_remoting_sender_unittest.cc
+++ b/components/mirroring/browser/cast_remoting_sender_unittest.cc
@@ -6,7 +6,6 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/callback_helpers.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
@@ -67,7 +66,7 @@
                                media::cast::FrameId frame_id) final {
     kickstarted_frame_id_ = frame_id;
     if (!kickstarted_callback_.is_null())
-      base::ResetAndReturn(&kickstarted_callback_).Run();
+      std::move(kickstarted_callback_).Run();
   }
 
   // The rest of the interface is not used for these tests.
diff --git a/components/mirroring/service/remoting_sender_unittest.cc b/components/mirroring/service/remoting_sender_unittest.cc
index 016c090..93d97f08 100644
--- a/components/mirroring/service/remoting_sender_unittest.cc
+++ b/components/mirroring/service/remoting_sender_unittest.cc
@@ -4,9 +4,10 @@
 
 #include "components/mirroring/service/remoting_sender.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/callback_helpers.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
@@ -67,7 +68,7 @@
                                media::cast::FrameId frame_id) final {
     kickstarted_frame_id_ = frame_id;
     if (!kickstarted_callback_.is_null())
-      base::ResetAndReturn(&kickstarted_callback_).Run();
+      std::move(kickstarted_callback_).Run();
   }
 
   // The rest of the interface is not used for these tests.
diff --git a/components/nacl/renderer/manifest_service_channel.cc b/components/nacl/renderer/manifest_service_channel.cc
index 66b2e4d..de91924 100644
--- a/components/nacl/renderer/manifest_service_channel.cc
+++ b/components/nacl/renderer/manifest_service_channel.cc
@@ -8,7 +8,6 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/callback_helpers.h"
 #include "build/build_config.h"
 #include "content/public/common/sandbox_init.h"
 #include "content/public/renderer/render_thread.h"
@@ -41,7 +40,7 @@
 
 ManifestServiceChannel::~ManifestServiceChannel() {
   if (!connected_callback_.is_null())
-    base::ResetAndReturn(&connected_callback_).Run(PP_ERROR_FAILED);
+    std::move(connected_callback_).Run(PP_ERROR_FAILED);
 }
 
 void ManifestServiceChannel::Send(IPC::Message* message) {
@@ -63,12 +62,12 @@
 void ManifestServiceChannel::OnChannelConnected(int32_t peer_pid) {
   peer_pid_ = peer_pid;
   if (!connected_callback_.is_null())
-    base::ResetAndReturn(&connected_callback_).Run(PP_OK);
+    std::move(connected_callback_).Run(PP_OK);
 }
 
 void ManifestServiceChannel::OnChannelError() {
   if (!connected_callback_.is_null())
-    base::ResetAndReturn(&connected_callback_).Run(PP_ERROR_FAILED);
+    std::move(connected_callback_).Run(PP_ERROR_FAILED);
 }
 
 void ManifestServiceChannel::OnStartupInitializationComplete() {
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc b/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
index ac840fe..ee7232f 100644
--- a/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl.cc
@@ -1713,9 +1713,11 @@
     // serialization / deserialization should not be done inside this
     // class. We should move that into a central place that also knows how to
     // parse data we received from remote backends.
+    // We don't want to use the restored title for BuildArticleCategoryInfo to
+    // avoid using a title that was calculated for a stale locale.
     CategoryInfo info =
         category == articles_category_
-            ? BuildArticleCategoryInfo(title)
+            ? BuildArticleCategoryInfo(base::nullopt)
             : BuildRemoteCategoryInfo(title, allow_fetching_more_results);
     CategoryContent* content = UpdateCategoryInfo(category, info);
     content->included_in_last_server_response =
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc b/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
index 7a77213..71578231 100644
--- a/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
@@ -13,6 +13,7 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/i18n/rtl.h"
 #include "base/json/json_reader.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
@@ -58,11 +59,13 @@
 #include "components/ntp_snippets/time_serialization.h"
 #include "components/ntp_snippets/user_classifier.h"
 #include "components/prefs/testing_pref_service.h"
+#include "components/strings/grit/components_strings.h"
 #include "components/variations/variations_params_manager.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gmock_mutant.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_unittest_util.h"
@@ -972,6 +975,7 @@
   CategoryInfo info_unknown_before = provider()->GetCategoryInfo(
       Category::FromRemoteCategory(kUnknownRemoteCategoryId));
 
+  base::i18n::SetICUDefaultLocale("de");
   // Recreate the provider to simulate a Chrome restart.
   ResetSuggestionsProvider(
       /*use_mock_prefetched_pages_tracker=*/false,
@@ -995,7 +999,12 @@
   CategoryInfo info_unknown_after = provider()->GetCategoryInfo(
       Category::FromRemoteCategory(kUnknownRemoteCategoryId));
 
-  EXPECT_EQ(info_articles_before.title(), info_articles_after.title());
+  // The new articles section title should reflect the current locale, not what
+  // we persisted earlier.
+  EXPECT_NE(info_articles_before.title(), info_articles_after.title());
+  EXPECT_EQ(
+      info_articles_after.title(),
+      l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_HEADER));
   EXPECT_EQ(info_unknown_before.title(), info_unknown_after.title());
 }
 
diff --git a/components/sync/nigori/nigori_local_change_processor.h b/components/sync/nigori/nigori_local_change_processor.h
index 35b3cadf..c26aed10 100644
--- a/components/sync/nigori/nigori_local_change_processor.h
+++ b/components/sync/nigori/nigori_local_change_processor.h
@@ -10,6 +10,7 @@
 
 #include "base/macros.h"
 #include "base/optional.h"
+#include "components/sync/model/model_error.h"
 #include "components/sync/protocol/entity_metadata.pb.h"
 #include "components/sync/protocol/model_type_state.pb.h"
 
@@ -51,6 +52,13 @@
   // model takes care of persisting them.
   virtual NigoriMetadataBatch GetMetadata() = 0;
 
+  // Reports an error in the model to sync. Should be called for any persistence
+  // or consistency error the bridge encounters outside of a method that allows
+  // returning a ModelError directly. Outstanding callbacks are not expected to
+  // be called after an error. This will result in sync being temporarily
+  // disabled (generally until the next restart).
+  virtual void ReportError(const ModelError& error) = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(NigoriLocalChangeProcessor);
 };
diff --git a/components/sync/nigori/nigori_model_type_processor.cc b/components/sync/nigori/nigori_model_type_processor.cc
index 3516cb0..ea46e06 100644
--- a/components/sync/nigori/nigori_model_type_processor.cc
+++ b/components/sync/nigori/nigori_model_type_processor.cc
@@ -139,8 +139,7 @@
       error = bridge_->MergeSyncData(std::move(*updates[0]->entity));
     }
     if (error) {
-      // TODO(mamir): ReportError(*error);
-      NOTIMPLEMENTED();
+      ReportError(*error);
     }
     return;
   }
@@ -171,8 +170,7 @@
   }
 
   if (error) {
-    // TODO(mamir): ReportError(*error);
-    NOTIMPLEMENTED();
+    ReportError(*error);
     return;
   }
 
@@ -354,6 +352,27 @@
   return nigori_metadata_batch;
 }
 
+void NigoriModelTypeProcessor::ReportError(const ModelError& error) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Ignore all errors after the first.
+  if (model_error_) {
+    return;
+  }
+
+  model_error_ = error;
+
+  if (IsConnected()) {
+    DisconnectSync();
+  }
+  // Shouldn't connect anymore.
+  start_callback_.Reset();
+  if (activation_request_.error_handler) {
+    // Tell sync about the error.
+    activation_request_.error_handler.Run(error);
+  }
+}
+
 bool NigoriModelTypeProcessor::IsConnectedForTest() const {
   return IsConnected();
 }
diff --git a/components/sync/nigori/nigori_model_type_processor.h b/components/sync/nigori/nigori_model_type_processor.h
index e15bbc66..846071d 100644
--- a/components/sync/nigori/nigori_model_type_processor.h
+++ b/components/sync/nigori/nigori_model_type_processor.h
@@ -51,6 +51,7 @@
                         NigoriMetadataBatch nigori_metadata) override;
   void Put(std::unique_ptr<EntityData> entity_data) override;
   NigoriMetadataBatch GetMetadata() override;
+  void ReportError(const ModelError& error) override;
 
   bool IsConnectedForTest() const;
 
diff --git a/components/sync/nigori/nigori_model_type_processor_unittest.cc b/components/sync/nigori/nigori_model_type_processor_unittest.cc
index eb04b07b..b45c4ee 100644
--- a/components/sync/nigori/nigori_model_type_processor_unittest.cc
+++ b/components/sync/nigori/nigori_model_type_processor_unittest.cc
@@ -493,6 +493,76 @@
   EXPECT_FALSE(processor()->IsConnectedForTest());
 }
 
+TEST_F(NigoriModelTypeProcessorTest, ShouldDisconnectWhenMergeSyncDataFails) {
+  SimulateModelReadyToSync(/*initial_sync_done=*/false);
+
+  syncer::DataTypeActivationRequest request;
+  base::MockCallback<ModelErrorHandler> error_handler_callback;
+  request.error_handler = error_handler_callback.Get();
+  request.cache_guid = kCacheGuid;
+  processor()->OnSyncStarting(request, base::DoNothing());
+  SimulateConnectSync();
+
+  // Simulate returning error at MergeSyncData()
+  ON_CALL(*mock_nigori_sync_bridge(), MergeSyncData(_))
+      .WillByDefault([&](const base::Optional<EntityData>& data) {
+        return ModelError(FROM_HERE, "some error");
+      });
+
+  UpdateResponseDataList updates;
+  updates.push_back(CreateDummyNigoriUpdateResponseData(
+      /*keystore_decryptor_token_key_name=*/"some key",
+      /*server_version=*/1));
+
+  ASSERT_TRUE(processor()->IsConnectedForTest());
+  EXPECT_CALL(error_handler_callback, Run(_));
+  processor()->OnUpdateReceived(CreateDummyModelTypeState(),
+                                std::move(updates));
+  EXPECT_FALSE(processor()->IsConnectedForTest());
+}
+
+TEST_F(NigoriModelTypeProcessorTest,
+       ShouldDisconnectWhenApplySyncChangesFails) {
+  SimulateModelReadyToSync(/*initial_sync_done=*/true, /*server_version=*/1);
+
+  syncer::DataTypeActivationRequest request;
+  base::MockCallback<ModelErrorHandler> error_handler_callback;
+  request.error_handler = error_handler_callback.Get();
+  request.cache_guid = kCacheGuid;
+  processor()->OnSyncStarting(request, base::DoNothing());
+  SimulateConnectSync();
+
+  // Simulate returning error at ApplySyncChanges()
+  ON_CALL(*mock_nigori_sync_bridge(), ApplySyncChanges(_))
+      .WillByDefault([&](const base::Optional<EntityData>& data) {
+        return ModelError(FROM_HERE, "some error");
+      });
+
+  UpdateResponseDataList updates;
+  updates.push_back(CreateDummyNigoriUpdateResponseData(
+      /*keystore_decryptor_token_key_name=*/"some key",
+      /*server_version=*/2));
+
+  ASSERT_TRUE(processor()->IsConnectedForTest());
+  EXPECT_CALL(error_handler_callback, Run(_));
+  processor()->OnUpdateReceived(CreateDummyModelTypeState(),
+                                std::move(updates));
+  EXPECT_FALSE(processor()->IsConnectedForTest());
+}
+
+TEST_F(NigoriModelTypeProcessorTest,
+       ShouldCallErrorHandlerIfModelErrorBeforeSyncStarts) {
+  processor()->ReportError(ModelError(FROM_HERE, "some error"));
+
+  syncer::DataTypeActivationRequest request;
+  base::MockCallback<ModelErrorHandler> error_handler_callback;
+  request.error_handler = error_handler_callback.Get();
+  request.cache_guid = kCacheGuid;
+
+  EXPECT_CALL(error_handler_callback, Run(_));
+  processor()->OnSyncStarting(request, base::DoNothing());
+}
+
 }  // namespace
 
 }  // namespace syncer
diff --git a/components/visitedlink/test/visitedlink_unittest.cc b/components/visitedlink/test/visitedlink_unittest.cc
index 0596985..db78e72 100644
--- a/components/visitedlink/test/visitedlink_unittest.cc
+++ b/components/visitedlink/test/visitedlink_unittest.cc
@@ -11,7 +11,6 @@
 #include <vector>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/files/file_util.h"
 #include "base/location.h"
 #include "base/macros.h"
@@ -550,7 +549,7 @@
 
   void NotifyUpdate() {
     if (!quit_closure_.is_null())
-      base::ResetAndReturn(&quit_closure_).Run();
+      std::move(quit_closure_).Run();
   }
 
   void UpdateVisitedLinks(
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index 0cdf417..078069a4 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -59,8 +59,10 @@
                                      base::FEATURE_DISABLED_BY_DEFAULT};
 
 bool IsSurfaceSynchronizationEnabled() {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
   return IsVizDisplayCompositorEnabled() ||
-         base::FeatureList::IsEnabled(kEnableSurfaceSynchronization);
+         base::FeatureList::IsEnabled(kEnableSurfaceSynchronization) ||
+         command_line->HasSwitch(switches::kEnableSurfaceSynchronization);
 }
 
 bool IsVizDisplayCompositorEnabled() {
diff --git a/components/viz/common/switches.cc b/components/viz/common/switches.cc
index 939af777..0c9d3c1 100644
--- a/components/viz/common/switches.cc
+++ b/components/viz/common/switches.cc
@@ -15,16 +15,17 @@
 const char kDeadlineToSynchronizeSurfaces[] =
     "deadline-to-synchronize-surfaces";
 
-// Disables begin frame limiting in both cc scheduler and display scheduler.
-// Also implies --disable-gpu-vsync (see //ui/gl/gl_switches.h).
-const char kDisableFrameRateLimit[] = "disable-frame-rate-limit";
-
 // Enable compositing individual elements via hardware overlays when
 // permitted by device.
 // Setting the flag to "single-fullscreen" will try to promote a single
 // fullscreen overlay and use it as main framebuffer where possible.
 const char kEnableHardwareOverlays[] = "enable-hardware-overlays";
 
+// Enables multi-client Surface synchronization. In practice, this indicates
+// that LayerTreeHost expects to be given a valid viz::LocalSurfaceId provided
+// by the parent compositor.
+const char kEnableSurfaceSynchronization[] = "enable-surface-synchronization";
+
 // Enables inspecting Viz Display Compositor objects. Default port is 9229.
 // For local inspection use chrome://inspect#other
 const char kEnableVizDevTools[] = "enable-viz-devtools";
@@ -41,6 +42,10 @@
 // hit-test data coming from surface layer.
 const char kUseVizHitTestSurfaceLayer[] = "use-viz-hit-test-surface-layer";
 
+// Disables begin frame limiting in both cc scheduler and display scheduler.
+// Also implies --disable-gpu-vsync (see //ui/gl/gl_switches.h).
+const char kDisableFrameRateLimit[] = "disable-frame-rate-limit";
+
 base::Optional<uint32_t> GetDeadlineToSynchronizeSurfaces() {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kRunAllCompositorStagesBeforeDraw)) {
diff --git a/components/viz/common/switches.h b/components/viz/common/switches.h
index 8326b1d..4757fd8 100644
--- a/components/viz/common/switches.h
+++ b/components/viz/common/switches.h
@@ -15,12 +15,13 @@
 
 // Keep list in alphabetical order.
 VIZ_COMMON_EXPORT extern const char kDeadlineToSynchronizeSurfaces[];
-VIZ_COMMON_EXPORT extern const char kDisableFrameRateLimit[];
 VIZ_COMMON_EXPORT extern const char kEnableHardwareOverlays[];
+VIZ_COMMON_EXPORT extern const char kEnableSurfaceSynchronization[];
 VIZ_COMMON_EXPORT extern const char kEnableVizDevTools[];
 VIZ_COMMON_EXPORT extern const char kEnableVizHitTestDebug[];
 VIZ_COMMON_EXPORT extern const char kRunAllCompositorStagesBeforeDraw[];
 VIZ_COMMON_EXPORT extern const char kUseVizHitTestSurfaceLayer[];
+VIZ_COMMON_EXPORT extern const char kDisableFrameRateLimit[];
 
 VIZ_COMMON_EXPORT base::Optional<uint32_t> GetDeadlineToSynchronizeSurfaces();
 
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index b87dda6..2134574 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -103,17 +103,17 @@
     "display_embedder/compositor_overlay_candidate_validator.h",
     "display_embedder/direct_context_provider.cc",
     "display_embedder/direct_context_provider.h",
-    "display_embedder/display_provider.h",
     "display_embedder/gl_output_surface.cc",
     "display_embedder/gl_output_surface.h",
     "display_embedder/gl_output_surface_buffer_queue.cc",
     "display_embedder/gl_output_surface_buffer_queue.h",
     "display_embedder/gl_output_surface_offscreen.cc",
     "display_embedder/gl_output_surface_offscreen.h",
-    "display_embedder/gpu_display_provider.cc",
-    "display_embedder/gpu_display_provider.h",
     "display_embedder/in_process_gpu_memory_buffer_manager.cc",
     "display_embedder/in_process_gpu_memory_buffer_manager.h",
+    "display_embedder/output_surface_provider.h",
+    "display_embedder/output_surface_provider_impl.cc",
+    "display_embedder/output_surface_provider_impl.h",
     "display_embedder/server_shared_bitmap_manager.cc",
     "display_embedder/server_shared_bitmap_manager.h",
     "display_embedder/software_output_surface.cc",
diff --git a/components/viz/service/compositor_frame_fuzzer/BUILD.gn b/components/viz/service/compositor_frame_fuzzer/BUILD.gn
index 12ea598..b2b1c76 100644
--- a/components/viz/service/compositor_frame_fuzzer/BUILD.gn
+++ b/components/viz/service/compositor_frame_fuzzer/BUILD.gn
@@ -64,8 +64,8 @@
     "compositor_frame_fuzzer_util.h",
     "fuzzer_browser_process.cc",
     "fuzzer_browser_process.h",
-    "fuzzer_software_display_provider.cc",
-    "fuzzer_software_display_provider.h",
+    "fuzzer_software_output_surface_provider.cc",
+    "fuzzer_software_output_surface_provider.h",
   ]
 
   deps = [
diff --git a/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc b/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc
index 028eea78..8546b29 100644
--- a/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc
+++ b/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc
@@ -30,10 +30,8 @@
 FuzzerBrowserProcess::FuzzerBrowserProcess(
     base::Optional<base::FilePath> png_dir_path)
     : root_local_surface_id_(1, 1, base::UnguessableToken::Create()),
-      display_provider_(&shared_bitmap_manager_, std::move(png_dir_path)),
-      frame_sink_manager_(&shared_bitmap_manager_,
-                          base::nullopt,
-                          &display_provider_) {
+      output_surface_provider_(std::move(png_dir_path)),
+      frame_sink_manager_(&shared_bitmap_manager_, &output_surface_provider_) {
   frame_sink_manager_.RegisterFrameSinkId(kEmbeddedFrameSinkId,
                                           /*report_activation=*/false);
   frame_sink_manager_.RegisterFrameSinkId(kRootFrameSinkId,
@@ -107,14 +105,12 @@
   params->display_private =
       MakeRequestAssociatedWithDedicatedPipe(&display_private_);
   params->display_client = display_client_.BindInterfacePtr().PassInterface();
-
-  // Since the RootCompositorFrameSink doesn't get replaced on each fuzzer
-  // iteration, ensure that only one frame is pending at a time and that begin
-  // frames are decoupled from frame rate (otherwise bugs can occur when the
-  // frame rate is slower than the length of the fuzzer iterations).
-  // TODO(kylechar): Stop sending begin frames back to clients.
-  params->disable_frame_rate_limit = true;
-
+  params->external_begin_frame_controller =
+      MakeRequestAssociatedWithDedicatedPipe(
+          &external_begin_frame_controller_ptr_);
+  params->external_begin_frame_controller_client =
+      external_begin_frame_controller_client_.BindInterfacePtr()
+          .PassInterface();
   return params;
 }
 
diff --git a/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.h b/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.h
index f06a979..39ce559 100644
--- a/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.h
+++ b/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.h
@@ -10,11 +10,12 @@
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.h"
-#include "components/viz/service/compositor_frame_fuzzer/fuzzer_software_display_provider.h"
+#include "components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.h"
 #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/test/fake_compositor_frame_sink_client.h"
 #include "components/viz/test/fake_display_client.h"
+#include "components/viz/test/fake_external_begin_frame_controller_client.h"
 
 namespace viz {
 
@@ -47,13 +48,17 @@
   const LocalSurfaceId root_local_surface_id_;
 
   ServerSharedBitmapManager shared_bitmap_manager_;
-  FuzzerSoftwareDisplayProvider display_provider_;
+  FuzzerSoftwareOutputSurfaceProvider output_surface_provider_;
   FrameSinkManagerImpl frame_sink_manager_;
 
   mojom::CompositorFrameSinkAssociatedPtr root_compositor_frame_sink_ptr_;
   FakeCompositorFrameSinkClient root_compositor_frame_sink_client_;
   mojom::DisplayPrivateAssociatedPtr display_private_;
   FakeDisplayClient display_client_;
+  mojom::ExternalBeginFrameControllerAssociatedPtr
+      external_begin_frame_controller_ptr_;
+  FakeExternalBeginFrameControllerClient
+      external_begin_frame_controller_client_;
 
   ParentLocalSurfaceIdAllocator lsi_allocator_;
 
diff --git a/components/viz/service/compositor_frame_fuzzer/fuzzer_software_display_provider.h b/components/viz/service/compositor_frame_fuzzer/fuzzer_software_display_provider.h
deleted file mode 100644
index da3d7db..0000000
--- a/components/viz/service/compositor_frame_fuzzer/fuzzer_software_display_provider.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_VIZ_SERVICE_COMPOSITOR_FRAME_FUZZER_FUZZER_SOFTWARE_DISPLAY_PROVIDER_H_
-#define COMPONENTS_VIZ_SERVICE_COMPOSITOR_FRAME_FUZZER_FUZZER_SOFTWARE_DISPLAY_PROVIDER_H_
-
-#include <memory>
-
-#include "components/viz/service/display/display.h"
-#include "components/viz/service/display_embedder/display_provider.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
-
-namespace viz {
-
-// DisplayProvider implementation that provides Displays that use a
-// SoftwareOutputSurface (no-op by default, with an option to dump pixmap to a
-// PNG for debugging).
-//
-// Provided Displays will only draw when explicitly asked to
-// (e.g. if Display::ForceImmediateDrawAndSwapIfPossible() is called),
-// ignoring the BeginFrameSource parameters passed to CreateDisplay.
-class FuzzerSoftwareDisplayProvider : public DisplayProvider {
- public:
-  FuzzerSoftwareDisplayProvider(
-      ServerSharedBitmapManager* server_shared_bitmap_manager,
-      base::Optional<base::FilePath> png_dir_path);
-  ~FuzzerSoftwareDisplayProvider() override;
-
-  // DisplayProvider implementation.
-  std::unique_ptr<Display> CreateDisplay(
-      const FrameSinkId& frame_sink_id,
-      gpu::SurfaceHandle surface_handle,
-      bool gpu_compositing,
-      mojom::DisplayClient* display_client,
-      BeginFrameSource* begin_frame_source,
-      UpdateVSyncParametersCallback update_vsync_callback,
-      const RendererSettings& renderer_settings,
-      bool send_swap_size_notifications) override;
-  uint32_t GetRestartId() const override;
-
- private:
-  ServerSharedBitmapManager* const shared_bitmap_manager_;
-  base::Optional<base::FilePath> png_dir_path_;
-  std::unique_ptr<StubBeginFrameSource> begin_frame_source_;
-
-  DISALLOW_COPY_AND_ASSIGN(FuzzerSoftwareDisplayProvider);
-};
-
-}  // namespace viz
-
-#endif  // COMPONENTS_VIZ_SERVICE_COMPOSITOR_FRAME_FUZZER_FUZZER_SOFTWARE_DISPLAY_PROVIDER_H_
diff --git a/components/viz/service/compositor_frame_fuzzer/fuzzer_software_display_provider.cc b/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.cc
similarity index 69%
rename from components/viz/service/compositor_frame_fuzzer/fuzzer_software_display_provider.cc
rename to components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.cc
index 53ed9e7..329b0f4 100644
--- a/components/viz/service/compositor_frame_fuzzer/fuzzer_software_display_provider.cc
+++ b/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.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 "components/viz/service/compositor_frame_fuzzer/fuzzer_software_display_provider.h"
+#include "components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.h"
 
 #include <string>
 #include <utility>
@@ -79,45 +79,24 @@
 
 }  // namespace
 
-FuzzerSoftwareDisplayProvider::FuzzerSoftwareDisplayProvider(
-    ServerSharedBitmapManager* server_shared_bitmap_manager,
+FuzzerSoftwareOutputSurfaceProvider::FuzzerSoftwareOutputSurfaceProvider(
     base::Optional<base::FilePath> png_dir_path)
-    : shared_bitmap_manager_(server_shared_bitmap_manager),
-      png_dir_path_(png_dir_path),
-      begin_frame_source_(std::make_unique<StubBeginFrameSource>()) {}
+    : png_dir_path_(png_dir_path) {}
 
-FuzzerSoftwareDisplayProvider::~FuzzerSoftwareDisplayProvider() = default;
+FuzzerSoftwareOutputSurfaceProvider::~FuzzerSoftwareOutputSurfaceProvider() =
+    default;
 
-std::unique_ptr<Display> FuzzerSoftwareDisplayProvider::CreateDisplay(
-    const FrameSinkId& frame_sink_id,
+std::unique_ptr<OutputSurface>
+FuzzerSoftwareOutputSurfaceProvider::CreateOutputSurface(
     gpu::SurfaceHandle surface_handle,
     bool gpu_compositing,
     mojom::DisplayClient* display_client,
-    BeginFrameSource* begin_frame_source,
-    UpdateVSyncParametersCallback update_vsync_callback,
-    const RendererSettings& renderer_settings,
-    bool send_swap_size_notifications) {
-  auto task_runner = base::ThreadTaskRunnerHandle::Get();
-  DCHECK(task_runner);
-
+    const RendererSettings& renderer_settings) {
   std::unique_ptr<SoftwareOutputDevice> software_output_device =
       png_dir_path_ ? std::make_unique<PNGSoftwareOutputDevice>(*png_dir_path_)
                     : std::make_unique<SoftwareOutputDevice>();
-
-  auto output_surface = std::make_unique<SoftwareOutputSurface>(
+  return std::make_unique<SoftwareOutputSurface>(
       std::move(software_output_device));
-
-  auto scheduler = std::make_unique<DisplayScheduler>(
-      begin_frame_source_.get(), task_runner.get(),
-      output_surface->capabilities().max_frames_pending);
-
-  return std::make_unique<Display>(shared_bitmap_manager_, renderer_settings,
-                                   frame_sink_id, std::move(output_surface),
-                                   std::move(scheduler), task_runner);
-}
-
-uint32_t FuzzerSoftwareDisplayProvider::GetRestartId() const {
-  return BeginFrameSource::kNotRestartableId;
 }
 
 }  // namespace viz
diff --git a/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.h b/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.h
new file mode 100644
index 0000000..0a3f293
--- /dev/null
+++ b/components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.h
@@ -0,0 +1,37 @@
+// 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_VIZ_SERVICE_COMPOSITOR_FRAME_FUZZER_FUZZER_SOFTWARE_OUTPUT_SURFACE_PROVIDER_H_
+#define COMPONENTS_VIZ_SERVICE_COMPOSITOR_FRAME_FUZZER_FUZZER_SOFTWARE_OUTPUT_SURFACE_PROVIDER_H_
+
+#include <memory>
+
+#include "components/viz/service/display_embedder/output_surface_provider.h"
+
+namespace viz {
+
+// OutputSurfaceProvider implementation that provides SoftwareOutputSurface
+// (no-op by default, with an option to dump pixmap to a PNG for debugging).
+class FuzzerSoftwareOutputSurfaceProvider : public OutputSurfaceProvider {
+ public:
+  explicit FuzzerSoftwareOutputSurfaceProvider(
+      base::Optional<base::FilePath> png_dir_path);
+  ~FuzzerSoftwareOutputSurfaceProvider() override;
+
+  // OutputSurfaceProvider implementation.
+  std::unique_ptr<OutputSurface> CreateOutputSurface(
+      gpu::SurfaceHandle surface_handle,
+      bool gpu_compositing,
+      mojom::DisplayClient* display_client,
+      const RendererSettings& renderer_settings) override;
+
+ private:
+  base::Optional<base::FilePath> png_dir_path_;
+
+  DISALLOW_COPY_AND_ASSIGN(FuzzerSoftwareOutputSurfaceProvider);
+};
+
+}  // namespace viz
+
+#endif  // COMPONENTS_VIZ_SERVICE_COMPOSITOR_FRAME_FUZZER_FUZZER_SOFTWARE_OUTPUT_SURFACE_PROVIDER_H_
diff --git a/components/viz/service/display_embedder/DEPS b/components/viz/service/display_embedder/DEPS
index 34132c6..1eae4c4c 100644
--- a/components/viz/service/display_embedder/DEPS
+++ b/components/viz/service/display_embedder/DEPS
@@ -50,11 +50,6 @@
     "+cc/test",
     "+third_party/khronos/GLES2",
   ],
-  # TODO(kylechar): Break these dependencies.
-  "gpu_display_provider.cc": [
-    "+components/viz/service/display/display.h",
-    "+components/viz/service/display/display_scheduler.h",
-  ],
   "software_output_device_ozone_unittest.cc": [
     "+ui/compositor",
     "+ui/gl",
diff --git a/components/viz/service/display_embedder/display_provider.h b/components/viz/service/display_embedder/display_provider.h
deleted file mode 100644
index 854fe48..0000000
--- a/components/viz/service/display_embedder/display_provider.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2017 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_VIZ_SERVICE_DISPLAY_EMBEDDER_DISPLAY_PROVIDER_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_DISPLAY_PROVIDER_H_
-
-#include <memory>
-
-#include "components/viz/common/display/update_vsync_parameters_callback.h"
-#include "gpu/ipc/common/surface_handle.h"
-#include "services/viz/privileged/interfaces/compositing/display_private.mojom.h"
-
-namespace viz {
-
-class BeginFrameSource;
-class Display;
-class FrameSinkId;
-class RendererSettings;
-
-// Handles creating Display and related classes for FrameSinkManagerImpl.
-class DisplayProvider {
- public:
-  virtual ~DisplayProvider() {}
-
-  // Creates a new Display for |surface_handle| with |frame_sink_id|. If
-  // creating a Display fails this function will return null.
-  virtual std::unique_ptr<Display> CreateDisplay(
-      const FrameSinkId& frame_sink_id,
-      gpu::SurfaceHandle surface_handle,
-      bool gpu_compositing,
-      mojom::DisplayClient* display_client,
-      BeginFrameSource* begin_frame_source,
-      UpdateVSyncParametersCallback update_vsync_callback,
-      const RendererSettings& renderer_settings,
-      bool send_swap_size_notifications) = 0;
-
-  // Returns an ID that changes on each GPU process restart. This ID can be used
-  // for creating unique BeginFrameSource ids.
-  virtual uint32_t GetRestartId() const = 0;
-};
-
-}  // namespace viz
-
-#endif  //  COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_DISPLAY_PROVIDER_H_
diff --git a/components/viz/service/display_embedder/output_surface_provider.h b/components/viz/service/display_embedder/output_surface_provider.h
new file mode 100644
index 0000000..95dbcb6
--- /dev/null
+++ b/components/viz/service/display_embedder/output_surface_provider.h
@@ -0,0 +1,34 @@
+// 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_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_SURFACE_PROVIDER_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_SURFACE_PROVIDER_H_
+
+#include <memory>
+
+#include "gpu/ipc/common/surface_handle.h"
+#include "services/viz/privileged/interfaces/compositing/display_private.mojom.h"
+
+namespace viz {
+
+class RendererSettings;
+class OutputSurface;
+
+// Handles creating OutputSurface for FrameSinkManagerImpl.
+class OutputSurfaceProvider {
+ public:
+  virtual ~OutputSurfaceProvider() {}
+
+  // Creates a new OutputSurface for |surface_handle|. If creating an
+  // OutputSurface fails this function will return null.
+  virtual std::unique_ptr<OutputSurface> CreateOutputSurface(
+      gpu::SurfaceHandle surface_handle,
+      bool gpu_compositing,
+      mojom::DisplayClient* display_client,
+      const RendererSettings& renderer_settings) = 0;
+};
+
+}  // namespace viz
+
+#endif  //  COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_SURFACE_PROVIDER_H_
diff --git a/components/viz/service/display_embedder/gpu_display_provider.cc b/components/viz/service/display_embedder/output_surface_provider_impl.cc
similarity index 80%
rename from components/viz/service/display_embedder/gpu_display_provider.cc
rename to components/viz/service/display_embedder/output_surface_provider_impl.cc
index e57ba4be..264c5699 100644
--- a/components/viz/service/display_embedder/gpu_display_provider.cc
+++ b/components/viz/service/display_embedder/output_surface_provider_impl.cc
@@ -2,18 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/viz/service/display_embedder/gpu_display_provider.h"
+#include "components/viz/service/display_embedder/output_surface_provider_impl.h"
 
 #include <utility>
 
+#include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "cc/base/switches.h"
 #include "components/viz/common/display/renderer_settings.h"
 #include "components/viz/common/frame_sinks/begin_frame_source.h"
-#include "components/viz/service/display/display.h"
-#include "components/viz/service/display/display_scheduler.h"
 #include "components/viz/service/display_embedder/gl_output_surface.h"
 #include "components/viz/service/display_embedder/gl_output_surface_offscreen.h"
 #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
@@ -66,54 +65,37 @@
 
 namespace viz {
 
-GpuDisplayProvider::GpuDisplayProvider(
-    uint32_t restart_id,
+OutputSurfaceProviderImpl::OutputSurfaceProviderImpl(
     GpuServiceImpl* gpu_service_impl,
     gpu::CommandBufferTaskExecutor* task_executor,
     gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate,
     std::unique_ptr<gpu::GpuMemoryBufferManager> gpu_memory_buffer_manager,
     gpu::ImageFactory* image_factory,
-    ServerSharedBitmapManager* server_shared_bitmap_manager,
-    bool headless,
-    bool wait_for_all_pipeline_stages_before_draw)
-    : restart_id_(restart_id),
-      gpu_service_impl_(gpu_service_impl),
+    bool headless)
+    : gpu_service_impl_(gpu_service_impl),
       task_executor_(task_executor),
       gpu_channel_manager_delegate_(gpu_channel_manager_delegate),
       gpu_memory_buffer_manager_(std::move(gpu_memory_buffer_manager)),
       image_factory_(image_factory),
-      server_shared_bitmap_manager_(server_shared_bitmap_manager),
       task_runner_(base::ThreadTaskRunnerHandle::Get()),
-      headless_(headless),
-      wait_for_all_pipeline_stages_before_draw_(
-          wait_for_all_pipeline_stages_before_draw) {}
+      headless_(headless) {}
 
-GpuDisplayProvider::GpuDisplayProvider(
-    uint32_t restart_id,
-    ServerSharedBitmapManager* server_shared_bitmap_manager,
-    bool headless,
-    bool wait_for_all_pipeline_stages_before_draw)
-    : GpuDisplayProvider(restart_id,
-                         /*gpu_service_impl=*/nullptr,
-                         /*task_executor=*/nullptr,
-                         /*gpu_channel_manager_delegate=*/nullptr,
-                         /*gpu_memory_buffer_manager=*/nullptr,
-                         /*image_factory=*/nullptr,
-                         server_shared_bitmap_manager,
-                         headless,
-                         wait_for_all_pipeline_stages_before_draw) {}
+OutputSurfaceProviderImpl::OutputSurfaceProviderImpl(bool headless)
+    : OutputSurfaceProviderImpl(
+          /*gpu_service_impl=*/nullptr,
+          /*task_executor=*/nullptr,
+          /*gpu_channel_manager_delegate=*/nullptr,
+          /*gpu_memory_buffer_manager=*/nullptr,
+          /*image_factory=*/nullptr,
+          headless) {}
 
-GpuDisplayProvider::~GpuDisplayProvider() = default;
+OutputSurfaceProviderImpl::~OutputSurfaceProviderImpl() = default;
 
-std::unique_ptr<Display> GpuDisplayProvider::CreateDisplay(
-    const FrameSinkId& frame_sink_id,
+std::unique_ptr<OutputSurface> OutputSurfaceProviderImpl::CreateOutputSurface(
     gpu::SurfaceHandle surface_handle,
     bool gpu_compositing,
     mojom::DisplayClient* display_client,
-    BeginFrameSource* begin_frame_source,
-    UpdateVSyncParametersCallback update_vsync_callback,
-    const RendererSettings& renderer_settings,
-    bool send_swap_size_notifications) {
+    const RendererSettings& renderer_settings) {
   // TODO(penghuang): Merge two output surfaces into one when GLRenderer and
   // software compositor is removed.
   std::unique_ptr<OutputSurface> output_surface;
@@ -248,30 +230,11 @@
     }
   }
 
-  // If we need swap size notifications tell the output surface now.
-  output_surface->SetNeedsSwapSizeNotifications(send_swap_size_notifications);
-
-  output_surface->SetUpdateVSyncParametersCallback(
-      std::move(update_vsync_callback));
-
-  int max_frames_pending = output_surface->capabilities().max_frames_pending;
-  DCHECK_GT(max_frames_pending, 0);
-
-  auto scheduler = std::make_unique<DisplayScheduler>(
-      begin_frame_source, task_runner_.get(), max_frames_pending,
-      wait_for_all_pipeline_stages_before_draw_);
-
-  return std::make_unique<Display>(
-      server_shared_bitmap_manager_, renderer_settings, frame_sink_id,
-      std::move(output_surface), std::move(scheduler), task_runner_);
-}
-
-uint32_t GpuDisplayProvider::GetRestartId() const {
-  return restart_id_;
+  return output_surface;
 }
 
 std::unique_ptr<SoftwareOutputDevice>
-GpuDisplayProvider::CreateSoftwareOutputDeviceForPlatform(
+OutputSurfaceProviderImpl::CreateSoftwareOutputDeviceForPlatform(
     gpu::SurfaceHandle surface_handle,
     mojom::DisplayClient* display_client) {
   if (headless_)
diff --git a/components/viz/service/display_embedder/gpu_display_provider.h b/components/viz/service/display_embedder/output_surface_provider_impl.h
similarity index 61%
rename from components/viz/service/display_embedder/gpu_display_provider.h
rename to components/viz/service/display_embedder/output_surface_provider_impl.h
index c79c7a2..4ccd789 100644
--- a/components/viz/service/display_embedder/gpu_display_provider.h
+++ b/components/viz/service/display_embedder/output_surface_provider_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GPU_DISPLAY_PROVIDER_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GPU_DISPLAY_PROVIDER_H_
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_SURFACE_PROVIDER_IMPL_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_SURFACE_PROVIDER_IMPL_H_
 
 #include <memory>
 
@@ -12,7 +12,7 @@
 #include "base/memory/ref_counted.h"
 #include "build/build_config.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
-#include "components/viz/service/display_embedder/display_provider.h"
+#include "components/viz/service/display_embedder/output_surface_provider.h"
 #include "components/viz/service/viz_service_export.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
 #include "gpu/ipc/common/surface_handle.h"
@@ -31,55 +31,41 @@
 }  // namespace gpu
 
 namespace viz {
-class Display;
 class GpuServiceImpl;
-class ServerSharedBitmapManager;
 class SoftwareOutputDevice;
 
-// In-process implementation of DisplayProvider.
-class VIZ_SERVICE_EXPORT GpuDisplayProvider : public DisplayProvider {
+// In-process implementation of OutputSurfaceProvider.
+class VIZ_SERVICE_EXPORT OutputSurfaceProviderImpl
+    : public OutputSurfaceProvider {
  public:
-  GpuDisplayProvider(
-      uint32_t restart_id,
+  OutputSurfaceProviderImpl(
       GpuServiceImpl* gpu_service_impl,
       gpu::CommandBufferTaskExecutor* task_executor,
       gpu::GpuChannelManagerDelegate* gpu_channel_manager_delegate,
       std::unique_ptr<gpu::GpuMemoryBufferManager> gpu_memory_buffer_manager,
       gpu::ImageFactory* image_factory,
-      ServerSharedBitmapManager* server_shared_bitmap_manager,
-      bool headless,
-      bool wait_for_all_pipeline_stages_before_draw);
+      bool headless);
   // Software compositing only.
-  GpuDisplayProvider(uint32_t restart_id,
-                     ServerSharedBitmapManager* server_shared_bitmap_manager,
-                     bool headless,
-                     bool wait_for_all_pipeline_stages_before_draw);
-  ~GpuDisplayProvider() override;
+  explicit OutputSurfaceProviderImpl(bool headless);
+  ~OutputSurfaceProviderImpl() override;
 
-  // DisplayProvider implementation.
-  std::unique_ptr<Display> CreateDisplay(
-      const FrameSinkId& frame_sink_id,
+  // OutputSurfaceProvider implementation.
+  std::unique_ptr<OutputSurface> CreateOutputSurface(
       gpu::SurfaceHandle surface_handle,
       bool gpu_compositing,
       mojom::DisplayClient* display_client,
-      BeginFrameSource* begin_frame_source,
-      UpdateVSyncParametersCallback update_vsync_callback,
-      const RendererSettings& renderer_settings,
-      bool send_swap_size_notifications) override;
-  uint32_t GetRestartId() const override;
+      const RendererSettings& renderer_settings) override;
 
  private:
   std::unique_ptr<SoftwareOutputDevice> CreateSoftwareOutputDeviceForPlatform(
       gpu::SurfaceHandle surface_handle,
       mojom::DisplayClient* display_client);
 
-  const uint32_t restart_id_;
   GpuServiceImpl* const gpu_service_impl_;
   gpu::CommandBufferTaskExecutor* const task_executor_;
   gpu::GpuChannelManagerDelegate* const gpu_channel_manager_delegate_;
   std::unique_ptr<gpu::GpuMemoryBufferManager> gpu_memory_buffer_manager_;
   gpu::ImageFactory* const image_factory_;
-  ServerSharedBitmapManager* const server_shared_bitmap_manager_;
 
 #if defined(OS_WIN)
   // Used for software compositing output on Windows.
@@ -94,11 +80,10 @@
   std::unique_ptr<gpu::SyncPointManager> sync_point_manager_;
 
   const bool headless_;
-  const bool wait_for_all_pipeline_stages_before_draw_;
 
-  DISALLOW_COPY_AND_ASSIGN(GpuDisplayProvider);
+  DISALLOW_COPY_AND_ASSIGN(OutputSurfaceProviderImpl);
 };
 
 }  // namespace viz
 
-#endif  //  COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GPU_DISPLAY_PROVIDER_H_
+#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_OUTPUT_SURFACE_PROVIDER_IMPL_H_
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
index dc9f51d..3a16274b 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -11,7 +11,7 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
 #include "components/viz/service/display/shared_bitmap_manager.h"
-#include "components/viz/service/display_embedder/display_provider.h"
+#include "components/viz/service/display_embedder/output_surface_provider.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/primary_begin_frame_source.h"
 #include "components/viz/service/frame_sinks/video_capture/capturable_frame_sink.h"
@@ -19,6 +19,17 @@
 
 namespace viz {
 
+FrameSinkManagerImpl::InitParams::InitParams() = default;
+FrameSinkManagerImpl::InitParams::InitParams(
+    SharedBitmapManager* shared_bitmap_manager,
+    OutputSurfaceProvider* output_surface_provider)
+    : shared_bitmap_manager(shared_bitmap_manager),
+      output_surface_provider(output_surface_provider) {}
+FrameSinkManagerImpl::InitParams::InitParams(InitParams&& other) = default;
+FrameSinkManagerImpl::InitParams::~InitParams() = default;
+FrameSinkManagerImpl::InitParams& FrameSinkManagerImpl::InitParams::operator=(
+    InitParams&& other) = default;
+
 FrameSinkManagerImpl::FrameSinkSourceMapping::FrameSinkSourceMapping() =
     default;
 
@@ -41,19 +52,25 @@
 FrameSinkManagerImpl::FrameSinkData& FrameSinkManagerImpl::FrameSinkData::
 operator=(FrameSinkData&& other) = default;
 
-FrameSinkManagerImpl::FrameSinkManagerImpl(
-    SharedBitmapManager* shared_bitmap_manager,
-    base::Optional<uint32_t> activation_deadline_in_frames,
-    DisplayProvider* display_provider)
-    : shared_bitmap_manager_(shared_bitmap_manager),
-      display_provider_(display_provider),
-      surface_manager_(this, activation_deadline_in_frames),
+FrameSinkManagerImpl::FrameSinkManagerImpl(const InitParams& params)
+    : shared_bitmap_manager_(params.shared_bitmap_manager),
+      output_surface_provider_(params.output_surface_provider),
+      surface_manager_(this, params.activation_deadline_in_frames),
       hit_test_manager_(surface_manager()),
+      restart_id_(params.restart_id),
+      run_all_compositor_stages_before_draw_(
+          params.run_all_compositor_stages_before_draw),
       binding_(this) {
   surface_manager_.AddObserver(&hit_test_manager_);
   surface_manager_.AddObserver(this);
 }
 
+FrameSinkManagerImpl::FrameSinkManagerImpl(
+    SharedBitmapManager* shared_bitmap_manager,
+    OutputSurfaceProvider* output_surface_provider)
+    : FrameSinkManagerImpl(
+          InitParams(shared_bitmap_manager, output_surface_provider)) {}
+
 FrameSinkManagerImpl::~FrameSinkManagerImpl() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   video_capturers_.clear();
@@ -148,14 +165,15 @@
     mojom::RootCompositorFrameSinkParamsPtr params) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!base::ContainsKey(root_sink_map_, params->frame_sink_id));
-  DCHECK(display_provider_);
+  DCHECK(output_surface_provider_);
 
   // We are transfering ownership of |params| so remember FrameSinkId here.
   FrameSinkId frame_sink_id = params->frame_sink_id;
 
   // Creating RootCompositorFrameSinkImpl can fail and return null.
   auto root_compositor_frame_sink = RootCompositorFrameSinkImpl::Create(
-      std::move(params), this, display_provider_);
+      std::move(params), this, output_surface_provider_, restart_id_,
+      run_all_compositor_stages_before_draw_);
   if (root_compositor_frame_sink)
     root_sink_map_[frame_sink_id] = std::move(root_compositor_frame_sink);
 }
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.h b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
index 29237d5..4b0efa2 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.h
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
@@ -44,7 +44,7 @@
 
 class CapturableFrameSink;
 class CompositorFrameSinkSupport;
-class DisplayProvider;
+class OutputSurfaceProvider;
 class SharedBitmapManager;
 
 // FrameSinkManagerImpl manages BeginFrame hierarchy. This is the implementation
@@ -56,11 +56,26 @@
       public HitTestAggregatorDelegate,
       public SurfaceManagerDelegate {
  public:
-  explicit FrameSinkManagerImpl(
+  struct VIZ_SERVICE_EXPORT InitParams {
+    InitParams();
+    InitParams(SharedBitmapManager* shared_bitmap_manager,
+               OutputSurfaceProvider* output_surface_provider);
+    InitParams(InitParams&& other);
+    ~InitParams();
+    InitParams& operator=(InitParams&& other);
+
+    SharedBitmapManager* shared_bitmap_manager = nullptr;
+    base::Optional<uint32_t> activation_deadline_in_frames =
+        kDefaultActivationDeadlineInFrames;
+    OutputSurfaceProvider* output_surface_provider = nullptr;
+    uint32_t restart_id = BeginFrameSource::kNotRestartableId;
+    bool run_all_compositor_stages_before_draw = false;
+  };
+  explicit FrameSinkManagerImpl(const InitParams& params);
+  // TODO(kylechar): Cleanup tests and remove this constructor.
+  FrameSinkManagerImpl(
       SharedBitmapManager* shared_bitmap_manager,
-      base::Optional<uint32_t> activation_deadline_in_frames =
-          kDefaultActivationDeadlineInFrames,
-      DisplayProvider* display_provider = nullptr);
+      OutputSurfaceProvider* output_surface_provider = nullptr);
   ~FrameSinkManagerImpl() override;
 
   // Performs cleanup needed to force shutdown from the GPU process. Stops all
@@ -262,8 +277,9 @@
   // SharedBitmapManager for the viz display service for receiving software
   // resources in CompositorFrameSinks.
   SharedBitmapManager* const shared_bitmap_manager_;
-  // Provides a Display for CreateRootCompositorFrameSink().
-  DisplayProvider* const display_provider_;
+
+  // Provides an output surface for CreateRootCompositorFrameSink().
+  OutputSurfaceProvider* const output_surface_provider_;
 
   PrimaryBeginFrameSource primary_source_;
 
@@ -273,6 +289,13 @@
   // Must be created after and destroyed before |surface_manager_|.
   HitTestManager hit_test_manager_;
 
+  // Restart id to generate unique begin frames across process restarts.  Used
+  // for creating a BeginFrameSource for RootCompositorFrameSink.
+  const uint32_t restart_id_;
+
+  // Whether display scheduler should wait for all pipeline stages before draw.
+  const bool run_all_compositor_stages_before_draw_;
+
   // Contains registered frame sink ids, debug labels and synchronization
   // labels. Map entries will be created when frame sink is registered and
   // destroyed when frame sink is invalidated.
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc b/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
index 5a6e3c8..17e6cd8 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
@@ -19,7 +19,7 @@
 #include "components/viz/test/fake_external_begin_frame_source.h"
 #include "components/viz/test/mock_compositor_frame_sink_client.h"
 #include "components/viz/test/mock_display_client.h"
-#include "components/viz/test/test_display_provider.h"
+#include "components/viz/test/test_output_surface_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace viz {
@@ -56,9 +56,7 @@
 class FrameSinkManagerTest : public testing::Test {
  public:
   FrameSinkManagerTest()
-      : manager_(&shared_bitmap_manager_,
-                 kDefaultActivationDeadlineInFrames,
-                 &display_provider_) {}
+      : manager_(&shared_bitmap_manager_, &output_surface_provider_) {}
   ~FrameSinkManagerTest() override = default;
 
   std::unique_ptr<CompositorFrameSinkSupport> CreateCompositorFrameSinkSupport(
@@ -98,7 +96,7 @@
 
  protected:
   ServerSharedBitmapManager shared_bitmap_manager_;
-  TestDisplayProvider display_provider_;
+  TestOutputSurfaceProvider output_surface_provider_;
   FrameSinkManagerImpl manager_;
 };
 
diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
index 68646411..ed8b0880 100644
--- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
+++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
@@ -11,7 +11,8 @@
 #include "build/build_config.h"
 #include "components/viz/common/frame_sinks/begin_frame_source.h"
 #include "components/viz/service/display/display.h"
-#include "components/viz/service/display_embedder/display_provider.h"
+#include "components/viz/service/display/output_surface.h"
+#include "components/viz/service/display_embedder/output_surface_provider.h"
 #include "components/viz/service/display_embedder/vsync_parameter_listener.h"
 #include "components/viz/service/frame_sinks/external_begin_frame_source_mojo.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
@@ -28,16 +29,31 @@
 RootCompositorFrameSinkImpl::Create(
     mojom::RootCompositorFrameSinkParamsPtr params,
     FrameSinkManagerImpl* frame_sink_manager,
-    DisplayProvider* display_provider) {
-  // First create some sort of a BeginFrameSource, depending on the platform
-  // and |params|.
+    OutputSurfaceProvider* output_surface_provider,
+    uint32_t restart_id,
+    bool run_all_compositor_stages_before_draw) {
+  // First create an output surface.
+  mojom::DisplayClientPtr display_client =
+      mojom::DisplayClientPtr(std::move(params->display_client));
+  auto output_surface = output_surface_provider->CreateOutputSurface(
+      params->widget, params->gpu_compositing, display_client.get(),
+      params->renderer_settings);
+
+  // Creating output surface failed. The host can send a new request, possibly
+  // with a different compositing mode.
+  if (!output_surface)
+    return nullptr;
+
+  // If we need swap size notifications tell the output surface now.
+  output_surface->SetNeedsSwapSizeNotifications(
+      params->send_swap_size_notifications);
+
+  // Create some sort of a BeginFrameSource, depending on the platform and
+  // |params|.
   std::unique_ptr<ExternalBeginFrameSource> external_begin_frame_source;
   std::unique_ptr<SyntheticBeginFrameSource> synthetic_begin_frame_source;
   ExternalBeginFrameSourceMojo* external_begin_frame_source_mojo = nullptr;
 
-  // BeginFrameSource::source_id component that changes on process restart.
-  uint32_t restart_id = display_provider->GetRestartId();
-
   if (params->external_begin_frame_controller.is_pending() &&
       params->external_begin_frame_controller_client) {
     auto owned_external_begin_frame_source_mojo =
@@ -71,45 +87,50 @@
 #endif
   }
 
-  // |impl| isn't ready to use until after a display has been created for it and
-  // Initialize() has been called.
+  BeginFrameSource* begin_frame_source = synthetic_begin_frame_source.get();
+  if (external_begin_frame_source)
+    begin_frame_source = external_begin_frame_source.get();
+  DCHECK(begin_frame_source);
+
+  auto task_runner = base::ThreadTaskRunnerHandle::Get();
+
+  int max_frames_pending = output_surface->capabilities().max_frames_pending;
+  DCHECK_GT(max_frames_pending, 0);
+
+  auto scheduler = std::make_unique<DisplayScheduler>(
+      begin_frame_source, task_runner.get(), max_frames_pending,
+      run_all_compositor_stages_before_draw);
+
+  auto* output_surface_ptr = output_surface.get();
+
+  auto display = std::make_unique<Display>(
+      frame_sink_manager->shared_bitmap_manager(), params->renderer_settings,
+      params->frame_sink_id, std::move(output_surface), std::move(scheduler),
+      std::move(task_runner));
+
+  if (external_begin_frame_source_mojo)
+    external_begin_frame_source_mojo->SetDisplay(display.get());
+
+  // base::WrapUnique instead of std::make_unique because the ctor is private.
   auto impl = base::WrapUnique(new RootCompositorFrameSinkImpl(
       frame_sink_manager, params->frame_sink_id,
       std::move(params->compositor_frame_sink),
       mojom::CompositorFrameSinkClientPtr(
           std::move(params->compositor_frame_sink_client)),
-      std::move(params->display_private),
-      mojom::DisplayClientPtr(std::move(params->display_client)),
+      std::move(params->display_private), std::move(display_client),
       std::move(synthetic_begin_frame_source),
-      std::move(external_begin_frame_source)));
+      std::move(external_begin_frame_source), std::move(display)));
 
-  UpdateVSyncParametersCallback update_vsync_callback;
-  if (impl->synthetic_begin_frame_source_) {
-    // |impl| owns the display and will outlive it so unretained is safe.
-    update_vsync_callback = base::BindRepeating(
-        &RootCompositorFrameSinkImpl::SetDisplayVSyncParameters,
-        base::Unretained(impl.get()));
-  }
   // TODO(kylechar): For the cases where we expect browser to providing vsync
   // parameter updates over mojo we shouldn't create |update_vsync_callback|.
   // I think this is always the case on mac.
-
-  auto display = display_provider->CreateDisplay(
-      params->frame_sink_id, params->widget, params->gpu_compositing,
-      impl->display_client_.get(), impl->begin_frame_source(),
-      std::move(update_vsync_callback), params->renderer_settings,
-      params->send_swap_size_notifications);
-
-  // Creating a display failed. Destroy |impl| which will close the message
-  // pipes. The host can send a new request, potential with a different
-  // compositing mode.
-  if (!display)
-    return nullptr;
-
-  if (external_begin_frame_source_mojo)
-    external_begin_frame_source_mojo->SetDisplay(display.get());
-
-  impl->Initialize(std::move(display));
+  if (impl->synthetic_begin_frame_source_) {
+    // |impl| owns and outlives display, and display owns the output surface so
+    // unretained is safe.
+    output_surface_ptr->SetUpdateVSyncParametersCallback(base::BindRepeating(
+        &RootCompositorFrameSinkImpl::SetDisplayVSyncParameters,
+        base::Unretained(impl.get())));
+  }
 
   return impl;
 }
@@ -260,7 +281,8 @@
     mojom::DisplayPrivateAssociatedRequest display_request,
     mojom::DisplayClientPtr display_client,
     std::unique_ptr<SyntheticBeginFrameSource> synthetic_begin_frame_source,
-    std::unique_ptr<ExternalBeginFrameSource> external_begin_frame_source)
+    std::unique_ptr<ExternalBeginFrameSource> external_begin_frame_source,
+    std::unique_ptr<Display> display)
     : compositor_frame_sink_client_(std::move(frame_sink_client)),
       compositor_frame_sink_binding_(this, std::move(frame_sink_request)),
       display_client_(std::move(display_client)),
@@ -272,17 +294,12 @@
           /*is_root=*/true,
           /*needs_sync_points=*/true)),
       synthetic_begin_frame_source_(std::move(synthetic_begin_frame_source)),
-      external_begin_frame_source_(std::move(external_begin_frame_source)) {
+      external_begin_frame_source_(std::move(external_begin_frame_source)),
+      display_(std::move(display)) {
+  DCHECK(display_);
   DCHECK(begin_frame_source());
-
   frame_sink_manager->RegisterBeginFrameSource(begin_frame_source(),
                                                support_->frame_sink_id());
-}
-
-void RootCompositorFrameSinkImpl::Initialize(std::unique_ptr<Display> display) {
-  display_ = std::move(display);
-  DCHECK(display_);
-
   display_->Initialize(this, support_->frame_sink_manager()->surface_manager());
   support_->SetUpHitTest(display_.get());
 }
diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
index cfdd1c3c..a1d7c9d6 100644
--- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
+++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
@@ -21,7 +21,7 @@
 namespace viz {
 
 class Display;
-class DisplayProvider;
+class OutputSurfaceProvider;
 class ExternalBeginFrameSource;
 class FrameSinkManagerImpl;
 class SyntheticBeginFrameSource;
@@ -37,7 +37,9 @@
   static std::unique_ptr<RootCompositorFrameSinkImpl> Create(
       mojom::RootCompositorFrameSinkParamsPtr params,
       FrameSinkManagerImpl* frame_sink_manager,
-      DisplayProvider* display_provider);
+      OutputSurfaceProvider* output_surface_provider,
+      uint32_t restart_id,
+      bool run_all_compositor_stages_before_draw);
 
   ~RootCompositorFrameSinkImpl() override;
 
@@ -89,10 +91,8 @@
       mojom::DisplayPrivateAssociatedRequest display_request,
       mojom::DisplayClientPtr display_client,
       std::unique_ptr<SyntheticBeginFrameSource> synthetic_begin_frame_source,
-      std::unique_ptr<ExternalBeginFrameSource> external_begin_frame_source);
-
-  // Initializes this object so it will start producing frames with |display|.
-  void Initialize(std::unique_ptr<Display> display);
+      std::unique_ptr<ExternalBeginFrameSource> external_begin_frame_source,
+      std::unique_ptr<Display> display);
 
   // DisplayClient:
   void DisplayOutputSurfaceLost() override;
@@ -121,11 +121,14 @@
   // change for the lifetime of RootCompositorFrameSinkImpl.
   const std::unique_ptr<CompositorFrameSinkSupport> support_;
 
-  // RootCompositorFrameSinkImpl holds a Display and its BeginFrameSource if
-  // it was created with a non-null gpu::SurfaceHandle.
+  // RootCompositorFrameSinkImpl holds a Display and a BeginFrameSource if it
+  // was created with a non-null gpu::SurfaceHandle. The source can either be a
+  // |synthetic_begin_frame_source_| or an |external_begin_frame_source_|.
   std::unique_ptr<SyntheticBeginFrameSource> synthetic_begin_frame_source_;
   // If non-null, |synthetic_begin_frame_source_| will not exist.
   std::unique_ptr<ExternalBeginFrameSource> external_begin_frame_source_;
+  // Should be destroyed before begin frame sources since it can issue callbacks
+  // to the BFS.
   std::unique_ptr<Display> display_;
 
   DISALLOW_COPY_AND_ASSIGN(RootCompositorFrameSinkImpl);
diff --git a/components/viz/service/main/viz_compositor_thread_runner.cc b/components/viz/service/main/viz_compositor_thread_runner.cc
index bc76bac..32621fa 100644
--- a/components/viz/service/main/viz_compositor_thread_runner.cc
+++ b/components/viz/service/main/viz_compositor_thread_runner.cc
@@ -14,8 +14,8 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "components/viz/common/switches.h"
-#include "components/viz/service/display_embedder/gpu_display_provider.h"
 #include "components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h"
+#include "components/viz/service/display_embedder/output_surface_provider_impl.h"
 #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/gl/gpu_service_impl.h"
@@ -142,31 +142,36 @@
 
   if (task_executor) {
     DCHECK(gpu_service);
-    // Create DisplayProvider usable for GPU + software compositing.
+    // Create OutputSurfaceProvider usable for GPU + software compositing.
     auto gpu_memory_buffer_manager =
         std::make_unique<InProcessGpuMemoryBufferManager>(
             gpu_service->gpu_memory_buffer_factory(),
             gpu_service->sync_point_manager());
     auto* image_factory = gpu_service->gpu_image_factory();
-    display_provider_ = std::make_unique<GpuDisplayProvider>(
-        params->restart_id, gpu_service, task_executor, gpu_service,
-        std::move(gpu_memory_buffer_manager), image_factory,
-        server_shared_bitmap_manager_.get(), headless,
-        run_all_compositor_stages_before_draw);
+    output_surface_provider_ = std::make_unique<OutputSurfaceProviderImpl>(
+        gpu_service, task_executor, gpu_service,
+        std::move(gpu_memory_buffer_manager), image_factory, headless);
   } else {
-    // Create DisplayProvider usable for software compositing only.
-    display_provider_ = std::make_unique<GpuDisplayProvider>(
-        params->restart_id, server_shared_bitmap_manager_.get(), headless,
-        run_all_compositor_stages_before_draw);
+    // Create OutputSurfaceProvider usable for software compositing only.
+    output_surface_provider_ =
+        std::make_unique<OutputSurfaceProviderImpl>(headless);
   }
 
   // Create FrameSinkManagerImpl.
-  base::Optional<uint32_t> activation_deadline_in_frames;
-  if (params->use_activation_deadline)
-    activation_deadline_in_frames = params->activation_deadline_in_frames;
-  frame_sink_manager_ = std::make_unique<FrameSinkManagerImpl>(
-      server_shared_bitmap_manager_.get(), activation_deadline_in_frames,
-      display_provider_.get());
+  FrameSinkManagerImpl::InitParams init_params;
+  init_params.shared_bitmap_manager = server_shared_bitmap_manager_.get();
+  // Set default activation deadline to infinite if client doesn't provide one.
+  init_params.activation_deadline_in_frames = base::nullopt;
+  if (params->use_activation_deadline) {
+    init_params.activation_deadline_in_frames =
+        params->activation_deadline_in_frames;
+  }
+  init_params.output_surface_provider = output_surface_provider_.get();
+  init_params.restart_id = params->restart_id;
+  init_params.run_all_compositor_stages_before_draw =
+      run_all_compositor_stages_before_draw;
+
+  frame_sink_manager_ = std::make_unique<FrameSinkManagerImpl>(init_params);
   frame_sink_manager_->BindAndSetClient(
       std::move(params->frame_sink_manager), nullptr,
       mojom::FrameSinkManagerClientPtr(
@@ -228,7 +233,7 @@
   devtools_server_.reset();
 #endif
   frame_sink_manager_.reset();
-  display_provider_.reset();
+  output_surface_provider_.reset();
   server_shared_bitmap_manager_.reset();
 }
 
diff --git a/components/viz/service/main/viz_compositor_thread_runner.h b/components/viz/service/main/viz_compositor_thread_runner.h
index badfbd5..8f2d2fd 100644
--- a/components/viz/service/main/viz_compositor_thread_runner.h
+++ b/components/viz/service/main/viz_compositor_thread_runner.h
@@ -31,7 +31,7 @@
 }  // namespace ui_devtools
 
 namespace viz {
-class DisplayProvider;
+class OutputSurfaceProvider;
 class FrameSinkManagerImpl;
 class GpuServiceImpl;
 class ServerSharedBitmapManager;
@@ -93,7 +93,7 @@
 
   // Start variables to be accessed only on |task_runner_|.
   std::unique_ptr<ServerSharedBitmapManager> server_shared_bitmap_manager_;
-  std::unique_ptr<DisplayProvider> display_provider_;
+  std::unique_ptr<OutputSurfaceProvider> output_surface_provider_;
   std::unique_ptr<FrameSinkManagerImpl> frame_sink_manager_;
 #if defined(USE_VIZ_DEVTOOLS)
   std::unique_ptr<ui_devtools::UiDevToolsServer> devtools_server_;
diff --git a/components/viz/test/BUILD.gn b/components/viz/test/BUILD.gn
index 90dc7d7..86618d6 100644
--- a/components/viz/test/BUILD.gn
+++ b/components/viz/test/BUILD.gn
@@ -19,6 +19,8 @@
     "fake_delay_based_time_source.h",
     "fake_display_client.cc",
     "fake_display_client.h",
+    "fake_external_begin_frame_controller_client.cc",
+    "fake_external_begin_frame_controller_client.h",
     "fake_external_begin_frame_source.cc",
     "fake_external_begin_frame_source.h",
     "fake_host_frame_sink_client.cc",
@@ -50,8 +52,6 @@
     "test_context_provider.h",
     "test_context_support.cc",
     "test_context_support.h",
-    "test_display_provider.cc",
-    "test_display_provider.h",
     "test_frame_sink_manager.cc",
     "test_frame_sink_manager.h",
     "test_gles2_interface.cc",
@@ -62,6 +62,8 @@
     "test_latest_local_surface_id_lookup_delegate.h",
     "test_layer_tree_frame_sink.cc",
     "test_layer_tree_frame_sink.h",
+    "test_output_surface_provider.cc",
+    "test_output_surface_provider.h",
     "test_shared_bitmap_manager.cc",
     "test_shared_bitmap_manager.h",
   ]
diff --git a/components/viz/test/fake_external_begin_frame_controller_client.cc b/components/viz/test/fake_external_begin_frame_controller_client.cc
new file mode 100644
index 0000000..9240709
--- /dev/null
+++ b/components/viz/test/fake_external_begin_frame_controller_client.cc
@@ -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.
+
+#include "components/viz/test/fake_external_begin_frame_controller_client.h"
+
+namespace viz {
+
+FakeExternalBeginFrameControllerClient::FakeExternalBeginFrameControllerClient()
+    : binding_(this) {}
+
+FakeExternalBeginFrameControllerClient::
+    ~FakeExternalBeginFrameControllerClient() = default;
+
+mojom::ExternalBeginFrameControllerClientPtr
+FakeExternalBeginFrameControllerClient::BindInterfacePtr() {
+  mojom::ExternalBeginFrameControllerClientPtr ptr;
+  binding_.Bind(mojo::MakeRequest(&ptr));
+  return ptr;
+}
+
+void FakeExternalBeginFrameControllerClient::OnNeedsBeginFrames(
+    bool needs_begin_frames) {}
+
+void FakeExternalBeginFrameControllerClient::OnDisplayDidFinishFrame(
+    const BeginFrameAck& ack) {}
+
+}  // namespace viz
diff --git a/components/viz/test/fake_external_begin_frame_controller_client.h b/components/viz/test/fake_external_begin_frame_controller_client.h
new file mode 100644
index 0000000..4d28eb33
--- /dev/null
+++ b/components/viz/test/fake_external_begin_frame_controller_client.h
@@ -0,0 +1,33 @@
+// 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_VIZ_TEST_FAKE_EXTERNAL_BEGIN_FRAME_CONTROLLER_CLIENT_H_
+#define COMPONENTS_VIZ_TEST_FAKE_EXTERNAL_BEGIN_FRAME_CONTROLLER_CLIENT_H_
+
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/viz/privileged/interfaces/compositing/external_begin_frame_controller.mojom.h"
+
+namespace viz {
+
+class FakeExternalBeginFrameControllerClient
+    : public mojom::ExternalBeginFrameControllerClient {
+ public:
+  FakeExternalBeginFrameControllerClient();
+  ~FakeExternalBeginFrameControllerClient() override;
+
+  mojom::ExternalBeginFrameControllerClientPtr BindInterfacePtr();
+
+ private:
+  // ExternalBeginFrameControllerClient implementation.
+  void OnNeedsBeginFrames(bool needs_begin_frames) override;
+  void OnDisplayDidFinishFrame(const BeginFrameAck& ack) override;
+
+  mojo::Binding<mojom::ExternalBeginFrameControllerClient> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeExternalBeginFrameControllerClient);
+};
+
+}  // namespace viz
+
+#endif  // COMPONENTS_VIZ_TEST_FAKE_EXTERNAL_BEGIN_FRAME_CONTROLLER_CLIENT_H_
diff --git a/components/viz/test/test_display_provider.cc b/components/viz/test/test_display_provider.cc
deleted file mode 100644
index ef3c1f2..0000000
--- a/components/viz/test/test_display_provider.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/viz/test/test_display_provider.h"
-
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/viz/common/frame_sinks/begin_frame_source.h"
-#include "components/viz/service/display/display_scheduler.h"
-#include "components/viz/service/display/software_output_device.h"
-#include "components/viz/test/fake_output_surface.h"
-
-namespace viz {
-
-TestDisplayProvider::TestDisplayProvider() = default;
-
-TestDisplayProvider::~TestDisplayProvider() = default;
-
-std::unique_ptr<Display> TestDisplayProvider::CreateDisplay(
-    const FrameSinkId& frame_sink_id,
-    gpu::SurfaceHandle surface_handle,
-    bool gpu_compositing,
-    mojom::DisplayClient* display_client,
-    BeginFrameSource* begin_frame_source,
-    UpdateVSyncParametersCallback update_vsync_callback,
-    const RendererSettings& renderer_settings,
-    bool send_swap_size_notifications) {
-  auto task_runner = base::ThreadTaskRunnerHandle::Get();
-  DCHECK(task_runner);
-
-  std::unique_ptr<OutputSurface> output_surface;
-  if (gpu_compositing) {
-    output_surface = FakeOutputSurface::Create3d();
-  } else {
-    output_surface = FakeOutputSurface::CreateSoftware(
-        std::make_unique<SoftwareOutputDevice>());
-  }
-
-  auto scheduler = std::make_unique<DisplayScheduler>(
-      begin_frame_source, task_runner.get(),
-      output_surface->capabilities().max_frames_pending);
-
-  return std::make_unique<Display>(&shared_bitmap_manager_, renderer_settings,
-                                   frame_sink_id, std::move(output_surface),
-                                   std::move(scheduler), task_runner);
-}
-
-uint32_t TestDisplayProvider::GetRestartId() const {
-  return BeginFrameSource::kNotRestartableId;
-}
-
-}  // namespace viz
diff --git a/components/viz/test/test_display_provider.h b/components/viz/test/test_display_provider.h
deleted file mode 100644
index 92a9e52..0000000
--- a/components/viz/test/test_display_provider.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_VIZ_TEST_TEST_DISPLAY_PROVIDER_H_
-#define COMPONENTS_VIZ_TEST_TEST_DISPLAY_PROVIDER_H_
-
-#include <memory>
-
-#include "components/viz/service/display/display.h"
-#include "components/viz/service/display_embedder/display_provider.h"
-#include "components/viz/test/test_shared_bitmap_manager.h"
-
-namespace viz {
-
-// Test implementation that creates a Display with a FakeOutputSurface.
-class TestDisplayProvider : public DisplayProvider {
- public:
-  TestDisplayProvider();
-  ~TestDisplayProvider() override;
-
-  // DisplayProvider implementation.
-  std::unique_ptr<Display> CreateDisplay(
-      const FrameSinkId& frame_sink_id,
-      gpu::SurfaceHandle surface_handle,
-      bool gpu_compositing,
-      mojom::DisplayClient* display_client,
-      BeginFrameSource* begin_frame_source,
-      UpdateVSyncParametersCallback update_vsync_callback,
-      const RendererSettings& renderer_settings,
-      bool send_swap_size_notifications) override;
-  uint32_t GetRestartId() const override;
-
- private:
-  TestSharedBitmapManager shared_bitmap_manager_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestDisplayProvider);
-};
-
-}  // namespace viz
-
-#endif  // COMPONENTS_VIZ_TEST_TEST_DISPLAY_PROVIDER_H_
diff --git a/components/viz/test/test_output_surface_provider.cc b/components/viz/test/test_output_surface_provider.cc
new file mode 100644
index 0000000..14e29254
--- /dev/null
+++ b/components/viz/test/test_output_surface_provider.cc
@@ -0,0 +1,29 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/test/test_output_surface_provider.h"
+
+#include "components/viz/service/display/software_output_device.h"
+#include "components/viz/test/fake_output_surface.h"
+
+namespace viz {
+
+TestOutputSurfaceProvider::TestOutputSurfaceProvider() = default;
+
+TestOutputSurfaceProvider::~TestOutputSurfaceProvider() = default;
+
+std::unique_ptr<OutputSurface> TestOutputSurfaceProvider::CreateOutputSurface(
+    gpu::SurfaceHandle surface_handle,
+    bool gpu_compositing,
+    mojom::DisplayClient* display_client,
+    const RendererSettings& renderer_settings) {
+  if (gpu_compositing) {
+    return FakeOutputSurface::Create3d();
+  } else {
+    return FakeOutputSurface::CreateSoftware(
+        std::make_unique<SoftwareOutputDevice>());
+  }
+}
+
+}  // namespace viz
diff --git a/components/viz/test/test_output_surface_provider.h b/components/viz/test/test_output_surface_provider.h
new file mode 100644
index 0000000..0fa3463
--- /dev/null
+++ b/components/viz/test/test_output_surface_provider.h
@@ -0,0 +1,33 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_TEST_TEST_OUTPUT_SURFACE_PROVIDER_H_
+#define COMPONENTS_VIZ_TEST_TEST_OUTPUT_SURFACE_PROVIDER_H_
+
+#include <memory>
+
+#include "components/viz/service/display_embedder/output_surface_provider.h"
+
+namespace viz {
+
+// Test implementation that creates a FakeOutputSurface.
+class TestOutputSurfaceProvider : public OutputSurfaceProvider {
+ public:
+  TestOutputSurfaceProvider();
+  ~TestOutputSurfaceProvider() override;
+
+  // OutputSurfaceProvider implementation.
+  std::unique_ptr<OutputSurface> CreateOutputSurface(
+      gpu::SurfaceHandle surface_handle,
+      bool gpu_compositing,
+      mojom::DisplayClient* display_client,
+      const RendererSettings& renderer_settings) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestOutputSurfaceProvider);
+};
+
+}  // namespace viz
+
+#endif  // COMPONENTS_VIZ_TEST_TEST_OUTPUT_SURFACE_PROVIDER_H_
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 5ec0665..dd96224 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1770,6 +1770,10 @@
     "service_worker/service_worker_version.h",
     "site_instance_impl.cc",
     "site_instance_impl.h",
+    "sms/sms_manager.cc",
+    "sms/sms_manager.h",
+    "sms/sms_provider.cc",
+    "sms/sms_provider.h",
     "speech/speech_recognition_dispatcher_host.cc",
     "speech/speech_recognition_dispatcher_host.h",
     "speech/speech_recognition_manager_impl.cc",
diff --git a/content/browser/DEPS b/content/browser/DEPS
index 0bc0b90..35cfbd4f 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -107,6 +107,7 @@
   "+third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h",
   "+third_party/blink/public/platform/modules/notifications/web_notification_constants.h",
   "+third_party/blink/public/platform/modules/service_worker/web_service_worker_error.h",
+  "+third_party/blink/public/platform/modules/sms/sms_manager.mojom.h",
   "+third_party/blink/public/public_buildflags.h",
   "+third_party/blink/public/web/web_ax_enums.h",
   "+third_party/blink/public/web/web_console_message.h",
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 329be01c..fe57a75 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -1234,6 +1234,13 @@
   // so this cannot happen any earlier than now.
   InitializeMojo();
 
+#if BUILDFLAG(ENABLE_MUS)
+  if (features::IsUsingWindowService()) {
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        switches::kEnableSurfaceSynchronization);
+  }
+#endif
+
   HistogramSynchronizer::GetInstance();
 
   // cc assumes a single client name for metrics in a process, which is
@@ -1296,9 +1303,12 @@
     } else {
       server_shared_bitmap_manager_ =
           std::make_unique<viz::ServerSharedBitmapManager>();
-      frame_sink_manager_impl_ = std::make_unique<viz::FrameSinkManagerImpl>(
-          server_shared_bitmap_manager_.get(),
-          switches::GetDeadlineToSynchronizeSurfaces());
+      viz::FrameSinkManagerImpl::InitParams params;
+      params.shared_bitmap_manager = server_shared_bitmap_manager_.get();
+      params.activation_deadline_in_frames =
+          switches::GetDeadlineToSynchronizeSurfaces();
+      frame_sink_manager_impl_ =
+          std::make_unique<viz::FrameSinkManagerImpl>(params);
 
       surface_utils::ConnectWithLocalFrameSinkManager(
           host_frame_sink_manager_.get(), frame_sink_manager_impl_.get(),
diff --git a/content/browser/loader/mojo_async_resource_handler_unittest.cc b/content/browser/loader/mojo_async_resource_handler_unittest.cc
index c1e20b65..00d38cd 100644
--- a/content/browser/loader/mojo_async_resource_handler_unittest.cc
+++ b/content/browser/loader/mojo_async_resource_handler_unittest.cc
@@ -1368,9 +1368,9 @@
     MockResourceLoader::Status result = mock_loader_->OnResponseStarted(
         base::MakeRefCounted<network::ResourceResponse>());
     EXPECT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, result);
-    std::unique_ptr<base::Value> request_state = request_->GetStateAsValue();
+    base::Value request_state = request_->GetStateAsValue();
     base::Value* delegate_blocked_by =
-        request_state->FindKey("delegate_blocked_by");
+        request_state.FindKey("delegate_blocked_by");
     EXPECT_TRUE(delegate_blocked_by);
     EXPECT_EQ("MojoAsyncResourceHandler", delegate_blocked_by->GetString());
   }
@@ -1381,9 +1381,9 @@
     handler_->ProceedWithResponse();
     mock_loader_->WaitUntilIdleOrCanceled();
     EXPECT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status());
-    std::unique_ptr<base::Value> request_state = request_->GetStateAsValue();
+    base::Value request_state = request_->GetStateAsValue();
     base::Value* delegate_blocked_by =
-        request_state->FindKey("delegate_blocked_by");
+        request_state.FindKey("delegate_blocked_by");
     EXPECT_FALSE(delegate_blocked_by);
   }
 }
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index dd8bdad9..abb4caa 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -3064,6 +3064,7 @@
     cc::switches::kSlowDownRasterScaleFactor,
     cc::switches::kBrowserControlsHideThreshold,
     cc::switches::kBrowserControlsShowThreshold,
+    switches::kEnableSurfaceSynchronization,
     switches::kRunAllCompositorStagesBeforeDraw,
     switches::kUseVizHitTestSurfaceLayer,
 
diff --git a/content/browser/renderer_host/render_widget_host_view_cocoa.mm b/content/browser/renderer_host/render_widget_host_view_cocoa.mm
index 03ee233..5f672831 100644
--- a/content/browser/renderer_host/render_widget_host_view_cocoa.mm
+++ b/content/browser/renderer_host/render_widget_host_view_cocoa.mm
@@ -1444,7 +1444,7 @@
     return [NSArray arrayWithObjects:root_element, nil];
   } else if ([attribute isEqualToString:NSAccessibilityParentAttribute] &&
              accessibilityParent_) {
-    return accessibilityParent_;
+    return NSAccessibilityUnignoredAncestor(accessibilityParent_);
   } else if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
     return NSAccessibilityScrollAreaRole;
   }
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
index 96caee2..856c8a1c 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -2262,12 +2262,19 @@
 // https://crbug.com/921109.
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
 TEST_F(RenderWidgetHostViewMacTest, AccessibilityParentTest) {
   NSView* view = rwhv_mac_->cocoa_view();
 
   // NSBox so it participates in the a11y hierarchy.
   base::scoped_nsobject<NSView> parent_view([[NSBox alloc] init]);
   base::scoped_nsobject<NSView> accessibility_parent([[NSView alloc] init]);
+  base::scoped_nsobject<NSWindow> window([[NSWindow alloc]
+      initWithContentRect:NSMakeRect(0, 0, 512, 512)
+                styleMask:NSWindowStyleMaskResizable | NSWindowStyleMaskTitled
+                  backing:NSBackingStoreBuffered
+                    defer:NO]);
+  [[window contentView] addSubview:accessibility_parent];
 
   [parent_view addSubview:view];
   EXPECT_NSEQ([view accessibilityAttributeValue:NSAccessibilityParentAttribute],
@@ -2275,7 +2282,9 @@
 
   rwhv_mac_->SetParentAccessibilityElement(accessibility_parent);
   EXPECT_NSEQ([view accessibilityAttributeValue:NSAccessibilityParentAttribute],
-              accessibility_parent);
+              NSAccessibilityUnignoredAncestor(accessibility_parent));
+  EXPECT_NE([view accessibilityAttributeValue:NSAccessibilityParentAttribute],
+            nil);
 
   rwhv_mac_->SetParentAccessibilityElement(nil);
   EXPECT_NSEQ([view accessibilityAttributeValue:NSAccessibilityParentAttribute],
diff --git a/content/browser/renderer_interface_binders.cc b/content/browser/renderer_interface_binders.cc
index 1bd1a3f..d689e26 100644
--- a/content/browser/renderer_interface_binders.cc
+++ b/content/browser/renderer_interface_binders.cc
@@ -184,6 +184,13 @@
             ->GetIdleManager()
             ->CreateService(std::move(request), origin);
       }));
+  parameterized_binder_registry_.AddInterface(base::BindRepeating(
+      [](blink::mojom::SmsManagerRequest request, RenderProcessHost* host,
+         const url::Origin& origin) {
+        static_cast<StoragePartitionImpl*>(host->GetStoragePartition())
+            ->GetSmsManager()
+            ->CreateService(std::move(request), origin);
+      }));
   parameterized_binder_registry_.AddInterface(
       base::Bind([](blink::mojom::NotificationServiceRequest request,
                     RenderProcessHost* host, const url::Origin& origin) {
diff --git a/content/browser/service_worker/service_worker_context_unittest.cc b/content/browser/service_worker/service_worker_context_unittest.cc
index 28e6427..1946997 100644
--- a/content/browser/service_worker/service_worker_context_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_unittest.cc
@@ -375,9 +375,6 @@
   auto version = base::MakeRefCounted<ServiceWorkerVersion>(
       registration.get(), script_url, blink::mojom::ScriptType::kClassic,
       2l /* dummy version id */, context()->AsWeakPtr());
-  version->set_fetch_handler_existence(
-      ServiceWorkerVersion::FetchHandlerExistence::EXISTS);
-  version->SetStatus(ServiceWorkerVersion::ACTIVATED);
 
   ServiceWorkerRemoteProviderEndpoint endpoint;
   base::WeakPtr<ServiceWorkerProviderHost> host =
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index 93db2cb5..27d80a29 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -8,7 +8,6 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "base/debug/alias.h"
 #include "base/guid.h"
 #include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
@@ -310,11 +309,6 @@
   return schemes.find(url_.scheme()) != schemes.end();
 }
 
-ServiceWorkerVersion* ServiceWorkerProviderHost::controller() const {
-  CheckControllerConsistency();
-  return controller_.get();
-}
-
 blink::mojom::ControllerServiceWorkerMode
 ServiceWorkerProviderHost::GetControllerMode() const {
   if (!controller_)
@@ -363,10 +357,10 @@
   if (controller_registration_ != registration)
     return;
 
-  DCHECK(controller_);
+  DCHECK(controller());
   ServiceWorkerVersion* active = controller_registration_->active_version();
   DCHECK(active);
-  DCHECK_NE(active, controller_.get());
+  DCHECK_NE(active, controller());
   DCHECK_EQ(active->status(), ServiceWorkerVersion::ACTIVATING);
   UpdateController(true /* notify_controllerchange */);
 }
@@ -849,6 +843,7 @@
   return true;
 }
 
+#if DCHECK_IS_ON()
 void ServiceWorkerProviderHost::CheckControllerConsistency() const {
   if (!controller_) {
     DCHECK(!controller_registration_);
@@ -857,23 +852,8 @@
   DCHECK(IsProviderForClient());
   DCHECK(controller_registration_);
   DCHECK_EQ(controller_->registration_id(), controller_registration_->id());
-  switch (controller_->status()) {
-    case ServiceWorkerVersion::NEW:
-    case ServiceWorkerVersion::INSTALLING:
-    case ServiceWorkerVersion::INSTALLED:
-    case ServiceWorkerVersion::REDUNDANT: {
-      ServiceWorkerVersion::Status status = controller_->status();
-      base::debug::Alias(&status);
-      CHECK(false) << "Controller service worker has a bad status: "
-                   << ServiceWorkerVersion::VersionStatusToString(status);
-      break;
-    }
-    case ServiceWorkerVersion::ACTIVATING:
-    case ServiceWorkerVersion::ACTIVATED:
-      // Valid status, controller is being activated.
-      break;
-  }
 }
+#endif
 
 void ServiceWorkerProviderHost::Register(
     const GURL& script_url,
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index 66216dcd..039a8f3 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -180,7 +180,13 @@
   blink::mojom::ControllerServiceWorkerMode GetControllerMode() const;
 
   // For service worker clients. Returns this client's controller.
-  ServiceWorkerVersion* controller() const;
+  ServiceWorkerVersion* controller() const {
+#if DCHECK_IS_ON()
+    CheckControllerConsistency();
+#endif  // DCHECK_IS_ON()
+
+    return controller_.get();
+  }
 
   ServiceWorkerRegistration* controller_registration() const {
 #if DCHECK_IS_ON()
@@ -508,9 +514,9 @@
   // controller has not yet been decided.
   bool IsControllerDecided() const;
 
-  // TODO(crbug.com/951571): Put this check function behind DCHECK_IS_ON() once
-  // we figured out the cause of invalid controller status.
+#if DCHECK_IS_ON()
   void CheckControllerConsistency() const;
+#endif  // DCHECK_IS_ON()
 
   // Implements blink::mojom::ServiceWorkerContainerHost.
   void Register(const GURL& script_url,
diff --git a/content/browser/service_worker/service_worker_registration_unittest.cc b/content/browser/service_worker/service_worker_registration_unittest.cc
index 3327ee3..028b022a0 100644
--- a/content/browser/service_worker/service_worker_registration_unittest.cc
+++ b/content/browser/service_worker/service_worker_registration_unittest.cc
@@ -1078,11 +1078,8 @@
   const GURL kScriptUrl("https://www.example.com/sw.js");
   scoped_refptr<ServiceWorkerRegistration> registration =
       CreateRegistration(kScope);
-
   scoped_refptr<ServiceWorkerVersion> version =
       CreateVersion(registration.get(), kScriptUrl);
-  version->SetStatus(ServiceWorkerVersion::ACTIVATED);
-
   ServiceWorkerRemoteProviderEndpoint remote_endpoint;
   base::WeakPtr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow(
       helper_->mock_render_process_id(), true /* is_parent_frame_secure */,
diff --git a/content/browser/service_worker/service_worker_storage_unittest.cc b/content/browser/service_worker/service_worker_storage_unittest.cc
index c9e1578..f2d3426 100644
--- a/content/browser/service_worker/service_worker_storage_unittest.cc
+++ b/content/browser/service_worker/service_worker_storage_unittest.cc
@@ -1465,7 +1465,6 @@
 TEST_F(ServiceWorkerResourceStorageTest, DeleteRegistration_ActiveVersion) {
   // Promote the worker to active and add a controllee.
   registration_->SetActiveVersion(registration_->waiting_version());
-  registration_->active_version()->SetStatus(ServiceWorkerVersion::ACTIVATED);
   storage()->UpdateToActiveState(registration_.get(), base::DoNothing());
   ServiceWorkerRemoteProviderEndpoint remote_endpoint;
   base::WeakPtr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow(
@@ -1513,7 +1512,6 @@
 TEST_F(ServiceWorkerResourceStorageDiskTest, CleanupOnRestart) {
   // Promote the worker to active and add a controllee.
   registration_->SetActiveVersion(registration_->waiting_version());
-  registration_->active_version()->SetStatus(ServiceWorkerVersion::ACTIVATED);
   registration_->SetWaitingVersion(nullptr);
   storage()->UpdateToActiveState(registration_.get(), base::DoNothing());
   ServiceWorkerRemoteProviderEndpoint remote_endpoint;
@@ -1674,7 +1672,6 @@
 TEST_F(ServiceWorkerResourceStorageTest, UpdateRegistration) {
   // Promote the worker to active worker and add a controllee.
   registration_->SetActiveVersion(registration_->waiting_version());
-  registration_->active_version()->SetStatus(ServiceWorkerVersion::ACTIVATED);
   storage()->UpdateToActiveState(registration_.get(), base::DoNothing());
   ServiceWorkerRemoteProviderEndpoint remote_endpoint;
   base::WeakPtr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow(
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index ac56f2c..ba36f0a 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -702,9 +702,6 @@
   const std::string& uuid = provider_host->client_uuid();
   CHECK(!provider_host->client_uuid().empty());
   DCHECK(!base::ContainsKey(controllee_map_, uuid));
-  // TODO(crbug.com/951571): Change to DCHECK once we figured out the cause of
-  // invalid controller status.
-  CHECK(status_ == ACTIVATING || status_ == ACTIVATED);
 
   controllee_map_[uuid] = provider_host;
   embedded_worker_->UpdateForegroundPriority();
diff --git a/content/browser/sms/OWNERS b/content/browser/sms/OWNERS
new file mode 100644
index 0000000..17e8f61e
--- /dev/null
+++ b/content/browser/sms/OWNERS
@@ -0,0 +1,4 @@
+jsbell@chromium.org
+reillyg@chromium.org
+
+ # COMPONENT: Blink>SMS
diff --git a/content/browser/sms/sms_manager.cc b/content/browser/sms/sms_manager.cc
new file mode 100644
index 0000000..a277ea7
--- /dev/null
+++ b/content/browser/sms/sms_manager.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 <utility>
+
+#include "content/browser/sms/sms_manager.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "content/public/browser/permission_controller.h"
+#include "content/public/browser/permission_type.h"
+
+namespace content {
+
+SmsManager::SmsManager()
+    : sms_provider_(std::make_unique<DefaultSmsProvider>()) {}
+
+SmsManager::~SmsManager() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void SmsManager::CreateService(blink::mojom::SmsManagerRequest request,
+                               const url::Origin& origin) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  bindings_.AddBinding(this, std::move(request));
+}
+
+void SmsManager::GetNextMessage(base::TimeDelta timeout,
+                                GetNextMessageCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (timeout <= base::TimeDelta::FromSeconds(0)) {
+    bindings_.ReportBadMessage("Invalid timeout.");
+    return;
+  }
+
+  sms_provider_->Retrieve(timeout, std::move(callback));
+}
+
+void SmsManager::SetSmsProviderForTest(
+    std::unique_ptr<SmsProvider> sms_provider) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  sms_provider_ = std::move(sms_provider);
+}
+
+}  // namespace content
diff --git a/content/browser/sms/sms_manager.h b/content/browser/sms/sms_manager.h
new file mode 100644
index 0000000..90635bf1
--- /dev/null
+++ b/content/browser/sms/sms_manager.h
@@ -0,0 +1,56 @@
+// 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 CONTENT_BROWSER_SMS_SMS_MANAGER_H_
+#define CONTENT_BROWSER_SMS_SMS_MANAGER_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "base/time/time.h"
+#include "content/browser/sms/sms_provider.h"
+#include "content/common/content_export.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "third_party/blink/public/mojom/sms/sms_manager.mojom.h"
+
+namespace url {
+class Origin;
+}
+
+namespace content {
+
+// The SmsManager is responsible for taking the incoming mojo calls from the
+// renderer process and dispatching them to the SmsProvider platform-specific
+// implementation.
+class CONTENT_EXPORT SmsManager : public blink::mojom::SmsManager {
+ public:
+  SmsManager();
+  ~SmsManager() override;
+
+  void CreateService(blink::mojom::SmsManagerRequest request,
+                     const url::Origin& origin);
+
+  // blink.mojom.SmsManager:
+  void GetNextMessage(base::TimeDelta timeout,
+                      GetNextMessageCallback callback) override;
+
+  // Testing helpers.
+  void SetSmsProviderForTest(std::unique_ptr<SmsProvider> sms_provider);
+
+ private:
+  std::unique_ptr<SmsProvider> sms_provider_;
+
+  // Registered clients.
+  mojo::BindingSet<blink::mojom::SmsManager> bindings_;
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(SmsManager);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_SMS_SMS_MANAGER_H_
diff --git a/content/browser/sms/sms_manager_unittest.cc b/content/browser/sms/sms_manager_unittest.cc
new file mode 100644
index 0000000..e9ea71a9
--- /dev/null
+++ b/content/browser/sms/sms_manager_unittest.cc
@@ -0,0 +1,116 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/sms/sms_manager.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "base/time/time.h"
+#include "content/public/common/service_manager_connection.h"
+#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_service_manager_context.h"
+#include "content/test/test_render_frame_host.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "services/service_manager/public/cpp/bind_source_info.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "sms_provider.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/sms/sms_manager.mojom.h"
+
+using blink::mojom::SmsManagerPtr;
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::NiceMock;
+
+namespace content {
+
+namespace {
+
+class MockSmsProvider : public SmsProvider {
+ public:
+  MockSmsProvider() = default;
+  ~MockSmsProvider() override = default;
+
+  void Retrieve(base::TimeDelta timeout, SmsCallback callback) {
+    DoRetrieve(timeout, &callback);
+  }
+
+  MOCK_METHOD2(DoRetrieve, void(base::TimeDelta, SmsCallback*));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockSmsProvider);
+};
+
+class SmsManagerTest : public RenderViewHostImplTestHarness {
+ protected:
+  SmsManagerTest() {}
+
+  ~SmsManagerTest() override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SmsManagerTest);
+};
+
+}  // namespace
+
+TEST_F(SmsManagerTest, AddMonitor) {
+  auto impl = std::make_unique<SmsManager>();
+  auto mock = std::make_unique<NiceMock<MockSmsProvider>>();
+  blink::mojom::SmsManagerPtr service_ptr;
+  GURL url("http://google.com");
+  impl->CreateService(mojo::MakeRequest(&service_ptr),
+                      url::Origin::Create(url));
+
+  base::RunLoop loop;
+
+  service_ptr.set_connection_error_handler(base::BindLambdaForTesting([&]() {
+    ADD_FAILURE() << "Unexpected connection error";
+
+    loop.Quit();
+  }));
+
+  EXPECT_CALL(*mock, DoRetrieve(base::TimeDelta::FromSeconds(10), _))
+      .WillOnce(
+          Invoke([](base::TimeDelta timeout,
+                    base::OnceCallback<void(const std::string&)>* callback) {
+            std::move(*callback).Run("hi");
+          }));
+
+  impl->SetSmsProviderForTest(std::move(mock));
+
+  service_ptr->GetNextMessage(
+      base::TimeDelta::FromSeconds(10),
+      base::BindLambdaForTesting([&](const std::string& sms) {
+        // The initial state of the status of the user is to be active.
+        EXPECT_EQ("hi", sms);
+        loop.Quit();
+      }));
+
+  loop.Run();
+}
+
+TEST_F(SmsManagerTest, InvalidArguments) {
+  auto impl = std::make_unique<SmsManager>();
+  auto mock = std::make_unique<NiceMock<MockSmsProvider>>();
+  impl->SetSmsProviderForTest(std::move(mock));
+  blink::mojom::SmsManagerPtr service_ptr;
+  GURL url("http://google.com");
+  impl->CreateService(mojo::MakeRequest(&service_ptr),
+                      url::Origin::Create(url));
+
+  service_ptr->GetNextMessage(base::TimeDelta::FromSeconds(-1),
+                              base::NullCallback());
+
+  mojo::test::BadMessageObserver bad_message_observer;
+  EXPECT_EQ("Invalid timeout.", bad_message_observer.WaitForBadMessage());
+}
+
+}  // namespace content
diff --git a/content/browser/sms/sms_provider.cc b/content/browser/sms/sms_provider.cc
new file mode 100644
index 0000000..ef09564
--- /dev/null
+++ b/content/browser/sms/sms_provider.cc
@@ -0,0 +1,18 @@
+// 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 <utility>
+
+#include "base/callback.h"
+#include "content/browser/sms/sms_provider.h"
+
+namespace content {
+
+void DefaultSmsProvider::Retrieve(base::TimeDelta timeout,
+                                  SmsCallback callback) {
+  // TODO(crbug.com/670299): implementation pending.
+  NOTIMPLEMENTED();
+}
+
+}  // namespace content
diff --git a/content/browser/sms/sms_provider.h b/content/browser/sms/sms_provider.h
new file mode 100644
index 0000000..8df482d9
--- /dev/null
+++ b/content/browser/sms/sms_provider.h
@@ -0,0 +1,42 @@
+// 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 CONTENT_BROWSER_SMS_SMS_PROVIDER_H_
+#define CONTENT_BROWSER_SMS_SMS_PROVIDER_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+
+namespace content {
+
+// This class wraps the platform-specific functions and allows tests to
+// inject custom providers.
+class SmsProvider {
+ public:
+  using SmsCallback = base::OnceCallback<void(const std::string&)>;
+
+  SmsProvider() {}
+  virtual ~SmsProvider() {}
+
+  // Listen to the next incoming SMS and call the callback when it is received.
+  // On timeout, call the callback with an empty message.
+  virtual void Retrieve(base::TimeDelta timeout, SmsCallback callback) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SmsProvider);
+};
+
+class DefaultSmsProvider : public SmsProvider {
+ public:
+  DefaultSmsProvider() = default;
+  ~DefaultSmsProvider() override = default;
+  void Retrieve(base::TimeDelta timeout, SmsCallback callback) override;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_SMS_SMS_PROVIDER_H_
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 3ab76283..64e0407d 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -679,6 +679,8 @@
   partition->service_worker_context_ = new ServiceWorkerContextWrapper(context);
   partition->service_worker_context_->set_storage_partition(partition.get());
 
+  partition->sms_manager_ = std::make_unique<SmsManager>();
+
   partition->appcache_service_ =
       base::MakeRefCounted<ChromeAppCacheService>(quota_manager_proxy.get());
 
@@ -869,6 +871,10 @@
   return service_worker_context_.get();
 }
 
+SmsManager* StoragePartitionImpl::GetSmsManager() {
+  return sms_manager_.get();
+}
+
 SharedWorkerServiceImpl* StoragePartitionImpl::GetSharedWorkerService() {
   return shared_worker_service_.get();
 }
diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h
index 84aa3aa2..6f16e26c 100644
--- a/content/browser/storage_partition_impl.h
+++ b/content/browser/storage_partition_impl.h
@@ -30,6 +30,7 @@
 #include "content/browser/payments/payment_app_context_impl.h"
 #include "content/browser/push_messaging/push_messaging_context.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/browser/sms/sms_manager.h"
 #include "content/browser/url_loader_factory_getter.h"
 #include "content/browser/worker_host/shared_worker_service_impl.h"
 #include "content/common/content_export.h"
@@ -104,6 +105,7 @@
   IndexedDBContextImpl* GetIndexedDBContext() override;
   CacheStorageContextImpl* GetCacheStorageContext() override;
   ServiceWorkerContextWrapper* GetServiceWorkerContext() override;
+  SmsManager* GetSmsManager();
   SharedWorkerServiceImpl* GetSharedWorkerService() override;
   GeneratedCodeCacheContext* GetGeneratedCodeCacheContext() override;
 #if !defined(OS_ANDROID)
@@ -323,6 +325,7 @@
   scoped_refptr<IndexedDBContextImpl> indexed_db_context_;
   scoped_refptr<CacheStorageContextImpl> cache_storage_context_;
   scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
+  std::unique_ptr<SmsManager> sms_manager_;
   std::unique_ptr<SharedWorkerServiceImpl> shared_worker_service_;
   scoped_refptr<PushMessagingContext> push_messaging_context_;
   scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy_;
diff --git a/content/browser/webui/web_ui_data_source_impl.cc b/content/browser/webui/web_ui_data_source_impl.cc
index 580c0ca..bc6b9f1 100644
--- a/content/browser/webui/web_ui_data_source_impl.cc
+++ b/content/browser/webui/web_ui_data_source_impl.cc
@@ -118,8 +118,7 @@
       frame_src_set_(false),
       deny_xframe_options_(true),
       add_load_time_data_defaults_(true),
-      replace_existing_source_(true),
-      use_gzip_(false) {}
+      replace_existing_source_(true) {}
 
 WebUIDataSourceImpl::~WebUIDataSourceImpl() {
 }
@@ -222,13 +221,14 @@
 }
 
 void WebUIDataSourceImpl::UseGzip() {
-  use_gzip_ = true;
+  // No-op since this call is unnecessary anymore.
+  // TODO(dpapad): Remove this and update all clients.
 }
 
 void WebUIDataSourceImpl::UseGzip(
     base::RepeatingCallback<bool(const std::string&)> is_gzipped_callback) {
-  UseGzip();
-  is_gzipped_callback_ = std::move(is_gzipped_callback);
+  // No-op since this call is unnecessary anymore.
+  // TODO(dpapad): Remove this and update all clients.
 }
 
 const ui::TemplateReplacements* WebUIDataSourceImpl::GetReplacements() const {
@@ -295,12 +295,7 @@
     return;
   }
 
-  int resource_id = default_resource_;
-  std::map<std::string, int>::iterator result;
-  // Remove the query string for named resource lookups.
-  result = path_to_idr_map_.find(CleanUpPath(path));
-  if (result != path_to_idr_map_.end())
-    resource_id = result->second;
+  int resource_id = PathToIdrOrDefault(CleanUpPath(path));
   DCHECK_NE(resource_id, -1);
   scoped_refptr<base::RefCountedMemory> response(
       GetContentClient()->GetDataResourceBytes(resource_id));
@@ -319,9 +314,6 @@
 }
 
 bool WebUIDataSourceImpl::IsGzipped(const std::string& path) const {
-  if (!use_gzip_)
-    return false;
-
   // Note: In the hypothetical case of requests handled by |filter_callback_|
   // that involve gzipped data, the callback itself is responsible for
   // ungzipping, and IsGzipped will return false for such cases.
@@ -330,12 +322,22 @@
     return false;
   }
 
-  // TODO(dbeam): does anybody care about the "dirty" path (i.e. stuff after ?).
-  const std::string clean_path = CleanUpPath(path);
-  if (!json_path_.empty() && clean_path == json_path_)
+  if (!json_path_.empty() && path == json_path_) {
     return false;
+  }
 
-  return is_gzipped_callback_.is_null() || is_gzipped_callback_.Run(clean_path);
+  std::string file_path = CleanUpPath(path);
+  int idr = PathToIdrOrDefault(file_path);
+  if (idr == -1) {
+    return false;
+  }
+
+  return GetContentClient()->IsDataResourceGzipped(idr);
+}
+
+int WebUIDataSourceImpl::PathToIdrOrDefault(const std::string& path) const {
+  auto it = path_to_idr_map_.find(path);
+  return it == path_to_idr_map_.end() ? default_resource_ : it->second;
 }
 
 }  // namespace content
diff --git a/content/browser/webui/web_ui_data_source_impl.h b/content/browser/webui/web_ui_data_source_impl.h
index 40278d6..28fc2ae7 100644
--- a/content/browser/webui/web_ui_data_source_impl.h
+++ b/content/browser/webui/web_ui_data_source_impl.h
@@ -81,7 +81,8 @@
   friend class WebUIDataSourceTest;
 
   FRIEND_TEST_ALL_PREFIXES(WebUIDataSourceTest, IsGzipped);
-  FRIEND_TEST_ALL_PREFIXES(WebUIDataSourceTest, IsGzippedWithCallback);
+  FRIEND_TEST_ALL_PREFIXES(WebUIDataSourceTest, IsGzippedNoDefaultResource);
+  FRIEND_TEST_ALL_PREFIXES(WebUIDataSourceTest, IsGzippedWithRequestFiltering);
 
   // Methods that match URLDataSource which are called by
   // InternalDataSource.
@@ -91,6 +92,8 @@
       const ResourceRequestInfo::WebContentsGetter& wc_getter,
       const URLDataSource::GotDataCallback& callback);
 
+  int PathToIdrOrDefault(const std::string& path) const;
+
   // Note: this must be called before StartDataRequest() to have an effect.
   void disable_load_time_data_defaults_for_testing() {
     add_load_time_data_defaults_ = false;
@@ -126,8 +129,6 @@
   bool deny_xframe_options_;
   bool add_load_time_data_defaults_;
   bool replace_existing_source_;
-  bool use_gzip_;
-  base::RepeatingCallback<bool(const std::string&)> is_gzipped_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(WebUIDataSourceImpl);
 };
diff --git a/content/browser/webui/web_ui_data_source_unittest.cc b/content/browser/webui/web_ui_data_source_unittest.cc
index 6c2f7dea..6b6fde8 100644
--- a/content/browser/webui/web_ui_data_source_unittest.cc
+++ b/content/browser/webui/web_ui_data_source_unittest.cc
@@ -24,7 +24,7 @@
 
 class TestClient : public TestContentClient {
  public:
-  TestClient() {}
+  TestClient() : is_gzipped_(false) {}
   ~TestClient() override {}
 
   base::string16 GetLocalizedString(int message_id) const override {
@@ -45,6 +45,16 @@
     }
     return bytes;
   }
+
+  bool IsDataResourceGzipped(int resource_id) const override {
+    return is_gzipped_;
+  }
+
+  // Sets the response for |IsDataResourceGzipped()|.
+  void SetIsDataResourceGzipped(bool is_gzipped) { is_gzipped_ = is_gzipped; }
+
+ private:
+  bool is_gzipped_;
 };
 
 }  // namespace
@@ -75,6 +85,7 @@
 
  protected:
   std::string request_path_;
+  TestClient client_;
 
  private:
   void SetUp() override {
@@ -88,7 +99,6 @@
 
   TestBrowserThreadBundle thread_bundle_;
   scoped_refptr<WebUIDataSourceImpl> source_;
-  TestClient client_;
 };
 
 void EmptyStringsCallback(scoped_refptr<base::RefCountedMemory> data) {
@@ -180,7 +190,8 @@
   request_path_ = std::string();
   source()->SetRequestFilter(
       base::BindRepeating([](const std::string& path) { return true; }),
-      base::Bind(&WebUIDataSourceTest::HandleRequest, base::Unretained(this)));
+      base::BindRepeating(&WebUIDataSourceTest::HandleRequest,
+                          base::Unretained(this)));
   source()->SetDefaultResource(kDummyDefaultResourceId);
   source()->AddResourcePath("foobar", kDummyResourceId);
   StartDataRequest(
@@ -217,32 +228,48 @@
 }
 
 TEST_F(WebUIDataSourceTest, IsGzipped) {
+  source()->SetJsonPath("strings.js");
+  source()->SetDefaultResource(kDummyDefaultResourceId);
+
+  // Test that WebUIDataSource delegates IsGzipped to the content client.
+  client_.SetIsDataResourceGzipped(false);
   EXPECT_FALSE(source()->IsGzipped("foobar"));
   EXPECT_FALSE(source()->IsGzipped(""));
-  source()->UseGzip();
+  // Test that |json_path_| is correctly reported as non-gzipped.
+  EXPECT_FALSE(source()->IsGzipped("strings.js"));
+
+  client_.SetIsDataResourceGzipped(true);
   EXPECT_TRUE(source()->IsGzipped("foobar"));
   EXPECT_TRUE(source()->IsGzipped(""));
+  // Test that |json_path_| is correctly reported as non-gzipped.
+  EXPECT_FALSE(source()->IsGzipped("strings.js"));
 }
 
-TEST_F(WebUIDataSourceTest, IsGzippedWithCallback) {
+TEST_F(WebUIDataSourceTest, IsGzippedNoDefaultResource) {
+  // Test that WebUIDataSource reports non existing resources as non-gzipped
+  // and does not trigger any CHECKs.
+  client_.SetIsDataResourceGzipped(false);
   EXPECT_FALSE(source()->IsGzipped("foobar"));
 
-  source()->AddResourcePath("foobar", kDummyResourceId);
+  client_.SetIsDataResourceGzipped(true);
+  EXPECT_FALSE(source()->IsGzipped("foobar"));
+}
+
+TEST_F(WebUIDataSourceTest, IsGzippedWithRequestFiltering) {
+  source()->SetRequestFilter(
+      base::BindRepeating(
+          [](const std::string& path) { return path == "json/special/path"; }),
+      base::BindRepeating(&WebUIDataSourceTest::HandleRequest,
+                          base::Unretained(this)));
   source()->SetDefaultResource(kDummyDefaultResourceId);
-  source()->SetJsonPath("strings.js");
-  source()->UseGzip(base::BindRepeating(
-      [](const std::string& path) { return path != "json/special/path"; }));
 
-  EXPECT_TRUE(source()->IsGzipped("foobar"));
-  EXPECT_TRUE(source()->IsGzipped("foobar?query"));
-
-  EXPECT_TRUE(source()->IsGzipped("unknown_path"));
-  EXPECT_TRUE(source()->IsGzipped("unknown_path?query"));
-
+  client_.SetIsDataResourceGzipped(false);
   EXPECT_FALSE(source()->IsGzipped("json/special/path"));
-  EXPECT_FALSE(source()->IsGzipped("json/special/path?query"));
-  EXPECT_FALSE(source()->IsGzipped("strings.js"));
-  EXPECT_FALSE(source()->IsGzipped("strings.js?query"));
+  EXPECT_FALSE(source()->IsGzipped("other/path"));
+
+  client_.SetIsDataResourceGzipped(true);
+  EXPECT_FALSE(source()->IsGzipped("json/special/path"));
+  EXPECT_TRUE(source()->IsGzipped("other/path"));
 }
 
 TEST_F(WebUIDataSourceTest, ShouldServeMimeTypeAsContentTypeHeader) {
diff --git a/content/content_resources.grd b/content/content_resources.grd
index e96e1875..b4168b2b 100644
--- a/content/content_resources.grd
+++ b/content/content_resources.grd
@@ -33,6 +33,7 @@
       <include name="IDR_NETWORK_ERROR_LISTING_HTML" file="browser/resources/net/network_errors_listing.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="BINDATA" />
       <include name="IDR_NETWORK_ERROR_LISTING_JS" file="browser/resources/net/network_errors_listing.js" flattenhtml="true" type="BINDATA" />
       <include name="IDR_NETWORK_ERROR_LISTING_CSS" file="browser/resources/net/network_errors_listing.css" flattenhtml="true" type="BINDATA" />
+      <include name="IDR_ORIGIN_MOJO_HTML" file="${root_gen_dir}/url/mojom/origin.mojom.html" use_base_dir="false" type="BINDATA" compress="gzip" />
       <include name="IDR_ORIGIN_MOJO_JS" file="${root_gen_dir}/url/mojom/origin.mojom-lite.js" use_base_dir="false" type="BINDATA" compress="gzip" />
       <include name="IDR_PROCESS_INTERNALS_HTML" file="browser/resources/process/process_internals.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="BINDATA" />
       <include name="IDR_PROCESS_INTERNALS_MOJO_JS" file="${root_gen_dir}/content/browser/process_internals/process_internals.mojom-lite.js" use_base_dir="false" type="BINDATA" compress="gzip" />
@@ -41,7 +42,9 @@
       <include name="IDR_SERVICE_WORKER_INTERNALS_HTML" file="browser/resources/service_worker/serviceworker_internals.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="BINDATA" />
       <include name="IDR_SERVICE_WORKER_INTERNALS_JS" file="browser/resources/service_worker/serviceworker_internals.js" flattenhtml="true" compress="gzip" type="BINDATA" />
       <include name="IDR_SERVICE_WORKER_INTERNALS_CSS" file="browser/resources/service_worker/serviceworker_internals.css" flattenhtml="true" compress="gzip" type="BINDATA" />
+      <include name="IDR_UNGUESSABLE_TOKEN_MOJO_HTML" file="${root_gen_dir}/mojo/public/mojom/base/unguessable_token.mojom.html" use_base_dir="false" type="BINDATA" compress="gzip" />
       <include name="IDR_UNGUESSABLE_TOKEN_MOJO_JS" file="${root_gen_dir}/mojo/public/mojom/base/unguessable_token.mojom-lite.js" use_base_dir="false" type="BINDATA" compress="gzip" />
+      <include name="IDR_URL_MOJO_HTML" file="${root_gen_dir}/url/mojom/url.mojom.html" use_base_dir="false" type="BINDATA" compress="gzip" />
       <include name="IDR_URL_MOJO_JS" file="${root_gen_dir}/url/mojom/url.mojom-lite.js" use_base_dir="false" type="BINDATA" compress="gzip" />
       <include name="IDR_WEBRTC_INTERNALS_HTML" file="browser/resources/media/webrtc_internals.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="BINDATA" />
       <include name="IDR_WEBRTC_INTERNALS_JS" file="browser/resources/media/webrtc_internals.js" flattenhtml="true" compress="gzip" type="BINDATA" />
diff --git a/content/public/app/content_browser_manifest.cc b/content/public/app/content_browser_manifest.cc
index afd9915..7d45150 100644
--- a/content/public/app/content_browser_manifest.cc
+++ b/content/public/app/content_browser_manifest.cc
@@ -176,7 +176,8 @@
                   "blink.mojom.PermissionService",
                   "blink.mojom.QuotaDispatcherHost",
                   "blink.mojom.SerialService", "blink.mojom.WebUsbService",
-                  "network.mojom.WebSocket", "payments.mojom.PaymentManager",
+                  "blink.mojom.SmsManager", "network.mojom.WebSocket",
+                  "payments.mojom.PaymentManager",
                   "shape_detection.mojom.BarcodeDetectionProvider",
                   "shape_detection.mojom.FaceDetectionProvider",
                   "shape_detection.mojom.TextDetection"})
@@ -226,6 +227,7 @@
                   "blink.mojom.QuotaDispatcherHost",
                   "blink.mojom.SerialService",
                   "blink.mojom.SharedWorkerConnector",
+                  "blink.mojom.SmsManager",
                   "blink.mojom.SpeechRecognizer",
                   "blink.mojom.TextSuggestionHost",
                   "blink.mojom.UnhandledTapNotifier",
diff --git a/content/public/common/content_client.cc b/content/public/common/content_client.cc
index 9ab2f239..b738513 100644
--- a/content/public/common/content_client.cc
+++ b/content/public/common/content_client.cc
@@ -92,6 +92,10 @@
   return nullptr;
 }
 
+bool ContentClient::IsDataResourceGzipped(int resource_id) const {
+  return false;
+}
+
 gfx::Image& ContentClient::GetNativeImageNamed(int resource_id) const {
   static base::NoDestructor<gfx::Image> kEmptyImage;
   return *kEmptyImage;
diff --git a/content/public/common/content_client.h b/content/public/common/content_client.h
index 394936e..83c2abb 100644
--- a/content/public/common/content_client.h
+++ b/content/public/common/content_client.h
@@ -168,6 +168,9 @@
   virtual base::RefCountedMemory* GetDataResourceBytes(
       int resource_id) const;
 
+  // Returns whether the contents of a resource are compressed (with gzip).
+  virtual bool IsDataResourceGzipped(int resource_id) const;
+
   // Returns a native image given its id.
   virtual gfx::Image& GetNativeImageNamed(int resource_id) const;
 
diff --git a/content/renderer/media/webrtc/webrtc_video_track_source.cc b/content/renderer/media/webrtc/webrtc_video_track_source.cc
index 7db108c0..121d5380 100644
--- a/content/renderer/media/webrtc/webrtc_video_track_source.cc
+++ b/content/renderer/media/webrtc/webrtc_video_track_source.cc
@@ -159,7 +159,7 @@
                                frame_adaptation_params.scale_to_height);
   // Soft-apply the new (combined) cropping and scaling.
   scoped_refptr<media::VideoFrame> video_frame =
-      media::VideoFrame::WrapVideoFrame(frame, frame->format(),
+      media::VideoFrame::WrapVideoFrame(*frame, frame->format(),
                                         cropped_visible_rect, adapted_size);
   if (!video_frame)
     return;
diff --git a/content/renderer/media_recorder/video_track_recorder.cc b/content/renderer/media_recorder/video_track_recorder.cc
index e486d48..423959a8 100644
--- a/content/renderer/media_recorder/video_track_recorder.cc
+++ b/content/renderer/media_recorder/video_track_recorder.cc
@@ -244,7 +244,7 @@
     wrapped_frame = media::WrapAsI420VideoFrame(video_frame);
   } else {
     wrapped_frame = media::VideoFrame::WrapVideoFrame(
-        video_frame, video_frame->format(), video_frame->visible_rect(),
+        *video_frame, video_frame->format(), video_frame->visible_rect(),
         video_frame->natural_size());
   }
   wrapped_frame->AddDestructionObserver(media::BindToCurrentLoop(
diff --git a/content/shell/common/shell_content_client.cc b/content/shell/common/shell_content_client.cc
index c713257..013978e 100644
--- a/content/shell/common/shell_content_client.cc
+++ b/content/shell/common/shell_content_client.cc
@@ -70,6 +70,10 @@
       resource_id);
 }
 
+bool ShellContentClient::IsDataResourceGzipped(int resource_id) const {
+  return ui::ResourceBundle::GetSharedInstance().IsGzipped(resource_id);
+}
+
 gfx::Image& ShellContentClient::GetNativeImageNamed(int resource_id) const {
   return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
       resource_id);
diff --git a/content/shell/common/shell_content_client.h b/content/shell/common/shell_content_client.h
index 3987340..f56224a5 100644
--- a/content/shell/common/shell_content_client.h
+++ b/content/shell/common/shell_content_client.h
@@ -25,6 +25,7 @@
       ui::ScaleFactor scale_factor) const override;
   base::RefCountedMemory* GetDataResourceBytes(
       int resource_id) const override;
+  bool IsDataResourceGzipped(int resource_id) const override;
   gfx::Image& GetNativeImageNamed(int resource_id) const override;
   base::DictionaryValue GetNetLogConstants() const override;
   blink::OriginTrialPolicy* GetOriginTrialPolicy() override;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 93a8a3b1..062209c9 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1697,6 +1697,7 @@
     "../browser/service_worker/service_worker_version_unittest.cc",
     "../browser/shareable_file_reference_unittest.cc",
     "../browser/site_instance_impl_unittest.cc",
+    "../browser/sms/sms_manager_unittest.cc",
     "../browser/speech/tts_controller_unittest.cc",
     "../browser/startup_task_runner_unittest.cc",
     "../browser/storage_partition_impl_map_unittest.cc",
diff --git a/content/test/data/android/webshare-apk.html b/content/test/data/android/webshare-apk.html
new file mode 100644
index 0000000..183ea755
--- /dev/null
+++ b/content/test/data/android/webshare-apk.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Web Share</title>
+        <script>
+            function initiate_share() {
+                if (navigator.share === undefined) {
+                    window.document.title = 'Fail: navigator.share === undefined';
+                    return;
+                }
+
+                const fileBits = ['contents'];
+                const fileName = 'blocked.apk';
+                const options = {type: 'text/plain'};
+                const data = {files: [new File(fileBits, fileName, options)]};
+                navigator.share(data).then(() => {
+                    window.document.title = 'Success';
+                }).catch(e => {
+                    window.document.title = 'Fail: ' + e;
+                });
+            }
+
+            window.addEventListener('load', () => {
+                window.addEventListener('click', initiate_share);
+            });
+        </script>
+    </head>
+    <body>
+    </body>
+</html>
diff --git a/content/test/data/android/webshare-dex.html b/content/test/data/android/webshare-dex.html
new file mode 100644
index 0000000..a10ac6c
--- /dev/null
+++ b/content/test/data/android/webshare-dex.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Web Share</title>
+        <script>
+            function initiate_share() {
+                if (navigator.share === undefined) {
+                    window.document.title = 'Fail: navigator.share === undefined';
+                    return;
+                }
+
+                const fileBits = ['contents'];
+                const fileName = 'blocked.dex';
+                const options = {type: 'text/plain'};
+                const data = {files: [new File(fileBits, fileName, options)]};
+                navigator.share(data).then(() => {
+                    window.document.title = 'Success';
+                }).catch(e => {
+                    window.document.title = 'Fail: ' + e;
+                });
+            }
+
+            window.addEventListener('load', () => {
+                window.addEventListener('click', initiate_share);
+            });
+        </script>
+    </head>
+    <body>
+    </body>
+</html>
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py b/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py
index 36fd0bac..c7cafbf 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_integration_test.py
@@ -89,7 +89,7 @@
         default='false')
     parser.add_option('--is-asan',
         help='Indicates whether currently running an ASAN build',
-        action='store_true')
+        action='store_true', default=False)
 
   @classmethod
   def GenerateGpuTests(cls, options):
diff --git a/device/bluetooth/bluetooth_adapter_winrt.cc b/device/bluetooth/bluetooth_adapter_winrt.cc
index 42cd63c7d..ba6a6bc8 100644
--- a/device/bluetooth/bluetooth_adapter_winrt.cc
+++ b/device/bluetooth/bluetooth_adapter_winrt.cc
@@ -27,8 +27,6 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/task/post_task.h"
-#include "base/task/task_traits.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/win/core_winrt_util.h"
 #include "base/win/post_async_results.h"
@@ -47,9 +45,6 @@
 namespace uwp {
 using ABI::Windows::Devices::Bluetooth::BluetoothAdapter;
 }  // namespace uwp
-using ABI::Windows::Devices::Bluetooth::IBluetoothAdapter;
-using ABI::Windows::Devices::Bluetooth::IBluetoothAdapterStatics;
-using ABI::Windows::Devices::Bluetooth::IID_IBluetoothAdapterStatics;
 using ABI::Windows::Devices::Bluetooth::Advertisement::
     BluetoothLEAdvertisementDataSection;
 using ABI::Windows::Devices::Bluetooth::Advertisement::
@@ -75,13 +70,13 @@
     IBluetoothLEAdvertisementWatcher;
 using ABI::Windows::Devices::Bluetooth::Advertisement::
     IBluetoothLEManufacturerData;
+using ABI::Windows::Devices::Bluetooth::IBluetoothAdapter;
+using ABI::Windows::Devices::Bluetooth::IBluetoothAdapterStatics;
 using ABI::Windows::Devices::Enumeration::DeviceInformation;
 using ABI::Windows::Devices::Enumeration::IDeviceInformation;
 using ABI::Windows::Devices::Enumeration::IDeviceInformationStatics;
 using ABI::Windows::Devices::Enumeration::IDeviceInformationUpdate;
 using ABI::Windows::Devices::Enumeration::IDeviceWatcher;
-using ABI::Windows::Devices::Enumeration::IID_IDeviceInformationStatics;
-using ABI::Windows::Devices::Radios::IID_IRadioStatics;
 using ABI::Windows::Devices::Radios::IRadio;
 using ABI::Windows::Devices::Radios::IRadioStatics;
 using ABI::Windows::Devices::Radios::Radio;
@@ -93,10 +88,10 @@
 using ABI::Windows::Devices::Radios::RadioState;
 using ABI::Windows::Devices::Radios::RadioState_Off;
 using ABI::Windows::Devices::Radios::RadioState_On;
-using ABI::Windows::Foundation::IAsyncOperation;
-using ABI::Windows::Foundation::IReference;
 using ABI::Windows::Foundation::Collections::IVector;
 using ABI::Windows::Foundation::Collections::IVectorView;
+using ABI::Windows::Foundation::IAsyncOperation;
+using ABI::Windows::Foundation::IReference;
 using ABI::Windows::Storage::Streams::IBuffer;
 using ABI::Windows::Storage::Streams::IDataReader;
 using ABI::Windows::Storage::Streams::IDataReaderStatics;
@@ -467,13 +462,6 @@
                                   ExtractManufacturerData(advertisement.Get()));
 }
 
-decltype(&::RoGetAgileReference) LoadGetAgileReference() {
-  auto funcptr = reinterpret_cast<decltype(&::RoGetAgileReference)>(
-      ::GetProcAddress(::GetModuleHandle(L"Ole32.dll"), "RoGetAgileReference"));
-  CHECK(funcptr);
-  return funcptr;
-}
-
 }  // namespace
 
 std::string BluetoothAdapterWinrt::GetAddress() const {
@@ -634,151 +622,12 @@
   }
 }
 
-BluetoothAdapterWinrt::StaticsInterfaces::StaticsInterfaces(
-    ComPtr<IAgileReference> adapter_statics_in,
-    ComPtr<IAgileReference> device_information_statics_in,
-    ComPtr<IAgileReference> radio_statics_in)
-    : adapter_statics(adapter_statics_in),
-      device_information_statics(device_information_statics_in),
-      radio_statics(radio_statics_in) {}
-
-BluetoothAdapterWinrt::StaticsInterfaces::StaticsInterfaces(
-    const StaticsInterfaces& copy_from) = default;
-
-BluetoothAdapterWinrt::StaticsInterfaces::StaticsInterfaces() = default;
-
-BluetoothAdapterWinrt::StaticsInterfaces::~StaticsInterfaces() {}
-
 void BluetoothAdapterWinrt::Init(InitCallback init_cb) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  // Some of the initialization work requires loading libraries and should not
-  // be run on the browser main thread.
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
-      base::BindOnce(&BluetoothAdapterWinrt::PerformSlowInitTasks),
-      base::BindOnce(&BluetoothAdapterWinrt::CompleteInit,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(init_cb)));
-}
-
-void BluetoothAdapterWinrt::InitForTests(
-    InitCallback init_cb,
-    ComPtr<IBluetoothAdapterStatics> bluetooth_adapter_statics,
-    ComPtr<IDeviceInformationStatics> device_information_statics,
-    ComPtr<IRadioStatics> radio_statics) {
-  if (!ResolveCoreWinRT()) {
-    CompleteInit(std::move(init_cb), StaticsInterfaces());
-  }
-
-  auto statics = PerformSlowInitTasks();
-  if (!bluetooth_adapter_statics) {
-    statics.adapter_statics->Resolve(IID_IBluetoothAdapterStatics,
-                                     &bluetooth_adapter_statics);
-  }
-  if (!device_information_statics) {
-    statics.device_information_statics->Resolve(IID_IDeviceInformationStatics,
-                                                &device_information_statics);
-  }
-  if (!radio_statics) {
-    statics.radio_statics->Resolve(IID_IRadioStatics, &radio_statics);
-  }
-
-  auto getAgileReferenceFunc = LoadGetAgileReference();
-
-  ComPtr<IAgileReference> radio_statics_agileref;
-  HRESULT hr =
-      getAgileReferenceFunc(AGILEREFERENCE_DEFAULT, IID_IRadioStatics,
-                            radio_statics.Get(), &radio_statics_agileref);
-  DCHECK(SUCCEEDED(hr));
-  ComPtr<IAgileReference> device_information_statics_agileref;
-  hr = getAgileReferenceFunc(
-      AGILEREFERENCE_DEFAULT, IID_IDeviceInformationStatics,
-      device_information_statics.Get(), &device_information_statics_agileref);
-  DCHECK(SUCCEEDED(hr));
-  ComPtr<IAgileReference> adapter_statics_agileref;
-  hr = getAgileReferenceFunc(
-      AGILEREFERENCE_DEFAULT, IID_IBluetoothAdapterStatics,
-      bluetooth_adapter_statics.Get(), &adapter_statics_agileref);
-  DCHECK(SUCCEEDED(hr));
-  CompleteInit(std::move(init_cb),
-               StaticsInterfaces(adapter_statics_agileref,
-                                 device_information_statics_agileref,
-                                 radio_statics_agileref));
-}
-
-// static
-BluetoothAdapterWinrt::StaticsInterfaces
-BluetoothAdapterWinrt::PerformSlowInitTasks() {
-  if (!ResolveCoreWinRT())
-    return BluetoothAdapterWinrt::StaticsInterfaces();
-
-  ComPtr<IBluetoothAdapterStatics> adapter_statics;
-  HRESULT hr = base::win::GetActivationFactory<
-      IBluetoothAdapterStatics,
-      RuntimeClass_Windows_Devices_Bluetooth_BluetoothAdapter>(
-      &adapter_statics);
-  if (FAILED(hr)) {
-    VLOG(2) << "GetBluetoothAdapterStaticsActivationFactory failed: "
-            << logging::SystemErrorCodeToString(hr);
-    return BluetoothAdapterWinrt::StaticsInterfaces();
-  }
-
-  ComPtr<IDeviceInformationStatics> device_information_statics;
-  hr = base::win::GetActivationFactory<
-      IDeviceInformationStatics,
-      RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>(
-      &device_information_statics);
-  if (FAILED(hr)) {
-    VLOG(2) << "GetDeviceInformationStaticsActivationFactory failed: "
-            << logging::SystemErrorCodeToString(hr);
-    return BluetoothAdapterWinrt::StaticsInterfaces();
-  }
-
-  ComPtr<IRadioStatics> radio_statics;
-  hr = base::win::GetActivationFactory<
-      IRadioStatics, RuntimeClass_Windows_Devices_Radios_Radio>(&radio_statics);
-  if (FAILED(hr)) {
-    VLOG(2) << "GetRadioStaticsActivationFactory failed: "
-            << logging::SystemErrorCodeToString(hr);
-    return BluetoothAdapterWinrt::StaticsInterfaces();
-  }
-
-  auto getAgileReferenceFunc = LoadGetAgileReference();
-
-  ComPtr<IAgileReference> radio_statics_agileref;
-  hr = getAgileReferenceFunc(AGILEREFERENCE_DEFAULT,
-                             ABI::Windows::Devices::Radios::IID_IRadioStatics,
-                             radio_statics.Get(), &radio_statics_agileref);
-  if (FAILED(hr))
-    return BluetoothAdapterWinrt::StaticsInterfaces();
-
-  ComPtr<IAgileReference> device_information_statics_agileref;
-  hr = getAgileReferenceFunc(
-      AGILEREFERENCE_DEFAULT,
-      ABI::Windows::Devices::Enumeration::IID_IDeviceInformationStatics,
-      device_information_statics.Get(), &device_information_statics_agileref);
-  if (FAILED(hr))
-    return BluetoothAdapterWinrt::StaticsInterfaces();
-
-  ComPtr<IAgileReference> adapter_statics_agileref;
-  hr = getAgileReferenceFunc(
-      AGILEREFERENCE_DEFAULT,
-      ABI::Windows::Devices::Bluetooth::IID_IBluetoothAdapterStatics,
-      adapter_statics.Get(), &adapter_statics_agileref);
-  if (FAILED(hr))
-    return BluetoothAdapterWinrt::StaticsInterfaces();
-
-  return BluetoothAdapterWinrt::StaticsInterfaces(
-      adapter_statics_agileref, device_information_statics_agileref,
-      radio_statics_agileref);
-}
-
-void BluetoothAdapterWinrt::CompleteInit(InitCallback init_cb,
-                                         StaticsInterfaces statics) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  // We are wrapping |init_cb| in a ScopedClosureRunner to ensure it gets run
-  // no matter how the function exits. Furthermore, we set |is_initialized_|
-  // to true if adapter is still active when the callback gets run.
+  // We are wrapping |init_cb| in a ScopedClosureRunner to ensure it gets run no
+  // matter how the function exits. Furthermore, we set |is_initialized_| to
+  // true if adapter is still active when the callback gets run.
   base::ScopedClosureRunner on_init(base::BindOnce(
       [](base::WeakPtr<BluetoothAdapterWinrt> adapter, InitCallback init_cb) {
         if (adapter)
@@ -787,22 +636,19 @@
       },
       weak_ptr_factory_.GetWeakPtr(), std::move(init_cb)));
 
-  if (!statics.adapter_statics || !statics.device_information_statics ||
-      !statics.radio_statics) {
+  if (!ResolveCoreWinRT())
+    return;
+
+  ComPtr<IBluetoothAdapterStatics> adapter_statics;
+  HRESULT hr = GetBluetoothAdapterStaticsActivationFactory(&adapter_statics);
+  if (FAILED(hr)) {
+    VLOG(2) << "GetBluetoothAdapterStaticsActivationFactory failed: "
+            << logging::SystemErrorCodeToString(hr);
     return;
   }
 
-  HRESULT hr = statics.adapter_statics->Resolve(IID_IBluetoothAdapterStatics,
-                                                &bluetooth_adapter_statics_);
-  DCHECK(SUCCEEDED(hr));
-  hr = statics.device_information_statics->Resolve(
-      IID_IDeviceInformationStatics, &device_information_statics_);
-  DCHECK(SUCCEEDED(hr));
-  hr = statics.radio_statics->Resolve(IID_IRadioStatics, &radio_statics_);
-  DCHECK(SUCCEEDED(hr));
-
   ComPtr<IAsyncOperation<uwp::BluetoothAdapter*>> get_default_adapter_op;
-  hr = bluetooth_adapter_statics_->GetDefaultAsync(&get_default_adapter_op);
+  hr = adapter_statics->GetDefaultAsync(&get_default_adapter_op);
   if (FAILED(hr)) {
     VLOG(2) << "BluetoothAdapter::GetDefaultAsync failed: "
             << logging::SystemErrorCodeToString(hr);
@@ -821,8 +667,8 @@
 }
 
 bool BluetoothAdapterWinrt::SetPoweredImpl(bool powered) {
-  // Due to an issue on WoW64 we might fail to obtain the radio in
-  // OnGetRadio(). This is why it can be null here.
+  // Due to an issue on WoW64 we might fail to obtain the radio in OnGetRadio().
+  // This is why it can be null here.
   if (!radio_)
     return false;
 
@@ -915,8 +761,8 @@
     VLOG(2) << "Getting the Watcher Status failed: "
             << logging::SystemErrorCodeToString(hr);
   } else if (watcher_status == BluetoothLEAdvertisementWatcherStatus_Aborted) {
-    VLOG(2) << "Starting Advertisement Watcher failed, it is in the Aborted "
-               "state.";
+    VLOG(2)
+        << "Starting Advertisement Watcher failed, it is in the Aborted state.";
     RemoveAdvertisementReceivedHandler();
     ui_task_runner_->PostTask(
         FROM_HERE,
@@ -978,6 +824,26 @@
   NOTIMPLEMENTED();
 }
 
+HRESULT BluetoothAdapterWinrt::GetBluetoothAdapterStaticsActivationFactory(
+    IBluetoothAdapterStatics** statics) const {
+  return base::win::GetActivationFactory<
+      IBluetoothAdapterStatics,
+      RuntimeClass_Windows_Devices_Bluetooth_BluetoothAdapter>(statics);
+}
+
+HRESULT BluetoothAdapterWinrt::GetDeviceInformationStaticsActivationFactory(
+    IDeviceInformationStatics** statics) const {
+  return base::win::GetActivationFactory<
+      IDeviceInformationStatics,
+      RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>(statics);
+}
+
+HRESULT BluetoothAdapterWinrt::GetRadioStaticsActivationFactory(
+    IRadioStatics** statics) const {
+  return base::win::GetActivationFactory<
+      IRadioStatics, RuntimeClass_Windows_Devices_Radios_Radio>(statics);
+}
+
 HRESULT
 BluetoothAdapterWinrt::ActivateBluetoothAdvertisementLEWatcherInstance(
     IBluetoothLEAdvertisementWatcher** instance) const {
@@ -1046,9 +912,18 @@
     return;
   }
 
+  ComPtr<IDeviceInformationStatics> device_information_statics;
+  hr =
+      GetDeviceInformationStaticsActivationFactory(&device_information_statics);
+  if (FAILED(hr)) {
+    VLOG(2) << "GetDeviceInformationStaticsActivationFactory failed: "
+            << logging::SystemErrorCodeToString(hr);
+    return;
+  }
+
   ComPtr<IAsyncOperation<DeviceInformation*>> create_from_id_op;
-  hr = device_information_statics_->CreateFromIdAsync(device_id,
-                                                      &create_from_id_op);
+  hr = device_information_statics->CreateFromIdAsync(device_id,
+                                                     &create_from_id_op);
   if (FAILED(hr)) {
     VLOG(2) << "CreateFromIdAsync failed: "
             << logging::SystemErrorCodeToString(hr);
@@ -1083,8 +958,16 @@
 
   name_ = base::win::ScopedHString(name).GetAsUTF8();
 
+  ComPtr<IRadioStatics> radio_statics;
+  hr = GetRadioStaticsActivationFactory(&radio_statics);
+  if (FAILED(hr)) {
+    VLOG(2) << "GetRadioStaticsActivationFactory failed: "
+            << logging::SystemErrorCodeToString(hr);
+    return;
+  }
+
   ComPtr<IAsyncOperation<RadioAccessStatus>> request_access_op;
-  hr = radio_statics_->RequestAccessAsync(&request_access_op);
+  hr = radio_statics->RequestAccessAsync(&request_access_op);
   if (FAILED(hr)) {
     VLOG(2) << "RequestAccessAsync failed: "
             << logging::SystemErrorCodeToString(hr);
@@ -1151,8 +1034,17 @@
 
   // Attempt to create a DeviceWatcher for powered radios, so that querying
   // the power state is still possible.
+  ComPtr<IDeviceInformationStatics> device_information_statics;
+  HRESULT hr =
+      GetDeviceInformationStaticsActivationFactory(&device_information_statics);
+  if (FAILED(hr)) {
+    VLOG(2) << "GetDeviceInformationStaticsActivationFactory failed: "
+            << logging::SystemErrorCodeToString(hr);
+    return;
+  }
+
   auto aqs_filter = base::win::ScopedHString::Create(kPoweredRadiosAqsFilter);
-  HRESULT hr = device_information_statics_->CreateWatcherAqsFilter(
+  hr = device_information_statics->CreateWatcherAqsFilter(
       aqs_filter.get(), &powered_radio_watcher_);
   if (FAILED(hr)) {
     VLOG(2) << "Creating Powered Radios Watcher failed: "
diff --git a/device/bluetooth/bluetooth_adapter_winrt.h b/device/bluetooth/bluetooth_adapter_winrt.h
index 22985a9..3daa169 100644
--- a/device/bluetooth/bluetooth_adapter_winrt.h
+++ b/device/bluetooth/bluetooth_adapter_winrt.h
@@ -79,17 +79,6 @@
   ~BluetoothAdapterWinrt() override;
 
   void Init(InitCallback init_cb);
-  // Allow tests to provide their own implementations of statics.
-  void InitForTests(
-      InitCallback init_cb,
-      Microsoft::WRL::ComPtr<
-          ABI::Windows::Devices::Bluetooth::IBluetoothAdapterStatics>
-          bluetooth_adapter_statics,
-      Microsoft::WRL::ComPtr<
-          ABI::Windows::Devices::Enumeration::IDeviceInformationStatics>
-          device_information_statics,
-      Microsoft::WRL::ComPtr<ABI::Windows::Devices::Radios::IRadioStatics>
-          radio_statics);
 
   // BluetoothAdapter:
   bool SetPoweredImpl(bool powered) override;
@@ -108,7 +97,18 @@
   void RemovePairingDelegateInternal(
       BluetoothDevice::PairingDelegate* pairing_delegate) override;
 
-  // Declared virtual so that it can be overridden by tests.
+  // These are declared virtual so that they can be overridden by tests.
+  virtual HRESULT GetBluetoothAdapterStaticsActivationFactory(
+      ABI::Windows::Devices::Bluetooth::IBluetoothAdapterStatics** statics)
+      const;
+
+  virtual HRESULT GetDeviceInformationStaticsActivationFactory(
+      ABI::Windows::Devices::Enumeration::IDeviceInformationStatics** statics)
+      const;
+
+  virtual HRESULT GetRadioStaticsActivationFactory(
+      ABI::Windows::Devices::Radios::IRadioStatics** statics) const;
+
   virtual HRESULT ActivateBluetoothAdvertisementLEWatcherInstance(
       ABI::Windows::Devices::Bluetooth::Advertisement::
           IBluetoothLEAdvertisementWatcher** instance) const;
@@ -120,24 +120,6 @@
       uint64_t raw_address);
 
  private:
-  struct StaticsInterfaces {
-    StaticsInterfaces(
-        Microsoft::WRL::ComPtr<IAgileReference>,   // IBluetoothStatics
-        Microsoft::WRL::ComPtr<IAgileReference>,   // IDeviceInformationStatics
-        Microsoft::WRL::ComPtr<IAgileReference>);  // IRadioStatics
-    StaticsInterfaces();
-    StaticsInterfaces(const StaticsInterfaces&);
-    ~StaticsInterfaces();
-
-    Microsoft::WRL::ComPtr<IAgileReference> adapter_statics;
-    Microsoft::WRL::ComPtr<IAgileReference> device_information_statics;
-    Microsoft::WRL::ComPtr<IAgileReference> radio_statics;
-  };
-
-  static StaticsInterfaces PerformSlowInitTasks();
-
-  void CompleteInit(InitCallback init_cb, StaticsInterfaces statics);
-
   void OnGetDefaultAdapter(
       base::ScopedClosureRunner on_init,
       Microsoft::WRL::ComPtr<
@@ -221,15 +203,6 @@
                              IBluetoothLEAdvertisementWatcher>
       ble_advertisement_watcher_;
 
-  Microsoft::WRL::ComPtr<
-      ABI::Windows::Devices::Bluetooth::IBluetoothAdapterStatics>
-      bluetooth_adapter_statics_;
-  Microsoft::WRL::ComPtr<
-      ABI::Windows::Devices::Enumeration::IDeviceInformationStatics>
-      device_information_statics_;
-  Microsoft::WRL::ComPtr<ABI::Windows::Devices::Radios::IRadioStatics>
-      radio_statics_;
-
   THREAD_CHECKER(thread_checker_);
 
   // Note: This should remain the last member so it'll be destroyed and
diff --git a/device/bluetooth/test/bluetooth_test_win.cc b/device/bluetooth/test/bluetooth_test_win.cc
index 5cf7bdf..93caee5 100644
--- a/device/bluetooth/test/bluetooth_test_win.cc
+++ b/device/bluetooth/test/bluetooth_test_win.cc
@@ -149,14 +149,7 @@
         device_information_(std::move(device_information)),
         watcher_(Make<FakeBluetoothLEAdvertisementWatcherWinrt>()),
         bluetooth_test_winrt_(bluetooth_test_winrt) {
-    ComPtr<IBluetoothAdapterStatics> bluetooth_adapter_statics;
-    Make<FakeBluetoothAdapterStaticsWinrt>(adapter_).CopyTo(
-        (IBluetoothAdapterStatics**)&bluetooth_adapter_statics);
-    ComPtr<IDeviceInformationStatics> device_information_statics;
-    Make<FakeDeviceInformationStaticsWinrt>(device_information_)
-        .CopyTo((IDeviceInformationStatics**)&device_information_statics);
-    InitForTests(std::move(init_cb), bluetooth_adapter_statics,
-                 device_information_statics, nullptr);
+    Init(std::move(init_cb));
   }
 
   FakeBluetoothLEAdvertisementWatcherWinrt* watcher() { return watcher_.Get(); }
@@ -164,15 +157,15 @@
  protected:
   ~TestBluetoothAdapterWinrt() override = default;
 
-  HRESULT GetTestBluetoothAdapterStaticsActivationFactory(
-      IBluetoothAdapterStatics** statics) const {
+  HRESULT GetBluetoothAdapterStaticsActivationFactory(
+      IBluetoothAdapterStatics** statics) const override {
     auto adapter_statics = Make<FakeBluetoothAdapterStaticsWinrt>(adapter_);
     return adapter_statics.CopyTo(statics);
   }
 
   HRESULT
-  GetTestDeviceInformationStaticsActivationFactory(
-      IDeviceInformationStatics** statics) const {
+  GetDeviceInformationStaticsActivationFactory(
+      IDeviceInformationStatics** statics) const override {
     auto device_information_statics =
         Make<FakeDeviceInformationStaticsWinrt>(device_information_);
     return device_information_statics.CopyTo(statics);
diff --git a/device/fido/ble/fido_ble_connection.cc b/device/fido/ble/fido_ble_connection.cc
index c707821c..7f19a3d 100644
--- a/device/fido/ble/fido_ble_connection.cc
+++ b/device/fido/ble/fido_ble_connection.cc
@@ -219,6 +219,7 @@
   }
 
   pending_connection_callback_ = std::move(callback);
+  FIDO_LOG(DEBUG) << "Creating a GATT connection...";
   device->CreateGattConnection(
       base::Bind(&FidoBleConnection::OnCreateGattConnection,
                  weak_factory_.GetWeakPtr()),
@@ -251,6 +252,7 @@
     return;
   }
 
+  FIDO_LOG(DEBUG) << "Read Control Point Length";
   // Work around legacy APIs. Only one of the callbacks to
   // ReadRemoteCharacteristic() gets invoked, but we don't know which one.
   auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
@@ -296,6 +298,7 @@
   }
 #endif  // defined(OS_MACOSX)
 
+  FIDO_LOG(DEBUG) << "Wrote Control Point.";
   auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
   control_point->WriteRemoteCharacteristic(
       data, base::Bind(OnWriteRemoteCharacteristic, copyable_callback),
@@ -446,10 +449,12 @@
 void FidoBleConnection::OnServiceRevisionWritten(bool success) {
   DCHECK(pending_connection_callback_);
   if (success) {
+    FIDO_LOG(DEBUG) << "Service Revision successfully written.";
     StartNotifySession();
     return;
   }
 
+  FIDO_LOG(ERROR) << "Failed to write Service Revision.";
   std::move(pending_connection_callback_).Run(false);
 }
 
diff --git a/device/fido/ble/fido_ble_transaction.cc b/device/fido/ble/fido_ble_transaction.cc
index bd26632..d7c111a 100644
--- a/device/fido/ble/fido_ble_transaction.cc
+++ b/device/fido/ble/fido_ble_transaction.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/device_event_log/device_event_log.h"
 #include "device/fido/ble/fido_ble_connection.h"
@@ -50,6 +51,8 @@
   fragment.Serialize(&buffer_);
   DCHECK(!has_pending_request_fragment_write_);
   has_pending_request_fragment_write_ = true;
+  FIDO_LOG(DEBUG) << "Writing request fragment: " +
+                         base::HexEncode(buffer_.data(), buffer_.size());
   // A weak pointer is required, since this call might time out. If that
   // happens, the current FidoBleTransaction could be destroyed.
   connection_->WriteControlPoint(
@@ -61,6 +64,7 @@
 }
 
 static void WriteCancel(FidoBleConnection* connection) {
+  FIDO_LOG(DEBUG) << "Writing control point 'Cancel'";
   connection->WriteControlPoint(
       {static_cast<uint8_t>(FidoBleDeviceCommand::kCancel), 0, 0},
       base::DoNothing());
diff --git a/device/fido/u2f_register_operation.cc b/device/fido/u2f_register_operation.cc
index 85ee37e..342287e 100644
--- a/device/fido/u2f_register_operation.cc
+++ b/device/fido/u2f_register_operation.cc
@@ -8,8 +8,10 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "components/apdu/apdu_response.h"
+#include "components/device_event_log/device_event_log.h"
 #include "device/fido/authenticator_make_credential_response.h"
 #include "device/fido/ctap_make_credential_request.h"
 #include "device/fido/device_response_converter.h"
@@ -138,6 +140,10 @@
 
   switch (result) {
     case apdu::ApduResponse::Status::SW_NO_ERROR: {
+      FIDO_LOG(DEBUG)
+          << "Received successful U2F register response from authenticator: "
+          << base::HexEncode(apdu_response->data().data(),
+                             apdu_response->data().size());
       auto response =
           AuthenticatorMakeCredentialResponse::CreateFromU2fRegisterResponse(
               device()->DeviceTransport(),
diff --git a/device/fido/u2f_sign_operation.cc b/device/fido/u2f_sign_operation.cc
index 05a250f..f687553 100644
--- a/device/fido/u2f_sign_operation.cc
+++ b/device/fido/u2f_sign_operation.cc
@@ -7,8 +7,10 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "components/apdu/apdu_response.h"
+#include "components/device_event_log/device_event_log.h"
 #include "device/fido/authenticator_get_assertion_response.h"
 #include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/device_response_converter.h"
@@ -90,6 +92,11 @@
             .Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
         return;
       }
+
+      FIDO_LOG(DEBUG)
+          << "Received successful U2F sign response from authenticator: "
+          << base::HexEncode(apdu_response->data().data(),
+                             apdu_response->data().size());
       std::move(callback())
           .Run(CtapDeviceResponseCode::kSuccess, std::move(sign_response));
       break;
diff --git a/device/usb/tools/usb_ids.py b/device/usb/tools/usb_ids.py
index 55c2f695..63117c7 100644
--- a/device/usb/tools/usb_ids.py
+++ b/device/usb/tools/usb_ids.py
@@ -6,13 +6,13 @@
 import optparse
 import re
 
-VENDOR_PATTERN = re.compile("^(?P<id>[0-9a-fA-F]{4})\s+(?P<name>.+)$")
-PRODUCT_PATTERN = re.compile("^\t(?P<id>[0-9a-fA-F]{4})\s+(?P<name>.+)$")
+VENDOR_PATTERN = re.compile(r"^(?P<id>[0-9a-fA-F]{4})\s+(?P<name>.+)$")
+PRODUCT_PATTERN = re.compile(r"^\t(?P<id>[0-9a-fA-F]{4})\s+(?P<name>.+)$")
 
 def EscapeName(name):
   name = name.replace("\\", "\\\\")
-  name = name.replace("\"", "\\\"")
-  name = name.replace("?", "\?")
+  name = name.replace('"', r'\"')
+  name = name.replace("?", r"\?")
   return name
 
 def ParseTable(input_path):
diff --git a/device/vr/BUILD.gn b/device/vr/BUILD.gn
index ddb8793..b64384c8 100644
--- a/device/vr/BUILD.gn
+++ b/device/vr/BUILD.gn
@@ -25,6 +25,8 @@
       "orientation/orientation_device.h",
       "orientation/orientation_device_provider.cc",
       "orientation/orientation_device_provider.h",
+      "util/copy_to_ustring.cc",
+      "util/copy_to_ustring.h",
       "util/fps_meter.cc",
       "util/fps_meter.h",
       "util/sample_queue.cc",
diff --git a/device/vr/oculus/oculus_gamepad_helper.cc b/device/vr/oculus/oculus_gamepad_helper.cc
index 03f37bdc..bcb30013 100644
--- a/device/vr/oculus/oculus_gamepad_helper.cc
+++ b/device/vr/oculus/oculus_gamepad_helper.cc
@@ -4,11 +4,15 @@
 
 #include "device/vr/oculus/oculus_gamepad_helper.h"
 
+#include <algorithm>
 #include <memory>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "device/gamepad/public/cpp/gamepads.h"
+#include "device/vr/util/copy_to_ustring.h"
 #include "device/vr/vr_device.h"
 #include "third_party/libovr/src/Include/OVR_CAPI.h"
 #include "ui/gfx/transform.h"
@@ -18,6 +22,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/953489): This is currently WebVR-only. If it is useful
+// for addressing this issue, give it a more meaningful name. Otherwise, remove
+// this enum when WebVR support is removed.
 enum GamepadIndex : unsigned int {
   kLeftTouchController = 0x0,
   kRightTouchController = 0x1,
@@ -181,6 +188,77 @@
   data->gamepads.push_back(std::move(remote));
 }
 
+class WebXROculusGamepadBuilder {
+ public:
+  explicit WebXROculusGamepadBuilder(ovrInputState state) : state_(state) {
+    gamepad_.connected = true;
+
+    // According to device::Gamepad comments, this is how to get the timestamp.
+    gamepad_.timestamp = base::TimeTicks::Now().since_origin().InMicroseconds();
+  }
+
+  void SetID(const base::string16& id) {
+    CopyToUString(id, gamepad_.id, base::size(gamepad_.id));
+  }
+
+  void SetMapping(const base::string16& mapping) {
+    CopyToUString(mapping, gamepad_.mapping, base::size(gamepad_.mapping));
+  }
+
+  void SetHand(GamepadHand hand) { gamepad_.hand = hand; }
+
+  void AddStandardButton(ovrButton id) {
+    bool pressed = (state_.Buttons & id) != 0;
+    bool touched = (state_.Touches & id) != 0;
+    double value = pressed ? 1.0 : 0.0;
+    AddButton(pressed, touched, value);
+  }
+
+  void AddTouchButton(ovrTouch id) {
+    bool touched = (state_.Touches & id) != 0;
+    AddButton(false, touched, 0.0f);
+  }
+
+  void AddTriggerButton(float value) {
+    value = ApplyTriggerDeadzone(value);
+    bool pressed = value != 0;
+    bool touched = pressed;
+    AddButton(pressed, touched, value);
+  }
+
+  void AddTouchTriggerButton(ovrTouch id, float value) {
+    value = ApplyTriggerDeadzone(value);
+    bool pressed = value != 0;
+    bool touched = (state_.Touches & id) != 0;
+    AddButton(pressed, touched, value);
+  }
+
+  // Used when xr-standard mapping requires an empty button in a reserved slot
+  // instead of just shifting all remaining buttons in the array down by 1
+  // index.
+  void AddEmptyButton() { AddButton(false, false, 0); }
+
+  void AddAxis(double value) {
+    DCHECK_LT(gamepad_.axes_length, Gamepad::kAxesLengthCap);
+    gamepad_.axes[gamepad_.axes_length++] = value;
+  }
+
+  Gamepad GetGamepad() { return gamepad_; }
+
+ private:
+  void AddButton(bool pressed, bool touched, double value) {
+    DCHECK_LT(gamepad_.buttons_length, Gamepad::kButtonsLengthCap);
+    gamepad_.buttons[gamepad_.buttons_length++] =
+        GamepadButton(pressed, touched, value);
+  }
+
+  ovrInputState state_;
+
+  Gamepad gamepad_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebXROculusGamepadBuilder);
+};
+
 }  // namespace
 
 mojom::XRGamepadDataPtr OculusGamepadHelper::GetGamepadData(
@@ -208,4 +286,64 @@
   return data;
 }
 
+// Order of buttons 1-4 is dictated by the xr-standard Gamepad mapping.
+// Buttons 5-7 are in order of decreasing importance.
+// 1) index trigger (primary trigger/button)
+// 2) thumb joystick button
+// 3) hand trigger (primary trigger/button)
+// 4) EMPTY (no secondary joystick/touchpad exists)
+// 5) A or X
+// 6) B or Y
+// 7) thumbrest touch sensor
+base::Optional<Gamepad> OculusGamepadHelper::CreateGamepad(ovrSession session,
+                                                           ovrHandType hand) {
+  ovrInputState input_touch;
+  bool have_touch = OVR_SUCCESS(
+      ovr_GetInputState(session, ovrControllerType_Touch, &input_touch));
+  if (!have_touch) {
+    return base::nullopt;
+  }
+
+  WebXROculusGamepadBuilder touch(input_touch);
+
+  // TODO(https://crbug.com/942201): Get correct ID string once WebXR spec issue
+  // #550 (https://github.com/immersive-web/webxr/issues/550) is resolved.
+  touch.SetID(base::UTF8ToUTF16("unknown"));
+
+  touch.SetMapping(base::UTF8ToUTF16("xr-standard"));
+
+  touch.AddAxis(input_touch.Thumbstick[hand].x);
+  touch.AddAxis(-input_touch.Thumbstick[hand].y);
+
+  switch (hand) {
+    case ovrHand_Left:
+      touch.SetHand(GamepadHand::kLeft);
+      touch.AddTouchTriggerButton(ovrTouch_LIndexTrigger,
+                                  input_touch.IndexTrigger[hand]);
+      touch.AddStandardButton(ovrButton_LThumb);
+      touch.AddTriggerButton(input_touch.HandTrigger[hand]);
+      touch.AddEmptyButton();
+      touch.AddStandardButton(ovrButton_X);
+      touch.AddStandardButton(ovrButton_Y);
+      touch.AddTouchButton(ovrTouch_LThumbRest);
+      break;
+    case ovrHand_Right:
+      touch.SetHand(GamepadHand::kRight);
+      touch.AddTouchTriggerButton(ovrTouch_RIndexTrigger,
+                                  input_touch.IndexTrigger[hand]);
+      touch.AddStandardButton(ovrButton_RThumb);
+      touch.AddTriggerButton(input_touch.HandTrigger[hand]);
+      touch.AddEmptyButton();
+      touch.AddStandardButton(ovrButton_A);
+      touch.AddStandardButton(ovrButton_B);
+      touch.AddTouchButton(ovrTouch_RThumbRest);
+      break;
+    default:
+      DLOG(WARNING) << "Unsupported hand configuration.";
+      return base::nullopt;
+  }
+
+  return touch.GetGamepad();
+}
+
 }  // namespace device
diff --git a/device/vr/oculus/oculus_gamepad_helper.h b/device/vr/oculus/oculus_gamepad_helper.h
index 0e4f690..dd6dd14 100644
--- a/device/vr/oculus/oculus_gamepad_helper.h
+++ b/device/vr/oculus/oculus_gamepad_helper.h
@@ -13,6 +13,8 @@
 class OculusGamepadHelper {
  public:
   static mojom::XRGamepadDataPtr GetGamepadData(ovrSession session);
+  static base::Optional<Gamepad> CreateGamepad(ovrSession session,
+                                               ovrHandType hand);
 };
 
 }  // namespace device
diff --git a/device/vr/oculus/oculus_render_loop.cc b/device/vr/oculus/oculus_render_loop.cc
index 577b030..1d5ac27 100644
--- a/device/vr/oculus/oculus_render_loop.cc
+++ b/device/vr/oculus/oculus_render_loop.cc
@@ -302,6 +302,8 @@
         state->primary_input_clicked = true;
       }
 
+      // TODO(https://crbug.com/956190): Expose remote as a gamepad to WebXR.
+
       primary_input_pressed[ovrControllerType_Remote] =
           state->primary_input_pressed;
 
@@ -369,6 +371,8 @@
 
   state->description = std::move(desc);
 
+  state->gamepad = OculusGamepadHelper::CreateGamepad(session_, hand);
+
   return state;
 }
 
diff --git a/device/vr/util/copy_to_ustring.cc b/device/vr/util/copy_to_ustring.cc
new file mode 100644
index 0000000..837f69b
--- /dev/null
+++ b/device/vr/util/copy_to_ustring.cc
@@ -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.
+
+#include "device/vr/util/copy_to_ustring.h"
+
+#include <algorithm>
+
+#include "device/gamepad/public/cpp/gamepad.h"
+
+namespace device {
+
+void CopyToUString(const base::string16& src, UChar* dest, size_t dest_length) {
+  static_assert(sizeof(base::string16::value_type) == sizeof(UChar),
+                "Mismatched string16/UChar size.");
+
+  const size_t copy_char_count = std::min(src.size(), dest_length - 1);
+  src.copy(dest, copy_char_count);
+  std::fill(dest + copy_char_count, dest + dest_length, 0);
+}
+
+}  // namespace device
diff --git a/device/vr/util/copy_to_ustring.h b/device/vr/util/copy_to_ustring.h
new file mode 100644
index 0000000..4645ae2
--- /dev/null
+++ b/device/vr/util/copy_to_ustring.h
@@ -0,0 +1,23 @@
+// 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 DEVICE_VR_UTIL_COPY_TO_USTRING_H_
+#define DEVICE_VR_UTIL_COPY_TO_USTRING_H_
+
+#include "base/strings/string16.h"
+
+class UChar;
+
+namespace device {
+
+// Cross-platform way of copying a string to fixed-length UTF-16 buffer. When
+// copying src to dest, the contents will be truncated if necessary.
+// TODO(https://crbug.com/957806): Code that does the same thing is copied in
+// several places across device/vr. Consolidate them and this function into one
+// shared among device/gamepad and device/vr.
+void CopyToUString(const base::string16& src, UChar* dest, size_t dest_length);
+
+}  // namespace device
+
+#endif  // DEVICE_VR_UTIL_COPY_TO_USTRING_H_
diff --git a/docs/infra/cq_builders.md b/docs/infra/cq_builders.md
index 19259a98..b3cd652 100644
--- a/docs/infra/cq_builders.md
+++ b/docs/infra/cq_builders.md
@@ -82,6 +82,12 @@
 `android_optional_gpu_tests_rel`, due to the `location_regexp` values for that
 builder.
 
+* [android-cronet-arm-dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android-cronet-arm-dbg) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android-cronet-arm-dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android-cronet-arm-dbg))
+
+  Path regular expressions:
+    * [`//components/cronet/.+`](https://cs.chromium.org/chromium/src/components/cronet/)
+    * [`//components/grpc_support/.+`](https://cs.chromium.org/chromium/src/components/grpc_support/)
+
 * [android_compile_x64_dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_compile_x64_dbg) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android_compile_x64_dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_compile_x64_dbg))
 
   Path regular expressions:
diff --git a/extensions/browser/api/socket/udp_socket.cc b/extensions/browser/api/socket/udp_socket.cc
index b0f3ca8..eedd1713 100644
--- a/extensions/browser/api/socket/udp_socket.cc
+++ b/extensions/browser/api/socket/udp_socket.cc
@@ -8,7 +8,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/lazy_instance.h"
 #include "base/stl_util.h"
 #include "extensions/browser/api/api_resource.h"
@@ -89,7 +88,7 @@
   read_callback_.Reset();
   // TODO(devlin): Should we do this for all callbacks?
   if (!recv_from_callback_.is_null()) {
-    base::ResetAndReturn(&recv_from_callback_)
+    std::move(recv_from_callback_)
         .Run(net::ERR_CONNECTION_CLOSED, nullptr, true /* socket_destroying */,
              std::string(), 0);
   }
@@ -227,7 +226,7 @@
           .Run(result, nullptr, false /* socket_destroying */);
       return;
     }
-    base::ResetAndReturn(&recv_from_callback_)
+    std::move(recv_from_callback_)
         .Run(result, nullptr, false /* socket_destroying */, ip, port);
     return;
   }
@@ -242,7 +241,7 @@
   }
 
   IPEndPointToStringAndPort(src_addr.value(), &ip, &port);
-  base::ResetAndReturn(&recv_from_callback_)
+  std::move(recv_from_callback_)
       .Run(data.value().size(), io_buffer, false /* socket_destroying */, ip,
            port);
 }
diff --git a/extensions/browser/api/web_request/web_request_info.cc b/extensions/browser/api/web_request/web_request_info.cc
index a664ca9..9d88519 100644
--- a/extensions/browser/api/web_request/web_request_info.cc
+++ b/extensions/browser/api/web_request/web_request_info.cc
@@ -78,12 +78,11 @@
   DISALLOW_COPY_AND_ASSIGN(FileUploadDataSource);
 };
 
-std::unique_ptr<base::Value> NetLogExtensionIdCallback(
-    const std::string& extension_id,
-    net::NetLogCaptureMode capture_mode) {
-  auto params = std::make_unique<base::DictionaryValue>();
-  params->SetString("extension_id", extension_id);
-  return params;
+base::Value NetLogExtensionIdCallback(const std::string& extension_id,
+                                      net::NetLogCaptureMode capture_mode) {
+  base::DictionaryValue params;
+  params.SetString("extension_id", extension_id);
+  return std::move(params);
 }
 
 // Implements Logger using NetLog, mirroring the logging facilities used prior
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc
index a70e178..600f13aa 100644
--- a/headless/app/headless_shell.cc
+++ b/headless/app/headless_shell.cc
@@ -650,6 +650,7 @@
     // Compositor flags
     command_line.AppendSwitch(::switches::kRunAllCompositorStagesBeforeDraw);
     command_line.AppendSwitch(::switches::kDisableNewContentRenderingTimeout);
+    command_line.AppendSwitch(::switches::kEnableSurfaceSynchronization);
     // Ensure that image animations don't resync their animation timestamps when
     // looping back around.
     command_line.AppendSwitch(::switches::kDisableImageAnimationResync);
diff --git a/infra/config/commit-queue.cfg b/infra/config/commit-queue.cfg
index f9dd4b1f..b088380 100644
--- a/infra/config/commit-queue.cfg
+++ b/infra/config/commit-queue.cfg
@@ -150,6 +150,12 @@
       ######################
 
       builders {
+        name: "chromium/try/android-cronet-arm-dbg"
+        location_regexp: ".+/[+]/components/cronet/.+"
+        location_regexp: ".+/[+]/components/grpc_support/.+"
+        location_regexp_exclude: ".+/[+]/components/cronet/ios/.+"
+      }
+      builders {
         name: "chromium/try/android_compile_x64_dbg"
         location_regexp: ".+/[+]/sandbox/linux/seccomp-bpf/.+"
         location_regexp: ".+/[+]/sandbox/linux/seccomp-bpf-helpers/.+"
@@ -356,12 +362,6 @@
         experiment_percentage: 20
       }
 
-      builders {
-        name: "*/master.tryserver.chromium.android/android_cronet_tester"
-        location_regexp: ".+/[+]/components/cronet/.+"
-        location_regexp: ".+/[+]/components/grpc_support/.+"
-        location_regexp_exclude: ".+/[+]/components/cronet/ios/.+"
-      }
       retry_config {
         single_quota: 1
         global_quota: 2
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 560aafd..de2c98bd 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -963,6 +963,9 @@
 }
 
 - (void)stopChromeMain {
+  // The UI should be stopped before the models they observe are stopped.
+  [_mainCoordinator stop];
+
   [_spotlightManager shutdown];
   _spotlightManager = nil;
 
@@ -974,8 +977,6 @@
   [_historyCoordinator stop];
   _historyCoordinator = nil;
 
-  [_mainCoordinator stop];
-
   ios::GetChromeBrowserProvider()
       ->GetMailtoHandlerProvider()
       ->RemoveMailtoHandling();
diff --git a/ios/chrome/browser/overlays/public/overlay_ui_delegate.h b/ios/chrome/browser/overlays/public/overlay_ui_delegate.h
index 784f645..17f8092 100644
--- a/ios/chrome/browser/overlays/public/overlay_ui_delegate.h
+++ b/ios/chrome/browser/overlays/public/overlay_ui_delegate.h
@@ -13,31 +13,50 @@
 class WebState;
 }
 
+// Enum type categorizing the different reasons overlay UI may be dismissed.
+// Used in OverlayDismissalCallbacks to notify the OverlayManager why the
+// overlay was dismissed.
+enum class OverlayDismissalReason {
+  // Used when the overlay UI is dismissed by the user.
+  kUserInteraction,
+  // Used when the overlay is hidden for HideOverlayUIForWebState().
+  kHiding,
+  // Used when the overlay is cancelled for CancelOverlayUIForWebState().
+  kCancellation,
+};
+
 // Overlays presented by the UI delegate are provided with an
 // OverlayDismissalCallback that is used to notify the OverlayManager when
-// requested overlay UI has finished being dismissed.
-typedef base::OnceCallback<void(void)> OverlayDismissalCallback;
+// requested overlay UI has finished being dismissed.  |reason| is used to
+// communicate what triggered the dismissal.  Overlays that are hidden may be
+// shown again, so the callback will not update the OverlayRequestQueue.
+// Overlays dismissed for user interaction or cancellation will never be shown
+// again; executing the dismissal callback for these reasons will execute the
+// request's callback and remove it from its queue.
+typedef base::OnceCallback<void(OverlayDismissalReason reason)>
+    OverlayDismissalCallback;
 
 // Delegate that handles presenting the overlay UI for requests supplied to
-// OverlayService.
+// OverlayManager.
 class OverlayUIDelegate {
  public:
   virtual ~OverlayUIDelegate() = default;
 
   // Called to show the overlay UI for the frontmost OverlayRequest in
   // |web_state|'s OverlayRequestQueue at |modality|.  |dismissal_callback| must
-  // be called  when the UI is finished being dismissed, either by cancellation
-  // or by user interaction.
+  // be stored by the delegate and called whenever the UI is finished being
+  // dismissed for user interaction, hiding, or cancellation.
   virtual void ShowOverlayUIForWebState(
       web::WebState* web_state,
       OverlayModality modality,
       OverlayDismissalCallback dismissal_callback) = 0;
 
   // Called to hide the overlay UI for the frontmost OverlayRequest in
-  // |web_state|'s OverlayRequestQueue at |modality|.  Hidden overlays may
-  // be shown again, so they should be kept in memory or serialized so that the
-  // state can be restored if shown again.  The dismissal callback should not
-  // be executed upon hiding overlays.
+  // |web_state|'s OverlayRequestQueue at |modality|.  Hidden overlays may be
+  // shown again, so they should be kept in memory or serialized so that the
+  // state can be restored if shown again.  When hiding an overlay, the
+  // presented UI must be dismissed, and the overlay's dismissal callback must
+  // must be executed upon the dismissal's completion.
   virtual void HideOverlayUIForWebState(web::WebState* web_state,
                                         OverlayModality modality) = 0;
 
diff --git a/ios/chrome/browser/overlays/test/fake_overlay_ui_delegate.cc b/ios/chrome/browser/overlays/test/fake_overlay_ui_delegate.cc
index 39a4a567..2591c3d31 100644
--- a/ios/chrome/browser/overlays/test/fake_overlay_ui_delegate.cc
+++ b/ios/chrome/browser/overlays/test/fake_overlay_ui_delegate.cc
@@ -15,10 +15,21 @@
 }
 
 void FakeOverlayUIDelegate::SimulateDismissalForWebState(
-    web::WebState* web_state) {
+    web::WebState* web_state,
+    OverlayDismissalReason reason) {
   DCHECK_EQ(PresentationState::kPresented, presentation_states_[web_state]);
-  std::move(overlay_callbacks_[web_state]).Run();
-  presentation_states_[web_state] = PresentationState::kNotPresented;
+  switch (reason) {
+    case OverlayDismissalReason::kUserInteraction:
+      presentation_states_[web_state] = PresentationState::kUserDismissed;
+      break;
+    case OverlayDismissalReason::kHiding:
+      presentation_states_[web_state] = PresentationState::kHidden;
+      break;
+    case OverlayDismissalReason::kCancellation:
+      presentation_states_[web_state] = PresentationState::kCancelled;
+      break;
+  }
+  std::move(overlay_callbacks_[web_state]).Run(reason);
 }
 
 void FakeOverlayUIDelegate::ShowOverlayUIForWebState(
@@ -31,7 +42,7 @@
 
 void FakeOverlayUIDelegate::HideOverlayUIForWebState(web::WebState* web_state,
                                                      OverlayModality modality) {
-  presentation_states_[web_state] = PresentationState::kHidden;
+  SimulateDismissalForWebState(web_state, OverlayDismissalReason::kHiding);
 }
 
 void FakeOverlayUIDelegate::CancelOverlayUIForWebState(
@@ -39,7 +50,8 @@
     OverlayModality modality) {
   PresentationState& state = presentation_states_[web_state];
   if (state == PresentationState::kPresented) {
-    SimulateDismissalForWebState(web_state);
+    SimulateDismissalForWebState(web_state,
+                                 OverlayDismissalReason::kCancellation);
   } else {
     state = PresentationState::kNotPresented;
   }
diff --git a/ios/chrome/browser/overlays/test/fake_overlay_ui_delegate.h b/ios/chrome/browser/overlays/test/fake_overlay_ui_delegate.h
index 954f9bda..1d23b3e 100644
--- a/ios/chrome/browser/overlays/test/fake_overlay_ui_delegate.h
+++ b/ios/chrome/browser/overlays/test/fake_overlay_ui_delegate.h
@@ -17,15 +17,23 @@
 
   // Enum describing the state of the overlay UI.
   enum class PresentationState {
+    // Default state.  No overlays have been presented.
     kNotPresented,
+    // An overlay is currently being presented.
     kPresented,
+    // A presented overlay was dismissed by user interaction.
+    kUserDismissed,
+    // A presented overlay was hidden.
     kHidden,
+    // A presented overlay was cancelled.
+    kCancelled,
   };
   // Returns the presentation state for the overlay UI.
   PresentationState GetPresentationState(web::WebState* web_state);
 
-  // Simulates the dismissal of overlay UI for user interaction.
-  void SimulateDismissalForWebState(web::WebState* web_state);
+  // Simulates the dismissal of overlay UI for |reason|.
+  void SimulateDismissalForWebState(web::WebState* web_state,
+                                    OverlayDismissalReason reason);
 
   // OverlayUIDelegate:
   void ShowOverlayUIForWebState(web::WebState* web_state,
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 0fd8ae1..06395afa 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
@@ -99,6 +99,9 @@
   [self.view.layer setShadowRadius:kBannerViewShadowRadius];
   [self.view.layer setShadowOpacity:kBannerViewShadowOpacity];
   self.view.accessibilityIdentifier = kInfobarBannerViewIdentifier;
+  self.view.isAccessibilityElement = YES;
+  self.view.accessibilityLabel = [self accessibilityLabel];
+  self.view.accessibilityCustomActions = [self accessibilityActions];
 
   // Bottom Grip setup.
   UIView* bottomGrip = [[UIView alloc] init];
@@ -307,4 +310,50 @@
                    completion:nil];
 }
 
+#pragma mark - Accessibility
+
+- (NSArray*)accessibilityActions {
+  UIAccessibilityCustomAction* acceptAction =
+      [[UIAccessibilityCustomAction alloc]
+          initWithName:self.buttonText
+                target:self
+              selector:@selector(acceptInfobar)];
+
+  UIAccessibilityCustomAction* dismissAction =
+      [[UIAccessibilityCustomAction alloc] initWithName:@"Dismiss"
+                                                 target:self
+                                               selector:@selector(dismiss)];
+
+  UIAccessibilityCustomAction* expandAction =
+      [[UIAccessibilityCustomAction alloc]
+          initWithName:@"More options"
+                target:self
+              selector:@selector(presentInfobarModal)];
+
+  return @[ acceptAction, dismissAction, expandAction ];
+}
+
+// A11y Custom actions selectors need to return a BOOL.
+- (BOOL)acceptInfobar {
+  [self.delegate bannerInfobarButtonWasPressed:nil];
+  return NO;
+}
+- (BOOL)presentInfobarModal {
+  [self.delegate presentInfobarModalFromBanner];
+  return NO;
+}
+- (BOOL)dismiss {
+  [self.delegate dismissInfobarBanner:self animated:YES completion:nil];
+  return NO;
+}
+
+- (NSString*)accessibilityLabel {
+  NSString* accessibilityLabel = self.titleText;
+  if ([self.subTitleText length]) {
+    accessibilityLabel =
+        [NSString stringWithFormat:@"%@,%@", self.titleText, self.subTitleText];
+  }
+  return accessibilityLabel;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/infobars/infobar_container_coordinator.mm b/ios/chrome/browser/ui/infobars/infobar_container_coordinator.mm
index 7a7afc2..64b19ad 100644
--- a/ios/chrome/browser/ui/infobars/infobar_container_coordinator.mm
+++ b/ios/chrome/browser/ui/infobars/infobar_container_coordinator.mm
@@ -182,12 +182,14 @@
 
   // Dismisses the presented InfobarCoordinator banner after
   // kInfobarBannerPresentationDurationInSeconds seconds.
-  dispatch_time_t popTime =
-      dispatch_time(DISPATCH_TIME_NOW,
-                    kInfobarBannerPresentationDurationInSeconds * NSEC_PER_SEC);
-  dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
-    [infobarCoordinator dismissInfobarBannerAfterInteraction];
-  });
+  if (!UIAccessibilityIsVoiceOverRunning()) {
+    dispatch_time_t popTime = dispatch_time(
+        DISPATCH_TIME_NOW,
+        kInfobarBannerPresentationDurationInSeconds * NSEC_PER_SEC);
+    dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
+      [infobarCoordinator dismissInfobarBannerAfterInteraction];
+    });
+  }
 }
 
 - (void)setUserInteractionEnabled:(BOOL)enabled {
diff --git a/ios/chrome/browser/ui/settings/settings_egtest.mm b/ios/chrome/browser/ui/settings/settings_egtest.mm
index 12da268..989b627b5 100644
--- a/ios/chrome/browser/ui/settings/settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/settings_egtest.mm
@@ -545,17 +545,15 @@
 
 // Verifies that Settings opens when signed-out and in Incognito mode.
 // This tests that crbug.com/607335 has not regressed.
-// DISABLED, see https://crbug.com/957687.
-// - (void)testSettingsSignedOutIncognito {
-//   CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewIncognitoTab]);
-//   [ChromeEarlGreyUI openSettingsMenu];
-//   [[EarlGrey selectElementWithMatcher:SettingsCollectionView()]
-//       assertWithMatcher:grey_notNil()];
-//
-//   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
-//       performAction:grey_tap()];
-//   CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey closeAllIncognitoTabs]);
-// }
+- (void)testSettingsSignedOutIncognito {
+  CHROME_EG_ASSERT_NO_ERROR([ChromeEarlGrey openNewIncognitoTab]);
+  [ChromeEarlGreyUI openSettingsMenu];
+  [[EarlGrey selectElementWithMatcher:SettingsCollectionView()]
+      assertWithMatcher:grey_notNil()];
+
+  [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
+      performAction:grey_tap()];
+}
 
 // Verifies the UI elements are accessible on the Settings page.
 - (void)testAccessibilityOnSettingsPage {
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
index 92e6c54..25b1e541 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
@@ -228,6 +228,10 @@
       stopDispatchingForProtocol:@protocol(ApplicationSettingsCommands)];
   [self.dispatcher stopDispatchingForProtocol:@protocol(BrowsingDataCommands)];
 
+  // Disconnect UI from models they observe.
+  self.regularTabsMediator.tabModel = nil;
+  self.incognitoTabsMediator.tabModel = nil;
+
   // TODO(crbug.com/845192) : RecentTabsTableViewController behaves like a
   // coordinator and that should be factored out.
   [self.baseViewController.remoteTabsViewController dismissModals];
diff --git a/ios/third_party/material_components_ios/README.chromium b/ios/third_party/material_components_ios/README.chromium
index 7bf5748..b5d64241c 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: be3dc47ac8992eaa26be283c0fa0356c085b1e8a
+Revision: 3afe64494ecf45b4165d3bacabe9dea35cb001c5
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/media/base/mac/video_frame_mac_unittests.cc b/media/base/mac/video_frame_mac_unittests.cc
index 86ecdf5..abe9e5142 100644
--- a/media/base/mac/video_frame_mac_unittests.cc
+++ b/media/base/mac/video_frame_mac_unittests.cc
@@ -94,7 +94,7 @@
 
   int instances_destroyed = 0;
   auto wrapper_frame = VideoFrame::WrapVideoFrame(
-      frame, frame->format(), frame->visible_rect(), frame->natural_size());
+      *frame, frame->format(), frame->visible_rect(), frame->natural_size());
   wrapper_frame->AddDestructionObserver(
       base::Bind(&Increment, &instances_destroyed));
   ASSERT_TRUE(wrapper_frame.get());
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 7345ae6..a8b11a3 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -377,14 +377,14 @@
 // MediaDrmBridge. If disabled, MediaDrmBridge will get unprovisioned origin IDs
 // which will trigger provisioning process after MediaDrmBridge is created.
 const base::Feature kMediaDrmPreprovisioning{"MediaDrmPreprovisioning",
-                                             base::FEATURE_DISABLED_BY_DEFAULT};
+                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Determines if MediaDrmOriginIdManager should attempt to pre-provision origin
 // IDs at startup (whenever a profile is loaded). Also used by tests that
 // disable it so that the tests can setup before pre-provisioning is done.
-// Note: Have no effect if kMediaDrmPreprovisioning feature is disabled.
+// Note: Has no effect if kMediaDrmPreprovisioning feature is disabled.
 const base::Feature kMediaDrmPreprovisioningAtStartup{
-    "MediaDrmPreprovisioningAtStartup", base::FEATURE_DISABLED_BY_DEFAULT};
+    "MediaDrmPreprovisioningAtStartup", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables the Android Image Reader path for Video decoding(for AVDA and MCVD)
 const base::Feature kAImageReaderVideoOutput{"AImageReaderVideoOutput",
diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc
index 4ea8a9ff..6eea862 100644
--- a/media/base/video_frame.cc
+++ b/media/base/video_frame.cc
@@ -588,48 +588,48 @@
 
 // static
 scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame(
-    const scoped_refptr<VideoFrame>& frame,
+    const VideoFrame& frame,
     VideoPixelFormat format,
     const gfx::Rect& visible_rect,
     const gfx::Size& natural_size) {
   // Frames with textures need mailbox info propagated, and there's no support
   // for that here yet, see http://crbug/362521.
-  CHECK(!frame->HasTextures());
-  DCHECK(frame->visible_rect().Contains(visible_rect));
+  CHECK(!frame.HasTextures());
+  DCHECK(frame.visible_rect().Contains(visible_rect));
 
-  if (!AreValidPixelFormatsForWrap(frame->format(), format)) {
+  if (!AreValidPixelFormatsForWrap(frame.format(), format)) {
     DLOG(ERROR) << __func__ << " Invalid format conversion."
-                << VideoPixelFormatToString(frame->format()) << " to "
+                << VideoPixelFormatToString(frame.format()) << " to "
                 << VideoPixelFormatToString(format);
     return nullptr;
   }
 
-  if (!IsValidConfig(format, frame->storage_type(), frame->coded_size(),
+  if (!IsValidConfig(format, frame.storage_type(), frame.coded_size(),
                      visible_rect, natural_size)) {
     DLOG(ERROR) << __func__ << " Invalid config."
-                << ConfigToString(format, frame->storage_type(),
-                                  frame->coded_size(), visible_rect,
+                << ConfigToString(format, frame.storage_type(),
+                                  frame.coded_size(), visible_rect,
                                   natural_size);
     return nullptr;
   }
 
   scoped_refptr<VideoFrame> wrapping_frame(
-      new VideoFrame(frame->layout(), frame->storage_type(), visible_rect,
-                     natural_size, frame->timestamp()));
+      new VideoFrame(frame.layout(), frame.storage_type(), visible_rect,
+                     natural_size, frame.timestamp()));
 
   // Copy all metadata to the wrapped frame.
-  wrapping_frame->metadata()->MergeMetadataFrom(frame->metadata());
+  wrapping_frame->metadata()->MergeMetadataFrom(frame.metadata());
 
-  if (frame->IsMappable()) {
+  if (frame.IsMappable()) {
     for (size_t i = 0; i < NumPlanes(format); ++i) {
-      wrapping_frame->data_[i] = frame->data(i);
+      wrapping_frame->data_[i] = frame.data_[i];
     }
   }
 
 #if defined(OS_LINUX)
   // If there are any |dmabuf_fds_| plugged in, we should duplicate them.
-  if (frame->storage_type() == STORAGE_DMABUFS) {
-    wrapping_frame->dmabuf_fds_ = DuplicateFDs(frame->dmabuf_fds_);
+  if (frame.storage_type() == STORAGE_DMABUFS) {
+    wrapping_frame->dmabuf_fds_ = DuplicateFDs(frame.dmabuf_fds_);
     if (wrapping_frame->dmabuf_fds_.empty()) {
       DLOG(ERROR) << __func__ << " Couldn't duplicate fds.";
       return nullptr;
@@ -637,18 +637,18 @@
   }
 #endif
 
-  if (frame->storage_type() == STORAGE_SHMEM) {
-    if (frame->read_only_shared_memory_region_) {
-      DCHECK(frame->read_only_shared_memory_region_->IsValid());
+  if (frame.storage_type() == STORAGE_SHMEM) {
+    if (frame.read_only_shared_memory_region_) {
+      DCHECK(frame.read_only_shared_memory_region_->IsValid());
       wrapping_frame->AddReadOnlySharedMemoryRegion(
-          frame->read_only_shared_memory_region_);
-    } else if (frame->unsafe_shared_memory_region_) {
-      DCHECK(frame->unsafe_shared_memory_region_->IsValid());
+          frame.read_only_shared_memory_region_);
+    } else if (frame.unsafe_shared_memory_region_) {
+      DCHECK(frame.unsafe_shared_memory_region_->IsValid());
       wrapping_frame->AddUnsafeSharedMemoryRegion(
-          frame->unsafe_shared_memory_region_);
+          frame.unsafe_shared_memory_region_);
     } else {
-      DCHECK(frame->shared_memory_handle_.IsValid());
-      wrapping_frame->AddSharedMemoryHandle(frame->shared_memory_handle_);
+      DCHECK(frame.shared_memory_handle_.IsValid());
+      wrapping_frame->AddSharedMemoryHandle(frame.shared_memory_handle_);
     }
   }
 
diff --git a/media/base/video_frame.h b/media/base/video_frame.h
index e6a371c..ac1eabf 100644
--- a/media/base/video_frame.h
+++ b/media/base/video_frame.h
@@ -302,7 +302,7 @@
   // Wraps |frame|. |visible_rect| must be a sub rect within
   // frame->visible_rect().
   static scoped_refptr<VideoFrame> WrapVideoFrame(
-      const scoped_refptr<VideoFrame>& frame,
+      const VideoFrame& frame,
       VideoPixelFormat format,
       const gfx::Rect& visible_rect,
       const gfx::Size& natural_size);
diff --git a/media/base/video_frame_pool.cc b/media/base/video_frame_pool.cc
index b70a1f3..16156e9 100644
--- a/media/base/video_frame_pool.cc
+++ b/media/base/video_frame_pool.cc
@@ -111,7 +111,7 @@
   }
 
   scoped_refptr<VideoFrame> wrapped_frame = VideoFrame::WrapVideoFrame(
-      frame, frame->format(), frame->visible_rect(), frame->natural_size());
+      *frame, frame->format(), frame->visible_rect(), frame->natural_size());
   wrapped_frame->AddDestructionObserver(base::Bind(
       &VideoFramePool::PoolImpl::FrameReleased, this, std::move(frame)));
   return wrapped_frame;
diff --git a/media/base/video_frame_unittest.cc b/media/base/video_frame_unittest.cc
index 7a2952981..be024a2 100644
--- a/media/base/video_frame_unittest.cc
+++ b/media/base/video_frame_unittest.cc
@@ -285,7 +285,7 @@
     wrapped_frame->metadata()->SetTimeDelta(
         media::VideoFrameMetadata::FRAME_DURATION, kFrameDuration);
     frame = media::VideoFrame::WrapVideoFrame(
-        wrapped_frame, wrapped_frame->format(), visible_rect, natural_size);
+        *wrapped_frame, wrapped_frame->format(), visible_rect, natural_size);
     frame->AddDestructionObserver(base::Bind(
         &FrameNoLongerNeededCallback, wrapped_frame, &done_callback_was_run));
     EXPECT_EQ(wrapped_frame->coded_size(), frame->coded_size());
diff --git a/media/base/video_util.cc b/media/base/video_util.cc
index cb29dcb..81c67675 100644
--- a/media/base/video_util.cc
+++ b/media/base/video_util.cc
@@ -428,7 +428,7 @@
   DCHECK_EQ(PIXEL_FORMAT_I420A, frame->format());
 
   scoped_refptr<media::VideoFrame> wrapped_frame =
-      media::VideoFrame::WrapVideoFrame(frame, PIXEL_FORMAT_I420,
+      media::VideoFrame::WrapVideoFrame(*frame, PIXEL_FORMAT_I420,
                                         frame->visible_rect(),
                                         frame->natural_size());
   if (!wrapped_frame)
diff --git a/media/capture/video/mock_device_factory.cc b/media/capture/video/mock_device_factory.cc
index cea527f..ebfa570 100644
--- a/media/capture/video/mock_device_factory.cc
+++ b/media/capture/video/mock_device_factory.cc
@@ -4,6 +4,8 @@
 
 #include "media/capture/video/mock_device_factory.h"
 
+#include <utility>
+
 namespace {
 
 // Report a single hard-coded supported format to clients.
@@ -84,7 +86,7 @@
 void MockDeviceFactory::GetCameraLocationsAsync(
     std::unique_ptr<media::VideoCaptureDeviceDescriptors> device_descriptors,
     DeviceDescriptorsCallback result_callback) {
-  base::ResetAndReturn(&result_callback).Run(std::move(device_descriptors));
+  std::move(result_callback).Run(std::move(device_descriptors));
 }
 
 }  // namespace media
diff --git a/media/filters/fake_video_decoder.h b/media/filters/fake_video_decoder.h
index c8fe39ac..dbcec16 100644
--- a/media/filters/fake_video_decoder.h
+++ b/media/filters/fake_video_decoder.h
@@ -23,8 +23,6 @@
 #include "media/base/video_frame.h"
 #include "ui/gfx/geometry/size.h"
 
-using base::ResetAndReturn;
-
 namespace media {
 
 typedef base::Callback<void(int)> BytesDecodedCB;
diff --git a/media/filters/fuchsia/fuchsia_video_decoder.cc b/media/filters/fuchsia/fuchsia_video_decoder.cc
index e1a56d0c..39c43d6 100644
--- a/media/filters/fuchsia/fuchsia_video_decoder.cc
+++ b/media/filters/fuchsia/fuchsia_video_decoder.cc
@@ -606,7 +606,8 @@
                                          bool error_detected_during) {
   if (!output_packet.has_header() ||
       !output_packet.header().has_buffer_lifetime_ordinal() ||
-      !output_packet.header().has_packet_index()) {
+      !output_packet.header().has_packet_index() ||
+      !output_packet.has_buffer_index()) {
     DLOG(ERROR) << "Received OnOutputPacket() with missing required fields.";
     OnError();
     return;
@@ -667,8 +668,11 @@
   }
 
   auto packet_index = output_packet.header().packet_index();
-  auto& buffer = output_buffers_[packet_index];
+  auto buffer_index = output_packet.buffer_index();
+  auto& buffer = output_buffers_[buffer_index];
 
+  // We're not using single buffer mode, so packet count will be equal to buffer
+  // count.
   DCHECK_LT(num_used_output_buffers_, static_cast<int>(output_buffers_.size()));
   num_used_output_buffers_++;
 
diff --git a/media/gpu/test/texture_ref.cc b/media/gpu/test/texture_ref.cc
index 24d1553..da060e50 100644
--- a/media/gpu/test/texture_ref.cc
+++ b/media/gpu/test/texture_ref.cc
@@ -69,7 +69,7 @@
 scoped_refptr<VideoFrame> TextureRef::ExportVideoFrame(
     gfx::Rect visible_rect) const {
 #if defined(OS_CHROMEOS)
-  return VideoFrame::WrapVideoFrame(frame_, frame_->format(), visible_rect,
+  return VideoFrame::WrapVideoFrame(*frame_, frame_->format(), visible_rect,
                                     visible_rect.size());
 #else
   return nullptr;
diff --git a/media/gpu/test/video_player/test_vda_video_decoder.cc b/media/gpu/test/video_player/test_vda_video_decoder.cc
index c39cac31..19fba7ee 100644
--- a/media/gpu/test/video_player/test_vda_video_decoder.cc
+++ b/media/gpu/test/video_player/test_vda_video_decoder.cc
@@ -271,7 +271,7 @@
                        picture.picture_buffer_id()));
 
     scoped_refptr<VideoFrame> wrapped_video_frame = VideoFrame::WrapVideoFrame(
-        video_frame, video_frame->format(), video_frame->visible_rect(),
+        *video_frame, video_frame->format(), video_frame->visible_rect(),
         video_frame->visible_rect().size());
     wrapped_video_frame->AddDestructionObserver(std::move(reuse_cb));
 
diff --git a/media/gpu/v4l2/v4l2_image_processor.cc b/media/gpu/v4l2/v4l2_image_processor.cc
index f8bd3bf5..f56b3e3 100644
--- a/media/gpu/v4l2/v4l2_image_processor.cc
+++ b/media/gpu/v4l2/v4l2_image_processor.cc
@@ -732,13 +732,15 @@
       case V4L2_MEMORY_MMAP:
         // Wrap the V4L2 VideoFrame into another one with a destruction observer
         // so we can reuse the MMAP buffer once the client is done with it.
-        output_frame = buffer->GetVideoFrame();
-        output_frame = VideoFrame::WrapVideoFrame(
-            output_frame, output_frame->format(), output_frame->visible_rect(),
-            output_frame->natural_size());
-        output_frame->AddDestructionObserver(BindToCurrentLoop(
-            base::BindOnce(&V4L2ImageProcessor::V4L2VFDestructionObserver,
-                           weak_this_factory_.GetWeakPtr(), buffer)));
+        {
+          const auto& orig_frame = buffer->GetVideoFrame();
+          output_frame = VideoFrame::WrapVideoFrame(
+              *orig_frame, orig_frame->format(), orig_frame->visible_rect(),
+              orig_frame->natural_size());
+          output_frame->AddDestructionObserver(BindToCurrentLoop(
+              base::BindOnce(&V4L2ImageProcessor::V4L2VFDestructionObserver,
+                             weak_this_factory_.GetWeakPtr(), buffer)));
+        }
         break;
 
       case V4L2_MEMORY_DMABUF:
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
index 090f822..dd16acc8 100644
--- a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
@@ -383,7 +383,7 @@
           ImageProcessor::OutputMode::IMPORT) {
         const auto& buf = image_processor_output_buffers_[output_buffer_index];
         auto output_frame = VideoFrame::WrapVideoFrame(
-            buf, buf->format(), buf->visible_rect(), buf->natural_size());
+            *buf, buf->format(), buf->visible_rect(), buf->natural_size());
 
         // We have to bind |weak_this| for FrameProcessed, because child
         // thread is outlive this V4L2VideoEncodeAccelerator.
diff --git a/media/gpu/windows/dxva_picture_buffer_win.cc b/media/gpu/windows/dxva_picture_buffer_win.cc
index bef6ac5..1e82740 100644
--- a/media/gpu/windows/dxva_picture_buffer_win.cc
+++ b/media/gpu/windows/dxva_picture_buffer_win.cc
@@ -312,9 +312,12 @@
     // when we receive a notification that the copy was completed or when the
     // DXVAPictureBuffer instance is destroyed.
     decoder_dx11_texture_ = dx11_texture;
-    decoder->CopyTexture(dx11_texture, dx11_decoding_texture_.Get(),
-                         dx11_keyed_mutex_, keyed_mutex_value_, id(),
-                         input_buffer_id, color_space_);
+    if (!decoder->CopyTexture(dx11_texture, dx11_decoding_texture_.Get(),
+                              dx11_keyed_mutex_, keyed_mutex_value_, id(),
+                              input_buffer_id, color_space_)) {
+      // |this| might be destroyed.
+      return false;
+    }
     return true;
   }
   D3DSURFACE_DESC surface_desc;
@@ -772,9 +775,12 @@
   // when we receive a notification that the copy was completed or when the
   // DXVAPictureBuffer instance is destroyed.
   dx11_decoding_texture_ = dx11_texture;
-  decoder->CopyTexture(dx11_texture, decoder_copy_texture_.Get(),
-                       dx11_keyed_mutex_, keyed_mutex_value_, id(),
-                       input_buffer_id, color_space_);
+  if (!decoder->CopyTexture(dx11_texture, decoder_copy_texture_.Get(),
+                            dx11_keyed_mutex_, keyed_mutex_value_, id(),
+                            input_buffer_id, color_space_)) {
+    // |this| might be destroyed
+    return false;
+  }
   // The texture copy will acquire the current keyed mutex value and release
   // with the value + 1.
   keyed_mutex_value_++;
diff --git a/media/gpu/windows/dxva_video_decode_accelerator_win.cc b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
index 985d5e7a..c9ae1108 100644
--- a/media/gpu/windows/dxva_video_decode_accelerator_win.cc
+++ b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
@@ -2745,7 +2745,7 @@
                      base::Unretained(this)));
 }
 
-void DXVAVideoDecodeAccelerator::CopyTexture(
+bool DXVAVideoDecodeAccelerator::CopyTexture(
     ID3D11Texture2D* src_texture,
     ID3D11Texture2D* dest_texture,
     Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dest_keyed_mutex,
@@ -2770,7 +2770,7 @@
                                       color_space)) {
     RETURN_AND_NOTIFY_ON_FAILURE(false,
                                  "Failed to initialize D3D11 video processor.",
-                                 PLATFORM_FAILURE, );
+                                 PLATFORM_FAILURE, false);
   }
 
   OutputBuffers::iterator it = output_picture_buffers_.find(picture_buffer_id);
@@ -2793,6 +2793,7 @@
                      dest_keyed_mutex, keyed_mutex_value,
                      input_sample_for_conversion, picture_buffer_id,
                      input_buffer_id));
+  return true;
 }
 
 void DXVAVideoDecodeAccelerator::CopyTextureOnDecoderThread(
diff --git a/media/gpu/windows/dxva_video_decode_accelerator_win.h b/media/gpu/windows/dxva_video_decode_accelerator_win.h
index c5d45ad8..4519ae2a 100644
--- a/media/gpu/windows/dxva_video_decode_accelerator_win.h
+++ b/media/gpu/windows/dxva_video_decode_accelerator_win.h
@@ -300,8 +300,8 @@
                                  int input_buffer_id);
 
   // Copies the source texture |src_texture| to the destination |dest_texture|.
-  // The copying is done on the decoder thread.
-  void CopyTexture(ID3D11Texture2D* src_texture,
+  // The copying is done on the decoder thread.  Returns true on success.
+  bool CopyTexture(ID3D11Texture2D* src_texture,
                    ID3D11Texture2D* dest_texture,
                    Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dest_keyed_mutex,
                    uint64_t keyed_mutex_value,
diff --git a/mojo/public/java/system/javatests/AndroidManifest.xml b/mojo/public/java/system/javatests/AndroidManifest.xml
index 8fad0c7c..84b217b 100644
--- a/mojo/public/java/system/javatests/AndroidManifest.xml
+++ b/mojo/public/java/system/javatests/AndroidManifest.xml
@@ -22,7 +22,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="org.chromium.base.test.BaseChromiumAndroidJUnitRunner"
         android:targetPackage="org.chromium.mojo.tests"
         android:label="Tests for org.chromium.mojo"/>
 
diff --git a/mojo/public/js/mojo_bindings_lite.html b/mojo/public/js/mojo_bindings_lite.html
new file mode 100644
index 0000000..1cfddc2
--- /dev/null
+++ b/mojo/public/js/mojo_bindings_lite.html
@@ -0,0 +1,6 @@
+<!--
+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.
+-->
+<script src="mojo_bindings_lite.js"></script>
diff --git a/mojo/public/js/mojo_bindings_resources.grd b/mojo/public/js/mojo_bindings_resources.grd
index eb61537..fe53509 100644
--- a/mojo/public/js/mojo_bindings_resources.grd
+++ b/mojo/public/js/mojo_bindings_resources.grd
@@ -21,27 +21,51 @@
             type="BINDATA"
             compress="gzip" />
       </if>
+      <include name="IDR_MOJO_MOJO_BINDINGS_LITE_HTML"
+          file="mojo_bindings_lite.html"
+          type="BINDATA"
+          compress="gzip" />
       <include name="IDR_MOJO_MOJO_BINDINGS_LITE_JS"
           file="${root_gen_dir}/mojo/public/js/mojo_bindings_lite.js"
           use_base_dir="false"
           type="BINDATA"
           compress="gzip" />
+      <include name="IDR_MOJO_BIG_BUFFER_MOJOM_HTML"
+          file="${root_gen_dir}/mojo/public/mojom/base/big_buffer.mojom.html"
+          use_base_dir="false"
+          type="BINDATA"
+          compress="gzip" />
       <include name="IDR_MOJO_BIG_BUFFER_MOJOM_LITE_JS"
           file="${root_gen_dir}/mojo/public/mojom/base/big_buffer.mojom-lite.js"
           use_base_dir="false"
           type="BINDATA"
           compress="gzip" />
+      <include name="IDR_MOJO_FILE_MOJOM_HTML"
+          file="${root_gen_dir}/mojo/public/mojom/base/file.mojom.html"
+          use_base_dir="false"
+          type="BINDATA"
+          compress="gzip" />
       <include name="IDR_MOJO_FILE_MOJOM_LITE_JS"
           file="${root_gen_dir}/mojo/public/mojom/base/file.mojom-lite.js"
           use_base_dir="false"
           type="BINDATA"
           compress="gzip" />
+      <include name="IDR_MOJO_STRING16_MOJOM_HTML"
+          file="${root_gen_dir}/mojo/public/mojom/base/string16.mojom.html"
+          use_base_dir="false"
+          type="BINDATA"
+          compress="gzip" />
       <include name="IDR_MOJO_STRING16_MOJOM_LITE_JS"
           file="${root_gen_dir}/mojo/public/mojom/base/string16.mojom-lite.js"
           use_base_dir="false"
           type="BINDATA"
           compress="gzip" />
       <if expr="is_win or is_macosx or is_linux">
+        <include name="IDR_MOJO_TIME_MOJOM_HTML"
+            file="${root_gen_dir}/mojo/public/mojom/base/time.mojom.html"
+            use_base_dir="false"
+            type="BINDATA"
+            compress="gzip" />
         <include name="IDR_MOJO_TIME_MOJOM_LITE_JS"
             file="${root_gen_dir}/mojo/public/mojom/base/time.mojom-lite.js"
             use_base_dir="false"
diff --git a/mojo/public/tools/bindings/concatenate_and_replace_closure_exports.py b/mojo/public/tools/bindings/concatenate_and_replace_closure_exports.py
index 63db884..6094a322 100755
--- a/mojo/public/tools/bindings/concatenate_and_replace_closure_exports.py
+++ b/mojo/public/tools/bindings/concatenate_and_replace_closure_exports.py
@@ -16,6 +16,8 @@
 namespace.
 """
 
+from __future__ import print_function
+
 import optparse
 import re
 
@@ -31,7 +33,7 @@
   if line.startswith("goog.provide"):
     match = re.match("goog.provide\('([^']+)'\);", line)
     if not match:
-      print "Invalid goog.provide line in %s:\n%s" % (filename, line)
+      print("Invalid goog.provide line in %s:\n%s" % (filename, line))
       exit(1)
 
     module_name = match.group(1)
@@ -45,7 +47,7 @@
 
 def ConcatenateAndReplaceExports(filenames):
   if (len(filenames) < 2):
-    print "At least two filenames (one input and the output) are required."
+    print("At least two filenames (one input and the output) are required.")
     return False
 
   try:
@@ -56,7 +58,7 @@
             FilterLine(filename, line, target)
     return True
   except IOError as e:
-    print "Error generating %s\n: %s" % (filenames[-1], e)
+    print("Error generating %s\n: %s" % (filenames[-1], e))
     return False
 
 def main():
diff --git a/mojo/public/tools/bindings/format_typemap_generator_args.py b/mojo/public/tools/bindings/format_typemap_generator_args.py
index 5057d6c..7ac4af5f 100755
--- a/mojo/public/tools/bindings/format_typemap_generator_args.py
+++ b/mojo/public/tools/bindings/format_typemap_generator_args.py
@@ -3,6 +3,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from __future__ import print_function
+
 import sys
 
 # This utility converts mojom dependencies into their corresponding typemap
@@ -26,8 +28,8 @@
 
 def main():
   typemaps = sys.argv[1:]
-  print ' '.join('--start-typemap %s' % ' '.join(FormatTypemap(typemap))
-                 for typemap in typemaps)
+  print(' '.join('--start-typemap %s' % ' '.join(FormatTypemap(typemap))
+                 for typemap in typemaps))
 
 
 if __name__ == '__main__':
diff --git a/mojo/public/tools/bindings/gen_data_files_list.py b/mojo/public/tools/bindings/gen_data_files_list.py
index 8d40173..e797749 100644
--- a/mojo/public/tools/bindings/gen_data_files_list.py
+++ b/mojo/public/tools/bindings/gen_data_files_list.py
@@ -12,6 +12,8 @@
 will be written to the list.
 """
 
+from __future__ import print_function
+
 import os
 import re
 import sys
@@ -39,7 +41,7 @@
 
   stream = StringIO()
   for f in files:
-    print >> stream, f
+    print(f, file=stream)
 
   WriteFile(stream.getvalue(), options.output)
   stream.close()
diff --git a/mojo/public/tools/bindings/generators/js_templates/lite/mojom.html.tmpl b/mojo/public/tools/bindings/generators/js_templates/lite/mojom.html.tmpl
new file mode 100644
index 0000000..ceb220ae
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/js_templates/lite/mojom.html.tmpl
@@ -0,0 +1,9 @@
+<!--
+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.
+-->
+{%- for html_import in html_imports %}
+<link rel="import" href="{{html_import}}.html">
+{% endfor %}
+<script src="{{mojom_filename}}-lite.js"></script>
diff --git a/mojo/public/tools/bindings/generators/mojom_js_generator.py b/mojo/public/tools/bindings/generators/mojom_js_generator.py
index 7e1bba36..19a94f273 100644
--- a/mojo/public/tools/bindings/generators/mojom_js_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_js_generator.py
@@ -253,10 +253,12 @@
   def _GetParameters(self, for_compile=False):
     return {
       "enums": self.module.enums,
+      "html_imports": self._GenerateHtmlImports(),
       "imports": self.module.imports,
       "interfaces": self.module.interfaces,
       "kinds": self.module.kinds,
       "module": self.module,
+      "mojom_filename": os.path.basename(self.module.path),
       "mojom_namespace": self.module.mojom_namespace,
       "structs": self.module.structs + self._GetStructsFromMethods(),
       "unions": self.module.unions,
@@ -341,6 +343,10 @@
   def _GenerateLiteBindings(self):
     return self._GetParameters()
 
+  @UseJinja("lite/mojom.html.tmpl")
+  def _GenerateLiteHtml(self):
+    return self._GetParameters()
+
   @UseJinja("lite/mojom-lite.js.tmpl")
   def _GenerateLiteBindingsForCompile(self):
     return self._GetParameters(for_compile=True)
@@ -359,6 +365,7 @@
     self.Write(self._GenerateAMDModule(), "%s.js" % self.module.path)
     self.Write(self._GenerateExterns(), "%s.externs.js" % self.module.path)
     if self.js_bindings_mode == "new":
+      self.Write(self._GenerateLiteHtml(), "%s.html" % self.module.path)
       self.Write(self._GenerateLiteBindings(), "%s-lite.js" % self.module.path)
       self.Write(self._GenerateLiteBindingsForCompile(),
           "%s-lite-for-compile.js" % self.module.path)
@@ -803,6 +810,13 @@
 
     return self._ExpressionToText(token)
 
+  def _GenerateHtmlImports(self):
+    result = []
+    for full_import in self.module.imports:
+      result.append(os.path.relpath(full_import.path,
+                                    os.path.dirname(self.module.path)))
+    return result
+
   def _GetStructsFromMethods(self):
     result = []
     for interface in self.module.interfaces:
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni
index 0c2e378..8310a1bc 100644
--- a/mojo/public/tools/bindings/mojom.gni
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -18,7 +18,6 @@
 import("//components/nacl/features.gni")
 import("//third_party/jinja2/jinja2.gni")
 import("//tools/ipc_fuzzer/ipc_fuzzer.gni")
-
 declare_args() {
   # Indicates whether typemapping should be supported in this build
   # configuration. This may be disabled when building external projects which
@@ -1245,6 +1244,7 @@
             "$target_gen_dir/$source.js",
             "$target_gen_dir/$source.externs.js",
             "$target_gen_dir/$source-lite.js",
+            "$target_gen_dir/$source.html",
             "$target_gen_dir/$source-lite-for-compile.js",
           ]
         }
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/module.py b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
index 711e6b3d..423c1fd2 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/module.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
@@ -153,7 +153,7 @@
          a = Struct('test_struct_1')
          b = a.MakeNullableKind()
          a.name = 'test_struct_2'
-         print b.name  # Outputs 'test_struct_2'.
+         print(b.name)  # Outputs 'test_struct_2'.
     """
     def Get(self):
       return self.shared_definition[name]
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py
index 41f11a2b..a40799b9 100755
--- a/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py
@@ -5,18 +5,20 @@
 
 """ Test runner for Mojom """
 
+from __future__ import print_function
+
 import subprocess
 import sys
 
 def TestMojom(testname, args):
-  print '\nRunning unit tests for %s.' % testname
+  print('\nRunning unit tests for %s.' % testname)
   try:
     args = [sys.executable, testname] + args
     subprocess.check_call(args, stdout=sys.stdout)
-    print 'Succeeded'
+    print('Succeeded')
     return 0
   except subprocess.CalledProcessError as err:
-    print 'Failed with %s.' % str(err)
+    print('Failed with %s.' % str(err))
     return 1
 
 
@@ -27,7 +29,7 @@
   errors += TestMojom('pack_tests.py', ['--test'])
 
   if errors:
-    print '\nFailed tests.'
+    print('\nFailed tests.')
   return min(errors, 127)  # Make sure the return value doesn't "wrap".
 
 
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py b/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py
index eb39461..b9f9e06 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from __future__ import print_function
+
 import sys
 import traceback
 
@@ -50,7 +52,7 @@
     return True
   if kind1.__class__ == mojom.Array:
     return KindsAreEqual(kind1.kind, kind2.kind)
-  print 'Unknown Kind class: ', kind1.__class__.__name__
+  print('Unknown Kind class: ', kind1.__class__.__name__)
   return False
 
 
@@ -158,7 +160,7 @@
   stack = traceback.extract_stack()
   frame = stack[len(stack)-3]
   sys.stderr.write("ERROR at %s:%d, %s\n" % (frame[0], frame[1], string))
-  print "Traceback:"
+  print("Traceback:")
   for line in traceback.format_list(stack[:len(stack)-2]):
     sys.stderr.write(line)
 
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py
index b160de6..cda0560 100755
--- a/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py
@@ -5,6 +5,7 @@
 
 """Simple testing utility to just run the mojom parser."""
 
+from __future__ import print_function
 
 import os.path
 import sys
@@ -17,16 +18,16 @@
 
 def main(argv):
   if len(argv) < 2:
-    print "usage: %s filename" % argv[0]
+    print("usage: %s filename" % argv[0])
     return 0
 
   for filename in argv[1:]:
     with open(filename) as f:
-      print "%s:" % filename
+      print("%s:" % filename)
       try:
-        print Parse(f.read(), filename)
+        print(Parse(f.read(), filename))
       except ParseError, e:
-        print e
+        print(e)
         return 1
 
   return 0
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py
index 899d40e5..fdb18a20 100755
--- a/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py
@@ -5,6 +5,7 @@
 
 """Simple testing utility to just run the mojom translate stage."""
 
+from __future__ import print_function
 
 import os.path
 import sys
@@ -18,14 +19,15 @@
 
 def main(argv):
   if len(argv) < 2:
-    print "usage: %s filename" % sys.argv[0]
+    print("usage: %s filename" % sys.argv[0])
     return 1
 
   for filename in argv[1:]:
     with open(filename) as f:
-      print "%s:" % filename
-      print Translate(Parse(f.read(), filename),
-                      os.path.splitext(os.path.basename(filename))[0])
+      print("%s:" % filename)
+      print(Translate(
+          Parse(f.read(), filename),
+          os.path.splitext(os.path.basename(filename))[0]))
 
   return 0
 
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py b/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py
index 2a4b17b2..e2fe87e 100644
--- a/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py
@@ -20,11 +20,11 @@
 
 def main(argv):
   if len(argv) != 3:
-    print "usage: %s path pattern" % argv[0]
+    print("usage: %s path pattern" % argv[0])
     return 1
 
   for filename in FindFiles(argv[1], argv[2]):
-    print filename
+    print(filename)
   return 0
 
 
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py b/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py
index 20ef461..9832b18b 100644
--- a/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py
@@ -36,7 +36,7 @@
 
 def main(argv):
   if len(argv) < 4:
-    print "usage: %s out_dir root_dir mojom_file [extra_flags]" % argv[0]
+    print("usage: %s out_dir root_dir mojom_file [extra_flags]" % argv[0])
     return 1
 
   RunBindingsGenerator(argv[1], argv[2], argv[3], extra_flags=argv[4:])
diff --git a/mojo/public/tools/chrome_ipc/generate_mojom.py b/mojo/public/tools/chrome_ipc/generate_mojom.py
index 04e933b..b15e50b3 100755
--- a/mojo/public/tools/chrome_ipc/generate_mojom.py
+++ b/mojo/public/tools/chrome_ipc/generate_mojom.py
@@ -429,7 +429,7 @@
   if args.count:
     count = generator.count()
     if count:
-      print '%d %s' % (generator.count(), args.input)
+      print('%d %s' % (generator.count(), args.input))
     return
   mojom = '\n'.join(generator.generate_mojom()).strip()
   if not mojom:
@@ -440,13 +440,13 @@
     with open(args.output_mojom, 'w') as f:
       f.write(mojom)
   else:
-    print mojom
+    print(mojom)
   if typemap:
     if args.output_typemap:
       with open(args.output_typemap, 'w') as f:
         f.write(typemap)
     else:
-      print typemap
+      print(typemap)
 
 
 if __name__ == '__main__':
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 11169e9..87eecdd8 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -5374,6 +5374,7 @@
     "third_party/quiche/src/quic/platform/api/quic_containers_test.cc",
     "third_party/quiche/src/quic/platform/api/quic_endian_test.cc",
     "third_party/quiche/src/quic/platform/api/quic_hostname_utils_test.cc",
+    "third_party/quiche/src/quic/platform/api/quic_ip_address_test.cc",
     "third_party/quiche/src/quic/platform/api/quic_mem_slice_span_test.cc",
     "third_party/quiche/src/quic/platform/api/quic_mem_slice_storage_test.cc",
     "third_party/quiche/src/quic/platform/api/quic_mem_slice_test.cc",
diff --git a/net/base/address_list.cc b/net/base/address_list.cc
index 7a783a6..20a6d3d 100644
--- a/net/base/address_list.cc
+++ b/net/base/address_list.cc
@@ -17,19 +17,17 @@
 
 namespace {
 
-std::unique_ptr<base::Value> NetLogAddressListCallback(
-    const AddressList* address_list,
-    NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  std::unique_ptr<base::ListValue> list(new base::ListValue());
+base::Value NetLogAddressListCallback(const AddressList* address_list,
+                                      NetLogCaptureMode capture_mode) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  base::Value list(base::Value::Type::LIST);
 
-  for (auto it = address_list->begin(); it != address_list->end(); ++it) {
-    list->AppendString(it->ToString());
-  }
+  for (const auto& ip_endpoint : *address_list)
+    list.GetList().emplace_back(ip_endpoint.ToString());
 
-  dict->Set("address_list", std::move(list));
-  dict->SetString("canonical_name", address_list->canonical_name());
-  return std::move(dict);
+  dict.SetKey("address_list", std::move(list));
+  dict.SetStringKey("canonical_name", address_list->canonical_name());
+  return dict;
 }
 
 }  // namespace
diff --git a/net/base/logging_network_change_observer.cc b/net/base/logging_network_change_observer.cc
index 4d91df3..5ca13b70 100644
--- a/net/base/logging_network_change_observer.cc
+++ b/net/base/logging_network_change_observer.cc
@@ -38,29 +38,28 @@
 // Return a dictionary of values that provide information about a
 // network-specific change. This also includes relevant current state
 // like the default network, and the types of active networks.
-std::unique_ptr<base::Value> NetworkSpecificNetLogCallback(
+base::Value NetworkSpecificNetLogCallback(
     NetworkChangeNotifier::NetworkHandle network,
     NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("changed_network_handle",
-                   HumanReadableNetworkHandle(network));
-  dict->SetString(
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetIntKey("changed_network_handle", HumanReadableNetworkHandle(network));
+  dict.SetStringKey(
       "changed_network_type",
       NetworkChangeNotifier::ConnectionTypeToString(
           NetworkChangeNotifier::GetNetworkConnectionType(network)));
-  dict->SetInteger(
+  dict.SetIntKey(
       "default_active_network_handle",
       HumanReadableNetworkHandle(NetworkChangeNotifier::GetDefaultNetwork()));
   NetworkChangeNotifier::NetworkList networks;
   NetworkChangeNotifier::GetConnectedNetworks(&networks);
   for (NetworkChangeNotifier::NetworkHandle active_network : networks) {
-    dict->SetString(
+    dict.SetStringKey(
         "current_active_networks." +
             base::NumberToString(HumanReadableNetworkHandle(active_network)),
         NetworkChangeNotifier::ConnectionTypeToString(
             NetworkChangeNotifier::GetNetworkConnectionType(active_network)));
   }
-  return std::move(dict);
+  return dict;
 }
 
 }  // namespace
diff --git a/net/base/port_util.cc b/net/base/port_util.cc
index f6ab41c8..7ff334c 100644
--- a/net/base/port_util.cc
+++ b/net/base/port_util.cc
@@ -72,8 +72,8 @@
     548,     // AFP (Apple Filing Protocol)
     556,     // remotefs
     563,     // nntp+ssl
-    587,     // stmp?
-    601,     // ??
+    587,     // smtp (rfc6409)
+    601,     // syslog-conn (rfc3195)
     636,     // ldap+ssl
     993,     // ldap+ssl
     995,     // pop3+ssl
diff --git a/net/base/upload_data_stream.cc b/net/base/upload_data_stream.cc
index 193872e63..6804a1e 100644
--- a/net/base/upload_data_stream.cc
+++ b/net/base/upload_data_stream.cc
@@ -15,26 +15,24 @@
 
 namespace {
 
-std::unique_ptr<base::Value> NetLogInitEndInfoCallback(
-    int result,
-    int total_size,
-    bool is_chunked,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+base::Value NetLogInitEndInfoCallback(int result,
+                                      int total_size,
+                                      bool is_chunked,
+                                      NetLogCaptureMode /* capture_mode */) {
+  base::Value dict(base::Value::Type::DICTIONARY);
 
-  dict->SetInteger("net_error", result);
-  dict->SetInteger("total_size", total_size);
-  dict->SetBoolean("is_chunked", is_chunked);
-  return std::move(dict);
+  dict.SetIntKey("net_error", result);
+  dict.SetIntKey("total_size", total_size);
+  dict.SetBoolKey("is_chunked", is_chunked);
+  return dict;
 }
 
-std::unique_ptr<base::Value> NetLogReadInfoCallback(
-    int current_position,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+base::Value NetLogReadInfoCallback(int current_position,
+                                   NetLogCaptureMode /* capture_mode */) {
+  base::Value dict(base::Value::Type::DICTIONARY);
 
-  dict->SetInteger("current_position", current_position);
-  return std::move(dict);
+  dict.SetIntKey("current_position", current_position);
+  return dict;
 }
 
 }  // namespace
diff --git a/net/cert/ct_signed_certificate_timestamp_log_param.cc b/net/cert/ct_signed_certificate_timestamp_log_param.cc
index 497dde5..88f0dd5 100644
--- a/net/cert/ct_signed_certificate_timestamp_log_param.cc
+++ b/net/cert/ct_signed_certificate_timestamp_log_param.cc
@@ -23,48 +23,48 @@
 // description |key|.
 void SetBinaryData(const char* key,
                    base::StringPiece value,
-                   base::DictionaryValue* dict) {
+                   base::Value* dict) {
   std::string b64_value;
   base::Base64Encode(value, &b64_value);
 
-  dict->SetString(key, b64_value);
+  dict->SetStringKey(key, b64_value);
 }
 
 // Returns a dictionary where each key is a field of the SCT and its value
 // is this field's value in the SCT. This dictionary is meant to be used for
 // outputting a de-serialized SCT to the NetLog.
-std::unique_ptr<base::DictionaryValue> SCTToDictionary(
-    const ct::SignedCertificateTimestamp& sct,
-    ct::SCTVerifyStatus status) {
-  std::unique_ptr<base::DictionaryValue> out(new base::DictionaryValue());
+base::Value SCTToDictionary(const ct::SignedCertificateTimestamp& sct,
+                            ct::SCTVerifyStatus status) {
+  base::Value out(base::Value::Type::DICTIONARY);
 
-  out->SetString("origin", OriginToString(sct.origin));
-  out->SetString("verification_status", StatusToString(status));
-  out->SetInteger("version", sct.version);
+  out.SetStringKey("origin", OriginToString(sct.origin));
+  out.SetStringKey("verification_status", StatusToString(status));
+  out.SetIntKey("version", sct.version);
 
-  SetBinaryData("log_id", sct.log_id, out.get());
+  SetBinaryData("log_id", sct.log_id, &out);
   base::TimeDelta time_since_unix_epoch =
       sct.timestamp - base::Time::UnixEpoch();
-  out->SetString("timestamp",
-                 base::NumberToString(time_since_unix_epoch.InMilliseconds()));
-  SetBinaryData("extensions", sct.extensions, out.get());
+  out.SetStringKey("timestamp", base::NumberToString(
+                                    time_since_unix_epoch.InMilliseconds()));
+  SetBinaryData("extensions", sct.extensions, &out);
 
-  out->SetString("hash_algorithm",
-                 HashAlgorithmToString(sct.signature.hash_algorithm));
-  out->SetString("signature_algorithm",
-                 SignatureAlgorithmToString(sct.signature.signature_algorithm));
-  SetBinaryData("signature_data", sct.signature.signature_data, out.get());
+  out.SetStringKey("hash_algorithm",
+                   HashAlgorithmToString(sct.signature.hash_algorithm));
+  out.SetStringKey(
+      "signature_algorithm",
+      SignatureAlgorithmToString(sct.signature.signature_algorithm));
+  SetBinaryData("signature_data", sct.signature.signature_data, &out);
 
   return out;
 }
 
-// Given a list of SCTs and their statuses, return a ListValue instance where
-// each item in the list is a dictionary created by SCTToDictionary.
-std::unique_ptr<base::ListValue> SCTListToPrintableValues(
+// Given a list of SCTs and their statuses, return a list Value where each item
+// is a dictionary created by SCTToDictionary.
+base::Value SCTListToPrintableValues(
     const SignedCertificateTimestampAndStatusList& sct_and_status_list) {
-  std::unique_ptr<base::ListValue> output_scts(new base::ListValue());
+  base::Value output_scts(base::Value::Type::LIST);
   for (const auto& sct_and_status : sct_and_status_list)
-    output_scts->Append(
+    output_scts.GetList().push_back(
         SCTToDictionary(*(sct_and_status.sct.get()), sct_and_status.status));
 
   return output_scts;
@@ -72,29 +72,28 @@
 
 }  // namespace
 
-std::unique_ptr<base::Value> NetLogSignedCertificateTimestampCallback(
+base::Value NetLogSignedCertificateTimestampCallback(
     const SignedCertificateTimestampAndStatusList* scts,
     NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  base::Value dict(base::Value::Type::DICTIONARY);
 
-  dict->Set("scts", SCTListToPrintableValues(*scts));
+  dict.SetKey("scts", SCTListToPrintableValues(*scts));
 
-  return std::move(dict);
+  return dict;
 }
 
-std::unique_ptr<base::Value> NetLogRawSignedCertificateTimestampCallback(
+base::Value NetLogRawSignedCertificateTimestampCallback(
     base::StringPiece embedded_scts,
     base::StringPiece sct_list_from_ocsp,
     base::StringPiece sct_list_from_tls_extension,
     NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  base::Value dict(base::Value::Type::DICTIONARY);
 
-  SetBinaryData("embedded_scts", embedded_scts, dict.get());
-  SetBinaryData("scts_from_ocsp_response", sct_list_from_ocsp, dict.get());
-  SetBinaryData("scts_from_tls_extension", sct_list_from_tls_extension,
-                dict.get());
+  SetBinaryData("embedded_scts", embedded_scts, &dict);
+  SetBinaryData("scts_from_ocsp_response", sct_list_from_ocsp, &dict);
+  SetBinaryData("scts_from_tls_extension", sct_list_from_tls_extension, &dict);
 
-  return std::move(dict);
+  return dict;
 }
 
 }  // namespace net
diff --git a/net/cert/ct_signed_certificate_timestamp_log_param.h b/net/cert/ct_signed_certificate_timestamp_log_param.h
index c2149cdb..89eb38ce 100644
--- a/net/cert/ct_signed_certificate_timestamp_log_param.h
+++ b/net/cert/ct_signed_certificate_timestamp_log_param.h
@@ -22,7 +22,7 @@
 // logged in the NetLog.
 // See the documentation for SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED
 // in net/log/net_log_event_type_list.h
-std::unique_ptr<base::Value> NetLogSignedCertificateTimestampCallback(
+base::Value NetLogSignedCertificateTimestampCallback(
     const SignedCertificateTimestampAndStatusList* scts,
     NetLogCaptureMode capture_mode);
 
@@ -30,7 +30,7 @@
 // in the NetLog.
 // See the documentation for SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED
 // in net/log/net_log_event_type_list.h
-std::unique_ptr<base::Value> NetLogRawSignedCertificateTimestampCallback(
+base::Value NetLogRawSignedCertificateTimestampCallback(
     base::StringPiece embedded_scts,
     base::StringPiece sct_list_from_ocsp,
     base::StringPiece sct_list_from_tls_extension,
diff --git a/net/cert/multi_threaded_cert_verifier.cc b/net/cert/multi_threaded_cert_verifier.cc
index 3b33d17..c578a473 100644
--- a/net/cert/multi_threaded_cert_verifier.cc
+++ b/net/cert/multi_threaded_cert_verifier.cc
@@ -83,28 +83,27 @@
 
 namespace {
 
-std::unique_ptr<base::Value> CertVerifyResultCallback(
-    const CertVerifyResult& verify_result,
-    NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> results(new base::DictionaryValue());
-  results->SetBoolean("has_md5", verify_result.has_md5);
-  results->SetBoolean("has_md2", verify_result.has_md2);
-  results->SetBoolean("has_md4", verify_result.has_md4);
-  results->SetBoolean("is_issued_by_known_root",
-                      verify_result.is_issued_by_known_root);
-  results->SetBoolean("is_issued_by_additional_trust_anchor",
-                      verify_result.is_issued_by_additional_trust_anchor);
-  results->SetInteger("cert_status", verify_result.cert_status);
-  results->Set("verified_cert",
-               NetLogX509CertificateCallback(verify_result.verified_cert.get(),
-                                             capture_mode));
+base::Value CertVerifyResultCallback(const CertVerifyResult& verify_result,
+                                     NetLogCaptureMode capture_mode) {
+  base::DictionaryValue results;
+  results.SetBoolean("has_md5", verify_result.has_md5);
+  results.SetBoolean("has_md2", verify_result.has_md2);
+  results.SetBoolean("has_md4", verify_result.has_md4);
+  results.SetBoolean("is_issued_by_known_root",
+                     verify_result.is_issued_by_known_root);
+  results.SetBoolean("is_issued_by_additional_trust_anchor",
+                     verify_result.is_issued_by_additional_trust_anchor);
+  results.SetInteger("cert_status", verify_result.cert_status);
+  results.SetKey("verified_cert",
+                 NetLogX509CertificateCallback(
+                     verify_result.verified_cert.get(), capture_mode));
 
   std::unique_ptr<base::ListValue> hashes(new base::ListValue());
   for (auto it = verify_result.public_key_hashes.begin();
        it != verify_result.public_key_hashes.end(); ++it) {
     hashes->AppendString(it->ToString());
   }
-  results->Set("public_key_hashes", std::move(hashes));
+  results.Set("public_key_hashes", std::move(hashes));
 
   return std::move(results);
 }
diff --git a/net/cert/trial_comparison_cert_verifier.cc b/net/cert/trial_comparison_cert_verifier.cc
index eb82799d..c3e71d2 100644
--- a/net/cert/trial_comparison_cert_verifier.cc
+++ b/net/cert/trial_comparison_cert_verifier.cc
@@ -31,12 +31,11 @@
 
 namespace {
 
-std::unique_ptr<base::Value> TrialVerificationJobResultCallback(
-    bool trial_success,
-    NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> results(new base::DictionaryValue());
-  results->SetKey("trial_success", base::Value(trial_success));
-  return std::move(results);
+base::Value TrialVerificationJobResultCallback(bool trial_success,
+                                               NetLogCaptureMode capture_mode) {
+  base::Value results(base::Value::Type::DICTIONARY);
+  results.SetBoolKey("trial_success", trial_success);
+  return results;
 }
 
 bool CertVerifyResultEqual(const CertVerifyResult& a,
diff --git a/net/cert/x509_certificate_net_log_param.cc b/net/cert/x509_certificate_net_log_param.cc
index d1cebd0..d2ddf07 100644
--- a/net/cert/x509_certificate_net_log_param.cc
+++ b/net/cert/x509_certificate_net_log_param.cc
@@ -15,17 +15,16 @@
 
 namespace net {
 
-std::unique_ptr<base::Value> NetLogX509CertificateCallback(
-    const X509Certificate* certificate,
-    NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  std::unique_ptr<base::ListValue> certs(new base::ListValue());
+base::Value NetLogX509CertificateCallback(const X509Certificate* certificate,
+                                          NetLogCaptureMode capture_mode) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  base::Value certs(base::Value::Type::LIST);
   std::vector<std::string> encoded_chain;
   certificate->GetPEMEncodedChain(&encoded_chain);
-  for (size_t i = 0; i < encoded_chain.size(); ++i)
-    certs->AppendString(encoded_chain[i]);
-  dict->Set("certificates", std::move(certs));
-  return std::move(dict);
+  for (auto& pem : encoded_chain)
+    certs.GetList().emplace_back(std::move(pem));
+  dict.SetKey("certificates", std::move(certs));
+  return dict;
 }
 
 }  // namespace net
diff --git a/net/cert/x509_certificate_net_log_param.h b/net/cert/x509_certificate_net_log_param.h
index ecde73c..c328189 100644
--- a/net/cert/x509_certificate_net_log_param.h
+++ b/net/cert/x509_certificate_net_log_param.h
@@ -19,7 +19,7 @@
 class X509Certificate;
 
 // Creates NetLog parameter to describe an X509Certificate.
-NET_EXPORT std::unique_ptr<base::Value> NetLogX509CertificateCallback(
+NET_EXPORT base::Value NetLogX509CertificateCallback(
     const X509Certificate* certificate,
     NetLogCaptureMode capture_mode);
 
diff --git a/net/cookies/cookie_monster_netlog_params.cc b/net/cookies/cookie_monster_netlog_params.cc
index dff8422..f7a58843 100644
--- a/net/cookies/cookie_monster_netlog_params.cc
+++ b/net/cookies/cookie_monster_netlog_params.cc
@@ -9,108 +9,98 @@
 
 namespace net {
 
-std::unique_ptr<base::Value> NetLogCookieMonsterConstructorCallback(
+base::Value NetLogCookieMonsterConstructorCallback(
     bool persistent_store,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::Value> dict =
-      std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
-  dict->SetKey("persistent_store", base::Value(persistent_store));
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetBoolKey("persistent_store", persistent_store);
   return dict;
 }
 
-std::unique_ptr<base::Value> NetLogCookieMonsterCookieAdded(
-    const CanonicalCookie* cookie,
-    bool sync_requested,
-    NetLogCaptureMode capture_mode) {
+base::Value NetLogCookieMonsterCookieAdded(const CanonicalCookie* cookie,
+                                           bool sync_requested,
+                                           NetLogCaptureMode capture_mode) {
   if (!capture_mode.include_cookies_and_credentials())
-    return nullptr;
+    return base::Value();
 
-  std::unique_ptr<base::Value> dict =
-      std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
-  dict->SetKey("name", base::Value(cookie->Name()));
-  dict->SetKey("value", base::Value(cookie->Value()));
-  dict->SetKey("domain", base::Value(cookie->Domain()));
-  dict->SetKey("path", base::Value(cookie->Path()));
-  dict->SetKey("httponly", base::Value(cookie->IsHttpOnly()));
-  dict->SetKey("secure", base::Value(cookie->IsSecure()));
-  dict->SetKey("priority",
-               base::Value(CookiePriorityToString(cookie->Priority())));
-  dict->SetKey("same_site",
-               base::Value(CookieSameSiteToString(cookie->SameSite())));
-  dict->SetKey("is_persistent", base::Value(cookie->IsPersistent()));
-  dict->SetKey("sync_requested", base::Value(sync_requested));
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetStringKey("name", cookie->Name());
+  dict.SetStringKey("value", cookie->Value());
+  dict.SetStringKey("domain", cookie->Domain());
+  dict.SetStringKey("path", cookie->Path());
+  dict.SetBoolKey("httponly", cookie->IsHttpOnly());
+  dict.SetBoolKey("secure", cookie->IsSecure());
+  dict.SetStringKey("priority", CookiePriorityToString(cookie->Priority()));
+  dict.SetStringKey("same_site", CookieSameSiteToString(cookie->SameSite()));
+  dict.SetBoolKey("is_persistent", cookie->IsPersistent());
+  dict.SetBoolKey("sync_requested", sync_requested);
   return dict;
 }
 
-std::unique_ptr<base::Value> NetLogCookieMonsterCookieDeleted(
-    const CanonicalCookie* cookie,
-    CookieChangeCause cause,
-    bool sync_requested,
-    NetLogCaptureMode capture_mode) {
+base::Value NetLogCookieMonsterCookieDeleted(const CanonicalCookie* cookie,
+                                             CookieChangeCause cause,
+                                             bool sync_requested,
+                                             NetLogCaptureMode capture_mode) {
   if (!capture_mode.include_cookies_and_credentials())
-    return nullptr;
+    return base::Value();
 
-  std::unique_ptr<base::Value> dict =
-      std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
-  dict->SetKey("name", base::Value(cookie->Name()));
-  dict->SetKey("value", base::Value(cookie->Value()));
-  dict->SetKey("domain", base::Value(cookie->Domain()));
-  dict->SetKey("path", base::Value(cookie->Path()));
-  dict->SetKey("is_persistent", base::Value(cookie->IsPersistent()));
-  dict->SetKey("deletion_cause", base::Value(CookieChangeCauseToString(cause)));
-  dict->SetKey("sync_requested", base::Value(sync_requested));
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetStringKey("name", cookie->Name());
+  dict.SetStringKey("value", cookie->Value());
+  dict.SetStringKey("domain", cookie->Domain());
+  dict.SetStringKey("path", cookie->Path());
+  dict.SetBoolKey("is_persistent", cookie->IsPersistent());
+  dict.SetStringKey("deletion_cause", CookieChangeCauseToString(cause));
+  dict.SetBoolKey("sync_requested", sync_requested);
   return dict;
 }
 
-std::unique_ptr<base::Value> NetLogCookieMonsterCookieRejectedSecure(
+base::Value NetLogCookieMonsterCookieRejectedSecure(
     const CanonicalCookie* old_cookie,
     const CanonicalCookie* new_cookie,
     NetLogCaptureMode capture_mode) {
   if (!capture_mode.include_cookies_and_credentials())
-    return nullptr;
-  std::unique_ptr<base::Value> dict =
-      std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
-  dict->SetKey("name", base::Value(old_cookie->Name()));
-  dict->SetKey("domain", base::Value(old_cookie->Domain()));
-  dict->SetKey("oldpath", base::Value(old_cookie->Path()));
-  dict->SetKey("newpath", base::Value(new_cookie->Path()));
-  dict->SetKey("oldvalue", base::Value(old_cookie->Value()));
-  dict->SetKey("newvalue", base::Value(new_cookie->Value()));
+    return base::Value();
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetStringKey("name", old_cookie->Name());
+  dict.SetStringKey("domain", old_cookie->Domain());
+  dict.SetStringKey("oldpath", old_cookie->Path());
+  dict.SetStringKey("newpath", new_cookie->Path());
+  dict.SetStringKey("oldvalue", old_cookie->Value());
+  dict.SetStringKey("newvalue", new_cookie->Value());
   return dict;
 }
 
-std::unique_ptr<base::Value> NetLogCookieMonsterCookieRejectedHttponly(
+base::Value NetLogCookieMonsterCookieRejectedHttponly(
     const CanonicalCookie* old_cookie,
     const CanonicalCookie* new_cookie,
     NetLogCaptureMode capture_mode) {
   if (!capture_mode.include_cookies_and_credentials())
-    return nullptr;
-  std::unique_ptr<base::Value> dict =
-      std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
-  dict->SetKey("name", base::Value(old_cookie->Name()));
-  dict->SetKey("domain", base::Value(old_cookie->Domain()));
-  dict->SetKey("path", base::Value(old_cookie->Path()));
-  dict->SetKey("oldvalue", base::Value(old_cookie->Value()));
-  dict->SetKey("newvalue", base::Value(new_cookie->Value()));
+    return base::Value();
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetStringKey("name", old_cookie->Name());
+  dict.SetStringKey("domain", old_cookie->Domain());
+  dict.SetStringKey("path", old_cookie->Path());
+  dict.SetStringKey("oldvalue", old_cookie->Value());
+  dict.SetStringKey("newvalue", new_cookie->Value());
   return dict;
 }
 
-std::unique_ptr<base::Value> NetLogCookieMonsterCookiePreservedSkippedSecure(
+base::Value NetLogCookieMonsterCookiePreservedSkippedSecure(
     const CanonicalCookie* skipped_secure,
     const CanonicalCookie* preserved,
     const CanonicalCookie* new_cookie,
     NetLogCaptureMode capture_mode) {
   if (!capture_mode.include_cookies_and_credentials())
-    return nullptr;
-  std::unique_ptr<base::Value> dict =
-      std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
-  dict->SetKey("name", base::Value(preserved->Name()));
-  dict->SetKey("domain", base::Value(preserved->Domain()));
-  dict->SetKey("path", base::Value(preserved->Path()));
-  dict->SetKey("securecookiedomain", base::Value(skipped_secure->Domain()));
-  dict->SetKey("securecookiepath", base::Value(skipped_secure->Path()));
-  dict->SetKey("preservedvalue", base::Value(preserved->Value()));
-  dict->SetKey("discardedvalue", base::Value(new_cookie->Value()));
+    return base::Value();
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetStringKey("name", preserved->Name());
+  dict.SetStringKey("domain", preserved->Domain());
+  dict.SetStringKey("path", preserved->Path());
+  dict.SetStringKey("securecookiedomain", skipped_secure->Domain());
+  dict.SetStringKey("securecookiepath", skipped_secure->Path());
+  dict.SetStringKey("preservedvalue", preserved->Value());
+  dict.SetStringKey("discardedvalue", new_cookie->Value());
   return dict;
 }
 
diff --git a/net/cookies/cookie_monster_netlog_params.h b/net/cookies/cookie_monster_netlog_params.h
index 16ab7727..a00e65b2 100644
--- a/net/cookies/cookie_monster_netlog_params.h
+++ b/net/cookies/cookie_monster_netlog_params.h
@@ -18,33 +18,31 @@
 
 // Returns a Value containing NetLog parameters for constructing
 // a CookieMonster.
-std::unique_ptr<base::Value> NetLogCookieMonsterConstructorCallback(
+base::Value NetLogCookieMonsterConstructorCallback(
     bool persistent_store,
     NetLogCaptureMode capture_mode);
 
 // Returns a Value containing NetLog parameters for adding a cookie.
-std::unique_ptr<base::Value> NetLogCookieMonsterCookieAdded(
-    const CanonicalCookie* cookie,
-    bool sync_requested,
-    NetLogCaptureMode capture_mode);
+base::Value NetLogCookieMonsterCookieAdded(const CanonicalCookie* cookie,
+                                           bool sync_requested,
+                                           NetLogCaptureMode capture_mode);
 
 // Returns a Value containing NetLog parameters for deleting a cookie.
-std::unique_ptr<base::Value> NetLogCookieMonsterCookieDeleted(
-    const CanonicalCookie* cookie,
-    CookieChangeCause cause,
-    bool sync_requested,
-    NetLogCaptureMode capture_mode);
+base::Value NetLogCookieMonsterCookieDeleted(const CanonicalCookie* cookie,
+                                             CookieChangeCause cause,
+                                             bool sync_requested,
+                                             NetLogCaptureMode capture_mode);
 
 // Returns a Value containing NetLog parameters for when a cookie addition
 // is rejected because of a conflict with a secure cookie.
-std::unique_ptr<base::Value> NetLogCookieMonsterCookieRejectedSecure(
+base::Value NetLogCookieMonsterCookieRejectedSecure(
     const CanonicalCookie* old_cookie,
     const CanonicalCookie* new_cookie,
     NetLogCaptureMode capture_mode);
 
 // Returns a Value containing NetLog parameters for when a cookie addition
 // is rejected because of a conflict with an httponly cookie.
-std::unique_ptr<base::Value> NetLogCookieMonsterCookieRejectedHttponly(
+base::Value NetLogCookieMonsterCookieRejectedHttponly(
     const CanonicalCookie* old_cookie,
     const CanonicalCookie* new_cookie,
     NetLogCaptureMode capture_mode);
@@ -53,7 +51,7 @@
 // cookie addition which is rejected due to a conflict with a secure cookie, a
 // pre-existing cookie would have been deleted but is instead preserved because
 // the addition failed.
-std::unique_ptr<base::Value> NetLogCookieMonsterCookiePreservedSkippedSecure(
+base::Value NetLogCookieMonsterCookiePreservedSkippedSecure(
     const CanonicalCookie* skipped_secure,
     const CanonicalCookie* preserved,
     const CanonicalCookie* new_cookie,
diff --git a/net/disk_cache/memory/mem_entry_impl.cc b/net/disk_cache/memory/mem_entry_impl.cc
index 2608970a..61845b9 100644
--- a/net/disk_cache/memory/mem_entry_impl.cc
+++ b/net/disk_cache/memory/mem_entry_impl.cc
@@ -70,10 +70,10 @@
 
 // Returns NetLog parameters for the creation of a MemEntryImpl. A separate
 // function is needed because child entries don't store their key().
-std::unique_ptr<base::Value> NetLogEntryCreationCallback(
+base::Value NetLogEntryCreationCallback(
     const MemEntryImpl* entry,
     net::NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  base::Value dict(base::Value::Type::DICTIONARY);
   std::string key;
   switch (entry->type()) {
     case MemEntryImpl::PARENT_ENTRY:
@@ -83,9 +83,9 @@
       key = GenerateChildName(entry->parent()->key(), entry->child_id());
       break;
   }
-  dict->SetString("key", key);
-  dict->SetBoolean("created", true);
-  return std::move(dict);
+  dict.SetStringKey("key", key);
+  dict.SetBoolKey("created", true);
+  return dict;
 }
 
 }  // namespace
diff --git a/net/disk_cache/net_log_parameters.cc b/net/disk_cache/net_log_parameters.cc
index 5dd6414..b3e1bbb 100644
--- a/net/disk_cache/net_log_parameters.cc
+++ b/net/disk_cache/net_log_parameters.cc
@@ -18,76 +18,76 @@
 
 namespace {
 
-std::unique_ptr<base::Value> NetLogParametersEntryCreationCallback(
+base::Value NetLogParametersEntryCreationCallback(
     const disk_cache::Entry* entry,
     bool created,
     net::NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("key", entry->GetKey());
-  dict->SetBoolean("created", created);
-  return std::move(dict);
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetStringKey("key", entry->GetKey());
+  dict.SetBoolKey("created", created);
+  return dict;
 }
 
-std::unique_ptr<base::Value> NetLogReadWriteDataCallback(
+base::Value NetLogReadWriteDataCallback(
     int index,
     int offset,
     int buf_len,
     bool truncate,
     net::NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("index", index);
-  dict->SetInteger("offset", offset);
-  dict->SetInteger("buf_len", buf_len);
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetIntKey("index", index);
+  dict.SetIntKey("offset", offset);
+  dict.SetIntKey("buf_len", buf_len);
   if (truncate)
-    dict->SetBoolean("truncate", truncate);
-  return std::move(dict);
+    dict.SetBoolKey("truncate", truncate);
+  return dict;
 }
 
-std::unique_ptr<base::Value> NetLogReadWriteCompleteCallback(
+base::Value NetLogReadWriteCompleteCallback(
     int bytes_copied,
     net::NetLogCaptureMode /* capture_mode */) {
   DCHECK_NE(bytes_copied, net::ERR_IO_PENDING);
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  base::Value dict(base::Value::Type::DICTIONARY);
   if (bytes_copied < 0) {
-    dict->SetInteger("net_error", bytes_copied);
+    dict.SetIntKey("net_error", bytes_copied);
   } else {
-    dict->SetInteger("bytes_copied", bytes_copied);
+    dict.SetIntKey("bytes_copied", bytes_copied);
   }
-  return std::move(dict);
+  return dict;
 }
 
-std::unique_ptr<base::Value> NetLogSparseOperationCallback(
+base::Value NetLogSparseOperationCallback(
     int64_t offset,
     int buf_len,
     net::NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetKey("offset", net::NetLogNumberValue(offset));
-  dict->SetInteger("buf_len", buf_len);
-  return std::move(dict);
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey("offset", net::NetLogNumberValue(offset));
+  dict.SetIntKey("buf_len", buf_len);
+  return dict;
 }
 
-std::unique_ptr<base::Value> NetLogSparseReadWriteCallback(
+base::Value NetLogSparseReadWriteCallback(
     const net::NetLogSource& source,
     int child_len,
     net::NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  source.AddToEventParameters(dict.get());
-  dict->SetInteger("child_len", child_len);
-  return std::move(dict);
+  base::Value dict(base::Value::Type::DICTIONARY);
+  source.AddToEventParameters(&dict);
+  dict.SetIntKey("child_len", child_len);
+  return dict;
 }
 
-std::unique_ptr<base::Value> NetLogGetAvailableRangeResultCallback(
+base::Value NetLogGetAvailableRangeResultCallback(
     int64_t start,
     int result,
     net::NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  base::Value dict(base::Value::Type::DICTIONARY);
   if (result > 0) {
-    dict->SetInteger("length", result);
-    dict->SetKey("start", net::NetLogNumberValue(start));
+    dict.SetIntKey("length", result);
+    dict.SetKey("start", net::NetLogNumberValue(start));
   } else {
-    dict->SetInteger("net_error", result);
+    dict.SetIntKey("net_error", result);
   }
-  return std::move(dict);
+  return dict;
 }
 
 }  // namespace
diff --git a/net/disk_cache/simple/simple_net_log_parameters.cc b/net/disk_cache/simple/simple_net_log_parameters.cc
index de32caee..63f1c14 100644
--- a/net/disk_cache/simple/simple_net_log_parameters.cc
+++ b/net/disk_cache/simple/simple_net_log_parameters.cc
@@ -18,24 +18,24 @@
 
 namespace {
 
-std::unique_ptr<base::Value> NetLogSimpleEntryConstructionCallback(
+base::Value NetLogSimpleEntryConstructionCallback(
     const disk_cache::SimpleEntryImpl* entry,
     net::NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("entry_hash",
-                  base::StringPrintf("%#016" PRIx64, entry->entry_hash()));
-  return std::move(dict);
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetStringKey("entry_hash",
+                    base::StringPrintf("%#016" PRIx64, entry->entry_hash()));
+  return dict;
 }
 
-std::unique_ptr<base::Value> NetLogSimpleEntryCreationCallback(
+base::Value NetLogSimpleEntryCreationCallback(
     const disk_cache::SimpleEntryImpl* entry,
     int net_error,
     net::NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("net_error", net_error);
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetIntKey("net_error", net_error);
   if (net_error == net::OK)
-    dict->SetString("key", entry->key());
-  return std::move(dict);
+    dict.SetStringKey("key", entry->key());
+  return dict;
 }
 
 }  // namespace
diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc
index 198b3dd4..e0b86e9 100644
--- a/net/dns/dns_transaction.cc
+++ b/net/dns/dns_transaction.cc
@@ -107,13 +107,12 @@
   return ip.AssignFromIPLiteral(hostname);
 }
 
-std::unique_ptr<base::Value> NetLogStartCallback(
-    const std::string* hostname,
-    uint16_t qtype,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("hostname", *hostname);
-  dict->SetInteger("query_type", qtype);
+base::Value NetLogStartCallback(const std::string* hostname,
+                                uint16_t qtype,
+                                NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  dict.SetString("hostname", *hostname);
+  dict.SetInteger("query_type", qtype);
   return std::move(dict);
 }
 
@@ -152,14 +151,13 @@
   // Returns a Value representing the received response, along with a reference
   // to the NetLog source source of the UDP socket used.  The request must have
   // completed before this is called.
-  std::unique_ptr<base::Value> NetLogResponseCallback(
-      NetLogCaptureMode capture_mode) const {
+  base::Value NetLogResponseCallback(NetLogCaptureMode capture_mode) const {
     DCHECK(GetResponse()->IsValid());
 
-    std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-    dict->SetInteger("rcode", GetResponse()->rcode());
-    dict->SetInteger("answer_count", GetResponse()->answer_count());
-    GetSocketNetLog().source().AddToEventParameters(dict.get());
+    base::DictionaryValue dict;
+    dict.SetInteger("rcode", GetResponse()->rcode());
+    dict.SetInteger("answer_count", GetResponse()->answer_count());
+    GetSocketNetLog().source().AddToEventParameters(&dict);
     return std::move(dict);
   }
 
diff --git a/net/dns/dns_transaction_unittest.cc b/net/dns/dns_transaction_unittest.cc
index 01ad86b..a488787 100644
--- a/net/dns/dns_transaction_unittest.cc
+++ b/net/dns/dns_transaction_unittest.cc
@@ -2021,8 +2021,8 @@
 
   void OnAddEntry(const NetLogEntry& entry) override {
     ++count_;
-    std::unique_ptr<base::Value> value = entry.ParametersToValue();
-    if (value && value->is_dict())
+    base::Value value = entry.ParametersToValue();
+    if (!value.is_none() && value.is_dict())
       dict_count_++;
   }
 
diff --git a/net/dns/host_cache.cc b/net/dns/host_cache.cc
index 8a027de..1c33f82 100644
--- a/net/dns/host_cache.cc
+++ b/net/dns/host_cache.cc
@@ -295,10 +295,9 @@
   out->stale_hits = stale_hits_;
 }
 
-std::unique_ptr<base::Value> HostCache::Entry::NetLogCallback(
+base::Value HostCache::Entry::NetLogCallback(
     NetLogCaptureMode capture_mode) const {
-  return std::make_unique<base::Value>(
-      GetAsValue(false /* include_staleness */));
+  return GetAsValue(false /* include_staleness */);
 }
 
 base::DictionaryValue HostCache::Entry::GetAsValue(
diff --git a/net/dns/host_cache.h b/net/dns/host_cache.h
index 7c717ba..e661652 100644
--- a/net/dns/host_cache.h
+++ b/net/dns/host_cache.h
@@ -219,8 +219,7 @@
                       int network_changes,
                       EntryStaleness* out) const;
 
-    std::unique_ptr<base::Value> NetLogCallback(
-        NetLogCaptureMode capture_mode) const;
+    base::Value NetLogCallback(NetLogCaptureMode capture_mode) const;
     base::DictionaryValue GetAsValue(bool include_staleness) const;
 
     // The resolve results for this entry.
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index 9610355..94b895a 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -271,19 +271,18 @@
 }
 
 // Creates NetLog parameters when the resolve failed.
-std::unique_ptr<base::Value> NetLogProcTaskFailedCallback(
-    uint32_t attempt_number,
-    int net_error,
-    int os_error,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+base::Value NetLogProcTaskFailedCallback(uint32_t attempt_number,
+                                         int net_error,
+                                         int os_error,
+                                         NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
   if (attempt_number)
-    dict->SetInteger("attempt_number", attempt_number);
+    dict.SetInteger("attempt_number", attempt_number);
 
-  dict->SetInteger("net_error", net_error);
+  dict.SetInteger("net_error", net_error);
 
   if (os_error) {
-    dict->SetInteger("os_error", os_error);
+    dict.SetInteger("os_error", os_error);
 #if defined(OS_WIN)
     // Map the error code to a human-readable string.
     LPWSTR error_string = nullptr;
@@ -294,10 +293,10 @@
                   (LPWSTR)&error_string,
                   0,         // Buffer size.
                   nullptr);  // Arguments (unused).
-    dict->SetString("os_error_string", base::WideToUTF8(error_string));
+    dict.SetString("os_error_string", base::WideToUTF8(error_string));
     LocalFree(error_string);
 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-    dict->SetString("os_error_string", gai_strerror(os_error));
+    dict.SetString("os_error_string", gai_strerror(os_error));
 #endif
   }
 
@@ -305,71 +304,66 @@
 }
 
 // Creates NetLog parameters when the DnsTask failed.
-std::unique_ptr<base::Value> NetLogDnsTaskFailedCallback(
+base::Value NetLogDnsTaskFailedCallback(
     int net_error,
     int dns_error,
     NetLogParametersCallback results_callback,
     NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("net_error", net_error);
+  base::DictionaryValue dict;
+  dict.SetInteger("net_error", net_error);
   if (dns_error)
-    dict->SetInteger("dns_error", dns_error);
+    dict.SetInteger("dns_error", dns_error);
   if (results_callback)
-    dict->Set("resolve_results", results_callback.Run(capture_mode));
+    dict.SetKey("resolve_results", results_callback.Run(capture_mode));
   return std::move(dict);
 }
 
 // Creates NetLog parameters containing the information of the request. Use
 // NetLogRequestInfoCallback if the request is specified via RequestInfo.
-std::unique_ptr<base::Value> NetLogRequestCallback(
-    const HostPortPair& host,
-    NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
+base::Value NetLogRequestCallback(const HostPortPair& host,
+                                  NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
 
-  dict->SetString("host", host.ToString());
-  dict->SetInteger("address_family",
-                   static_cast<int>(ADDRESS_FAMILY_UNSPECIFIED));
-  dict->SetBoolean("allow_cached_response", true);
-  dict->SetBoolean("is_speculative", false);
+  dict.SetString("host", host.ToString());
+  dict.SetInteger("address_family",
+                  static_cast<int>(ADDRESS_FAMILY_UNSPECIFIED));
+  dict.SetBoolean("allow_cached_response", true);
+  dict.SetBoolean("is_speculative", false);
   return std::move(dict);
 }
 
 // Creates NetLog parameters for the creation of a HostResolverManager::Job.
-std::unique_ptr<base::Value> NetLogJobCreationCallback(
-    const NetLogSource& source,
-    const std::string* host,
-    NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  source.AddToEventParameters(dict.get());
-  dict->SetString("host", *host);
+base::Value NetLogJobCreationCallback(const NetLogSource& source,
+                                      const std::string* host,
+                                      NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  source.AddToEventParameters(&dict);
+  dict.SetString("host", *host);
   return std::move(dict);
 }
 
 // Creates NetLog parameters for HOST_RESOLVER_IMPL_JOB_ATTACH/DETACH events.
-std::unique_ptr<base::Value> NetLogJobAttachCallback(
-    const NetLogSource& source,
-    RequestPriority priority,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  source.AddToEventParameters(dict.get());
-  dict->SetString("priority", RequestPriorityToString(priority));
+base::Value NetLogJobAttachCallback(const NetLogSource& source,
+                                    RequestPriority priority,
+                                    NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  source.AddToEventParameters(&dict);
+  dict.SetString("priority", RequestPriorityToString(priority));
   return std::move(dict);
 }
 
 // Creates NetLog parameters for the DNS_CONFIG_CHANGED event.
-std::unique_ptr<base::Value> NetLogDnsConfigCallback(
-    const DnsConfig* config,
-    NetLogCaptureMode /* capture_mode */) {
-  return config->ToValue();
+base::Value NetLogDnsConfigCallback(const DnsConfig* config,
+                                    NetLogCaptureMode /* capture_mode */) {
+  return base::Value::FromUniquePtrValue(config->ToValue());
 }
 
-std::unique_ptr<base::Value> NetLogIPv6AvailableCallback(
-    bool ipv6_available,
-    bool cached,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetBoolean("ipv6_available", ipv6_available);
-  dict->SetBoolean("cached", cached);
+base::Value NetLogIPv6AvailableCallback(bool ipv6_available,
+                                        bool cached,
+                                        NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  dict.SetBoolean("ipv6_available", ipv6_available);
+  dict.SetBoolean("cached", cached);
   return std::move(dict);
 }
 
diff --git a/net/extras/sqlite/sqlite_persistent_cookie_store.cc b/net/extras/sqlite/sqlite_persistent_cookie_store.cc
index 64674de..a1b65f3 100644
--- a/net/extras/sqlite/sqlite_persistent_cookie_store.cc
+++ b/net/extras/sqlite/sqlite_persistent_cookie_store.cc
@@ -45,14 +45,13 @@
 
 namespace {
 
-std::unique_ptr<base::Value> CookieKeyedLoadNetLogCallback(
-    const std::string& key,
-    net::NetLogCaptureMode capture_mode) {
+base::Value CookieKeyedLoadNetLogCallback(const std::string& key,
+                                          net::NetLogCaptureMode capture_mode) {
   if (!capture_mode.include_cookies_and_credentials())
-    return nullptr;
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetString("key", key);
-  return dict;
+    return base::Value();
+  base::DictionaryValue dict;
+  dict.SetString("key", key);
+  return std::move(dict);
 }
 
 // Used to populate a histogram for problems when loading cookies.
diff --git a/net/ftp/ftp_ctrl_response_buffer.cc b/net/ftp/ftp_ctrl_response_buffer.cc
index af924fa..997c2c6 100644
--- a/net/ftp/ftp_ctrl_response_buffer.cc
+++ b/net/ftp/ftp_ctrl_response_buffer.cc
@@ -84,16 +84,15 @@
 
 namespace {
 
-std::unique_ptr<base::Value> NetLogFtpCtrlResponseCallback(
-    const FtpCtrlResponse* response,
-    NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::ListValue> lines(new base::ListValue());
+base::Value NetLogFtpCtrlResponseCallback(const FtpCtrlResponse* response,
+                                          NetLogCaptureMode capture_mode) {
+  base::ListValue lines;
   for (const auto& line : response->lines)
-    lines->GetList().push_back(NetLogStringValue(line));
+    lines.GetList().push_back(NetLogStringValue(line));
 
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("status_code", response->status_code);
-  dict->Set("lines", std::move(lines));
+  base::DictionaryValue dict;
+  dict.SetInteger("status_code", response->status_code);
+  dict.SetKey("lines", std::move(lines));
   return std::move(dict);
 }
 
diff --git a/net/http/bidirectional_stream.cc b/net/http/bidirectional_stream.cc
index 1279ef4..780b8e4 100644
--- a/net/http/bidirectional_stream.cc
+++ b/net/http/bidirectional_stream.cc
@@ -36,25 +36,23 @@
 
 namespace {
 
-std::unique_ptr<base::Value> NetLogHeadersCallback(
-    const spdy::SpdyHeaderBlock* headers,
-    NetLogCaptureMode capture_mode) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->Set("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode));
+base::Value NetLogHeadersCallback(const spdy::SpdyHeaderBlock* headers,
+                                  NetLogCaptureMode capture_mode) {
+  base::DictionaryValue dict;
+  dict.SetKey("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode));
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogCallback(const GURL* url,
-                                            const std::string* method,
-                                            const HttpRequestHeaders* headers,
-                                            NetLogCaptureMode capture_mode) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetString("url", url->possibly_invalid_spec());
-  dict->SetString("method", *method);
+base::Value NetLogCallback(const GURL* url,
+                           const std::string* method,
+                           const HttpRequestHeaders* headers,
+                           NetLogCaptureMode capture_mode) {
+  base::DictionaryValue dict;
+  dict.SetString("url", url->possibly_invalid_spec());
+  dict.SetString("method", *method);
   std::string empty;
-  std::unique_ptr<base::Value> headers_param(
-      headers->NetLogCallback(&empty, capture_mode));
-  dict->Set("headers", std::move(headers_param));
+  base::Value headers_param(headers->NetLogCallback(&empty, capture_mode));
+  dict.SetKey("headers", std::move(headers_param));
   return std::move(dict);
 }
 
diff --git a/net/http/http_auth_handler_negotiate.cc b/net/http/http_auth_handler_negotiate.cc
index 72515cf..a55a3e4f 100644
--- a/net/http/http_auth_handler_negotiate.cc
+++ b/net/http/http_auth_handler_negotiate.cc
@@ -32,16 +32,16 @@
 
 namespace {
 
-std::unique_ptr<base::Value> NetLogParameterChannelBindings(
+base::Value NetLogParameterChannelBindings(
     const std::string& channel_binding_token,
     NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict;
+  base::DictionaryValue dict;
   if (!capture_mode.include_socket_bytes())
     return std::move(dict);
 
-  dict.reset(new base::DictionaryValue());
-  dict->SetString("token", base::HexEncode(channel_binding_token.data(),
-                                           channel_binding_token.size()));
+  dict.Clear();
+  dict.SetString("token", base::HexEncode(channel_binding_token.data(),
+                                          channel_binding_token.size()));
   return std::move(dict);
 }
 
diff --git a/net/http/http_cache_lookup_manager.cc b/net/http/http_cache_lookup_manager.cc
index 3f105f8a..6fd010f 100644
--- a/net/http/http_cache_lookup_manager.cc
+++ b/net/http/http_cache_lookup_manager.cc
@@ -14,13 +14,13 @@
 
 // Returns parameters associated with the start of a server push lookup
 // transaction.
-std::unique_ptr<base::Value> NetLogPushLookupTransactionCallback(
+base::Value NetLogPushLookupTransactionCallback(
     const NetLogSource& net_log,
     const ServerPushDelegate::ServerPushHelper* push_helper,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  net_log.AddToEventParameters(dict.get());
-  dict->SetString("push_url", push_helper->GetURL().possibly_invalid_spec());
+  base::DictionaryValue dict;
+  net_log.AddToEventParameters(&dict);
+  dict.SetString("push_url", push_helper->GetURL().possibly_invalid_spec());
   return std::move(dict);
 }
 
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 3062d6f9..df46a606 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -20091,6 +20091,7 @@
   SSLSocketDataProvider ssl_proxy1(ASYNC, ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
   ssl_proxy1.cert_request_info = cert_request_info_proxy.get();
   ssl_proxy1.expected_send_client_cert = false;
+  ssl_proxy1.expected_false_start_enabled = true;
   StaticSocketDataProvider data1;
   session_deps_.socket_factory->AddSocketDataProvider(&data1);
   session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_proxy1);
@@ -20100,6 +20101,8 @@
   SSLSocketDataProvider ssl_proxy2(ASYNC, OK);
   ssl_proxy2.expected_send_client_cert = true;
   ssl_proxy2.expected_client_cert = identity_proxy->certificate();
+  // Proxy connections with client certs disable False Start.
+  ssl_proxy2.expected_false_start_enabled = false;
   // The client attempts an HTTP CONNECT, but the proxy requests basic auth.
   std::vector<MockWrite> mock_writes2;
   std::vector<MockRead> mock_reads2;
@@ -20122,6 +20125,9 @@
   // The origin requests client certificates.
   SSLSocketDataProvider ssl_origin2(ASYNC, ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
   ssl_origin2.cert_request_info = cert_request_info_origin.get();
+  // The origin connection is eligible for False Start, despite the proxy
+  // connection disabling it.
+  ssl_origin2.expected_false_start_enabled = true;
   StaticSocketDataProvider data2(mock_reads2, mock_writes2);
   session_deps_.socket_factory->AddSocketDataProvider(&data2);
   session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_proxy2);
@@ -20132,6 +20138,8 @@
   SSLSocketDataProvider ssl_proxy3(ASYNC, OK);
   ssl_proxy3.expected_send_client_cert = true;
   ssl_proxy3.expected_client_cert = identity_proxy->certificate();
+  // Proxy connections with client certs disable False Start.
+  ssl_proxy3.expected_false_start_enabled = false;
   std::vector<MockWrite> mock_writes3;
   std::vector<MockRead> mock_reads3;
   mock_writes3.emplace_back(
@@ -20144,6 +20152,9 @@
   SSLSocketDataProvider ssl_origin3(ASYNC, OK);
   ssl_origin3.expected_send_client_cert = true;
   ssl_origin3.expected_client_cert = identity_origin->certificate();
+  // The origin connection is eligible for False Start, despite the proxy
+  // connection disabling it.
+  ssl_origin3.expected_false_start_enabled = true;
   // The client sends the origin HTTP request, which results in another HTTP
   // auth request.
   mock_writes3.emplace_back(
@@ -20540,4 +20551,87 @@
   EXPECT_EQ(200, trans->GetResponseInfo()->headers->response_code());
 }
 
+// Test that socket reuse works with client certificates.
+TEST_F(HttpNetworkTransactionTest, ClientCertSocketReuse) {
+  auto cert_request_info = base::MakeRefCounted<SSLCertRequestInfo>();
+  cert_request_info->host_and_port = HostPortPair("www.example.org", 443);
+
+  std::unique_ptr<FakeClientCertIdentity> identity =
+      FakeClientCertIdentity::CreateFromCertAndKeyFiles(
+          GetTestCertsDirectory(), "client_1.pem", "client_1.pk8");
+  ASSERT_TRUE(identity);
+
+  HttpRequestInfo request1;
+  request1.method = "GET";
+  request1.url = GURL("https://www.example.org/a");
+  request1.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  HttpRequestInfo request2;
+  request2.method = "GET";
+  request2.url = GURL("https://www.example.org/b");
+  request2.traffic_annotation =
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  // The first connection results in a client certificate request.
+  StaticSocketDataProvider data1;
+  session_deps_.socket_factory->AddSocketDataProvider(&data1);
+  SSLSocketDataProvider ssl1(ASYNC, ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
+  ssl1.cert_request_info = cert_request_info.get();
+  ssl1.expected_send_client_cert = false;
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl1);
+
+  // The second connection succeeds and is usable for both requests.
+  MockWrite mock_writes[] = {
+      MockWrite("GET /a HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n\r\n"),
+      MockWrite("GET /b HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n\r\n"),
+  };
+  MockRead mock_reads[] = {
+      MockRead("HTTP/1.1 200 OK\r\n"
+               "Content-Length: 0\r\n\r\n"),
+      MockRead("HTTP/1.1 200 OK\r\n"
+               "Content-Length: 0\r\n\r\n"),
+  };
+  StaticSocketDataProvider data2(mock_reads, mock_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data2);
+  SSLSocketDataProvider ssl2(ASYNC, OK);
+  ssl2.expected_send_client_cert = true;
+  ssl2.expected_client_cert = identity->certificate();
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl2);
+
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  // Start the first request. It succeeds after providing client certificates.
+  TestCompletionCallback callback;
+  auto trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+  ASSERT_THAT(callback.GetResult(trans->Start(&request1, callback.callback(),
+                                              NetLogWithSource())),
+              IsError(ERR_SSL_CLIENT_AUTH_CERT_NEEDED));
+
+  SSLCertRequestInfo* info = trans->GetResponseInfo()->cert_request_info.get();
+  ASSERT_TRUE(info);
+  EXPECT_FALSE(info->is_proxy);
+  EXPECT_EQ(info->host_and_port, cert_request_info->host_and_port);
+
+  ASSERT_THAT(callback.GetResult(trans->RestartWithCertificate(
+                  identity->certificate(), identity->ssl_private_key(),
+                  callback.callback())),
+              IsOk());
+  EXPECT_EQ(200, trans->GetResponseInfo()->headers->response_code());
+
+  // Make the second request. It completes without requesting client
+  // certificates.
+  trans =
+      std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
+  ASSERT_THAT(callback.GetResult(trans->Start(&request2, callback.callback(),
+                                              NetLogWithSource())),
+              IsOk());
+  EXPECT_EQ(200, trans->GetResponseInfo()->headers->response_code());
+}
+
 }  // namespace net
diff --git a/net/http/http_request_headers.cc b/net/http/http_request_headers.cc
index 7ee9a40..50190a90 100644
--- a/net/http/http_request_headers.cc
+++ b/net/http/http_request_headers.cc
@@ -184,11 +184,11 @@
   return output;
 }
 
-std::unique_ptr<base::Value> HttpRequestHeaders::NetLogCallback(
+base::Value HttpRequestHeaders::NetLogCallback(
     const std::string* request_line,
     NetLogCaptureMode capture_mode) const {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetKey("line", NetLogStringValue(*request_line));
+  base::DictionaryValue dict;
+  dict.SetKey("line", NetLogStringValue(*request_line));
   auto headers = std::make_unique<base::ListValue>();
   for (auto it = headers_.begin(); it != headers_.end(); ++it) {
     std::string log_value =
@@ -196,7 +196,7 @@
     headers->GetList().push_back(
         NetLogStringValue(base::StrCat({it->key, ": ", log_value})));
   }
-  dict->Set("headers", std::move(headers));
+  dict.Set("headers", std::move(headers));
   return std::move(dict);
 }
 
diff --git a/net/http/http_request_headers.h b/net/http/http_request_headers.h
index 3bb9e43..2ec412731 100644
--- a/net/http/http_request_headers.h
+++ b/net/http/http_request_headers.h
@@ -173,9 +173,8 @@
 
   // Takes in the request line and returns a Value for use with the NetLog
   // containing both the request line and all headers fields.
-  std::unique_ptr<base::Value> NetLogCallback(
-      const std::string* request_line,
-      NetLogCaptureMode capture_mode) const;
+  base::Value NetLogCallback(const std::string* request_line,
+                             NetLogCaptureMode capture_mode) const;
 
   const HeaderVector& GetHeaderVector() const { return headers_; }
 
diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc
index fe875ba..8b3a4ac 100644
--- a/net/http/http_response_headers.cc
+++ b/net/http/http_response_headers.cc
@@ -1330,21 +1330,21 @@
       instance_length);
 }
 
-std::unique_ptr<base::Value> HttpResponseHeaders::NetLogCallback(
+base::Value HttpResponseHeaders::NetLogCallback(
     NetLogCaptureMode capture_mode) const {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  auto headers = std::make_unique<base::ListValue>();
-  headers->GetList().push_back(NetLogStringValue(GetStatusLine()));
+  base::DictionaryValue dict;
+  base::ListValue headers;
+  headers.GetList().push_back(NetLogStringValue(GetStatusLine()));
   size_t iterator = 0;
   std::string name;
   std::string value;
   while (EnumerateHeaderLines(&iterator, &name, &value)) {
     std::string log_value =
         ElideHeaderValueForNetLog(capture_mode, name, value);
-    headers->GetList().push_back(
+    headers.GetList().push_back(
         NetLogStringValue(base::StrCat({name, ": ", log_value})));
   }
-  dict->Set("headers", std::move(headers));
+  dict.SetKey("headers", std::move(headers));
   return std::move(dict);
 }
 
diff --git a/net/http/http_response_headers.h b/net/http/http_response_headers.h
index 527e07f..be21635 100644
--- a/net/http/http_response_headers.h
+++ b/net/http/http_response_headers.h
@@ -295,8 +295,7 @@
   bool IsChunkEncoded() const;
 
   // Creates a Value for use with the NetLog containing the response headers.
-  std::unique_ptr<base::Value> NetLogCallback(
-      NetLogCaptureMode capture_mode) const;
+  base::Value NetLogCallback(NetLogCaptureMode capture_mode) const;
 
   // Returns the HTTP response code.  This is 0 if the response code text seems
   // to exist but could not be parsed.  Otherwise, it defaults to 200 if the
diff --git a/net/http/http_server_properties_manager.cc b/net/http/http_server_properties_manager.cc
index 9328603..50658601 100644
--- a/net/http/http_server_properties_manager.cc
+++ b/net/http/http_server_properties_manager.cc
@@ -73,10 +73,9 @@
                   NextProtoToString(alternative_service.protocol));
 }
 
-std::unique_ptr<base::Value> NetLogCallback(
-    const base::Value* http_server_properties_dict,
-    NetLogCaptureMode capture_mode) {
-  return http_server_properties_dict->CreateDeepCopy();
+base::Value NetLogCallback(const base::Value* http_server_properties_dict,
+                           NetLogCaptureMode capture_mode) {
+  return http_server_properties_dict->Clone();
 }
 
 // A local or temporary data structure to hold preferences for a server.
diff --git a/net/http/http_stream_factory_job.cc b/net/http/http_stream_factory_job.cc
index 29e7beb0..152f818 100644
--- a/net/http/http_stream_factory_job.cc
+++ b/net/http/http_stream_factory_job.cc
@@ -65,33 +65,32 @@
 }  // namespace
 
 // Returns parameters associated with the start of a HTTP stream job.
-std::unique_ptr<base::Value> NetLogHttpStreamJobCallback(
-    const NetLogSource& source,
-    const GURL* original_url,
-    const GURL* url,
-    bool expect_spdy,
-    bool using_quic,
-    RequestPriority priority,
-    NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
+base::Value NetLogHttpStreamJobCallback(const NetLogSource& source,
+                                        const GURL* original_url,
+                                        const GURL* url,
+                                        bool expect_spdy,
+                                        bool using_quic,
+                                        RequestPriority priority,
+                                        NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
   if (source.IsValid())
-    source.AddToEventParameters(dict.get());
-  dict->SetString("original_url", original_url->GetOrigin().spec());
-  dict->SetString("url", url->GetOrigin().spec());
-  dict->SetBoolean("expect_spdy", expect_spdy);
-  dict->SetBoolean("using_quic", using_quic);
-  dict->SetString("priority", RequestPriorityToString(priority));
+    source.AddToEventParameters(&dict);
+  dict.SetString("original_url", original_url->GetOrigin().spec());
+  dict.SetString("url", url->GetOrigin().spec());
+  dict.SetBoolean("expect_spdy", expect_spdy);
+  dict.SetBoolean("using_quic", using_quic);
+  dict.SetString("priority", RequestPriorityToString(priority));
   return std::move(dict);
 }
 
 // Returns parameters associated with the Proto (with NPN negotiation) of a HTTP
 // stream.
-std::unique_ptr<base::Value> NetLogHttpStreamProtoCallback(
+base::Value NetLogHttpStreamProtoCallback(
     NextProto negotiated_protocol,
     NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
+  base::DictionaryValue dict;
 
-  dict->SetString("proto", NextProtoToString(negotiated_protocol));
+  dict.SetString("proto", NextProtoToString(negotiated_protocol));
   return std::move(dict);
 }
 
@@ -719,13 +718,41 @@
   }
 
   if (proxy_info_.is_https() || proxy_info_.is_quic()) {
-    InitSSLConfig(&proxy_ssl_config_, /*is_proxy=*/true);
     // Disable network fetches for HTTPS proxies, since the network requests
     // are probably going to need to go through the proxy too.
     proxy_ssl_config_.disable_cert_verification_network_fetches = true;
+
+    if (proxy_ssl_config_.send_client_cert) {
+      // When connecting through an HTTPS proxy, disable TLS False Start so that
+      // client authentication errors can be distinguished between those
+      // originating from the proxy server (ERR_PROXY_CONNECTION_FAILED) and
+      // those originating from the endpoint (ERR_SSL_PROTOCOL_ERROR /
+      // ERR_BAD_SSL_CLIENT_AUTH_CERT).
+      //
+      // We now handle this fine for SSLClientAuthCache updates, though not
+      // ReconsiderProxyAfterError() below. In case of issues there, and general
+      // False Start compatibility risk, we continue to disable False Start. (If
+      // it becomes a problem, the risk of removing this is likely low.)
+      //
+      // This assumes the proxy will only request certificates on the initial
+      // handshake; renegotiation on the proxy connection is unsupported.
+      //
+      // See https://crbug.com/828965.
+      proxy_ssl_config_.false_start_enabled = false;
+    }
   }
   if (using_ssl_) {
-    InitSSLConfig(&server_ssl_config_, /*is_proxy=*/false);
+    // Prior to HTTP/2 and SPDY, some servers use TLS renegotiation to request
+    // TLS client authentication after the HTTP request was sent. Allow
+    // renegotiation for only those connections.
+    //
+    // Note that this does NOT implement the provision in
+    // https://http2.github.io/http2-spec/#rfc.section.9.2.1 which allows the
+    // server to request a renegotiation immediately before sending the
+    // connection preface as waiting for the preface would cost the round trip
+    // that False Start otherwise saves.
+    server_ssl_config_.renego_allowed_default = true;
+    server_ssl_config_.renego_allowed_for_protos.push_back(kProtoHTTP11);
   }
 
   if (using_quic_) {
@@ -1222,42 +1249,6 @@
   RunLoop(net::OK);
 }
 
-void HttpStreamFactory::Job::InitSSLConfig(SSLConfig* ssl_config,
-                                           bool is_proxy) const {
-  if (!is_proxy) {
-    // Prior to HTTP/2 and SPDY, some servers use TLS renegotiation to request
-    // TLS client authentication after the HTTP request was sent. Allow
-    // renegotiation for only those connections.
-    //
-    // Note that this does NOT implement the provision in
-    // https://http2.github.io/http2-spec/#rfc.section.9.2.1 which allows the
-    // server to request a renegotiation immediately before sending the
-    // connection preface as waiting for the preface would cost the round trip
-    // that False Start otherwise saves.
-    ssl_config->renego_allowed_default = true;
-    ssl_config->renego_allowed_for_protos.push_back(kProtoHTTP11);
-  }
-
-  if (proxy_info_.is_https() && ssl_config->send_client_cert) {
-    // When connecting through an HTTPS proxy, disable TLS False Start so that
-    // client authentication errors can be distinguished between those
-    // originating from the proxy server (ERR_PROXY_CONNECTION_FAILED) and those
-    // originating from the endpoint (ERR_SSL_PROTOCOL_ERROR /
-    // ERR_BAD_SSL_CLIENT_AUTH_CERT).
-    //
-    // We now handle this fine for SSLClientAuthCache updates, though not
-    // ReconsiderProxyAfterError() below. In case of issues there, and general
-    // False Start compatibility risk, we continue to disable False Start. (If
-    // it becomes a problem, the risk of removing this is likely low.)
-    //
-    // This assumes the proxy will only request certificates on the initial
-    // handshake; renegotiation on the proxy connection is unsupported.
-    //
-    // See https://crbug.com/828965.
-    ssl_config->false_start_enabled = false;
-  }
-}
-
 int HttpStreamFactory::Job::ReconsiderProxyAfterError(int error) {
   // Check if the error was a proxy failure.
   if (!CanFalloverToNextProxy(proxy_info_.proxy_server(), error, &error))
diff --git a/net/http/http_stream_factory_job.h b/net/http/http_stream_factory_job.h
index b4cf717..e7fb8e63 100644
--- a/net/http/http_stream_factory_job.h
+++ b/net/http/http_stream_factory_job.h
@@ -322,10 +322,6 @@
   // SpdySessionPool::SpdySessionRequest::Delegate implementation:
   void OnSpdySessionAvailable(base::WeakPtr<SpdySession> spdy_session) override;
 
-  // Sets several fields of |ssl_config| based on the proxy info and other
-  // factors.
-  void InitSSLConfig(SSLConfig* ssl_config, bool is_proxy) const;
-
   // Retrieve SSLInfo from our SSL Socket.
   // This must only be called when we are using an SSLSocket.
   void GetSSLInfo(SSLInfo* ssl_info);
diff --git a/net/http/http_stream_factory_job_controller.cc b/net/http/http_stream_factory_job_controller.cc
index de95183..4d3bcc185 100644
--- a/net/http/http_stream_factory_job_controller.cc
+++ b/net/http/http_stream_factory_job_controller.cc
@@ -31,14 +31,14 @@
 namespace {
 
 // Returns parameters associated with the proxy resolution.
-std::unique_ptr<base::Value> NetLogHttpStreamJobProxyServerResolved(
+base::Value NetLogHttpStreamJobProxyServerResolved(
     const ProxyServer& proxy_server,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  base::DictionaryValue dict;
 
-  dict->SetString("proxy_server", proxy_server.is_valid()
-                                      ? proxy_server.ToPacString()
-                                      : std::string());
+  dict.SetString("proxy_server", proxy_server.is_valid()
+                                     ? proxy_server.ToPacString()
+                                     : std::string());
   return std::move(dict);
 }
 
@@ -48,23 +48,21 @@
 // the main job.
 const int kMaxDelayTimeForMainJobSecs = 3;
 
-std::unique_ptr<base::Value> NetLogJobControllerCallback(
-    const GURL* url,
-    bool is_preconnect,
-    NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetString("url", url->possibly_invalid_spec());
-  dict->SetBoolean("is_preconnect", is_preconnect);
+base::Value NetLogJobControllerCallback(const GURL* url,
+                                        bool is_preconnect,
+                                        NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  dict.SetString("url", url->possibly_invalid_spec());
+  dict.SetBoolean("is_preconnect", is_preconnect);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogAltSvcCallback(
-    const AlternativeServiceInfo* alt_svc_info,
-    bool is_broken,
-    NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetString("alt_svc", alt_svc_info->ToString());
-  dict->SetBoolean("is_broken", is_broken);
+base::Value NetLogAltSvcCallback(const AlternativeServiceInfo* alt_svc_info,
+                                 bool is_broken,
+                                 NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  dict.SetString("alt_svc", alt_svc_info->ToString());
+  dict.SetBoolean("is_broken", is_broken);
   return std::move(dict);
 }
 
diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc
index 448e28a..6615a48 100644
--- a/net/http/http_stream_parser.cc
+++ b/net/http/http_stream_parser.cc
@@ -67,15 +67,15 @@
   return false;
 }
 
-std::unique_ptr<base::Value> NetLogSendRequestBodyCallback(
+base::Value NetLogSendRequestBodyCallback(
     uint64_t length,
     bool is_chunked,
     bool did_merge,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("length", static_cast<int>(length));
-  dict->SetBoolean("is_chunked", is_chunked);
-  dict->SetBoolean("did_merge", did_merge);
+  base::DictionaryValue dict;
+  dict.SetInteger("length", static_cast<int>(length));
+  dict.SetBoolean("is_chunked", is_chunked);
+  dict.SetBoolean("did_merge", did_merge);
   return std::move(dict);
 }
 
diff --git a/net/log/file_net_log_observer.cc b/net/log/file_net_log_observer.cc
index 1b1b7e40..f85805a 100644
--- a/net/log/file_net_log_observer.cc
+++ b/net/log/file_net_log_observer.cc
@@ -402,7 +402,7 @@
 void FileNetLogObserver::OnAddEntry(const NetLogEntry& entry) {
   std::unique_ptr<std::string> json(new std::string);
 
-  *json = SerializeNetLogValueToJson(*entry.ToValue());
+  *json = SerializeNetLogValueToJson(entry.ToValue());
 
   size_t queue_size = write_queue_->AddEntryToQueue(std::move(json));
 
diff --git a/net/log/file_net_log_observer_unittest.cc b/net/log/file_net_log_observer_unittest.cc
index 399143d..bb078fd 100644
--- a/net/log/file_net_log_observer_unittest.cc
+++ b/net/log/file_net_log_observer_unittest.cc
@@ -63,9 +63,9 @@
                                   base::TimeTicks::Now(), &callback);
   NetLogEntry base_entry(&base_entry_data,
                          NetLogCaptureMode::IncludeSocketBytes());
-  std::unique_ptr<base::Value> value(base_entry.ToValue());
+  base::Value value = base_entry.ToValue();
   std::string json;
-  base::JSONWriter::Write(*value, &json);
+  base::JSONWriter::Write(value, &json);
   size_t base_entry_size = json.size();
 
   // The maximum value of base::TimeTicks::Now() will be the maximum value of
@@ -105,7 +105,7 @@
 
   // Initializes the ParsedNetLog by parsing a JSON file.
   // Owner for the Value tree.
-  std::unique_ptr<base::Value> container;
+  base::Value container;
 
   // A dictionary for the entire netlog.
   const base::DictionaryValue* root = nullptr;
@@ -127,12 +127,13 @@
   }
 
   base::JSONReader reader;
-  container = reader.ReadToValueDeprecated(input);
-  if (!container) {
+  base::Optional<base::Value> container_optional = reader.Read(input);
+  if (!container_optional) {
     return ::testing::AssertionFailure() << reader.GetErrorMessage();
   }
+  container = std::move(*container_optional);
 
-  if (!container->GetAsDictionary(&root)) {
+  if (!container.GetAsDictionary(&root)) {
     return ::testing::AssertionFailure() << "Not a dictionary";
   }
 
diff --git a/net/log/net_log.cc b/net/log/net_log.cc
index afb760e..88b3fa8 100644
--- a/net/log/net_log.cc
+++ b/net/log/net_log.cc
@@ -23,63 +23,51 @@
 
 namespace {
 
-std::unique_ptr<base::Value> NetLogBoolCallback(
-    const char* name,
-    bool value,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> event_params(
-      new base::DictionaryValue());
-  event_params->SetBoolean(name, value);
+base::Value NetLogBoolCallback(const char* name,
+                               bool value,
+                               NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue event_params;
+  event_params.SetBoolean(name, value);
   return std::move(event_params);
 }
 
-std::unique_ptr<base::Value> NetLogIntCallback(
-    const char* name,
-    int value,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> event_params(
-      new base::DictionaryValue());
-  event_params->SetInteger(name, value);
+base::Value NetLogIntCallback(const char* name,
+                              int value,
+                              NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue event_params;
+  event_params.SetInteger(name, value);
   return std::move(event_params);
 }
 
-std::unique_ptr<base::Value> NetLogInt64Callback(
-    const char* name,
-    int64_t value,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> event_params(
-      new base::DictionaryValue());
-  event_params->SetKey(name, NetLogNumberValue(value));
+base::Value NetLogInt64Callback(const char* name,
+                                int64_t value,
+                                NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue event_params;
+  event_params.SetKey(name, NetLogNumberValue(value));
   return std::move(event_params);
 }
 
-std::unique_ptr<base::Value> NetLogStringCallback(
-    const char* name,
-    const std::string* value,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> event_params(
-      new base::DictionaryValue());
-  event_params->SetString(name, *value);
+base::Value NetLogStringCallback(const char* name,
+                                 const std::string* value,
+                                 NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue event_params;
+  event_params.SetString(name, *value);
   return std::move(event_params);
 }
 
-std::unique_ptr<base::Value> NetLogCharStringCallback(
-    const char* name,
-    const char* value,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> event_params(
-      new base::DictionaryValue());
-  event_params->SetString(name, value);
+base::Value NetLogCharStringCallback(const char* name,
+                                     const char* value,
+                                     NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue event_params;
+  event_params.SetString(name, value);
   return std::move(event_params);
 }
 
-std::unique_ptr<base::Value> NetLogString16Callback(
-    const char* name,
-    const base::string16* value,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> event_params(
-      new base::DictionaryValue());
-  event_params->SetString(name, *value);
+base::Value NetLogString16Callback(const char* name,
+                                   const base::string16* value,
+                                   NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue event_params;
+  event_params.SetString(name, *value);
   return std::move(event_params);
 }
 
@@ -236,10 +224,10 @@
 }
 
 // static
-std::unique_ptr<base::Value> NetLog::GetEventTypesAsValue() {
-  auto dict = std::make_unique<base::DictionaryValue>();
+base::Value NetLog::GetEventTypesAsValue() {
+  base::DictionaryValue dict;
   for (int i = 0; i < static_cast<int>(NetLogEventType::COUNT); ++i) {
-    dict->SetInteger(EventTypeToString(static_cast<NetLogEventType>(i)), i);
+    dict.SetInteger(EventTypeToString(static_cast<NetLogEventType>(i)), i);
   }
   return std::move(dict);
 }
@@ -259,10 +247,10 @@
 }
 
 // static
-std::unique_ptr<base::Value> NetLog::GetSourceTypesAsValue() {
-  auto dict = std::make_unique<base::DictionaryValue>();
+base::Value NetLog::GetSourceTypesAsValue() {
+  base::DictionaryValue dict;
   for (int i = 0; i < static_cast<int>(NetLogSourceType::COUNT); ++i) {
-    dict->SetInteger(SourceTypeToString(static_cast<NetLogSourceType>(i)), i);
+    dict.SetInteger(SourceTypeToString(static_cast<NetLogSourceType>(i)), i);
   }
   return std::move(dict);
 }
diff --git a/net/log/net_log.h b/net/log/net_log.h
index 0dfba99..14c9a74 100644
--- a/net/log/net_log.h
+++ b/net/log/net_log.h
@@ -165,14 +165,14 @@
 
   // Returns a dictionary that maps event type symbolic names to their enum
   // values.
-  static std::unique_ptr<base::Value> GetEventTypesAsValue();
+  static base::Value GetEventTypesAsValue();
 
   // Returns a C-String symbolic name for |source_type|.
   static const char* SourceTypeToString(NetLogSourceType source_type);
 
   // Returns a dictionary that maps source type symbolic names to their enum
   // values.
-  static std::unique_ptr<base::Value> GetSourceTypesAsValue();
+  static base::Value GetSourceTypesAsValue();
 
   // Returns a C-String symbolic name for |event_phase|.
   static const char* EventPhaseToString(NetLogEventPhase event_phase);
diff --git a/net/log/net_log_entry.cc b/net/log/net_log_entry.cc
index 3cdfc452..da6329ac 100644
--- a/net/log/net_log_entry.cc
+++ b/net/log/net_log_entry.cc
@@ -12,38 +12,33 @@
 
 namespace net {
 
-std::unique_ptr<base::Value> NetLogEntry::ToValue() const {
-  std::unique_ptr<base::DictionaryValue> entry_dict(
-      new base::DictionaryValue());
+base::Value NetLogEntry::ToValue() const {
+  base::DictionaryValue entry_dict;
 
-  entry_dict->SetString("time", NetLog::TickCountToString(data_->time));
+  entry_dict.SetString("time", NetLog::TickCountToString(data_->time));
 
   // Set the entry source.
-  std::unique_ptr<base::DictionaryValue> source_dict(
-      new base::DictionaryValue());
-  source_dict->SetInteger("id", data_->source.id);
-  source_dict->SetInteger("type", static_cast<int>(data_->source.type));
-  entry_dict->Set("source", std::move(source_dict));
+  base::DictionaryValue source_dict;
+  source_dict.SetInteger("id", data_->source.id);
+  source_dict.SetInteger("type", static_cast<int>(data_->source.type));
+  entry_dict.SetKey("source", std::move(source_dict));
 
   // Set the event info.
-  entry_dict->SetInteger("type", static_cast<int>(data_->type));
-  entry_dict->SetInteger("phase", static_cast<int>(data_->phase));
+  entry_dict.SetInteger("type", static_cast<int>(data_->type));
+  entry_dict.SetInteger("phase", static_cast<int>(data_->phase));
 
   // Set the event-specific parameters.
   if (data_->parameters_callback) {
-    std::unique_ptr<base::Value> value(
-        data_->parameters_callback->Run(capture_mode_));
-    if (value)
-      entry_dict->Set("params", std::move(value));
+    entry_dict.SetKey("params", data_->parameters_callback->Run(capture_mode_));
   }
 
   return std::move(entry_dict);
 }
 
-std::unique_ptr<base::Value> NetLogEntry::ParametersToValue() const {
+base::Value NetLogEntry::ParametersToValue() const {
   if (data_->parameters_callback)
     return data_->parameters_callback->Run(capture_mode_);
-  return nullptr;
+  return base::Value();
 }
 
 NetLogEntryData::NetLogEntryData(
diff --git a/net/log/net_log_entry.h b/net/log/net_log_entry.h
index 48127041..b9280844 100644
--- a/net/log/net_log_entry.h
+++ b/net/log/net_log_entry.h
@@ -50,11 +50,12 @@
 
   // Serializes the specified event to a Value.  The Value also includes the
   // current time.  Takes in a time to allow back-dating entries.
-  std::unique_ptr<base::Value> ToValue() const;
+  base::Value ToValue() const;
 
-  // Returns the parameters as a Value.  Returns nullptr if there are no
+  // Returns the parameters as a Value.  Returns a none value if there are no
   // parameters.
-  std::unique_ptr<base::Value> ParametersToValue() const;
+  // TODO(eroman): Make this base::Optional instead?
+  base::Value ParametersToValue() const;
 
  private:
   const NetLogEntryData* const data_;
diff --git a/net/log/net_log_parameters_callback.h b/net/log/net_log_parameters_callback.h
index e472a330..05ff3ff 100644
--- a/net/log/net_log_parameters_callback.h
+++ b/net/log/net_log_parameters_callback.h
@@ -19,9 +19,9 @@
 // A callback that returns a Value representation of the parameters
 // associated with an event.  If called, it will be called synchronously,
 // so it need not have owning references.  May be called more than once, or
-// not at all.  May return nullptr.
-typedef base::Callback<std::unique_ptr<base::Value>(NetLogCaptureMode)>
-    NetLogParametersCallback;
+// not at all.  May return a none value to indicate no parameters.
+using NetLogParametersCallback =
+    base::RepeatingCallback<base::Value(NetLogCaptureMode)>;
 
 }  // namespace net
 
diff --git a/net/log/net_log_source.cc b/net/log/net_log_source.cc
index a427f68..09b1daa6 100644
--- a/net/log/net_log_source.cc
+++ b/net/log/net_log_source.cc
@@ -17,14 +17,13 @@
 
 namespace {
 
-std::unique_ptr<base::Value> SourceEventParametersCallback(
+base::Value SourceEventParametersCallback(
     const NetLogSource source,
     NetLogCaptureMode /* capture_mode */) {
   if (!source.IsValid())
-    return std::unique_ptr<base::Value>();
-  std::unique_ptr<base::DictionaryValue> event_params(
-      new base::DictionaryValue());
-  source.AddToEventParameters(event_params.get());
+    return base::Value();
+  base::DictionaryValue event_params;
+  source.AddToEventParameters(&event_params);
   return std::move(event_params);
 }
 
@@ -42,12 +41,12 @@
   return id != kInvalidId;
 }
 
-void NetLogSource::AddToEventParameters(
-    base::DictionaryValue* event_params) const {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("type", static_cast<int>(type));
-  dict->SetInteger("id", static_cast<int>(id));
-  event_params->Set("source_dependency", std::move(dict));
+void NetLogSource::AddToEventParameters(base::Value* event_params) const {
+  DCHECK(event_params->is_dict());
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetIntKey("type", static_cast<int>(type));
+  dict.SetIntKey("id", static_cast<int>(id));
+  event_params->SetKey("source_dependency", std::move(dict));
 }
 
 NetLogParametersCallback NetLogSource::ToEventParametersCallback() const {
diff --git a/net/log/net_log_source.h b/net/log/net_log_source.h
index 9ec8990..87748454 100644
--- a/net/log/net_log_source.h
+++ b/net/log/net_log_source.h
@@ -30,7 +30,7 @@
 
   // Adds the source to a DictionaryValue containing event parameters,
   // using the name "source_dependency".
-  void AddToEventParameters(base::DictionaryValue* event_params) const;
+  void AddToEventParameters(base::Value* event_params) const;
 
   // Returns a callback that returns a dictionary with a single entry
   // named "source_dependency" that describes |this|.
diff --git a/net/log/net_log_unittest.cc b/net/log/net_log_unittest.cc
index 21a14096..6ff65a3 100644
--- a/net/log/net_log_unittest.cc
+++ b/net/log/net_log_unittest.cc
@@ -44,15 +44,13 @@
   return -1;
 }
 
-std::unique_ptr<base::Value> CaptureModeToValue(
-    NetLogCaptureMode capture_mode) {
-  return std::make_unique<base::Value>(CaptureModeToInt(capture_mode));
+base::Value CaptureModeToValue(NetLogCaptureMode capture_mode) {
+  return base::Value(CaptureModeToInt(capture_mode));
 }
 
-std::unique_ptr<base::Value> NetCaptureModeCallback(
-    NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->Set("capture_mode", CaptureModeToValue(capture_mode));
+base::Value NetCaptureModeCallback(NetLogCaptureMode capture_mode) {
+  base::DictionaryValue dict;
+  dict.SetKey("capture_mode", CaptureModeToValue(capture_mode));
   return std::move(dict);
 }
 
@@ -137,8 +135,8 @@
   }
 
   void OnAddEntry(const NetLogEntry& entry) override {
-    std::unique_ptr<base::DictionaryValue> dict =
-        base::DictionaryValue::From(entry.ToValue());
+    std::unique_ptr<base::DictionaryValue> dict = base::DictionaryValue::From(
+        base::Value::ToUniquePtrValue(entry.ToValue()));
     ASSERT_TRUE(dict);
     values_.push_back(std::move(dict));
   }
diff --git a/net/log/net_log_util.cc b/net/log/net_log_util.cc
index 59bd8b04..7ec226f7 100644
--- a/net/log/net_log_util.cc
+++ b/net/log/net_log_util.cc
@@ -130,9 +130,8 @@
 
 // Returns a Value representing the state of a pre-existing URLRequest when
 // net-internals was opened.
-std::unique_ptr<base::Value> GetRequestStateAsValue(
-    const net::URLRequest* request,
-    NetLogCaptureMode capture_mode) {
+base::Value GetRequestStateAsValue(const net::URLRequest* request,
+                                   NetLogCaptureMode capture_mode) {
   return request->GetStateAsValue();
 }
 
@@ -147,7 +146,7 @@
 
   // Add a dictionary with information on the relationship between event type
   // enums and their symbolic names.
-  constants_dict->Set("logEventTypes", NetLog::GetEventTypesAsValue());
+  constants_dict->SetKey("logEventTypes", NetLog::GetEventTypesAsValue());
 
   // Add a dictionary with information about the relationship between CertStatus
   // flags and their symbolic names.
@@ -245,7 +244,7 @@
 
   // Information about the relationship between source type enums and
   // their symbolic names.
-  constants_dict->Set("logSourceType", NetLog::GetSourceTypesAsValue());
+  constants_dict->SetKey("logSourceType", NetLog::GetSourceTypesAsValue());
 
   // TODO(eroman): This is here for compatibility in loading new log files with
   // older builds of Chrome. Safe to remove this once M45 is on the stable
diff --git a/net/log/net_log_with_source.cc b/net/log/net_log_with_source.cc
index 8997f64c..bdaa42e 100644
--- a/net/log/net_log_with_source.cc
+++ b/net/log/net_log_with_source.cc
@@ -23,14 +23,13 @@
 // Returns parameters for logging data transferred events. At a minimum includes
 // the number of bytes transferred. If the capture mode allows logging byte
 // contents and |byte_count| > 0, then will include the actual bytes.
-std::unique_ptr<base::Value> BytesTransferredCallback(
-    int byte_count,
-    const char* bytes,
-    NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("byte_count", byte_count);
+base::Value BytesTransferredCallback(int byte_count,
+                                     const char* bytes,
+                                     NetLogCaptureMode capture_mode) {
+  base::DictionaryValue dict;
+  dict.SetInteger("byte_count", byte_count);
   if (capture_mode.include_socket_bytes() && byte_count > 0)
-    dict->SetKey("bytes", NetLogBinaryValue(bytes, byte_count));
+    dict.SetKey("bytes", NetLogBinaryValue(bytes, byte_count));
   return std::move(dict);
 }
 
diff --git a/net/log/test_net_log.cc b/net/log/test_net_log.cc
index 36ef00d..6814f15 100644
--- a/net/log/test_net_log.cc
+++ b/net/log/test_net_log.cc
@@ -55,7 +55,8 @@
     // Using Dictionaries instead of Values makes checking values a little
     // simpler.
     std::unique_ptr<base::DictionaryValue> param_dict =
-        base::DictionaryValue::From(entry.ParametersToValue());
+        base::DictionaryValue::From(
+            base::Value::ToUniquePtrValue(entry.ParametersToValue()));
 
     // Only need to acquire the lock when accessing class variables.
     base::AutoLock lock(lock_);
diff --git a/net/log/trace_net_log_observer.cc b/net/log/trace_net_log_observer.cc
index 5129deb5..a6ecb034 100644
--- a/net/log/trace_net_log_observer.cc
+++ b/net/log/trace_net_log_observer.cc
@@ -26,16 +26,15 @@
 
 class TracedValue : public base::trace_event::ConvertableToTraceFormat {
  public:
-  explicit TracedValue(std::unique_ptr<base::Value> value)
-      : value_(std::move(value)) {}
+  explicit TracedValue(base::Value value) : value_(std::move(value)) {}
 
  private:
   ~TracedValue() override = default;
 
   void AppendAsTraceFormat(std::string* out) const override {
-    if (value_) {
+    if (!value_.is_none()) {
       std::string tmp;
-      base::JSONWriter::Write(*value_, &tmp);
+      base::JSONWriter::Write(value_, &tmp);
       *out += tmp;
     } else {
       *out += "\"\"";
@@ -43,7 +42,7 @@
   }
 
  private:
-  std::unique_ptr<base::Value> value_;
+  base::Value value_;
 };
 
 }  // namespace
@@ -57,7 +56,7 @@
 }
 
 void TraceNetLogObserver::OnAddEntry(const NetLogEntry& entry) {
-  std::unique_ptr<base::Value> params(entry.ParametersToValue());
+  base::Value params(entry.ParametersToValue());
   switch (entry.phase()) {
     case NetLogEventPhase::BEGIN:
       TRACE_EVENT_NESTABLE_ASYNC_BEGIN2(
diff --git a/net/nqe/event_creator.cc b/net/nqe/event_creator.cc
index ee7407d2d..0161801a 100644
--- a/net/nqe/event_creator.cc
+++ b/net/nqe/event_creator.cc
@@ -23,18 +23,18 @@
 
 namespace {
 
-std::unique_ptr<base::Value> NetworkQualityChangedNetLogCallback(
+base::Value NetworkQualityChangedNetLogCallback(
     base::TimeDelta http_rtt,
     base::TimeDelta transport_rtt,
     int32_t downstream_throughput_kbps,
     EffectiveConnectionType effective_connection_type,
     NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("http_rtt_ms", http_rtt.InMilliseconds());
-  dict->SetInteger("transport_rtt_ms", transport_rtt.InMilliseconds());
-  dict->SetInteger("downstream_throughput_kbps", downstream_throughput_kbps);
-  dict->SetString("effective_connection_type",
-                  GetNameForEffectiveConnectionType(effective_connection_type));
+  base::DictionaryValue dict;
+  dict.SetInteger("http_rtt_ms", http_rtt.InMilliseconds());
+  dict.SetInteger("transport_rtt_ms", transport_rtt.InMilliseconds());
+  dict.SetInteger("downstream_throughput_kbps", downstream_throughput_kbps);
+  dict.SetString("effective_connection_type",
+                 GetNameForEffectiveConnectionType(effective_connection_type));
   return std::move(dict);
 }
 
diff --git a/net/proxy_resolution/dhcp_pac_file_fetcher_win.cc b/net/proxy_resolution/dhcp_pac_file_fetcher_win.cc
index 348de98..3b92223 100644
--- a/net/proxy_resolution/dhcp_pac_file_fetcher_win.cc
+++ b/net/proxy_resolution/dhcp_pac_file_fetcher_win.cc
@@ -209,11 +209,10 @@
   dict->SetKey(key, base::Value(value));
 }
 
-std::unique_ptr<base::Value> NetLogGetAdaptersDoneCallback(
+base::Value NetLogGetAdaptersDoneCallback(
     DhcpAdapterNamesLoggingInfo* info,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> result =
-      std::make_unique<base::DictionaryValue>();
+  base::DictionaryValue result;
 
   // Add information on each of the adapters enumerated (including those that
   // were subsequently skipped).
@@ -235,38 +234,36 @@
 
     adapters_value.GetList().push_back(std::move(adapter_value));
   }
-  result->SetKey("adapters", std::move(adapters_value));
+  result.SetKey("adapters", std::move(adapters_value));
 
   SetInt("origin_to_worker_thread_hop_dt",
          (info->worker_thread_start_time - info->origin_thread_start_time)
              .InMilliseconds(),
-         result.get());
+         &result);
   SetInt("worker_to_origin_thread_hop_dt",
          (info->origin_thread_end_time - info->worker_thread_end_time)
              .InMilliseconds(),
-         result.get());
+         &result);
   SetInt("worker_dt",
          (info->worker_thread_end_time - info->worker_thread_start_time)
              .InMilliseconds(),
-         result.get());
+         &result);
 
   if (info->error != ERROR_SUCCESS)
-    SetInt("error", info->error, result.get());
+    SetInt("error", info->error, &result);
 
-  return result;
+  return std::move(result);
 }
 
-std::unique_ptr<base::Value> NetLogFetcherDoneCallback(
-    int fetcher_index,
-    int net_error,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> result =
-      std::make_unique<base::DictionaryValue>();
+base::Value NetLogFetcherDoneCallback(int fetcher_index,
+                                      int net_error,
+                                      NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue result;
 
-  result->SetKey("fetcher_index", base::Value(fetcher_index));
-  result->SetKey("net_error", base::Value(net_error));
+  result.SetKey("fetcher_index", base::Value(fetcher_index));
+  result.SetKey("net_error", base::Value(net_error));
 
-  return result;
+  return std::move(result);
 }
 
 }  // namespace
diff --git a/net/proxy_resolution/pac_file_decider.cc b/net/proxy_resolution/pac_file_decider.cc
index 48af28f..4c30c4d 100644
--- a/net/proxy_resolution/pac_file_decider.cc
+++ b/net/proxy_resolution/pac_file_decider.cc
@@ -66,10 +66,10 @@
 PacFileDataWithSource& PacFileDataWithSource::operator=(
     const PacFileDataWithSource&) = default;
 
-std::unique_ptr<base::Value> PacFileDecider::PacSource::NetLogCallback(
+base::Value PacFileDecider::PacSource::NetLogCallback(
     const GURL* effective_pac_url,
     NetLogCaptureMode /* capture_mode */) const {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  base::DictionaryValue dict;
   std::string source;
   switch (type) {
     case PacSource::WPAD_DHCP:
@@ -84,7 +84,7 @@
       source += effective_pac_url->possibly_invalid_spec();
       break;
   }
-  dict->SetString("source", source);
+  dict.SetString("source", source);
   return std::move(dict);
 }
 
diff --git a/net/proxy_resolution/pac_file_decider.h b/net/proxy_resolution/pac_file_decider.h
index 6b91d9d1..24002bbc3 100644
--- a/net/proxy_resolution/pac_file_decider.h
+++ b/net/proxy_resolution/pac_file_decider.h
@@ -122,9 +122,8 @@
     // Returns a Value representing the PacSource.  |effective_pac_url| must
     // be non-NULL and point to the URL derived from information contained in
     // |this|, if Type is not WPAD_DHCP.
-    std::unique_ptr<base::Value> NetLogCallback(
-        const GURL* effective_pac_url,
-        NetLogCaptureMode capture_mode) const;
+    base::Value NetLogCallback(const GURL* effective_pac_url,
+                               NetLogCaptureMode capture_mode) const;
 
     Type type;
     GURL url;  // Empty unless |type == PAC_SOURCE_CUSTOM|.
diff --git a/net/proxy_resolution/proxy_resolution_service.cc b/net/proxy_resolution/proxy_resolution_service.cc
index 0cb73b25..a4644d7 100644
--- a/net/proxy_resolution/proxy_resolution_service.cc
+++ b/net/proxy_resolution/proxy_resolution_service.cc
@@ -314,38 +314,37 @@
 };
 
 // Returns NetLog parameters describing a proxy configuration change.
-std::unique_ptr<base::Value> NetLogProxyConfigChangedCallback(
+base::Value NetLogProxyConfigChangedCallback(
     const base::Optional<ProxyConfigWithAnnotation>* old_config,
     const ProxyConfigWithAnnotation* new_config,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  base::DictionaryValue dict;
   // The "old_config" is optional -- the first notification will not have
   // any "previous" configuration.
   if (old_config->has_value())
-    dict->Set("old_config", (*old_config)->value().ToValue());
-  dict->Set("new_config", new_config->value().ToValue());
+    dict.Set("old_config", (*old_config)->value().ToValue());
+  dict.Set("new_config", new_config->value().ToValue());
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogBadProxyListCallback(
-    const ProxyRetryInfoMap* retry_info,
-    NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  auto list = std::make_unique<base::ListValue>();
+base::Value NetLogBadProxyListCallback(const ProxyRetryInfoMap* retry_info,
+                                       NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  base::ListValue list;
 
   for (auto iter = retry_info->begin(); iter != retry_info->end(); ++iter) {
-    list->AppendString(iter->first);
+    list.AppendString(iter->first);
   }
-  dict->Set("bad_proxy_list", std::move(list));
+  dict.SetKey("bad_proxy_list", std::move(list));
   return std::move(dict);
 }
 
 // Returns NetLog parameters on a successfuly proxy resolution.
-std::unique_ptr<base::Value> NetLogFinishedResolvingProxyCallback(
+base::Value NetLogFinishedResolvingProxyCallback(
     const ProxyInfo* result,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("pac_string", result->ToPacString());
+  base::DictionaryValue dict;
+  dict.SetString("pac_string", result->ToPacString());
   return std::move(dict);
 }
 
diff --git a/net/proxy_resolution/proxy_resolver_v8_tracing_wrapper.cc b/net/proxy_resolution/proxy_resolver_v8_tracing_wrapper.cc
index 39e14f6..ce9a3250 100644
--- a/net/proxy_resolution/proxy_resolver_v8_tracing_wrapper.cc
+++ b/net/proxy_resolution/proxy_resolver_v8_tracing_wrapper.cc
@@ -24,13 +24,12 @@
 namespace {
 
 // Returns event parameters for a PAC error message (line number + message).
-std::unique_ptr<base::Value> NetLogErrorCallback(
-    int line_number,
-    const base::string16* message,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("line_number", line_number);
-  dict->SetString("message", *message);
+base::Value NetLogErrorCallback(int line_number,
+                                const base::string16* message,
+                                NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  dict.SetInteger("line_number", line_number);
+  dict.SetString("message", *message);
   return std::move(dict);
 }
 
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index a9e82c7..8afe739 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -138,33 +138,33 @@
   return NetLog::StringCallback("trigger", trigger);
 }
 
-std::unique_ptr<base::Value> NetLogQuicConnectionMigrationFailureCallback(
+base::Value NetLogQuicConnectionMigrationFailureCallback(
     quic::QuicConnectionId connection_id,
     std::string reason,
     NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("connection_id", connection_id.ToString());
-  dict->SetString("reason", reason);
+  base::DictionaryValue dict;
+  dict.SetString("connection_id", connection_id.ToString());
+  dict.SetString("reason", reason);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicConnectionMigrationSuccessCallback(
+base::Value NetLogQuicConnectionMigrationSuccessCallback(
     quic::QuicConnectionId connection_id,
     NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("connection_id", connection_id.ToString());
+  base::DictionaryValue dict;
+  dict.SetString("connection_id", connection_id.ToString());
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogProbingResultCallback(
+base::Value NetLogProbingResultCallback(
     NetworkChangeNotifier::NetworkHandle network,
     const quic::QuicSocketAddress* peer_address,
     bool is_success,
     NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("network", base::NumberToString(network));
-  dict->SetString("peer address", peer_address->ToString());
-  dict->SetBoolean("is_success", is_success);
+  base::DictionaryValue dict;
+  dict.SetString("network", base::NumberToString(network));
+  dict.SetString("peer address", peer_address->ToString());
+  dict.SetBoolean("is_success", is_success);
   return std::move(dict);
 }
 
@@ -221,29 +221,29 @@
   return "InvalidCause";
 }
 
-std::unique_ptr<base::Value> NetLogQuicClientSessionCallback(
+base::Value NetLogQuicClientSessionCallback(
     const quic::QuicServerId* server_id,
     int cert_verify_flags,
     bool require_confirmation,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("host", server_id->host());
-  dict->SetInteger("port", server_id->port());
-  dict->SetBoolean("privacy_mode", server_id->privacy_mode_enabled());
-  dict->SetBoolean("require_confirmation", require_confirmation);
-  dict->SetInteger("cert_verify_flags", cert_verify_flags);
+  base::DictionaryValue dict;
+  dict.SetString("host", server_id->host());
+  dict.SetInteger("port", server_id->port());
+  dict.SetBoolean("privacy_mode", server_id->privacy_mode_enabled());
+  dict.SetBoolean("require_confirmation", require_confirmation);
+  dict.SetInteger("cert_verify_flags", cert_verify_flags);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicPushPromiseReceivedCallback(
+base::Value NetLogQuicPushPromiseReceivedCallback(
     const spdy::SpdyHeaderBlock* headers,
     spdy::SpdyStreamId stream_id,
     spdy::SpdyStreamId promised_stream_id,
     NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->Set("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode));
-  dict->SetInteger("id", stream_id);
-  dict->SetInteger("promised_stream_id", promised_stream_id);
+  base::DictionaryValue dict;
+  dict.SetKey("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode));
+  dict.SetInteger("id", stream_id);
+  dict.SetInteger("promised_stream_id", promised_stream_id);
   return std::move(dict);
 }
 
@@ -2740,34 +2740,34 @@
       base::Bind(&NetLogQuicConnectionMigrationSuccessCallback, connection_id));
 }
 
-std::unique_ptr<base::Value> QuicChromiumClientSession::GetInfoAsValue(
+base::Value QuicChromiumClientSession::GetInfoAsValue(
     const std::set<HostPortPair>& aliases) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("version",
-                  QuicVersionToString(connection()->transport_version()));
-  dict->SetInteger("open_streams", GetNumOpenOutgoingStreams());
+  base::DictionaryValue dict;
+  dict.SetString("version",
+                 QuicVersionToString(connection()->transport_version()));
+  dict.SetInteger("open_streams", GetNumOpenOutgoingStreams());
   std::unique_ptr<base::ListValue> stream_list(new base::ListValue());
   for (DynamicStreamMap::const_iterator it = dynamic_streams().begin();
        it != dynamic_streams().end(); ++it) {
     stream_list->AppendString(base::NumberToString(it->second->id()));
   }
-  dict->Set("active_streams", std::move(stream_list));
+  dict.Set("active_streams", std::move(stream_list));
 
-  dict->SetInteger("total_streams", num_total_streams_);
-  dict->SetString("peer_address", peer_address().ToString());
-  dict->SetString("connection_id", connection_id().ToString());
-  dict->SetBoolean("connected", connection()->connected());
+  dict.SetInteger("total_streams", num_total_streams_);
+  dict.SetString("peer_address", peer_address().ToString());
+  dict.SetString("connection_id", connection_id().ToString());
+  dict.SetBoolean("connected", connection()->connected());
   const quic::QuicConnectionStats& stats = connection()->GetStats();
-  dict->SetInteger("packets_sent", stats.packets_sent);
-  dict->SetInteger("packets_received", stats.packets_received);
-  dict->SetInteger("packets_lost", stats.packets_lost);
+  dict.SetInteger("packets_sent", stats.packets_sent);
+  dict.SetInteger("packets_received", stats.packets_received);
+  dict.SetInteger("packets_lost", stats.packets_lost);
   SSLInfo ssl_info;
 
   std::unique_ptr<base::ListValue> alias_list(new base::ListValue());
   for (const auto& alias : aliases) {
     alias_list->AppendString(alias.ToString());
   }
-  dict->Set("aliases", std::move(alias_list));
+  dict.Set("aliases", std::move(alias_list));
 
   return std::move(dict);
 }
diff --git a/net/quic/quic_chromium_client_session.h b/net/quic/quic_chromium_client_session.h
index 4f0faef..51111ee95 100644
--- a/net/quic/quic_chromium_client_session.h
+++ b/net/quic/quic_chromium_client_session.h
@@ -537,8 +537,7 @@
                                 quic::QuicErrorCode quic_error,
                                 quic::ConnectionCloseBehavior behavior);
 
-  std::unique_ptr<base::Value> GetInfoAsValue(
-      const std::set<HostPortPair>& aliases);
+  base::Value GetInfoAsValue(const std::set<HostPortPair>& aliases);
 
   const NetLogWithSource& net_log() const { return net_log_; }
 
diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc
index 1aac6b8..833a3b9 100644
--- a/net/quic/quic_connection_logger.cc
+++ b/net/quic/quic_connection_logger.cc
@@ -38,97 +38,94 @@
 
 namespace {
 
-std::unique_ptr<base::Value> NetLogQuicPacketCallback(
-    const IPEndPoint* self_address,
-    const IPEndPoint* peer_address,
-    size_t packet_size,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("self_address", self_address->ToString());
-  dict->SetString("peer_address", peer_address->ToString());
-  dict->SetInteger("size", packet_size);
+base::Value NetLogQuicPacketCallback(const IPEndPoint* self_address,
+                                     const IPEndPoint* peer_address,
+                                     size_t packet_size,
+                                     NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  dict.SetString("self_address", self_address->ToString());
+  dict.SetString("peer_address", peer_address->ToString());
+  dict.SetInteger("size", packet_size);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicPacketSentCallback(
+base::Value NetLogQuicPacketSentCallback(
     const quic::SerializedPacket& serialized_packet,
     quic::TransmissionType transmission_type,
     quic::QuicTime sent_time,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("transmission_type", transmission_type);
-  dict->SetKey("packet_number",
-               NetLogNumberValue(serialized_packet.packet_number.ToUint64()));
-  dict->SetInteger("size", serialized_packet.encrypted_length);
-  dict->SetKey("sent_time_us", NetLogNumberValue(sent_time.ToDebuggingValue()));
+  base::DictionaryValue dict;
+  dict.SetInteger("transmission_type", transmission_type);
+  dict.SetKey("packet_number",
+              NetLogNumberValue(serialized_packet.packet_number.ToUint64()));
+  dict.SetInteger("size", serialized_packet.encrypted_length);
+  dict.SetKey("sent_time_us", NetLogNumberValue(sent_time.ToDebuggingValue()));
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicPacketRetransmittedCallback(
+base::Value NetLogQuicPacketRetransmittedCallback(
     quic::QuicPacketNumber old_packet_number,
     quic::QuicPacketNumber new_packet_number,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetKey("old_packet_number",
-               NetLogNumberValue(old_packet_number.ToUint64()));
-  dict->SetKey("new_packet_number",
-               NetLogNumberValue(new_packet_number.ToUint64()));
+  base::DictionaryValue dict;
+  dict.SetKey("old_packet_number",
+              NetLogNumberValue(old_packet_number.ToUint64()));
+  dict.SetKey("new_packet_number",
+              NetLogNumberValue(new_packet_number.ToUint64()));
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicPacketLostCallback(
+base::Value NetLogQuicPacketLostCallback(
     quic::QuicPacketNumber packet_number,
     quic::TransmissionType transmission_type,
     quic::QuicTime detection_time,
     NetLogCaptureMode /*capture_mode*/) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetInteger("transmission_type", transmission_type);
-  dict->SetKey("packet_number", NetLogNumberValue(packet_number.ToUint64()));
-  dict->SetKey("detection_time_us",
-               NetLogNumberValue(detection_time.ToDebuggingValue()));
-  return dict;
+  base::DictionaryValue dict;
+  dict.SetInteger("transmission_type", transmission_type);
+  dict.SetKey("packet_number", NetLogNumberValue(packet_number.ToUint64()));
+  dict.SetKey("detection_time_us",
+              NetLogNumberValue(detection_time.ToDebuggingValue()));
+  return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicDuplicatePacketCallback(
+base::Value NetLogQuicDuplicatePacketCallback(
     quic::QuicPacketNumber packet_number,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetKey("packet_number", NetLogNumberValue(packet_number.ToUint64()));
+  base::DictionaryValue dict;
+  dict.SetKey("packet_number", NetLogNumberValue(packet_number.ToUint64()));
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicPacketHeaderCallback(
+base::Value NetLogQuicPacketHeaderCallback(
     const quic::QuicPacketHeader* header,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("connection_id",
-                  header->destination_connection_id.ToString());
-  dict->SetInteger("reset_flag", header->reset_flag);
-  dict->SetInteger("version_flag", header->version_flag);
-  dict->SetKey("packet_number",
-               NetLogNumberValue(header->packet_number.ToUint64()));
+  base::DictionaryValue dict;
+  dict.SetString("connection_id", header->destination_connection_id.ToString());
+  dict.SetInteger("reset_flag", header->reset_flag);
+  dict.SetInteger("version_flag", header->version_flag);
+  dict.SetKey("packet_number",
+              NetLogNumberValue(header->packet_number.ToUint64()));
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicStreamFrameCallback(
+base::Value NetLogQuicStreamFrameCallback(
     const quic::QuicStreamFrame& frame,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("stream_id", frame.stream_id);
-  dict->SetBoolean("fin", frame.fin);
-  dict->SetKey("offset", NetLogNumberValue(frame.offset));
-  dict->SetInteger("length", frame.data_length);
+  base::DictionaryValue dict;
+  dict.SetInteger("stream_id", frame.stream_id);
+  dict.SetBoolean("fin", frame.fin);
+  dict.SetKey("offset", NetLogNumberValue(frame.offset));
+  dict.SetInteger("length", frame.data_length);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicAckFrameCallback(
-    const quic::QuicAckFrame* frame,
-    NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetKey("largest_observed",
-               NetLogNumberValue(frame->largest_acked.ToUint64()));
-  dict->SetKey("delta_time_largest_observed_us",
-               NetLogNumberValue(frame->ack_delay_time.ToMicroseconds()));
+base::Value NetLogQuicAckFrameCallback(const quic::QuicAckFrame* frame,
+                                       NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  dict.SetKey("largest_observed",
+              NetLogNumberValue(frame->largest_acked.ToUint64()));
+  dict.SetKey("delta_time_largest_observed_us",
+              NetLogNumberValue(frame->ack_delay_time.ToMicroseconds()));
 
   auto missing = std::make_unique<base::ListValue>();
   if (!frame->packets.Empty()) {
@@ -141,7 +138,7 @@
       }
     }
   }
-  dict->Set("missing_packets", std::move(missing));
+  dict.Set("missing_packets", std::move(missing));
 
   auto received = std::make_unique<base::ListValue>();
   const quic::PacketTimeVector& received_times = frame->received_packet_times;
@@ -151,124 +148,124 @@
     info->SetKey("received", NetLogNumberValue(it->second.ToDebuggingValue()));
     received->Append(std::move(info));
   }
-  dict->Set("received_packet_times", std::move(received));
+  dict.Set("received_packet_times", std::move(received));
 
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicRstStreamFrameCallback(
+base::Value NetLogQuicRstStreamFrameCallback(
     const quic::QuicRstStreamFrame* frame,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("stream_id", frame->stream_id);
-  dict->SetInteger("quic_rst_stream_error", frame->error_code);
+  base::DictionaryValue dict;
+  dict.SetInteger("stream_id", frame->stream_id);
+  dict.SetInteger("quic_rst_stream_error", frame->error_code);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicConnectionCloseFrameCallback(
+base::Value NetLogQuicConnectionCloseFrameCallback(
     const quic::QuicConnectionCloseFrame* frame,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("quic_error", frame->quic_error_code);
-  dict->SetString("details", frame->error_details);
+  base::DictionaryValue dict;
+  dict.SetInteger("quic_error", frame->quic_error_code);
+  dict.SetString("details", frame->error_details);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicWindowUpdateFrameCallback(
+base::Value NetLogQuicWindowUpdateFrameCallback(
     const quic::QuicWindowUpdateFrame* frame,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("stream_id", frame->stream_id);
-  dict->SetKey("byte_offset", NetLogNumberValue(frame->byte_offset));
+  base::DictionaryValue dict;
+  dict.SetInteger("stream_id", frame->stream_id);
+  dict.SetKey("byte_offset", NetLogNumberValue(frame->byte_offset));
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicBlockedFrameCallback(
+base::Value NetLogQuicBlockedFrameCallback(
     const quic::QuicBlockedFrame* frame,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("stream_id", frame->stream_id);
+  base::DictionaryValue dict;
+  dict.SetInteger("stream_id", frame->stream_id);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicGoAwayFrameCallback(
+base::Value NetLogQuicGoAwayFrameCallback(
     const quic::QuicGoAwayFrame* frame,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("quic_error", frame->error_code);
-  dict->SetInteger("last_good_stream_id", frame->last_good_stream_id);
-  dict->SetString("reason_phrase", frame->reason_phrase);
+  base::DictionaryValue dict;
+  dict.SetInteger("quic_error", frame->error_code);
+  dict.SetInteger("last_good_stream_id", frame->last_good_stream_id);
+  dict.SetString("reason_phrase", frame->reason_phrase);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicStopWaitingFrameCallback(
+base::Value NetLogQuicStopWaitingFrameCallback(
     const quic::QuicStopWaitingFrame* frame,
     NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
+  base::DictionaryValue dict;
   auto sent_info = std::make_unique<base::DictionaryValue>();
   sent_info->SetKey("least_unacked",
                     NetLogNumberValue(frame->least_unacked.ToUint64()));
-  dict->Set("sent_info", std::move(sent_info));
+  dict.Set("sent_info", std::move(sent_info));
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicVersionNegotiationPacketCallback(
+base::Value NetLogQuicVersionNegotiationPacketCallback(
     const quic::QuicVersionNegotiationPacket* packet,
     NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
+  base::DictionaryValue dict;
   auto versions = std::make_unique<base::ListValue>();
   for (auto it = packet->versions.begin(); it != packet->versions.end(); ++it) {
     versions->AppendString(ParsedQuicVersionToString(*it));
   }
-  dict->Set("versions", std::move(versions));
+  dict.Set("versions", std::move(versions));
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicPublicResetPacketCallback(
+base::Value NetLogQuicPublicResetPacketCallback(
     const IPEndPoint* server_hello_address,
     const IPEndPoint* public_reset_address,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("server_hello_address", server_hello_address->ToString());
-  dict->SetString("public_reset_address", public_reset_address->ToString());
+  base::DictionaryValue dict;
+  dict.SetString("server_hello_address", server_hello_address->ToString());
+  dict.SetString("public_reset_address", public_reset_address->ToString());
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicCryptoHandshakeMessageCallback(
+base::Value NetLogQuicCryptoHandshakeMessageCallback(
     const quic::CryptoHandshakeMessage* message,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("quic_crypto_handshake_message", message->DebugString());
+  base::DictionaryValue dict;
+  dict.SetString("quic_crypto_handshake_message", message->DebugString());
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicOnConnectionClosedCallback(
+base::Value NetLogQuicOnConnectionClosedCallback(
     quic::QuicErrorCode error,
     string error_details,
     quic::ConnectionCloseSource source,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("quic_error", error);
-  dict->SetString("details", error_details);
-  dict->SetBoolean("from_peer", source == quic::ConnectionCloseSource::FROM_PEER
-                                    ? true
-                                    : false);
+  base::DictionaryValue dict;
+  dict.SetInteger("quic_error", error);
+  dict.SetString("details", error_details);
+  dict.SetBoolean("from_peer", source == quic::ConnectionCloseSource::FROM_PEER
+                                   ? true
+                                   : false);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicCertificateVerifiedCallback(
+base::Value NetLogQuicCertificateVerifiedCallback(
     scoped_refptr<X509Certificate> cert,
     NetLogCaptureMode /* capture_mode */) {
   // Only the subjects are logged so that we can investigate connection pooling.
   // More fields could be logged in the future.
   std::vector<std::string> dns_names;
   cert->GetSubjectAltName(&dns_names, nullptr);
-  auto dict = std::make_unique<base::DictionaryValue>();
+  base::DictionaryValue dict;
   auto subjects = std::make_unique<base::ListValue>();
   for (auto& dns_name : dns_names) {
     subjects->GetList().emplace_back(std::move(dns_name));
   }
-  dict->Set("subjects", std::move(subjects));
+  dict.Set("subjects", std::move(subjects));
   return std::move(dict);
 }
 
diff --git a/net/quic/quic_connectivity_probing_manager.cc b/net/quic/quic_connectivity_probing_manager.cc
index 78e8461..5ce272a 100644
--- a/net/quic/quic_connectivity_probing_manager.cc
+++ b/net/quic/quic_connectivity_probing_manager.cc
@@ -17,38 +17,38 @@
 // Default to 2 seconds timeout as the maximum timeout.
 const int64_t kMaxProbingTimeoutMs = 2000;
 
-std::unique_ptr<base::Value> NetLogStartProbingCallback(
+base::Value NetLogStartProbingCallback(
     NetworkChangeNotifier::NetworkHandle network,
     const quic::QuicSocketAddress* peer_address,
     base::TimeDelta initial_timeout,
     NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetKey("network", NetLogNumberValue(network));
-  dict->SetString("peer address", peer_address->ToString());
-  dict->SetKey("initial_timeout_ms",
-               NetLogNumberValue(initial_timeout.InMilliseconds()));
+  base::DictionaryValue dict;
+  dict.SetKey("network", NetLogNumberValue(network));
+  dict.SetString("peer address", peer_address->ToString());
+  dict.SetKey("initial_timeout_ms",
+              NetLogNumberValue(initial_timeout.InMilliseconds()));
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogProbeReceivedCallback(
+base::Value NetLogProbeReceivedCallback(
     NetworkChangeNotifier::NetworkHandle network,
     const IPEndPoint* self_address,
     const quic::QuicSocketAddress* peer_address,
     NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetKey("network", NetLogNumberValue(network));
-  dict->SetString("self address", self_address->ToString());
-  dict->SetString("peer address", peer_address->ToString());
+  base::DictionaryValue dict;
+  dict.SetKey("network", NetLogNumberValue(network));
+  dict.SetString("self address", self_address->ToString());
+  dict.SetString("peer address", peer_address->ToString());
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogProbingDestinationCallback(
+base::Value NetLogProbingDestinationCallback(
     NetworkChangeNotifier::NetworkHandle network,
     const quic::QuicSocketAddress* peer_address,
     NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("network", base::NumberToString(network));
-  dict->SetString("peer address", peer_address->ToString());
+  base::DictionaryValue dict;
+  dict.SetString("network", base::NumberToString(network));
+  dict.SetString("peer address", peer_address->ToString());
   return std::move(dict);
 }
 
diff --git a/net/quic/quic_http_stream.cc b/net/quic/quic_http_stream.cc
index b032201..3908021 100644
--- a/net/quic/quic_http_stream.cc
+++ b/net/quic/quic_http_stream.cc
@@ -34,13 +34,12 @@
 
 namespace {
 
-std::unique_ptr<base::Value> NetLogQuicPushStreamCallback(
-    quic::QuicStreamId stream_id,
-    const GURL* url,
-    NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("stream_id", stream_id);
-  dict->SetString("url", url->spec());
+base::Value NetLogQuicPushStreamCallback(quic::QuicStreamId stream_id,
+                                         const GURL* url,
+                                         NetLogCaptureMode capture_mode) {
+  base::DictionaryValue dict;
+  dict.SetInteger("stream_id", stream_id);
+  dict.SetString("url", url->spec());
   return std::move(dict);
 }
 
diff --git a/net/quic/quic_http_utils.cc b/net/quic/quic_http_utils.cc
index e93ccc5..fa9ab08 100644
--- a/net/quic/quic_http_utils.cc
+++ b/net/quic/quic_http_utils.cc
@@ -36,30 +36,25 @@
                          : static_cast<RequestPriority>(HIGHEST - priority);
 }
 
-std::unique_ptr<base::Value> QuicRequestNetLogCallback(
-    quic::QuicStreamId stream_id,
-    const spdy::SpdyHeaderBlock* headers,
-    spdy::SpdyPriority priority,
-    NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(
-      static_cast<base::DictionaryValue*>(
-          SpdyHeaderBlockNetLogCallback(headers, capture_mode).release()));
-  dict->SetInteger("quic_priority", static_cast<int>(priority));
-  dict->SetInteger("quic_stream_id", static_cast<int>(stream_id));
-  return std::move(dict);
+base::Value QuicRequestNetLogCallback(quic::QuicStreamId stream_id,
+                                      const spdy::SpdyHeaderBlock* headers,
+                                      spdy::SpdyPriority priority,
+                                      NetLogCaptureMode capture_mode) {
+  base::Value dict = SpdyHeaderBlockNetLogCallback(headers, capture_mode);
+  DCHECK(dict.is_dict());
+  dict.SetIntKey("quic_priority", static_cast<int>(priority));
+  dict.SetIntKey("quic_stream_id", static_cast<int>(stream_id));
+  return dict;
 }
 
-std::unique_ptr<base::Value> QuicResponseNetLogCallback(
-    quic::QuicStreamId stream_id,
-    bool fin_received,
-    const spdy::SpdyHeaderBlock* headers,
-    NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(
-      static_cast<base::DictionaryValue*>(
-          SpdyHeaderBlockNetLogCallback(headers, capture_mode).release()));
-  dict->SetInteger("quic_stream_id", static_cast<int>(stream_id));
-  dict->SetBoolean("fin", fin_received);
-  return std::move(dict);
+base::Value QuicResponseNetLogCallback(quic::QuicStreamId stream_id,
+                                       bool fin_received,
+                                       const spdy::SpdyHeaderBlock* headers,
+                                       NetLogCaptureMode capture_mode) {
+  base::Value dict = SpdyHeaderBlockNetLogCallback(headers, capture_mode);
+  dict.SetIntKey("quic_stream_id", static_cast<int>(stream_id));
+  dict.SetBoolKey("fin", fin_received);
+  return dict;
 }
 
 quic::QuicTransportVersionVector FilterSupportedAltSvcVersions(
diff --git a/net/quic/quic_http_utils.h b/net/quic/quic_http_utils.h
index c12050f..5990ad1 100644
--- a/net/quic/quic_http_utils.h
+++ b/net/quic/quic_http_utils.h
@@ -23,14 +23,14 @@
 
 // Converts a spdy::SpdyHeaderBlock, stream_id and priority into NetLog event
 // parameters.
-NET_EXPORT std::unique_ptr<base::Value> QuicRequestNetLogCallback(
+NET_EXPORT base::Value QuicRequestNetLogCallback(
     quic::QuicStreamId stream_id,
     const spdy::SpdyHeaderBlock* headers,
     spdy::SpdyPriority priority,
     NetLogCaptureMode capture_mode);
 
 // Converts a spdy::SpdyHeaderBlock and stream into NetLog event parameters.
-NET_EXPORT std::unique_ptr<base::Value> QuicResponseNetLogCallback(
+NET_EXPORT base::Value QuicResponseNetLogCallback(
     quic::QuicStreamId stream_id,
     bool fin_received,
     const spdy::SpdyHeaderBlock* headers,
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index 1290c5b..61ab684 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -104,11 +104,11 @@
 // Set the maximum number of undecryptable packets the connection will store.
 const int32_t kMaxUndecryptablePackets = 100;
 
-std::unique_ptr<base::Value> NetLogQuicStreamFactoryJobCallback(
+base::Value NetLogQuicStreamFactoryJobCallback(
     const quic::QuicServerId* server_id,
     NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString(
+  base::DictionaryValue dict;
+  dict.SetString(
       "server_id",
       "https://" +
           HostPortPair(server_id->host(), server_id->port()).ToString() +
@@ -1547,7 +1547,7 @@
         hosts.insert(HostPortPair(alias_it->server_id().host(),
                                   alias_it->server_id().port()));
       }
-      list->Append(session->GetInfoAsValue(hosts));
+      list->GetList().push_back(session->GetInfoAsValue(hosts));
     }
   }
   return std::move(list);
diff --git a/net/socket/client_socket_pool.cc b/net/socket/client_socket_pool.cc
index 153d3c76..df6de12 100644
--- a/net/socket/client_socket_pool.cc
+++ b/net/socket/client_socket_pool.cc
@@ -109,13 +109,12 @@
   }
 }
 
-std::unique_ptr<base::Value> ClientSocketPool::NetLogGroupIdCallback(
+base::Value ClientSocketPool::NetLogGroupIdCallback(
     const GroupId* group_id,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> event_params(
-      new base::DictionaryValue());
-  event_params->SetString("group_id", group_id->ToString());
-  return event_params;
+  base::DictionaryValue event_params;
+  event_params.SetString("group_id", group_id->ToString());
+  return std::move(event_params);
 }
 
 std::unique_ptr<ConnectJob> ClientSocketPool::CreateConnectJob(
diff --git a/net/socket/client_socket_pool.h b/net/socket/client_socket_pool.h
index 11e46bf..fdf9534 100644
--- a/net/socket/client_socket_pool.h
+++ b/net/socket/client_socket_pool.h
@@ -360,9 +360,8 @@
                                                 const GroupId& group_id);
 
   // Utility method to log a GroupId with a NetLog event.
-  static std::unique_ptr<base::Value> NetLogGroupIdCallback(
-      const GroupId* group_id,
-      NetLogCaptureMode capture_mode);
+  static base::Value NetLogGroupIdCallback(const GroupId* group_id,
+                                           NetLogCaptureMode capture_mode);
 
   static std::unique_ptr<ConnectJob> CreateConnectJob(
       GroupId group_id,
diff --git a/net/socket/socket_net_log_params.cc b/net/socket/socket_net_log_params.cc
index 726cebef..bde4530 100644
--- a/net/socket/socket_net_log_params.cc
+++ b/net/socket/socket_net_log_params.cc
@@ -17,41 +17,37 @@
 
 namespace {
 
-std::unique_ptr<base::Value> NetLogSocketErrorCallback(
-    int net_error,
-    int os_error,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("net_error", net_error);
-  dict->SetInteger("os_error", os_error);
+base::Value NetLogSocketErrorCallback(int net_error,
+                                      int os_error,
+                                      NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  dict.SetInteger("net_error", net_error);
+  dict.SetInteger("os_error", os_error);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogHostPortPairCallback(
-    const HostPortPair* host_and_port,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("host_and_port", host_and_port->ToString());
+base::Value NetLogHostPortPairCallback(const HostPortPair* host_and_port,
+                                       NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  dict.SetString("host_and_port", host_and_port->ToString());
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogIPEndPointCallback(
-    const IPEndPoint* address,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("address", address->ToString());
+base::Value NetLogIPEndPointCallback(const IPEndPoint* address,
+                                     NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  dict.SetString("address", address->ToString());
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSourceAddressCallback(
-    const struct sockaddr* net_address,
-    socklen_t address_len,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+base::Value NetLogSourceAddressCallback(const struct sockaddr* net_address,
+                                        socklen_t address_len,
+                                        NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
   IPEndPoint ipe;
   bool result = ipe.FromSockAddr(net_address, address_len);
   DCHECK(result);
-  dict->SetString("source_address", ipe.ToString());
+  dict.SetString("source_address", ipe.ToString());
   return std::move(dict);
 }
 
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index 007d237..bb05c92 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -812,6 +812,10 @@
       EXPECT_FALSE(ssl_config.client_cert);
     }
   }
+  if (next_ssl_data->expected_false_start_enabled) {
+    EXPECT_EQ(*next_ssl_data->expected_false_start_enabled,
+              ssl_config.false_start_enabled);
+  }
   return std::unique_ptr<SSLClientSocket>(new MockSSLClientSocket(
       std::move(stream_socket), host_and_port, ssl_config, next_ssl_data));
 }
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index f0b305a..05d4c51e 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -482,6 +482,7 @@
   uint16_t expected_ssl_version_max;
   base::Optional<bool> expected_send_client_cert;
   scoped_refptr<X509Certificate> expected_client_cert;
+  base::Optional<bool> expected_false_start_enabled;
 
   bool is_connect_data_consumed = false;
   bool is_confirm_data_consumed = false;
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index 49a3523..71fdb600 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -78,55 +78,51 @@
 // Default size of the internal BoringSSL buffers.
 const int kDefaultOpenSSLBufferSize = 17 * 1024;
 
-std::unique_ptr<base::Value> NetLogPrivateKeyOperationCallback(
-    uint16_t algorithm,
-    SSLPrivateKey* key,
-    NetLogCaptureMode mode) {
-  std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
-  value->SetString("algorithm", SSL_get_signature_algorithm_name(
-                                    algorithm, 0 /* exclude curve */));
-  value->SetString("provider", key->GetProviderName());
+base::Value NetLogPrivateKeyOperationCallback(uint16_t algorithm,
+                                              SSLPrivateKey* key,
+                                              NetLogCaptureMode mode) {
+  base::DictionaryValue value;
+  value.SetString("algorithm", SSL_get_signature_algorithm_name(
+                                   algorithm, 0 /* exclude curve */));
+  value.SetString("provider", key->GetProviderName());
   return std::move(value);
 }
 
-std::unique_ptr<base::Value> NetLogSSLInfoCallback(
-    SSLClientSocketImpl* socket,
-    NetLogCaptureMode capture_mode) {
+base::Value NetLogSSLInfoCallback(SSLClientSocketImpl* socket,
+                                  NetLogCaptureMode capture_mode) {
   SSLInfo ssl_info;
   if (!socket->GetSSLInfo(&ssl_info))
-    return nullptr;
+    return base::Value();
 
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  base::DictionaryValue dict;
   const char* version_str;
   SSLVersionToString(&version_str,
                      SSLConnectionStatusToVersion(ssl_info.connection_status));
-  dict->SetString("version", version_str);
-  dict->SetBoolean("is_resumed",
-                   ssl_info.handshake_type == SSLInfo::HANDSHAKE_RESUME);
-  dict->SetInteger("cipher_suite", SSLConnectionStatusToCipherSuite(
-                                       ssl_info.connection_status));
+  dict.SetString("version", version_str);
+  dict.SetBoolean("is_resumed",
+                  ssl_info.handshake_type == SSLInfo::HANDSHAKE_RESUME);
+  dict.SetInteger("cipher_suite",
+                  SSLConnectionStatusToCipherSuite(ssl_info.connection_status));
 
-  dict->SetString("next_proto",
-                  NextProtoToString(socket->GetNegotiatedProtocol()));
+  dict.SetString("next_proto",
+                 NextProtoToString(socket->GetNegotiatedProtocol()));
 
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSSLAlertCallback(
-    const void* bytes,
-    size_t len,
-    NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetKey("bytes", NetLogBinaryValue(bytes, len));
+base::Value NetLogSSLAlertCallback(const void* bytes,
+                                   size_t len,
+                                   NetLogCaptureMode capture_mode) {
+  base::DictionaryValue dict;
+  dict.SetKey("bytes", NetLogBinaryValue(bytes, len));
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSSLMessageCallback(
-    bool is_write,
-    const void* bytes,
-    size_t len,
-    NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+base::Value NetLogSSLMessageCallback(bool is_write,
+                                     const void* bytes,
+                                     size_t len,
+                                     NetLogCaptureMode capture_mode) {
+  base::DictionaryValue dict;
   if (len == 0) {
     NOTREACHED();
     return std::move(dict);
@@ -135,7 +131,7 @@
   // The handshake message type is the first byte. Include it so elided messages
   // still report their type.
   uint8_t type = reinterpret_cast<const uint8_t*>(bytes)[0];
-  dict->SetInteger("type", type);
+  dict.SetInteger("type", type);
 
   // Elide client certificate messages unless logging socket bytes. The client
   // certificate does not contain information needed to impersonate the user
@@ -143,7 +139,7 @@
   // information on the user's identity.
   if (!is_write || type != SSL3_MT_CERTIFICATE ||
       capture_mode.include_socket_bytes()) {
-    dict->SetKey("bytes", NetLogBinaryValue(bytes, len));
+    dict.SetKey("bytes", NetLogBinaryValue(bytes, len));
   }
 
   return std::move(dict);
diff --git a/net/socket/transport_client_socket_pool.cc b/net/socket/transport_client_socket_pool.cc
index 95b9427..46f1ee0667 100644
--- a/net/socket/transport_client_socket_pool.cc
+++ b/net/socket/transport_client_socket_pool.cc
@@ -38,13 +38,13 @@
 // after a certain timeout has passed without receiving an ACK.
 bool g_connect_backup_jobs_enabled = true;
 
-std::unique_ptr<base::Value> NetLogCreateConnectJobCallback(
+base::Value NetLogCreateConnectJobCallback(
     bool backup_job,
     const ClientSocketPool::GroupId* group_id,
     net::NetLogCaptureMode capture_mode) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetBoolean("backup_job", backup_job);
-  dict->SetString("group_id", group_id->ToString());
+  base::DictionaryValue dict;
+  dict.SetBoolean("backup_job", backup_job);
+  dict.SetString("group_id", group_id->ToString());
   return std::move(dict);
 }
 
diff --git a/net/socket/udp_net_log_parameters.cc b/net/socket/udp_net_log_parameters.cc
index 4efcbd9..d6b6fad3 100644
--- a/net/socket/udp_net_log_parameters.cc
+++ b/net/socket/udp_net_log_parameters.cc
@@ -15,28 +15,27 @@
 
 namespace {
 
-std::unique_ptr<base::Value> NetLogUDPDataTranferCallback(
-    int byte_count,
-    const char* bytes,
-    const IPEndPoint* address,
-    NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("byte_count", byte_count);
+base::Value NetLogUDPDataTranferCallback(int byte_count,
+                                         const char* bytes,
+                                         const IPEndPoint* address,
+                                         NetLogCaptureMode capture_mode) {
+  base::DictionaryValue dict;
+  dict.SetInteger("byte_count", byte_count);
   if (capture_mode.include_socket_bytes())
-    dict->SetKey("bytes", NetLogBinaryValue(bytes, byte_count));
+    dict.SetKey("bytes", NetLogBinaryValue(bytes, byte_count));
   if (address)
-    dict->SetString("address", address->ToString());
+    dict.SetString("address", address->ToString());
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogUDPConnectCallback(
+base::Value NetLogUDPConnectCallback(
     const IPEndPoint* address,
     NetworkChangeNotifier::NetworkHandle network,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("address", address->ToString());
+  base::DictionaryValue dict;
+  dict.SetString("address", address->ToString());
   if (network != NetworkChangeNotifier::kInvalidNetworkHandle)
-    dict->SetInteger("bound_to_network", network);
+    dict.SetInteger("bound_to_network", network);
   return std::move(dict);
 }
 
diff --git a/net/spdy/header_coalescer.cc b/net/spdy/header_coalescer.cc
index be4c9c53..9a3bb18 100644
--- a/net/spdy/header_coalescer.cc
+++ b/net/spdy/header_coalescer.cc
@@ -22,17 +22,16 @@
 namespace net {
 namespace {
 
-std::unique_ptr<base::Value> ElideNetLogHeaderCallback(
-    base::StringPiece header_name,
-    base::StringPiece header_value,
-    base::StringPiece error_message,
-    NetLogCaptureMode capture_mode) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetKey("header_name", NetLogStringValue(header_name));
-  dict->SetKey("header_value", NetLogStringValue(ElideHeaderValueForNetLog(
-                                   capture_mode, header_name.as_string(),
-                                   header_value.as_string())));
-  dict->SetString("error", error_message);
+base::Value ElideNetLogHeaderCallback(base::StringPiece header_name,
+                                      base::StringPiece header_value,
+                                      base::StringPiece error_message,
+                                      NetLogCaptureMode capture_mode) {
+  base::DictionaryValue dict;
+  dict.SetKey("header_name", NetLogStringValue(header_name));
+  dict.SetKey("header_value", NetLogStringValue(ElideHeaderValueForNetLog(
+                                  capture_mode, header_name.as_string(),
+                                  header_value.as_string())));
+  dict.SetString("error", error_message);
   return std::move(dict);
 }
 
diff --git a/net/spdy/spdy_log_util.cc b/net/spdy/spdy_log_util.cc
index 1ee31bb..bb44301d 100644
--- a/net/spdy/spdy_log_util.cc
+++ b/net/spdy/spdy_log_util.cc
@@ -22,14 +22,14 @@
       {"[", base::NumberToString(debug_data.size()), " bytes were stripped]"}));
 }
 
-std::unique_ptr<base::ListValue> ElideSpdyHeaderBlockForNetLog(
+base::ListValue ElideSpdyHeaderBlockForNetLog(
     const spdy::SpdyHeaderBlock& headers,
     NetLogCaptureMode capture_mode) {
-  auto headers_list = std::make_unique<base::ListValue>();
+  base::ListValue headers_list;
   for (const auto& header : headers) {
     base::StringPiece key = header.first;
     base::StringPiece value = header.second;
-    headers_list->GetList().push_back(NetLogStringValue(
+    headers_list.GetList().push_back(NetLogStringValue(
         base::StrCat({key, ": ",
                       ElideHeaderValueForNetLog(capture_mode, key.as_string(),
                                                 value.as_string())})));
@@ -37,11 +37,10 @@
   return headers_list;
 }
 
-std::unique_ptr<base::Value> SpdyHeaderBlockNetLogCallback(
-    const spdy::SpdyHeaderBlock* headers,
-    NetLogCaptureMode capture_mode) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->Set("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode));
+base::Value SpdyHeaderBlockNetLogCallback(const spdy::SpdyHeaderBlock* headers,
+                                          NetLogCaptureMode capture_mode) {
+  base::DictionaryValue dict;
+  dict.SetKey("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode));
   return std::move(dict);
 }
 
diff --git a/net/spdy/spdy_log_util.h b/net/spdy/spdy_log_util.h
index 4eb89b2..df08152 100644
--- a/net/spdy/spdy_log_util.h
+++ b/net/spdy/spdy_log_util.h
@@ -29,12 +29,12 @@
     base::StringPiece debug_data);
 
 // Given a spdy::SpdyHeaderBlock, return its base::ListValue representation.
-NET_EXPORT_PRIVATE std::unique_ptr<base::ListValue>
-ElideSpdyHeaderBlockForNetLog(const spdy::SpdyHeaderBlock& headers,
-                              NetLogCaptureMode capture_mode);
+NET_EXPORT_PRIVATE base::ListValue ElideSpdyHeaderBlockForNetLog(
+    const spdy::SpdyHeaderBlock& headers,
+    NetLogCaptureMode capture_mode);
 
 // Converts a spdy::SpdyHeaderBlock into NetLog event parameters.
-NET_EXPORT_PRIVATE std::unique_ptr<base::Value> SpdyHeaderBlockNetLogCallback(
+NET_EXPORT_PRIVATE base::Value SpdyHeaderBlockNetLogCallback(
     const spdy::SpdyHeaderBlock* headers,
     NetLogCaptureMode capture_mode);
 
diff --git a/net/spdy/spdy_log_util_unittest.cc b/net/spdy/spdy_log_util_unittest.cc
index cd3aa89..53f788a 100644
--- a/net/spdy/spdy_log_util_unittest.cc
+++ b/net/spdy/spdy_log_util_unittest.cc
@@ -37,29 +37,29 @@
   headers["foo"] = "bar";
   headers["cookie"] = "name=value";
 
-  std::unique_ptr<base::ListValue> list =
+  base::ListValue list =
       ElideSpdyHeaderBlockForNetLog(headers, NetLogCaptureMode::Default());
 
-  ASSERT_TRUE(list);
-  ASSERT_EQ(2u, list->GetList().size());
+  ASSERT_FALSE(list.is_none());
+  ASSERT_EQ(2u, list.GetList().size());
 
-  ASSERT_TRUE(list->GetList()[0].is_string());
-  EXPECT_EQ("foo: bar", list->GetList()[0].GetString());
+  ASSERT_TRUE(list.GetList()[0].is_string());
+  EXPECT_EQ("foo: bar", list.GetList()[0].GetString());
 
-  ASSERT_TRUE(list->GetList()[1].is_string());
-  EXPECT_EQ("cookie: [10 bytes were stripped]", list->GetList()[1].GetString());
+  ASSERT_TRUE(list.GetList()[1].is_string());
+  EXPECT_EQ("cookie: [10 bytes were stripped]", list.GetList()[1].GetString());
 
   list = ElideSpdyHeaderBlockForNetLog(
       headers, NetLogCaptureMode::IncludeCookiesAndCredentials());
 
-  ASSERT_TRUE(list);
-  ASSERT_EQ(2u, list->GetList().size());
+  ASSERT_FALSE(list.is_none());
+  ASSERT_EQ(2u, list.GetList().size());
 
-  ASSERT_TRUE(list->GetList()[0].is_string());
-  EXPECT_EQ("foo: bar", list->GetList()[0].GetString());
+  ASSERT_TRUE(list.GetList()[0].is_string());
+  EXPECT_EQ("foo: bar", list.GetList()[0].GetString());
 
-  ASSERT_TRUE(list->GetList()[1].is_string());
-  EXPECT_EQ("cookie: name=value", list->GetList()[1].GetString());
+  ASSERT_TRUE(list.GetList()[1].is_string());
+  EXPECT_EQ("cookie: name=value", list.GetList()[1].GetString());
 }
 
 TEST(SpdyLogUtilTest, SpdyHeaderBlockNetLogCallback) {
@@ -67,8 +67,8 @@
   headers["foo"] = "bar";
   headers["cookie"] = "name=value";
 
-  std::unique_ptr<base::Value> dict =
-      SpdyHeaderBlockNetLogCallback(&headers, NetLogCaptureMode::Default());
+  std::unique_ptr<base::Value> dict = base::Value::ToUniquePtrValue(
+      SpdyHeaderBlockNetLogCallback(&headers, NetLogCaptureMode::Default()));
 
   ASSERT_TRUE(dict);
   ASSERT_TRUE(dict->is_dict());
@@ -86,8 +86,8 @@
   EXPECT_EQ("cookie: [10 bytes were stripped]",
             header_list->GetList()[1].GetString());
 
-  dict = SpdyHeaderBlockNetLogCallback(
-      &headers, NetLogCaptureMode::IncludeCookiesAndCredentials());
+  dict = base::Value::ToUniquePtrValue(SpdyHeaderBlockNetLogCallback(
+      &headers, NetLogCaptureMode::IncludeCookiesAndCredentials()));
 
   ASSERT_TRUE(dict);
   ASSERT_TRUE(dict->is_dict());
@@ -112,16 +112,16 @@
   headers["O\xe2"] = "bar";
   headers["\xde\xad"] = "\xbe\xef";
 
-  std::unique_ptr<base::ListValue> list =
+  base::ListValue list =
       ElideSpdyHeaderBlockForNetLog(headers, NetLogCaptureMode::Default());
 
-  ASSERT_EQ(3u, list->GetSize());
+  ASSERT_EQ(3u, list.GetSize());
   std::string field;
-  EXPECT_TRUE(list->GetString(0, &field));
+  EXPECT_TRUE(list.GetString(0, &field));
   EXPECT_EQ("%ESCAPED:\xE2\x80\x8B foo: bar%81", field);
-  EXPECT_TRUE(list->GetString(1, &field));
+  EXPECT_TRUE(list.GetString(1, &field));
   EXPECT_EQ("%ESCAPED:\xE2\x80\x8B O%E2: bar", field);
-  EXPECT_TRUE(list->GetString(2, &field));
+  EXPECT_TRUE(list.GetString(2, &field));
   EXPECT_EQ("%ESCAPED:\xE2\x80\x8B %DE%AD: %BE%EF", field);
 }
 
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 9b6ac6832..ff1a81c 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -224,240 +224,233 @@
   return it->second == 1;
 }
 
-std::unique_ptr<base::Value> NetLogSpdyHeadersSentCallback(
-    const spdy::SpdyHeaderBlock* headers,
-    bool fin,
-    spdy::SpdyStreamId stream_id,
-    bool has_priority,
-    int weight,
-    spdy::SpdyStreamId parent_stream_id,
-    bool exclusive,
-    NetLogSource source_dependency,
-    NetLogCaptureMode capture_mode) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->Set("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode));
-  dict->SetBoolean("fin", fin);
-  dict->SetInteger("stream_id", stream_id);
-  dict->SetBoolean("has_priority", has_priority);
+base::Value NetLogSpdyHeadersSentCallback(const spdy::SpdyHeaderBlock* headers,
+                                          bool fin,
+                                          spdy::SpdyStreamId stream_id,
+                                          bool has_priority,
+                                          int weight,
+                                          spdy::SpdyStreamId parent_stream_id,
+                                          bool exclusive,
+                                          NetLogSource source_dependency,
+                                          NetLogCaptureMode capture_mode) {
+  base::DictionaryValue dict;
+  dict.SetKey("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode));
+  dict.SetBoolean("fin", fin);
+  dict.SetInteger("stream_id", stream_id);
+  dict.SetBoolean("has_priority", has_priority);
   if (has_priority) {
-    dict->SetInteger("parent_stream_id", parent_stream_id);
-    dict->SetInteger("weight", weight);
-    dict->SetBoolean("exclusive", exclusive);
+    dict.SetInteger("parent_stream_id", parent_stream_id);
+    dict.SetInteger("weight", weight);
+    dict.SetBoolean("exclusive", exclusive);
   }
   if (source_dependency.IsValid()) {
-    source_dependency.AddToEventParameters(dict.get());
+    source_dependency.AddToEventParameters(&dict);
   }
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdyHeadersReceivedCallback(
+base::Value NetLogSpdyHeadersReceivedCallback(
     const spdy::SpdyHeaderBlock* headers,
     bool fin,
     spdy::SpdyStreamId stream_id,
     NetLogCaptureMode capture_mode) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->Set("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode));
-  dict->SetBoolean("fin", fin);
-  dict->SetInteger("stream_id", stream_id);
+  base::DictionaryValue dict;
+  dict.SetKey("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode));
+  dict.SetBoolean("fin", fin);
+  dict.SetInteger("stream_id", stream_id);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdySessionCloseCallback(
+base::Value NetLogSpdySessionCloseCallback(
     int net_error,
     const std::string* description,
     NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetInteger("net_error", net_error);
-  dict->SetString("description", *description);
+  base::DictionaryValue dict;
+  dict.SetInteger("net_error", net_error);
+  dict.SetString("description", *description);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdySessionCallback(
-    const HostPortProxyPair* host_pair,
-    NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetString("host", host_pair->first.ToString());
-  dict->SetString("proxy", host_pair->second.ToPacString());
+base::Value NetLogSpdySessionCallback(const HostPortProxyPair* host_pair,
+                                      NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  dict.SetString("host", host_pair->first.ToString());
+  dict.SetString("proxy", host_pair->second.ToPacString());
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdyInitializedCallback(
+base::Value NetLogSpdyInitializedCallback(
     NetLogSource source,
     NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
+  base::DictionaryValue dict;
   if (source.IsValid()) {
-    source.AddToEventParameters(dict.get());
+    source.AddToEventParameters(&dict);
   }
-  dict->SetString("protocol", NextProtoToString(kProtoHTTP2));
+  dict.SetString("protocol", NextProtoToString(kProtoHTTP2));
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdySendSettingsCallback(
+base::Value NetLogSpdySendSettingsCallback(
     const spdy::SettingsMap* settings,
     NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  auto settings_list = std::make_unique<base::ListValue>();
+  base::DictionaryValue dict;
+  base::ListValue settings_list;
   for (auto it = settings->begin(); it != settings->end(); ++it) {
     const spdy::SpdySettingsId id = it->first;
     const uint32_t value = it->second;
-    settings_list->AppendString(
+    settings_list.AppendString(
         base::StringPrintf("[id:%u (%s) value:%u]", id,
                            spdy::SettingsIdToString(id).c_str(), value));
   }
-  dict->Set("settings", std::move(settings_list));
+  dict.SetKey("settings", std::move(settings_list));
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdyRecvSettingCallback(
+base::Value NetLogSpdyRecvSettingCallback(
     spdy::SpdySettingsId id,
     uint32_t value,
     NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetString(
+  base::DictionaryValue dict;
+  dict.SetString(
       "id",
       base::StringPrintf("%u (%s)", id, spdy::SettingsIdToString(id).c_str()));
-  dict->SetInteger("value", value);
+  dict.SetInteger("value", value);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdyWindowUpdateFrameCallback(
+base::Value NetLogSpdyWindowUpdateFrameCallback(
     spdy::SpdyStreamId stream_id,
     uint32_t delta,
     NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetInteger("stream_id", static_cast<int>(stream_id));
-  dict->SetInteger("delta", delta);
+  base::DictionaryValue dict;
+  dict.SetInteger("stream_id", static_cast<int>(stream_id));
+  dict.SetInteger("delta", delta);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdySessionWindowUpdateCallback(
+base::Value NetLogSpdySessionWindowUpdateCallback(
     int32_t delta,
     int32_t window_size,
     NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetInteger("delta", delta);
-  dict->SetInteger("window_size", window_size);
+  base::DictionaryValue dict;
+  dict.SetInteger("delta", delta);
+  dict.SetInteger("window_size", window_size);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdyDataCallback(
-    spdy::SpdyStreamId stream_id,
-    int size,
-    bool fin,
-    NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetInteger("stream_id", static_cast<int>(stream_id));
-  dict->SetInteger("size", size);
-  dict->SetBoolean("fin", fin);
+base::Value NetLogSpdyDataCallback(spdy::SpdyStreamId stream_id,
+                                   int size,
+                                   bool fin,
+                                   NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  dict.SetInteger("stream_id", static_cast<int>(stream_id));
+  dict.SetInteger("size", size);
+  dict.SetBoolean("fin", fin);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdyRecvRstStreamCallback(
+base::Value NetLogSpdyRecvRstStreamCallback(
     spdy::SpdyStreamId stream_id,
     spdy::SpdyErrorCode error_code,
     NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetInteger("stream_id", static_cast<int>(stream_id));
-  dict->SetString(
+  base::DictionaryValue dict;
+  dict.SetInteger("stream_id", static_cast<int>(stream_id));
+  dict.SetString(
       "error_code",
       base::StringPrintf("%u (%s)", error_code, ErrorCodeToString(error_code)));
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdySendRstStreamCallback(
+base::Value NetLogSpdySendRstStreamCallback(
     spdy::SpdyStreamId stream_id,
     spdy::SpdyErrorCode error_code,
     const std::string* description,
     NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetInteger("stream_id", static_cast<int>(stream_id));
-  dict->SetString(
+  base::DictionaryValue dict;
+  dict.SetInteger("stream_id", static_cast<int>(stream_id));
+  dict.SetString(
       "error_code",
       base::StringPrintf("%u (%s)", error_code, ErrorCodeToString(error_code)));
-  dict->SetString("description", *description);
+  dict.SetString("description", *description);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdyPingCallback(
-    spdy::SpdyPingId unique_id,
-    bool is_ack,
-    const char* type,
-    NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetInteger("unique_id", static_cast<int>(unique_id));
-  dict->SetString("type", type);
-  dict->SetBoolean("is_ack", is_ack);
+base::Value NetLogSpdyPingCallback(spdy::SpdyPingId unique_id,
+                                   bool is_ack,
+                                   const char* type,
+                                   NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  dict.SetInteger("unique_id", static_cast<int>(unique_id));
+  dict.SetString("type", type);
+  dict.SetBoolean("is_ack", is_ack);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdyRecvGoAwayCallback(
-    spdy::SpdyStreamId last_stream_id,
-    int active_streams,
-    int unclaimed_streams,
-    spdy::SpdyErrorCode error_code,
-    base::StringPiece debug_data,
-    NetLogCaptureMode capture_mode) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetInteger("last_accepted_stream_id", static_cast<int>(last_stream_id));
-  dict->SetInteger("active_streams", active_streams);
-  dict->SetInteger("unclaimed_streams", unclaimed_streams);
-  dict->SetString(
+base::Value NetLogSpdyRecvGoAwayCallback(spdy::SpdyStreamId last_stream_id,
+                                         int active_streams,
+                                         int unclaimed_streams,
+                                         spdy::SpdyErrorCode error_code,
+                                         base::StringPiece debug_data,
+                                         NetLogCaptureMode capture_mode) {
+  base::DictionaryValue dict;
+  dict.SetInteger("last_accepted_stream_id", static_cast<int>(last_stream_id));
+  dict.SetInteger("active_streams", active_streams);
+  dict.SetInteger("unclaimed_streams", unclaimed_streams);
+  dict.SetString(
       "error_code",
       base::StringPrintf("%u (%s)", error_code, ErrorCodeToString(error_code)));
-  dict->SetKey("debug_data",
-               ElideGoAwayDebugDataForNetLog(capture_mode, debug_data));
+  dict.SetKey("debug_data",
+              ElideGoAwayDebugDataForNetLog(capture_mode, debug_data));
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdyPushPromiseReceivedCallback(
+base::Value NetLogSpdyPushPromiseReceivedCallback(
     const spdy::SpdyHeaderBlock* headers,
     spdy::SpdyStreamId stream_id,
     spdy::SpdyStreamId promised_stream_id,
     NetLogCaptureMode capture_mode) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->Set("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode));
-  dict->SetInteger("id", stream_id);
-  dict->SetInteger("promised_stream_id", promised_stream_id);
+  base::DictionaryValue dict;
+  dict.SetKey("headers", ElideSpdyHeaderBlockForNetLog(*headers, capture_mode));
+  dict.SetInteger("id", stream_id);
+  dict.SetInteger("promised_stream_id", promised_stream_id);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdyAdoptedPushStreamCallback(
+base::Value NetLogSpdyAdoptedPushStreamCallback(
     spdy::SpdyStreamId stream_id,
     const GURL* url,
     NetLogCaptureMode capture_mode) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetInteger("stream_id", stream_id);
-  dict->SetString("url", url->spec());
+  base::DictionaryValue dict;
+  dict.SetInteger("stream_id", stream_id);
+  dict.SetString("url", url->spec());
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdySessionStalledCallback(
-    size_t num_active_streams,
-    size_t num_created_streams,
-    size_t num_pushed_streams,
-    size_t max_concurrent_streams,
-    const std::string& url,
-    NetLogCaptureMode capture_mode) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetInteger("num_active_streams", num_active_streams);
-  dict->SetInteger("num_created_streams", num_created_streams);
-  dict->SetInteger("num_pushed_streams", num_pushed_streams);
-  dict->SetInteger("max_concurrent_streams", max_concurrent_streams);
-  dict->SetString("url", url);
+base::Value NetLogSpdySessionStalledCallback(size_t num_active_streams,
+                                             size_t num_created_streams,
+                                             size_t num_pushed_streams,
+                                             size_t max_concurrent_streams,
+                                             const std::string& url,
+                                             NetLogCaptureMode capture_mode) {
+  base::DictionaryValue dict;
+  dict.SetInteger("num_active_streams", num_active_streams);
+  dict.SetInteger("num_created_streams", num_created_streams);
+  dict.SetInteger("num_pushed_streams", num_pushed_streams);
+  dict.SetInteger("max_concurrent_streams", max_concurrent_streams);
+  dict.SetString("url", url);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdyPriorityCallback(
-    spdy::SpdyStreamId stream_id,
-    spdy::SpdyStreamId parent_stream_id,
-    int weight,
-    bool exclusive,
-    NetLogCaptureMode capture_mode) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetInteger("stream_id", stream_id);
-  dict->SetInteger("parent_stream_id", parent_stream_id);
-  dict->SetInteger("weight", weight);
-  dict->SetBoolean("exclusive", exclusive);
+base::Value NetLogSpdyPriorityCallback(spdy::SpdyStreamId stream_id,
+                                       spdy::SpdyStreamId parent_stream_id,
+                                       int weight,
+                                       bool exclusive,
+                                       NetLogCaptureMode capture_mode) {
+  base::DictionaryValue dict;
+  dict.SetInteger("stream_id", stream_id);
+  dict.SetInteger("parent_stream_id", parent_stream_id);
+  dict.SetInteger("weight", weight);
+  dict.SetBoolean("exclusive", exclusive);
   return std::move(dict);
 }
 
@@ -1442,44 +1435,44 @@
   }
 }
 
-std::unique_ptr<base::Value> SpdySession::GetInfoAsValue() const {
-  auto dict = std::make_unique<base::DictionaryValue>();
+base::Value SpdySession::GetInfoAsValue() const {
+  base::DictionaryValue dict;
 
-  dict->SetInteger("source_id", net_log_.source().id);
+  dict.SetInteger("source_id", net_log_.source().id);
 
-  dict->SetString("host_port_pair", host_port_pair().ToString());
+  dict.SetString("host_port_pair", host_port_pair().ToString());
   if (!pooled_aliases_.empty()) {
     auto alias_list = std::make_unique<base::ListValue>();
     for (const auto& alias : pooled_aliases_) {
       alias_list->AppendString(alias.host_port_pair().ToString());
     }
-    dict->Set("aliases", std::move(alias_list));
+    dict.Set("aliases", std::move(alias_list));
   }
-  dict->SetString("proxy", host_port_proxy_pair().second.ToURI());
+  dict.SetString("proxy", host_port_proxy_pair().second.ToURI());
 
-  dict->SetInteger("active_streams", active_streams_.size());
+  dict.SetInteger("active_streams", active_streams_.size());
 
-  dict->SetInteger("unclaimed_pushed_streams",
-                   pool_->push_promise_index()->CountStreamsForSession(this));
+  dict.SetInteger("unclaimed_pushed_streams",
+                  pool_->push_promise_index()->CountStreamsForSession(this));
 
-  dict->SetString("negotiated_protocol",
-                  NextProtoToString(socket_->GetNegotiatedProtocol()));
+  dict.SetString("negotiated_protocol",
+                 NextProtoToString(socket_->GetNegotiatedProtocol()));
 
-  dict->SetInteger("error", error_on_close_);
-  dict->SetInteger("max_concurrent_streams", max_concurrent_streams_);
+  dict.SetInteger("error", error_on_close_);
+  dict.SetInteger("max_concurrent_streams", max_concurrent_streams_);
 
-  dict->SetInteger("streams_initiated_count", streams_initiated_count_);
-  dict->SetInteger("streams_pushed_count", streams_pushed_count_);
-  dict->SetInteger("streams_pushed_and_claimed_count",
-                   streams_pushed_and_claimed_count_);
-  dict->SetInteger("streams_abandoned_count", streams_abandoned_count_);
+  dict.SetInteger("streams_initiated_count", streams_initiated_count_);
+  dict.SetInteger("streams_pushed_count", streams_pushed_count_);
+  dict.SetInteger("streams_pushed_and_claimed_count",
+                  streams_pushed_and_claimed_count_);
+  dict.SetInteger("streams_abandoned_count", streams_abandoned_count_);
   DCHECK(buffered_spdy_framer_.get());
-  dict->SetInteger("frames_received", buffered_spdy_framer_->frames_received());
+  dict.SetInteger("frames_received", buffered_spdy_framer_->frames_received());
 
-  dict->SetInteger("send_window_size", session_send_window_size_);
-  dict->SetInteger("recv_window_size", session_recv_window_size_);
-  dict->SetInteger("unacked_recv_window_bytes",
-                   session_unacked_recv_window_bytes_);
+  dict.SetInteger("send_window_size", session_send_window_size_);
+  dict.SetInteger("recv_window_size", session_recv_window_size_);
+  dict.SetInteger("unacked_recv_window_bytes",
+                  session_unacked_recv_window_bytes_);
   return std::move(dict);
 }
 
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index 827f1b8..b9da335 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -498,7 +498,7 @@
 
   // Retrieves information on the current state of the SPDY session as a
   // Value.
-  std::unique_ptr<base::Value> GetInfoAsValue() const;
+  base::Value GetInfoAsValue() const;
 
   // Indicates whether the session is being reused after having successfully
   // used to send/receive data in the past or if the underlying socket was idle
diff --git a/net/spdy/spdy_session_pool.cc b/net/spdy/spdy_session_pool.cc
index 282199b..e2447a2 100644
--- a/net/spdy/spdy_session_pool.cc
+++ b/net/spdy/spdy_session_pool.cc
@@ -419,7 +419,7 @@
     const SpdySessionKey& key = it->first;
     const SpdySessionKey& session_key = it->second->spdy_session_key();
     if (key == session_key)
-      list->Append(it->second->GetInfoAsValue());
+      list->GetList().push_back(it->second->GetInfoAsValue());
   }
   return std::move(list);
 }
diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc
index a4ee0953..ffdc452 100644
--- a/net/spdy/spdy_stream.cc
+++ b/net/spdy/spdy_stream.cc
@@ -32,27 +32,27 @@
 
 namespace {
 
-std::unique_ptr<base::Value> NetLogSpdyStreamErrorCallback(
+base::Value NetLogSpdyStreamErrorCallback(
     spdy::SpdyStreamId stream_id,
     int net_error,
     const std::string* description,
     NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetInteger("stream_id", static_cast<int>(stream_id));
-  dict->SetString("net_error", ErrorToShortString(net_error));
-  dict->SetString("description", *description);
+  base::DictionaryValue dict;
+  dict.SetInteger("stream_id", static_cast<int>(stream_id));
+  dict.SetString("net_error", ErrorToShortString(net_error));
+  dict.SetString("description", *description);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdyStreamWindowUpdateCallback(
+base::Value NetLogSpdyStreamWindowUpdateCallback(
     spdy::SpdyStreamId stream_id,
     int32_t delta,
     int32_t window_size,
     NetLogCaptureMode /* capture_mode */) {
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetInteger("stream_id", stream_id);
-  dict->SetInteger("delta", delta);
-  dict->SetInteger("window_size", window_size);
+  base::DictionaryValue dict;
+  dict.SetInteger("stream_id", stream_id);
+  dict.SetInteger("delta", delta);
+  dict.SetInteger("window_size", window_size);
   return std::move(dict);
 }
 
diff --git a/net/ssl/openssl_ssl_util.cc b/net/ssl/openssl_ssl_util.cc
index 324bf48..fea33f4 100644
--- a/net/ssl/openssl_ssl_util.cc
+++ b/net/ssl/openssl_ssl_util.cc
@@ -128,22 +128,21 @@
   }
 }
 
-std::unique_ptr<base::Value> NetLogOpenSSLErrorCallback(
-    int net_error,
-    int ssl_error,
-    const OpenSSLErrorInfo& error_info,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("net_error", net_error);
-  dict->SetInteger("ssl_error", ssl_error);
+base::Value NetLogOpenSSLErrorCallback(int net_error,
+                                       int ssl_error,
+                                       const OpenSSLErrorInfo& error_info,
+                                       NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  dict.SetInteger("net_error", net_error);
+  dict.SetInteger("ssl_error", ssl_error);
   if (error_info.error_code != 0) {
-    dict->SetInteger("error_lib", ERR_GET_LIB(error_info.error_code));
-    dict->SetInteger("error_reason", ERR_GET_REASON(error_info.error_code));
+    dict.SetInteger("error_lib", ERR_GET_LIB(error_info.error_code));
+    dict.SetInteger("error_reason", ERR_GET_REASON(error_info.error_code));
   }
   if (error_info.file != nullptr)
-    dict->SetString("file", error_info.file);
+    dict.SetString("file", error_info.file);
   if (error_info.line != 0)
-    dict->SetInteger("line", error_info.line);
+    dict.SetInteger("line", error_info.line);
   return std::move(dict);
 }
 
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 524459f..de71c64 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -280,53 +280,53 @@
                             base::string16());
 }
 
-std::unique_ptr<base::Value> URLRequest::GetStateAsValue() const {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("url", original_url().possibly_invalid_spec());
+base::Value URLRequest::GetStateAsValue() const {
+  base::DictionaryValue dict;
+  dict.SetString("url", original_url().possibly_invalid_spec());
 
   if (url_chain_.size() > 1) {
-    std::unique_ptr<base::ListValue> list(new base::ListValue());
+    base::ListValue list;
     for (const GURL& url : url_chain_) {
-      list->AppendString(url.possibly_invalid_spec());
+      list.AppendString(url.possibly_invalid_spec());
     }
-    dict->Set("url_chain", std::move(list));
+    dict.SetKey("url_chain", std::move(list));
   }
 
-  dict->SetInteger("load_flags", load_flags_);
+  dict.SetInteger("load_flags", load_flags_);
 
   LoadStateWithParam load_state = GetLoadState();
-  dict->SetInteger("load_state", load_state.state);
+  dict.SetInteger("load_state", load_state.state);
   if (!load_state.param.empty())
-    dict->SetString("load_state_param", load_state.param);
+    dict.SetString("load_state_param", load_state.param);
   if (!blocked_by_.empty())
-    dict->SetString("delegate_blocked_by", blocked_by_);
+    dict.SetString("delegate_blocked_by", blocked_by_);
 
-  dict->SetString("method", method_);
-  dict->SetBoolean("has_upload", has_upload());
-  dict->SetBoolean("is_pending", is_pending_);
+  dict.SetString("method", method_);
+  dict.SetBoolean("has_upload", has_upload());
+  dict.SetBoolean("is_pending", is_pending_);
 
-  dict->SetInteger("traffic_annotation",
-                   traffic_annotation_.unique_id_hash_code);
+  dict.SetInteger("traffic_annotation",
+                  traffic_annotation_.unique_id_hash_code);
 
   // Add the status of the request.  The status should always be IO_PENDING, and
   // the error should always be OK, unless something is holding onto a request
   // that has finished or a request was leaked.  Neither of these should happen.
   switch (status_.status()) {
     case URLRequestStatus::SUCCESS:
-      dict->SetString("status", "SUCCESS");
+      dict.SetString("status", "SUCCESS");
       break;
     case URLRequestStatus::IO_PENDING:
-      dict->SetString("status", "IO_PENDING");
+      dict.SetString("status", "IO_PENDING");
       break;
     case URLRequestStatus::CANCELED:
-      dict->SetString("status", "CANCELED");
+      dict.SetString("status", "CANCELED");
       break;
     case URLRequestStatus::FAILED:
-      dict->SetString("status", "FAILED");
+      dict.SetString("status", "FAILED");
       break;
   }
   if (status_.error() != OK)
-    dict->SetInteger("net_error", status_.error());
+    dict.SetInteger("net_error", status_.error());
   return std::move(dict);
 }
 
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index 689fc82..166b76f 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -453,7 +453,7 @@
 
   // Returns a partial representation of the request's state as a value, for
   // debugging.
-  std::unique_ptr<base::Value> GetStateAsValue() const;
+  base::Value GetStateAsValue() const;
 
   // Logs information about the what external object currently blocking the
   // request.  LogUnblocked must be called before resuming the request.  This
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index 84826ed..9a9a042 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -36,12 +36,10 @@
 namespace {
 
 // Callback for TYPE_URL_REQUEST_FILTERS_SET net-internals event.
-std::unique_ptr<base::Value> SourceStreamSetCallback(
-    SourceStream* source_stream,
-    NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> event_params(
-      new base::DictionaryValue());
-  event_params->SetString("filters", source_stream->Description());
+base::Value SourceStreamSetCallback(SourceStream* source_stream,
+                                    NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue event_params;
+  event_params.SetString("filters", source_stream->Description());
   return std::move(event_params);
 }
 
diff --git a/net/url_request/url_request_netlog_params.cc b/net/url_request/url_request_netlog_params.cc
index fc4882a..2ade38e9 100644
--- a/net/url_request/url_request_netlog_params.cc
+++ b/net/url_request/url_request_netlog_params.cc
@@ -13,33 +13,32 @@
 
 namespace net {
 
-std::unique_ptr<base::Value> NetLogURLRequestConstructorCallback(
+base::Value NetLogURLRequestConstructorCallback(
     const GURL* url,
     RequestPriority priority,
     NetworkTrafficAnnotationTag traffic_annotation,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("url", url->possibly_invalid_spec());
-  dict->SetString("priority", RequestPriorityToString(priority));
-  dict->SetInteger("traffic_annotation",
-                   traffic_annotation.unique_id_hash_code);
+  base::DictionaryValue dict;
+  dict.SetString("url", url->possibly_invalid_spec());
+  dict.SetString("priority", RequestPriorityToString(priority));
+  dict.SetInteger("traffic_annotation", traffic_annotation.unique_id_hash_code);
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogURLRequestStartCallback(
+base::Value NetLogURLRequestStartCallback(
     const GURL* url,
     const std::string* method,
     int load_flags,
     PrivacyMode privacy_mode,
     int64_t upload_id,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("url", url->possibly_invalid_spec());
-  dict->SetString("method", *method);
-  dict->SetInteger("load_flags", load_flags);
-  dict->SetInteger("privacy_mode", privacy_mode == PRIVACY_MODE_ENABLED);
+  base::DictionaryValue dict;
+  dict.SetString("url", url->possibly_invalid_spec());
+  dict.SetString("method", *method);
+  dict.SetInteger("load_flags", load_flags);
+  dict.SetInteger("privacy_mode", privacy_mode == PRIVACY_MODE_ENABLED);
   if (upload_id > -1)
-    dict->SetString("upload_id", base::NumberToString(upload_id));
+    dict.SetString("upload_id", base::NumberToString(upload_id));
   return std::move(dict);
 }
 
diff --git a/net/url_request/url_request_netlog_params.h b/net/url_request/url_request_netlog_params.h
index e1be060..6fd10177 100644
--- a/net/url_request/url_request_netlog_params.h
+++ b/net/url_request/url_request_netlog_params.h
@@ -26,14 +26,14 @@
 class NetLogCaptureMode;
 
 // Returns a Value containing NetLog parameters for constructing a URLRequest.
-NET_EXPORT std::unique_ptr<base::Value> NetLogURLRequestConstructorCallback(
+NET_EXPORT base::Value NetLogURLRequestConstructorCallback(
     const GURL* url,
     RequestPriority priority,
     NetworkTrafficAnnotationTag traffic_annotation,
     NetLogCaptureMode /* capture_mode */);
 
 // Returns a Value containing NetLog parameters for starting a URLRequest.
-NET_EXPORT std::unique_ptr<base::Value> NetLogURLRequestStartCallback(
+NET_EXPORT base::Value NetLogURLRequestStartCallback(
     const GURL* url,
     const std::string* method,
     int load_flags,
diff --git a/net/url_request/url_request_throttler_entry.cc b/net/url_request/url_request_throttler_entry.cc
index 7a1d7fc..abba61f4 100644
--- a/net/url_request/url_request_throttler_entry.cc
+++ b/net/url_request/url_request_throttler_entry.cc
@@ -51,16 +51,16 @@
 const int URLRequestThrottlerEntry::kDefaultEntryLifetimeMs = 2 * 60 * 1000;
 
 // Returns NetLog parameters when a request is rejected by throttling.
-std::unique_ptr<base::Value> NetLogRejectedRequestCallback(
+base::Value NetLogRejectedRequestCallback(
     const std::string* url_id,
     int num_failures,
     const base::TimeDelta& release_after,
     NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("url", *url_id);
-  dict->SetInteger("num_failures", num_failures);
-  dict->SetInteger("release_after_ms",
-                   static_cast<int>(release_after.InMilliseconds()));
+  base::DictionaryValue dict;
+  dict.SetString("url", *url_id);
+  dict.SetInteger("num_failures", num_failures);
+  dict.SetInteger("release_after_ms",
+                  static_cast<int>(release_after.InMilliseconds()));
   return std::move(dict);
 }
 
diff --git a/remoting/base/BUILD.gn b/remoting/base/BUILD.gn
index 1ccbccf..d6d3ba7 100644
--- a/remoting/base/BUILD.gn
+++ b/remoting/base/BUILD.gn
@@ -91,6 +91,8 @@
     "gaia_oauth_client.cc",
     "gaia_oauth_client.h",
     "oauth_client.h",
+    "oauth_token_exchanger.cc",
+    "oauth_token_exchanger.h",
     "oauth_token_getter.cc",
     "oauth_token_getter.h",
     "oauth_token_getter_impl.cc",
diff --git a/remoting/base/grpc_support/BUILD.gn b/remoting/base/grpc_support/BUILD.gn
index 63fdd3f..03d73c19 100644
--- a/remoting/base/grpc_support/BUILD.gn
+++ b/remoting/base/grpc_support/BUILD.gn
@@ -18,6 +18,8 @@
     "grpc_channel.cc",
     "grpc_channel.h",
     "grpc_executor.h",
+    "grpc_util.cc",
+    "grpc_util.h",
     "scoped_grpc_server_stream.cc",
     "scoped_grpc_server_stream.h",
     "using_grpc_channel_shared_ptr.inc",
diff --git a/remoting/base/grpc_support/grpc_async_executor.cc b/remoting/base/grpc_support/grpc_async_executor.cc
index 88d70ec..fdcf123 100644
--- a/remoting/base/grpc_support/grpc_async_executor.cc
+++ b/remoting/base/grpc_support/grpc_async_executor.cc
@@ -12,13 +12,18 @@
 #include "base/no_destructor.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread.h"
+#include "base/time/time.h"
 #include "remoting/base/grpc_support/grpc_async_request.h"
+#include "remoting/base/grpc_support/grpc_util.h"
 #include "third_party/grpc/src/include/grpcpp/completion_queue.h"
 
 namespace remoting {
 
 namespace {
 
+constexpr base::TimeDelta kDefaultRequestTimeout =
+    base::TimeDelta::FromSeconds(30);
+
 using DequeueCallback = base::OnceCallback<void(bool operation_succeeded)>;
 
 struct DispatchTask {
@@ -100,6 +105,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   auto* unowned_request = request.get();
   DCHECK(FindRequest(unowned_request) == pending_requests_.end());
+  if (GetDeadline(*request->context()).is_max()) {
+    VLOG(1) << "Deadline is not set. Using the default request timeout.";
+    SetDeadline(request->context(), base::Time::Now() + kDefaultRequestTimeout);
+  }
   auto task = std::make_unique<DispatchTask>();
   task->caller_sequence_task_runner = base::SequencedTaskRunnerHandle::Get();
   task->callback =
diff --git a/remoting/base/grpc_support/grpc_async_executor_unittest.cc b/remoting/base/grpc_support/grpc_async_executor_unittest.cc
index b9e37052..bc5dfb9e 100644
--- a/remoting/base/grpc_support/grpc_async_executor_unittest.cc
+++ b/remoting/base/grpc_support/grpc_async_executor_unittest.cc
@@ -20,6 +20,7 @@
 #include "remoting/base/grpc_support/grpc_async_unary_request.h"
 #include "remoting/base/grpc_support/grpc_support_test_services.grpc.pb.h"
 #include "remoting/base/grpc_support/grpc_test_util.h"
+#include "remoting/base/grpc_support/grpc_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/grpc/src/include/grpcpp/grpcpp.h"
@@ -544,4 +545,41 @@
   run_loop.Run();
 }
 
+TEST_F(GrpcAsyncExecutorTest, ExecuteWithoutDeadline_DefaultDeadlineSet) {
+  EchoRequest request;
+  auto grpc_request = CreateGrpcAsyncUnaryRequest(
+      base::BindOnce(&GrpcAsyncExecutorTestService::Stub::AsyncEcho,
+                     base::Unretained(stub_.get())),
+      std::make_unique<grpc::ClientContext>(), request,
+      base::BindOnce(
+          [](const grpc::Status&, const EchoResponse&) { NOTREACHED(); }));
+  auto* context = grpc_request->context();
+  ASSERT_TRUE(GetDeadline(*context).is_max());
+  base::Time min_deadline = base::Time::Now();
+  base::Time max_deadline = min_deadline + base::TimeDelta::FromHours(1);
+  executor_->ExecuteRpc(std::move(grpc_request));
+  base::Time deadline = GetDeadline(*context);
+  ASSERT_LT(min_deadline, deadline);
+  ASSERT_GT(max_deadline, deadline);
+}
+
+TEST_F(GrpcAsyncExecutorTest, ExecuteWithDeadline_DeadlineNotChanged) {
+  constexpr base::TimeDelta kDeadlineEpsilon = base::TimeDelta::FromSeconds(1);
+  EchoRequest request;
+  auto context = std::make_unique<grpc::ClientContext>();
+  base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(10);
+  SetDeadline(context.get(), deadline);
+  auto grpc_request = CreateGrpcAsyncUnaryRequest(
+      base::BindOnce(&GrpcAsyncExecutorTestService::Stub::AsyncEcho,
+                     base::Unretained(stub_.get())),
+      std::move(context), request,
+      base::BindOnce(
+          [](const grpc::Status&, const EchoResponse&) { NOTREACHED(); }));
+  auto* unowned_context = grpc_request->context();
+  executor_->ExecuteRpc(std::move(grpc_request));
+  base::Time new_deadline = GetDeadline(*unowned_context);
+  ASSERT_LT(deadline - kDeadlineEpsilon, new_deadline);
+  ASSERT_GT(deadline + kDeadlineEpsilon, new_deadline);
+}
+
 }  // namespace remoting
diff --git a/remoting/base/grpc_support/grpc_util.cc b/remoting/base/grpc_support/grpc_util.cc
new file mode 100644
index 0000000..3adfd576
--- /dev/null
+++ b/remoting/base/grpc_support/grpc_util.cc
@@ -0,0 +1,27 @@
+// 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 "remoting/base/grpc_support/grpc_util.h"
+
+#include <chrono>
+
+#include "third_party/grpc/src/include/grpcpp/client_context.h"
+
+namespace remoting {
+
+void SetDeadline(grpc::ClientContext* context, base::Time deadline) {
+  context->set_deadline(
+      std::chrono::system_clock::from_time_t(deadline.ToTimeT()));
+}
+
+base::Time GetDeadline(const grpc::ClientContext& context) {
+  auto deadline_tp = context.deadline();
+  if (deadline_tp == std::chrono::system_clock::time_point::max()) {
+    return base::Time::Max();
+  }
+  return base::Time::FromTimeT(
+      std::chrono::system_clock::to_time_t(deadline_tp));
+}
+
+}  // namespace remoting
diff --git a/remoting/base/grpc_support/grpc_util.h b/remoting/base/grpc_support/grpc_util.h
new file mode 100644
index 0000000..7c4a8cb
--- /dev/null
+++ b/remoting/base/grpc_support/grpc_util.h
@@ -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.
+
+#ifndef REMOTING_BASE_GRPC_SUPPORT_GRPC_UTIL_H_
+#define REMOTING_BASE_GRPC_SUPPORT_GRPC_UTIL_H_
+
+#include "base/time/time.h"
+
+namespace grpc {
+class ClientContext;
+}  // namespace grpc
+
+namespace remoting {
+
+// Sets the deadline on |context|.
+void SetDeadline(grpc::ClientContext* context, base::Time deadline);
+
+// Gets the deadline in base::Time. Returns base::Time::Max if the deadline is
+// not set.
+base::Time GetDeadline(const grpc::ClientContext& context);
+
+}  // namespace remoting
+
+#endif  // REMOTING_BASE_GRPC_SUPPORT_GRPC_UTIL_H_
diff --git a/remoting/base/oauth_token_exchanger.cc b/remoting/base/oauth_token_exchanger.cc
new file mode 100644
index 0000000..f748b9f
--- /dev/null
+++ b/remoting/base/oauth_token_exchanger.cc
@@ -0,0 +1,86 @@
+// 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 "remoting/base/oauth_token_exchanger.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "google_apis/gaia/gaia_oauth_client.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+
+namespace remoting {
+
+namespace {
+
+// Maximum number of retries on network/500 errors.
+const int kMaxRetries = 3;
+
+}  // namespace
+
+OAuthTokenExchanger::OAuthTokenExchanger(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+    : gaia_oauth_client_(std::make_unique<gaia::GaiaOAuthClient>(
+          std::move(url_loader_factory))) {}
+
+OAuthTokenExchanger::~OAuthTokenExchanger() = default;
+
+void OAuthTokenExchanger::ExchangeToken(const std::string& access_token,
+                                        TokenCallback on_new_token) {
+  oauth_access_token_ = access_token;
+  pending_callbacks_.push(std::move(on_new_token));
+
+  if (!need_token_exchange_.has_value()) {
+    gaia_oauth_client_->GetTokenInfo(access_token, kMaxRetries, this);
+    return;
+  }
+
+  if (need_token_exchange_.value()) {
+    // TODO(lambroslambrou): Implement token-exchange.
+    NOTIMPLEMENTED();
+    return;
+  }
+
+  // Return the original token, as it already has required scopes.
+  NotifyCallbacks(OAuthTokenGetter::SUCCESS, oauth_access_token_);
+}
+
+void OAuthTokenExchanger::OnGetTokenInfoResponse(
+    std::unique_ptr<base::DictionaryValue> token_info) {
+  VLOG(1) << "GetTokenInfoResponse: " << *token_info;
+  base::Value* scopes_value = token_info->FindKey("scope");
+  if (!scopes_value || !scopes_value->is_string()) {
+    NotifyCallbacks(OAuthTokenGetter::AUTH_ERROR, std::string());
+    return;
+  }
+  std::string scopes = scopes_value->GetString();
+
+  // TODO(lambroslambrou): Check the scopes (one time only, cache the result),
+  // and either return the current token or try to exchange it.
+  VLOG(1) << "Token scopes: " << scopes;
+  NotifyCallbacks(OAuthTokenGetter::SUCCESS, oauth_access_token_);
+}
+
+void OAuthTokenExchanger::OnOAuthError() {
+  LOG(ERROR) << "OAuth error.";
+  NotifyCallbacks(OAuthTokenGetter::AUTH_ERROR, std::string());
+}
+
+void OAuthTokenExchanger::OnNetworkError(int response_code) {
+  LOG(ERROR) << "Network error: " << response_code;
+  NotifyCallbacks(OAuthTokenGetter::NETWORK_ERROR, std::string());
+}
+
+void OAuthTokenExchanger::NotifyCallbacks(OAuthTokenGetter::Status status,
+                                          const std::string& access_token) {
+  // Protect against recursion by moving the callbacks into a temporary list.
+  base::queue<TokenCallback> callbacks;
+  callbacks.swap(pending_callbacks_);
+  while (!callbacks.empty()) {
+    std::move(callbacks.front()).Run(status, access_token);
+    callbacks.pop();
+  }
+}
+
+}  // namespace remoting
diff --git a/remoting/base/oauth_token_exchanger.h b/remoting/base/oauth_token_exchanger.h
new file mode 100644
index 0000000..a8372ac
--- /dev/null
+++ b/remoting/base/oauth_token_exchanger.h
@@ -0,0 +1,61 @@
+// 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 REMOTING_BASE_OAUTH_TOKEN_EXCHANGER_H_
+#define REMOTING_BASE_OAUTH_TOKEN_EXCHANGER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/containers/queue.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "google_apis/gaia/gaia_oauth_client.h"
+#include "remoting/base/oauth_token_getter.h"
+
+namespace remoting {
+
+class OAuthTokenExchanger : public gaia::GaiaOAuthClient::Delegate {
+ public:
+  typedef base::OnceCallback<void(OAuthTokenGetter::Status status,
+                                  const std::string& access_token)>
+      TokenCallback;
+
+  explicit OAuthTokenExchanger(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+  ~OAuthTokenExchanger() override;
+
+  void ExchangeToken(const std::string& access_token,
+                     TokenCallback on_new_token);
+
+  // gaia::GaiaOAuthClient::Delegate interface.
+  void OnGetTokenInfoResponse(
+      std::unique_ptr<base::DictionaryValue> token_info) override;
+  void OnOAuthError() override;
+  void OnNetworkError(int response_code) override;
+
+ private:
+  void NotifyCallbacks(OAuthTokenGetter::Status status,
+                       const std::string& access_token);
+
+  std::unique_ptr<gaia::GaiaOAuthClient> gaia_oauth_client_;
+  base::queue<TokenCallback> pending_callbacks_;
+  std::string oauth_access_token_;
+
+  // True if the OAuth refresh token is lacking required scopes and the
+  // token-exchange service is needed to provide a new access-token.
+  // False if the refresh token is up-to-date with required scopes.
+  // Unset if the scopes are unknown and the tokeninfo endpoint needs to be
+  // queried.
+
+  // TODO(lambroslambrou): Activate tokeninfo fetch by removing '= false'.
+  base::Optional<bool> need_token_exchange_ = false;
+  DISALLOW_COPY_AND_ASSIGN(OAuthTokenExchanger);
+};
+
+}  // namespace remoting
+
+#endif  // REMOTING_BASE_OAUTH_TOKEN_EXCHANGER_H_
diff --git a/remoting/base/oauth_token_getter_impl.cc b/remoting/base/oauth_token_getter_impl.cc
index 577e542..1e3aa8d 100644
--- a/remoting/base/oauth_token_getter_impl.cc
+++ b/remoting/base/oauth_token_getter_impl.cc
@@ -12,6 +12,7 @@
 #include "base/strings/string_util.h"
 #include "google_apis/google_api_keys.h"
 #include "remoting/base/logging.h"
+#include "remoting/base/oauth_token_exchanger.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace remoting {
@@ -32,9 +33,9 @@
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     bool auto_refresh)
     : intermediate_credentials_(std::move(intermediate_credentials)),
-      gaia_oauth_client_(
-          new gaia::GaiaOAuthClient(std::move(url_loader_factory))),
+      gaia_oauth_client_(new gaia::GaiaOAuthClient(url_loader_factory)),
       credentials_updated_callback_(on_credentials_update),
+      token_exchanger_(url_loader_factory),
       weak_factory_(this) {
   if (auto_refresh) {
     refresh_timer_.reset(new base::OneShotTimer());
@@ -46,8 +47,8 @@
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     bool auto_refresh)
     : authorization_credentials_(std::move(authorization_credentials)),
-      gaia_oauth_client_(
-          new gaia::GaiaOAuthClient(std::move(url_loader_factory))),
+      gaia_oauth_client_(new gaia::GaiaOAuthClient(url_loader_factory)),
+      token_exchanger_(url_loader_factory),
       weak_factory_(this) {
   if (auto_refresh) {
     refresh_timer_.reset(new base::OneShotTimer());
@@ -281,10 +282,32 @@
 }
 
 void OAuthTokenGetterImpl::ExchangeAccessToken() {
-  // Not yet implemented - return the current access token immediately.
-  // TODO(lambroslambrou): Fetch scopes and exchange the token.
-  NotifyTokenCallbacks(OAuthTokenGetterImpl::SUCCESS,
-                       authorization_credentials_->login, oauth_access_token_);
+  // Unretained() is safe because |this| owns its token-exchanger, which
+  // owns its GaiaOAuthClient, which cancels callbacks on destruction.
+  token_exchanger_.ExchangeToken(
+      oauth_access_token_,
+      base::BindOnce(&OAuthTokenGetterImpl::OnExchangeTokenResponse,
+                     base::Unretained(this)));
+}
+
+void OAuthTokenGetterImpl::OnExchangeTokenResponse(
+    Status status,
+    const std::string& access_token) {
+  oauth_access_token_ = access_token;
+  switch (status) {
+    case AUTH_ERROR:
+      OnOAuthError();
+      break;
+    case NETWORK_ERROR:
+      NotifyTokenCallbacks(status, std::string(), std::string());
+      break;
+    case SUCCESS:
+      NotifyTokenCallbacks(status, authorization_credentials_->login,
+                           oauth_access_token_);
+      break;
+    default:
+      NOTREACHED();
+  }
 }
 
 }  // namespace remoting
diff --git a/remoting/base/oauth_token_getter_impl.h b/remoting/base/oauth_token_getter_impl.h
index 0e882a0..0747234 100644
--- a/remoting/base/oauth_token_getter_impl.h
+++ b/remoting/base/oauth_token_getter_impl.h
@@ -12,6 +12,7 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
+#include "remoting/base/oauth_token_exchanger.h"
 #include "remoting/base/oauth_token_getter.h"
 
 namespace network {
@@ -67,6 +68,7 @@
                               const std::string& refresh_token);
   void GetOauthTokensFromAuthCode();
   void RefreshAccessToken();
+  void OnExchangeTokenResponse(Status status, const std::string& access_token);
 
   // Fetches the OAuth scopes for |oauth_access_token_|. If it is missing the
   // new scopes required by FTL signaling, it exchanges it for a new access
@@ -86,6 +88,8 @@
   base::queue<OAuthTokenGetter::TokenCallback> pending_callbacks_;
   std::unique_ptr<base::OneShotTimer> refresh_timer_;
 
+  OAuthTokenExchanger token_exchanger_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   base::WeakPtrFactory<OAuthTokenGetterImpl> weak_factory_;
diff --git a/remoting/base/vlog_net_log.cc b/remoting/base/vlog_net_log.cc
index d655ed6..75a6eace 100644
--- a/remoting/base/vlog_net_log.cc
+++ b/remoting/base/vlog_net_log.cc
@@ -34,9 +34,9 @@
 
 void VlogNetLog::Observer::OnAddEntry(const net::NetLogEntry& entry) {
   if (VLOG_IS_ON(4)) {
-    std::unique_ptr<base::Value> value(entry.ToValue());
+    base::Value value = entry.ToValue();
     std::string json;
-    base::JSONWriter::Write(*value, &json);
+    base::JSONWriter::Write(value, &json);
     VLOG(4) << json;
   }
 }
diff --git a/remoting/client/BUILD.gn b/remoting/client/BUILD.gn
index 57ec138..9889a93 100644
--- a/remoting/client/BUILD.gn
+++ b/remoting/client/BUILD.gn
@@ -63,6 +63,7 @@
       "//remoting/base:authorization",
       "//remoting/client/input",
       "//remoting/client/ui",
+      "//remoting/proto/remoting/v1:directory_grpc_library",
       "//services/network:network_service",
       "//services/network/public/mojom",
     ]
diff --git a/remoting/host/client_session.cc b/remoting/host/client_session.cc
index 52c0abf..b035b62 100644
--- a/remoting/host/client_session.cc
+++ b/remoting/host/client_session.cc
@@ -534,6 +534,7 @@
 
 void ClientSession::OnDesktopDisplayChanged(
     std::unique_ptr<protocol::VideoLayout> displays) {
+  LOG(INFO) << "OnDesktopDisplayChanged";
   // Scan display list to calculate the full desktop size.
   int min_x = 0;
   int max_x = 0;
@@ -583,15 +584,19 @@
   video_track->set_height(size_dips.height());
   video_track->set_x_dpi(dpi_x);
   video_track->set_y_dpi(dpi_y);
+  LOG(INFO) << "  Desktop (DIPS) = 0,0 " << size_dips.width() << "x"
+            << size_dips.height() << " [" << dpi_x << "," << dpi_y << "]";
 
   // Add raw geometry for entire desktop (in pixels).
   video_track = layout.add_video_track();
   video_track->set_position_x(0);
   video_track->set_position_y(0);
-  video_track->set_width(max_x - min_x);
-  video_track->set_height(max_y - min_y);
+  video_track->set_width(size.width());
+  video_track->set_height(size.height());
   video_track->set_x_dpi(dpi_x);
   video_track->set_y_dpi(dpi_y);
+  LOG(INFO) << "  Desktop (pixels) = 0,0 " << size.width() << "x"
+            << size.height() << " [" << dpi_x << "," << dpi_y << "]";
 
   // Add a VideoTrackLayout entry for each separate display.
   desktop_display_info_.Reset();
@@ -602,6 +607,10 @@
 
     protocol::VideoTrackLayout* video_track = layout.add_video_track();
     video_track->CopyFrom(display);
+    LOG(INFO) << "  Display " << display_id << " " << display.position_x()
+              << "," << display.position_y() << display.width() << "x"
+              << display.height() << " [" << display.x_dpi() << ","
+              << display.y_dpi() << "]";
   }
 
   connection_->client_stub()->SetVideoLayout(layout);
diff --git a/remoting/proto/remoting/v1/BUILD.gn b/remoting/proto/remoting/v1/BUILD.gn
new file mode 100644
index 0000000..0a27851
--- /dev/null
+++ b/remoting/proto/remoting/v1/BUILD.gn
@@ -0,0 +1,13 @@
+# 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/grpc/grpc_library.gni")
+
+cc_grpc_library("directory_grpc_library") {
+  sources = [
+    "directory_messages.proto",
+    "directory_service.proto",
+    "host_info.proto",
+  ]
+}
diff --git a/remoting/proto/remoting/v1/directory_messages.proto b/remoting/proto/remoting/v1/directory_messages.proto
new file mode 100644
index 0000000..6a087d9
--- /dev/null
+++ b/remoting/proto/remoting/v1/directory_messages.proto
@@ -0,0 +1,94 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package remoting.apis.v1;
+
+import "host_info.proto";
+
+// Requests the deletion of a specific host from the Directory.
+message DeleteHostRequest {
+  // The ID of the host to delete.
+  optional string host_id = 1;
+}
+
+// The response to a DeleteHostRequest.
+message DeleteHostResponse {}
+
+// Request host info for a single Me2Me host.  This is typically used when the
+// client already has the host list and wants updated host info or if a deeplink
+// is used to connect to a specific host.
+message GetHostRequest {
+  // The ID of the host to request info for.
+  optional string host_id = 1;
+}
+
+// The response to a GetHostRequest.
+message GetHostResponse {
+  // The info for the host_id in the request.
+  optional HostInfo host_info = 1;
+}
+
+// Requests a list of hosts owned by the user making the request.
+message GetHostListRequest {}
+
+// Response to a GetHostListRequest.
+message GetHostListResponse {
+  // The list of users owned by the user.
+  repeated remoting.apis.v1.HostInfo hosts = 1;
+}
+
+// Sent from a Me2Me host to update it's info and online presence.
+message HeartbeatRequest {
+  // An encypted token used to validate the request's origin.
+  optional string signature = 1;
+  // Host identity. Normally a UUID.
+  optional string host_id = 2;
+  // Current ID for the GoogleTalk service, if applicable.
+  optional string jabber_id = 3;
+  // Current ID for the Tachyon service, if applicable.
+  optional string tachyon_id = 4;
+  // Monotonically increasing value which is used to prevent replay attacks.
+  optional int32 sequence_id = 5;
+  // Version of the Me2Me host software installed.
+  optional string host_version = 6;
+  // A reason for the host reporting itself as offline.
+  optional string host_offline_reason = 7;
+  // Operating system type the host is running on.
+  optional string host_os_name = 8;
+  // Operating system version the host is running on.
+  optional string host_os_version = 9;
+}
+
+// Sent in response to a HeartbeatRequest.
+message HeartbeatResponse {
+  // The result of the request.
+  optional HeartbeatResult result = 1;
+  // The amount of time to wait before the host should send its next update.
+  optional int32 set_interval_seconds = 2;
+}
+
+// TODO(joedow): Translate XMPP stanza errors into proto format.
+// The result of a HeartBeatRequest.
+enum HeartbeatResult {
+  // Field was not set.
+  HEARTBEATRESULT_UNSET = 0;
+  // The heartbeat was handled successfully.
+  SUCCESS = 1;
+  // There was an error processing the heartbeat request.
+  ERROR = 2;
+}
+
+// Requests an auth_code with updated OAuth scopes.  Should only be called by
+// robot accounts assigned to a Me2me host.
+message UpdateRobotTokenRequest {}
+
+// Response to a UpdateRobotTokenRequest.
+message UpdateRobotTokenResponse {
+  // An auth code nonce which can be exchanged for a new token from Gaia.
+  optional string auth_code = 1;
+}
diff --git a/remoting/proto/remoting/v1/directory_service.proto b/remoting/proto/remoting/v1/directory_service.proto
new file mode 100644
index 0000000..b66338a
--- /dev/null
+++ b/remoting/proto/remoting/v1/directory_service.proto
@@ -0,0 +1,30 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+// Note that the package name is used to build the service URL.
+package remoting.apis.v1;
+
+import "directory_messages.proto";
+
+// A set of RPC services which provide functionality for remote access hosts
+// and clients.
+service RemotingDirectoryService {
+  // RPC service which returns the info for a specific host.
+  rpc GetHost(GetHostRequest) returns (GetHostResponse) {}
+
+  // RPC service which returns a list of remote hosts.
+  rpc GetHostList(GetHostListRequest) returns (GetHostListResponse) {}
+
+  // RPC service called by the remote access host to indicate online presence
+  // and update Directory info such as the signaling ID and OS info.
+  rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse) {}
+
+  // RPC service which provides an update token for a robot account.
+  rpc UpdateRobotToken(UpdateRobotTokenRequest)
+      returns (UpdateRobotTokenResponse) {}
+}
diff --git a/remoting/proto/remoting/v1/host_info.proto b/remoting/proto/remoting/v1/host_info.proto
new file mode 100644
index 0000000..bd90a3b
--- /dev/null
+++ b/remoting/proto/remoting/v1/host_info.proto
@@ -0,0 +1,78 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package remoting.apis.v1;
+
+// Represents a Chrome Remote Desktop host instance.  It includes static data
+// from when the host was first registered as well as transient data such as
+// the signaling ID and the last heartbeat time.
+message HostInfo {
+  // Host identity. Normally a UUID.
+  optional string host_id = 1;
+
+  // Public key for the host. Must be a 2048-bit RSA key encoded with BASE64.
+  optional string public_key = 2;
+
+  // A readable name shown to the user.
+  optional string host_name = 3;
+
+  // Time when the host was created (milliseconds since 01/01/1970 GMT).
+  optional int64 created_time = 4;
+
+  // Most recent JabberID.
+  optional string jabber_id = 5;
+
+  // JID-like ID of the form user@domain.com/chromoting_ftl_<reg ID>.
+  optional string ftl_id = 6;
+
+  // Status of the host.
+  enum Status {
+    // Field was not set.
+    STATUS_UNSET = 0;
+    // The host is offline.
+    OFFLINE = 1;
+    // The host is online.
+    ONLINE = 2;
+  }
+  // The current online status of the host based on the last heartbeat seen.
+  optional Status status = 7 [default = OFFLINE];
+
+  // Host offline reason.  See also:
+  // - chromium/src/remoting/host/heartbeat_sender.h
+  // - kHostExitCodeStrings in chromium/src/remoting/host/host_exit_codes.cc
+  optional string host_offline_reason = 8;
+
+  // Time when the host was last online (milliseconds since 01/01/1970 GMT).
+  optional int64 last_seen_time = 9;
+
+  // State of the host registration process.
+  enum RegistrationState {
+    // Field was not set.
+    REGISTRATION_STATE_UNSET = 0;
+    // The service is waiting for the host to send its signaling ID.
+    WAITING_FOR_HOST_STATUS = 1;
+    // The service has timed out waiting for the host to send its signaling ID.
+    TIMED_OUT_WAITING_FOR_HOST_STATUS = 2;
+    // The host has sent its signaling ID and completed registration.
+    HOST_STATUS_RECEIVED = 3;
+  }
+  // The registration state of the host.
+  optional RegistrationState registration_state = 10;
+
+  // A RegEx used to validate an incoming third-party auth URL from the host.
+  repeated string token_url_pattern = 11;
+
+  // Version of the Me2Me/It2Me host software installed on the host.
+  optional string host_version = 12;
+
+  // Operating system type of the host.
+  optional string host_os_name = 13;
+
+  // Operating system version of the host.
+  optional string host_os_version = 14;
+}
diff --git a/remoting/protocol/chromium_socket_factory.cc b/remoting/protocol/chromium_socket_factory.cc
index 2979d419..81c4bac 100644
--- a/remoting/protocol/chromium_socket_factory.cc
+++ b/remoting/protocol/chromium_socket_factory.cc
@@ -13,6 +13,7 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/rand_util.h"
 #include "base/time/time.h"
 #include "jingle/glue/utils.h"
 #include "net/base/io_buffer.h"
@@ -40,6 +41,20 @@
 // reached under normal conditions.
 const int kMaxSendBufferSize = 256 * 1024;
 
+// Creates a UDP socket and make it listen at |local_address| and |port|.
+// Returns nullptr if the socket fails to listen.
+std::unique_ptr<net::UDPServerSocket> CreateUdpSocketAndListen(
+    const net::IPAddress& local_address,
+    uint16_t port) {
+  auto socket =
+      std::make_unique<net::UDPServerSocket>(nullptr, net::NetLogSource());
+  int result = socket->Listen(net::IPEndPoint(local_address, port));
+  if (result != net::OK) {
+    socket.reset();
+  }
+  return socket;
+}
+
 class UdpPacketSocket : public rtc::AsyncPacketSocket {
  public:
   UdpPacketSocket();
@@ -131,20 +146,31 @@
 bool UdpPacketSocket::Init(const rtc::SocketAddress& local_address,
                            uint16_t min_port,
                            uint16_t max_port) {
+  DCHECK_LE(min_port, max_port);
   net::IPEndPoint local_endpoint;
   if (!jingle_glue::SocketAddressToIPEndPoint(
           local_address, &local_endpoint)) {
     return false;
   }
 
-  for (uint32_t port = min_port; port <= max_port; ++port) {
-    socket_.reset(new net::UDPServerSocket(nullptr, net::NetLogSource()));
-    int result = socket_->Listen(
-        net::IPEndPoint(local_endpoint.address(), static_cast<uint16_t>(port)));
-    if (result == net::OK) {
-      break;
-    } else {
-      socket_.reset();
+  if (min_port == 0 && max_port == 0) {
+    // Just listen to any port that is available.
+    socket_ = CreateUdpSocketAndListen(local_endpoint.address(), 0u);
+  } else {
+    // Randomly pick a port to start trying with so that we will less likely
+    // pick the same port for relay. TURN server doesn't allow allocating relay
+    // session from the same port until the old session is timed out.
+    uint32_t port_count = max_port - min_port + 1;
+    uint32_t starting_offset = base::RandGenerator(port_count);
+    for (uint32_t i = 0; i < port_count; i++) {
+      uint16_t port = static_cast<uint16_t>(
+          min_port + ((starting_offset + i) % port_count));
+      DCHECK_LE(min_port, port);
+      DCHECK_LE(port, max_port);
+      socket_ = CreateUdpSocketAndListen(local_endpoint.address(), port);
+      if (socket_) {
+        break;
+      }
     }
   }
 
diff --git a/remoting/protocol/chromium_socket_factory_unittest.cc b/remoting/protocol/chromium_socket_factory_unittest.cc
index 41b40a8..4d92426 100644
--- a/remoting/protocol/chromium_socket_factory_unittest.cc
+++ b/remoting/protocol/chromium_socket_factory_unittest.cc
@@ -8,7 +8,9 @@
 #include <stdint.h>
 
 #include <memory>
+#include <vector>
 
+#include "base/containers/flat_set.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
@@ -119,16 +121,44 @@
 }
 
 TEST_F(ChromiumSocketFactoryTest, PortRange) {
-  const uint16_t kMinPort = 12400;
-  const uint16_t kMaxPort = 12410;
+  constexpr uint16_t kMinPort = 12400;
+  constexpr uint16_t kMaxPort = 12410;
   socket_.reset(socket_factory_->CreateUdpSocket(
-      rtc::SocketAddress("127.0.0.1", 0), kMaxPort, kMaxPort));
+      rtc::SocketAddress("127.0.0.1", 0), kMinPort, kMaxPort));
   ASSERT_TRUE(socket_.get() != nullptr);
   EXPECT_EQ(socket_->GetState(), rtc::AsyncPacketSocket::STATE_BOUND);
   EXPECT_GE(socket_->GetLocalAddress().port(), kMinPort);
   EXPECT_LE(socket_->GetLocalAddress().port(), kMaxPort);
 }
 
+TEST_F(ChromiumSocketFactoryTest, CreateMultiplePortsFromPortRange) {
+  constexpr uint16_t kPortCount = 5;
+  constexpr uint16_t kMinPort = 12400;
+  constexpr uint16_t kMaxPort = kMinPort + kPortCount - 1;
+  std::vector<std::unique_ptr<rtc::AsyncPacketSocket>> sockets;
+  for (int i = 0; i < kPortCount; i++) {
+    sockets.push_back(std::unique_ptr<rtc::AsyncPacketSocket>(
+        socket_factory_->CreateUdpSocket(rtc::SocketAddress("127.0.0.1", 0),
+                                         kMinPort, kMaxPort)));
+  }
+  base::flat_set<uint16_t> assigned_ports;
+  for (auto& socket : sockets) {
+    ASSERT_TRUE(socket.get() != nullptr);
+    EXPECT_EQ(socket->GetState(), rtc::AsyncPacketSocket::STATE_BOUND);
+    uint16_t port = socket->GetLocalAddress().port();
+    EXPECT_GE(port, kMinPort);
+    EXPECT_LE(port, kMaxPort);
+    ASSERT_EQ(assigned_ports.end(), assigned_ports.find(port));
+    assigned_ports.insert(port);
+  }
+
+  // Try to create another socket, which should fail because no more port is
+  // available.
+  auto* extra_socket = socket_factory_->CreateUdpSocket(
+      rtc::SocketAddress("127.0.0.1", 0), kMinPort, kMaxPort);
+  ASSERT_EQ(nullptr, extra_socket);
+}
+
 TEST_F(ChromiumSocketFactoryTest, TransientError) {
   std::unique_ptr<rtc::AsyncPacketSocket> sending_socket(
       socket_factory_->CreateUdpSocket(rtc::SocketAddress("127.0.0.1", 0), 0,
diff --git a/services/network/proxy_resolver_factory_mojo.cc b/services/network/proxy_resolver_factory_mojo.cc
index 5d6a8c5..5101628 100644
--- a/services/network/proxy_resolver_factory_mojo.cc
+++ b/services/network/proxy_resolver_factory_mojo.cc
@@ -40,13 +40,12 @@
 
 namespace {
 
-std::unique_ptr<base::Value> NetLogErrorCallback(
-    int line_number,
-    const std::string* message,
-    net::NetLogCaptureMode /* capture_mode */) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetInteger("line_number", line_number);
-  dict->SetString("message", *message);
+base::Value NetLogErrorCallback(int line_number,
+                                const std::string* message,
+                                net::NetLogCaptureMode /* capture_mode */) {
+  base::DictionaryValue dict;
+  dict.SetInteger("line_number", line_number);
+  dict.SetString("message", *message);
   return std::move(dict);
 }
 
diff --git a/services/network/session_cleanup_cookie_store.cc b/services/network/session_cleanup_cookie_store.cc
index 6eb18ad..651101c 100644
--- a/services/network/session_cleanup_cookie_store.cc
+++ b/services/network/session_cleanup_cookie_store.cc
@@ -26,16 +26,15 @@
 
 namespace {
 
-std::unique_ptr<base::Value> CookieStoreOriginFiltered(
-    const std::string& origin,
-    bool is_https,
-    net::NetLogCaptureMode capture_mode) {
+base::Value CookieStoreOriginFiltered(const std::string& origin,
+                                      bool is_https,
+                                      net::NetLogCaptureMode capture_mode) {
   if (!capture_mode.include_cookies_and_credentials())
-    return nullptr;
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->SetString("origin", origin);
-  dict->SetBoolean("is_https", is_https);
-  return dict;
+    return base::Value();
+  base::DictionaryValue dict;
+  dict.SetString("origin", origin);
+  dict.SetBoolean("is_https", is_https);
+  return std::move(dict);
 }
 
 }  // namespace
diff --git a/services/network/tcp_socket_unittest.cc b/services/network/tcp_socket_unittest.cc
index ad91b78..4fd78ff 100644
--- a/services/network/tcp_socket_unittest.cc
+++ b/services/network/tcp_socket_unittest.cc
@@ -9,7 +9,6 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/macros.h"
@@ -93,7 +92,7 @@
 
     *accept_socket_ = CreateMockAcceptSocket();
     accept_socket_ = nullptr;
-    base::ResetAndReturn(&accept_callback_).Run(result);
+    std::move(accept_callback_).Run(result);
   }
 
  private:
diff --git a/services/preferences/tracked/interceptable_pref_filter.cc b/services/preferences/tracked/interceptable_pref_filter.cc
index 7a6eb8bb7..8eaeabe9 100644
--- a/services/preferences/tracked/interceptable_pref_filter.cc
+++ b/services/preferences/tracked/interceptable_pref_filter.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 
 InterceptablePrefFilter::InterceptablePrefFilter() {}
 InterceptablePrefFilter::~InterceptablePrefFilter() {}
@@ -27,7 +26,7 @@
     const FinalizeFilterOnLoadCallback finalize_filter_on_load(
         base::Bind(&InterceptablePrefFilter::FinalizeFilterOnLoad, AsWeakPtr(),
                    post_filter_on_load_callback));
-    base::ResetAndReturn(&filter_on_load_interceptor_)
+    std::move(filter_on_load_interceptor_)
         .Run(finalize_filter_on_load, std::move(pref_store_contents));
   }
 }
diff --git a/services/service_manager/sandbox/win/sandbox_win.cc b/services/service_manager/sandbox/win/sandbox_win.cc
index 6592ad25..491ce82 100644
--- a/services/service_manager/sandbox/win/sandbox_win.cc
+++ b/services/service_manager/sandbox/win/sandbox_win.cc
@@ -575,8 +575,8 @@
   if (sandbox_type == service_manager::SANDBOX_TYPE_GPU ||
       sandbox_type == service_manager::SANDBOX_TYPE_RENDERER) {
     int64_t GB = 1024 * 1024 * 1024;
-    // Allow the GPU process's sandbox to access more physical memory if
-    // it's available on the system.
+    // Allow the GPU/RENDERER process's sandbox to access more physical memory
+    // if it's available on the system.
     int64_t physical_memory = base::SysInfo::AmountOfPhysicalMemory();
     if (physical_memory > 16 * GB) {
       memory_limit = 16 * GB;
@@ -608,7 +608,7 @@
   // CreateAppContainerProfile requires that the profile name is at most 64
   // characters.  The size of sha1 is a constant 40, so validate that the base
   // names are sufficiently short that the total length is valid.
-  DCHECK(profile_name.length() <= 64);
+  DCHECK_LE(profile_name.length(), 64U);
   return base::UTF8ToWide(profile_name);
 }
 
diff --git a/services/tracing/coordinator.cc b/services/tracing/coordinator.cc
index fecd79da6..9c8c2965 100644
--- a/services/tracing/coordinator.cc
+++ b/services/tracing/coordinator.cc
@@ -12,7 +12,6 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback_forward.h"
-#include "base/callback_helpers.h"
 #include "base/guid.h"
 #include "base/json/json_writer.h"
 #include "base/json/string_escape.h"
@@ -304,13 +303,13 @@
   start_tracing_callback_timer_.Stop();
 
   if (!stop_and_flush_callback_.is_null()) {
-    base::ResetAndReturn(&stop_and_flush_callback_)
+    std::move(stop_and_flush_callback_)
         .Run(base::Value(base::Value::Type::DICTIONARY));
   }
   if (!start_tracing_callback_.is_null())
-    base::ResetAndReturn(&start_tracing_callback_).Run(false);
+    std::move(start_tracing_callback_).Run(false);
   if (!request_buffer_usage_callback_.is_null())
-    base::ResetAndReturn(&request_buffer_usage_callback_).Run(false, 0, 0);
+    std::move(request_buffer_usage_callback_).Run(false, 0, 0);
 
   if (trace_streamer_) {
     // We are in the middle of flushing trace data. We need to
diff --git a/services/tracing/perfetto/perfetto_tracing_coordinator.cc b/services/tracing/perfetto/perfetto_tracing_coordinator.cc
index 23d56ec..f12f6a1 100644
--- a/services/tracing/perfetto/perfetto_tracing_coordinator.cc
+++ b/services/tracing/perfetto/perfetto_tracing_coordinator.cc
@@ -84,7 +84,7 @@
 
   ~TracingSession() override {
     if (!stop_and_flush_callback_.is_null()) {
-      base::ResetAndReturn(&stop_and_flush_callback_)
+      std::move(stop_and_flush_callback_)
           .Run(base::Value(base::Value::Type::DICTIONARY));
     }
 
@@ -133,7 +133,7 @@
 
     if (!has_more) {
       DCHECK(!stop_and_flush_callback_.is_null());
-      base::ResetAndReturn(&stop_and_flush_callback_)
+      std::move(stop_and_flush_callback_)
           .Run(/*metadata=*/std::move(*metadata));
 
       std::move(tracing_over_callback_).Run();
diff --git a/storage/browser/blob/blob_storage_context.cc b/storage/browser/blob/blob_storage_context.cc
index b0b98b5..57aba5d5 100644
--- a/storage/browser/blob/blob_storage_context.cc
+++ b/storage/browser/blob/blob_storage_context.cc
@@ -13,7 +13,6 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
@@ -569,7 +568,7 @@
     std::vector<BlobMemoryController::FileCreationInfo> files) {
   BlobEntry::BuildingState* building_state = entry->building_state_.get();
   if (building_state->transport_allowed_callback) {
-    base::ResetAndReturn(&building_state->transport_allowed_callback)
+    std::move(building_state->transport_allowed_callback)
         .Run(BlobStatus::PENDING_TRANSPORT, std::move(files));
     return;
   }
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 3cafc1d..066c2463 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -129,7 +129,6 @@
     "platform/mac/web_scrollbar_theme.h",
     "platform/modules/background_fetch/web_background_fetch_registration.h",
     "platform/modules/indexeddb/web_idb_database_exception.h",
-    "platform/modules/installedapp/web_related_application.h",
     "platform/modules/media_capabilities/web_audio_configuration.h",
     "platform/modules/media_capabilities/web_media_capabilities_callbacks.h",
     "platform/modules/media_capabilities/web_media_capabilities_client.h",
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 181640e..df8b95c 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -101,6 +101,7 @@
     "service_worker/service_worker_state.mojom",
     "service_worker/service_worker_stream_handle.mojom",
     "site_engagement/site_engagement.mojom",
+    "sms/sms_manager.mojom",
     "speech/speech_recognition_error.mojom",
     "speech/speech_recognition_error_code.mojom",
     "speech/speech_recognition_grammar.mojom",
diff --git a/third_party/blink/public/mojom/sms/OWNERS b/third_party/blink/public/mojom/sms/OWNERS
new file mode 100644
index 0000000..1f8a174
--- /dev/null
+++ b/third_party/blink/public/mojom/sms/OWNERS
@@ -0,0 +1,4 @@
+file://content/browser/sms/OWNERS
+
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/mojom/sms/sms_manager.mojom b/third_party/blink/public/mojom/sms/sms_manager.mojom
new file mode 100644
index 0000000..badf1bd8
--- /dev/null
+++ b/third_party/blink/public/mojom/sms/sms_manager.mojom
@@ -0,0 +1,21 @@
+// 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.
+
+module blink.mojom;
+
+import "mojo/public/mojom/base/time.mojom";
+
+// Implementation of the proposed "Sms Detection API".
+//
+// Proposal: https://github.com/sso-google/sms-otp-retrieval
+
+// This interface is created per storage partition but its execution is context
+// associated: there is an origin associated with a request that is multiplexed
+// through one instance on a storage partition.
+interface SmsManager {
+  // Retrieves the next SMS message that arrives on the phone that is addressed
+  // to the caller's origin.
+  // Returns the raw content of the received SMS.
+  GetNextMessage(mojo_base.mojom.TimeDelta timeout) => (string sms);
+};
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index a7e4383..4059e8f 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2276,6 +2276,9 @@
   kV8RTCQuicTransport_SendDatagram_Method = 2869,
   kV8RTCQuicTransport_ReceiveDatagrams_Method = 2870,
   kCSSValueContainStyle = 2871,
+  kWebShareContainingFiles = 2872,
+  kWebShareWithoutFiles = 2873,
+  kWebShareCancelled = 2874,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/modules/installedapp/web_related_application.h b/third_party/blink/public/platform/modules/installedapp/web_related_application.h
deleted file mode 100644
index f24f0f4..0000000
--- a/third_party/blink/public/platform/modules/installedapp/web_related_application.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INSTALLEDAPP_WEB_RELATED_APPLICATION_H_
-#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INSTALLEDAPP_WEB_RELATED_APPLICATION_H_
-
-#include "third_party/blink/public/platform/web_string.h"
-
-namespace blink {
-
-struct WebRelatedApplication {
-  WebRelatedApplication() = default;
-
-  WebString platform;
-  WebString url;
-  WebString id;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INSTALLEDAPP_WEB_RELATED_APPLICATION_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/activity_logger_test.cc b/third_party/blink/renderer/bindings/core/v8/activity_logger_test.cc
index 3abbaa3..5acbbaa 100644
--- a/third_party/blink/renderer/bindings/core/v8/activity_logger_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/activity_logger_test.cc
@@ -375,12 +375,16 @@
       "blinkRequestResource | Main resource | data:text/html;charset=utf-8,A\n"
       "blinkSetAttribute | iframe | src | data:text/html;charset=utf-8,A | "
       "data:text/html;charset=utf-8,B\n"
+      "blinkRequestResource | Main resource | data:text/html;charset=utf-8,B\n"
       "blinkSetAttribute | iframe | src | data:text/html;charset=utf-8,B | "
       "data:text/html;charset=utf-8,C\n"
+      "blinkRequestResource | Main resource | data:text/html;charset=utf-8,C\n"
       "blinkSetAttribute | iframe | src | data:text/html;charset=utf-8,C | "
       "data:text/html;charset=utf-8,D\n"
+      "blinkRequestResource | Main resource | data:text/html;charset=utf-8,D\n"
       "blinkSetAttribute | iframe | src | data:text/html;charset=utf-8,D | "
-      "data:text/html;charset=utf-8,E";
+      "data:text/html;charset=utf-8,E\n"
+      "blinkRequestResource | Main resource | data:text/html;charset=utf-8,E";
   ExecuteScriptInMainWorld(code);
   ASSERT_TRUE(VerifyActivities(""));
   ExecuteScriptInIsolatedWorld(code);
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context_test.cc b/third_party/blink/renderer/core/display_lock/display_lock_context_test.cc
index c133d0f..14cb849 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context_test.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context_test.cc
@@ -142,7 +142,7 @@
     div {
       width: 100px;
       height: 100px;
-      contain: content;
+      contain: style layout paint;
     }
     </style>
     <body><div id="container"><div id="child"></div></div></body>
@@ -246,7 +246,7 @@
     #container {
       width: 100px;
       height: 100px;
-      contain: content;
+      contain: style layout paint;
     }
     </style>
     <body><div id="container">testing</div></body>
@@ -314,7 +314,7 @@
     #container {
       width: 100px;
       height: 100px;
-      contain: content;
+      contain: style layout paint;
     }
     </style>
     <body><div id="container">testing</div></body>
@@ -402,7 +402,7 @@
     #container {
       width: 100px;
       height: 100px;
-      contain: content;
+      contain: style layout paint;
     }
     </style>
     <body><div id="container">testing</div></body>
@@ -570,7 +570,7 @@
     div {
       width: 100px;
       height: 100px;
-      contain: content;
+      contain: style layout paint;
     }
     </style>
     <body>
@@ -725,7 +725,7 @@
     #container {
       width: 100px;
       height: 100px;
-      contain: content;
+      contain: style layout paint;
     }
     </style>
     <body><div id="container"><b>t</b>esting</div></body>
@@ -809,7 +809,7 @@
     #container {
       width: 100px;
       height: 100px;
-      contain: content;
+      contain: style layout paint;
     }
     </style>
     <body>
@@ -898,7 +898,8 @@
   ShadowRoot& shadow_root =
       host->AttachShadowRootInternal(ShadowRootType::kOpen);
   shadow_root.SetInnerHTMLFromString(
-      "<div id='container' style='contain:content;'><slot></slot></div>");
+      "<div id='container' style='contain:style layout "
+      "paint;'><slot></slot></div>");
   UpdateAllLifecyclePhasesForTest();
 
   auto* container = shadow_root.getElementById("container");
@@ -958,7 +959,8 @@
   ShadowRoot& shadow_root =
       host->AttachShadowRootInternal(ShadowRootType::kOpen);
   shadow_root.SetInnerHTMLFromString(
-      "<div id='container' style='contain:content;'><slot></slot></div>");
+      "<div id='container' style='contain:style layout "
+      "paint;'><slot></slot></div>");
 
   UpdateAllLifecyclePhasesForTest();
   ASSERT_TRUE(text_field->IsKeyboardFocusable());
@@ -999,7 +1001,7 @@
     .container {
       width: 100px;
       height: 100px;
-      contain: content;
+      contain: style layout paint;
     }
     </style>
     <body>
@@ -1091,7 +1093,7 @@
     .container {
       width: 100px;
       height: 100px;
-      contain: content;
+      contain: style layout paint;
     }
     </style>
     <body>
@@ -1180,7 +1182,7 @@
     #child {
       width: 100px;
       height: 100px;
-      contain: content;
+      contain: style layout paint;
     }
     #grandchild {
       color: blue;
@@ -1284,7 +1286,7 @@
     #locked {
       width: 100px;
       height: 100px;
-      contain: content;
+      contain: style layout paint;
     }
     </style>
     <div id="ancestor">
@@ -1430,7 +1432,7 @@
     #locked {
       width: 100px;
       height: 100px;
-      contain: content;
+      contain: style layout paint;
     }
     </style>
     <div id="ancestor">
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index d55ab81d..30f6773 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -287,6 +287,7 @@
 #include "third_party/blink/renderer/platform/network/http_parsers.h"
 #include "third_party/blink/renderer/platform/network/network_state_notifier.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scheduler/public/dummy_schedulers.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
@@ -7577,7 +7578,10 @@
   // cases, though, there isn't a good candidate (most commonly when either the
   // passed-in document or ContextDocument() used to be attached to a Frame but
   // has since been detached).
-  return nullptr;
+  if (!detached_scheduler_) {
+    detached_scheduler_ = scheduler::CreateDummyFrameScheduler();
+  }
+  return detached_scheduler_.get();
 }
 
 scoped_refptr<base::SingleThreadTaskRunner> Document::GetTaskRunner(
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 0b5bbb1..2d1ac7a4 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -2035,6 +2035,15 @@
   // Used to communicate state associated with resource management to the
   // embedder.
   std::unique_ptr<DocumentResourceCoordinator> resource_coordinator_;
+
+  // A dummy scheduler to return when the document is detached.
+  // All operations on it result in no-op, but due to this it's safe to
+  // use the returned value of GetScheduler() without additional checks.
+  // A task posted to a task runner obtained from one of its task runners
+  // will be forwarded to the default task runner.
+  // TODO(altimin): We should be able to remove it after we complete
+  // frame:document lifetime refactoring.
+  std::unique_ptr<FrameOrWorkerScheduler> detached_scheduler_;
 };
 
 extern template class CORE_EXTERN_TEMPLATE_EXPORT Supplement<Document>;
diff --git a/third_party/blink/renderer/core/frame/dom_window.h b/third_party/blink/renderer/core/frame/dom_window.h
index b13487c1..c466de2 100644
--- a/third_party/blink/renderer/core/frame/dom_window.h
+++ b/third_party/blink/renderer/core/frame/dom_window.h
@@ -17,6 +17,7 @@
 
 class Document;
 class InputDeviceCapabilitiesConstants;
+class KURL;
 class LocalDOMWindow;
 class Location;
 class MessageEvent;
diff --git a/third_party/blink/renderer/core/frame/frame.h b/third_party/blink/renderer/core/frame/frame.h
index 25310fb..6010b13 100644
--- a/third_party/blink/renderer/core/frame/frame.h
+++ b/third_party/blink/renderer/core/frame/frame.h
@@ -57,7 +57,6 @@
 class FrameClient;
 class FrameOwner;
 class HTMLFrameOwnerElement;
-class KURL;
 class LayoutEmbeddedContent;
 class LocalFrame;
 class Page;
@@ -84,23 +83,6 @@
   virtual bool IsLocalFrame() const = 0;
   virtual bool IsRemoteFrame() const = 0;
 
-  // Asynchronously schedules a task to begin a navigation: this roughly
-  // corresponds to "queue a task to navigate the target browsing context to
-  // resource" in https://whatwg.org/C/links.html#following-hyperlinks.
-  //
-  // Note that there's currently an exception for same-origin same-document
-  // navigations: these are never scheduled and are always synchronously
-  // processed.
-  //
-  // TODO(dcheng): Note that despite the comment, most navigations in the spec
-  // are *not* currently queued. See https://github.com/whatwg/html/issues/3730.
-  // How this discussion is resolved will affect how https://crbug.com/848171 is
-  // eventually fixed.
-  virtual void ScheduleNavigation(Document& origin_document,
-                                  const KURL&,
-                                  WebFrameLoadType,
-                                  UserGestureStatus) = 0;
-  // Synchronously begins a navigation.
   virtual void Navigate(const FrameLoadRequest&, WebFrameLoadType) = 0;
 
   void Detach(FrameDetachType);
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index f5e5cfb..24f40cc2 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -325,14 +325,24 @@
   return Tree().Parent()->IsRemoteFrame();
 }
 
-void LocalFrame::ScheduleNavigation(Document& origin_document,
-                                    const KURL& url,
-                                    WebFrameLoadType frame_load_type,
-                                    UserGestureStatus user_gesture_status) {
-  if (!navigation_rate_limiter().CanProceed())
-    return;
-  navigation_scheduler_->ScheduleFrameNavigation(&origin_document, url,
-                                                 frame_load_type);
+static bool MustReplaceCurrentItem(LocalFrame* frame) {
+  // Non-user navigation before the page has finished firing onload should not
+  // create a new back/forward item. See https://webkit.org/b/42861 for the
+  // original motivation for this.
+  if (!frame->GetDocument()->LoadEventFinished() &&
+      !LocalFrame::HasTransientUserActivation(frame)) {
+    return true;
+  }
+
+  // Navigation of a subframe during loading of an ancestor frame does not
+  // create a new back/forward item. The definition of "during load" is any time
+  // before all handlers for the load event have been run. See
+  // https://bugs.webkit.org/show_bug.cgi?id=14957 for the original motivation
+  // for this.
+  Frame* parent_frame = frame->Tree().Parent();
+  auto* parent_local_frame = DynamicTo<LocalFrame>(parent_frame);
+  return parent_local_frame &&
+         !parent_local_frame->Loader().AllAncestorsAreComplete();
 }
 
 void LocalFrame::Navigate(const FrameLoadRequest& request,
@@ -342,7 +352,7 @@
   if (request.ClientRedirect() == ClientRedirectPolicy::kClientRedirect) {
     probe::FrameScheduledNavigation(this, request.GetResourceRequest().Url(),
                                     0.0, request.ClientRedirectReason());
-    if (NavigationScheduler::MustReplaceCurrentItem(this))
+    if (MustReplaceCurrentItem(this))
       frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
   }
   loader_.StartNavigation(request, frame_load_type);
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index f1b11ec0..6e5b813 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -133,10 +133,6 @@
   // Frame overrides:
   ~LocalFrame() override;
   void Trace(blink::Visitor*) override;
-  void ScheduleNavigation(Document& origin_document,
-                          const KURL&,
-                          WebFrameLoadType,
-                          UserGestureStatus) override;
   void Navigate(const FrameLoadRequest&, WebFrameLoadType) override;
   bool ShouldClose() override;
   SecurityContext* GetSecurityContext() const override;
diff --git a/third_party/blink/renderer/core/frame/remote_frame.cc b/third_party/blink/renderer/core/frame/remote_frame.cc
index 107a324..0b4e5d7 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame.cc
@@ -63,18 +63,6 @@
   Frame::Trace(visitor);
 }
 
-void RemoteFrame::ScheduleNavigation(Document& origin_document,
-                                     const KURL& url,
-                                     WebFrameLoadType frame_load_type,
-                                     UserGestureStatus user_gesture_status) {
-  FrameLoadRequest frame_request(&origin_document, ResourceRequest(url));
-  frame_request.GetResourceRequest().SetHasUserGesture(
-      user_gesture_status == UserGestureStatus::kActive);
-  frame_request.SetClientRedirectReason(
-      ClientNavigationReason::kFrameNavigation);
-  Navigate(frame_request, frame_load_type);
-}
-
 void RemoteFrame::Navigate(const FrameLoadRequest& passed_request,
                            WebFrameLoadType frame_load_type) {
   if (!navigation_rate_limiter().CanProceed())
diff --git a/third_party/blink/renderer/core/frame/remote_frame.h b/third_party/blink/renderer/core/frame/remote_frame.h
index 23ad42ba..748273f 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.h
+++ b/third_party/blink/renderer/core/frame/remote_frame.h
@@ -31,10 +31,6 @@
 
   // Frame overrides:
   void Trace(blink::Visitor*) override;
-  void ScheduleNavigation(Document& origin_document,
-                          const KURL&,
-                          WebFrameLoadType,
-                          UserGestureStatus) override;
   void Navigate(const FrameLoadRequest&, WebFrameLoadType) override;
   RemoteSecurityContext* GetSecurityContext() const override;
   bool PrepareForCommit() override;
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index 76fd036..8836bb5b 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -25,6 +25,7 @@
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
+#include "third_party/blink/renderer/core/events/current_input_event.h"
 #include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
@@ -404,13 +405,12 @@
   KURL url_to_request = url.IsNull() ? BlankURL() : url;
   if (ContentFrame()) {
     // TODO(sclittle): Support lazily loading frame navigations.
+    FrameLoadRequest request(&GetDocument(), ResourceRequest(url_to_request));
+    request.SetClientRedirectReason(ClientNavigationReason::kFrameNavigation);
     WebFrameLoadType frame_load_type = WebFrameLoadType::kStandard;
     if (replace_current_item)
       frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
-
-    ContentFrame()->ScheduleNavigation(GetDocument(), url_to_request,
-                                       frame_load_type,
-                                       UserGestureStatus::kNone);
+    ContentFrame()->Navigate(request, frame_load_type);
     return true;
   }
 
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
index a0b7c80..d587071 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
@@ -492,7 +492,7 @@
   return IsHorizontalFlow() ? child.Size().Height() : child.Size().Width();
 }
 
-LayoutUnit LayoutFlexibleBox::ChildIntrinsicLogicalHeight(
+LayoutUnit LayoutFlexibleBox::ChildUnstretchedLogicalHeight(
     const LayoutBox& child) const {
   // This should only be called if the logical height is the cross size
   DCHECK(MainAxisIsInlineAxis(child));
@@ -512,19 +512,23 @@
 }
 
 DISABLE_CFI_PERF
-LayoutUnit LayoutFlexibleBox::ChildIntrinsicLogicalWidth(
+LayoutUnit LayoutFlexibleBox::ChildUnstretchedLogicalWidth(
     const LayoutBox& child) const {
   // This should only be called if the logical width is the cross size
   DCHECK(!MainAxisIsInlineAxis(child));
-  // If our height is auto, make sure that our returned height is unaffected by
-  // earlier layouts by returning the shrink-to-fit size.
+  DCHECK(!child.HasOverrideLogicalWidth());
+  // We compute the width as if we were unstretched. Only the main axis
+  // override size is set at this point.
+  // However, if our cross axis length is definite we don't need to recompute
+  // and can just return the already-set logical width.
   if (!CrossAxisLengthIsDefinite(child, child.StyleRef().LogicalWidth())) {
-    LayoutUnit available_size =
-        ContentLogicalWidth() - child.MarginLogicalWidth();
-    MinMaxSize sizes{child.MinPreferredLogicalWidth(),
-                     child.MaxPreferredLogicalWidth()};
-    return child.ConstrainLogicalWidthByMinMax(
-        sizes.ShrinkToFit(available_size), available_size, this);
+    LogicalExtentComputedValues values;
+    // ComputeLogicalWidth has the side-effect of setting LogicalWidth to 0;
+    // so we store and re-set it.
+    LayoutUnit w = child.LogicalWidth();
+    child.ComputeLogicalWidth(values);
+    const_cast<LayoutBox&>(child).SetLogicalWidth(w);
+    return values.extent_;
   }
 
   return child.LogicalWidth();
@@ -532,8 +536,8 @@
 
 LayoutUnit LayoutFlexibleBox::CrossAxisUnstretchedExtentForChild(
     const LayoutBox& child) const {
-  return MainAxisIsInlineAxis(child) ? ChildIntrinsicLogicalHeight(child)
-                                     : ChildIntrinsicLogicalWidth(child);
+  return MainAxisIsInlineAxis(child) ? ChildUnstretchedLogicalHeight(child)
+                                     : ChildUnstretchedLogicalWidth(child);
 }
 
 LayoutUnit LayoutFlexibleBox::MainAxisExtentForChild(
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.h b/third_party/blink/renderer/core/layout/layout_flexible_box.h
index 99d3092..6d1255dc 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.h
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.h
@@ -123,8 +123,8 @@
   Length FlexBasisForChild(const LayoutBox& child) const;
   LayoutUnit CrossAxisExtentForChild(const LayoutBox& child) const;
   LayoutUnit CrossAxisUnstretchedExtentForChild(const LayoutBox& child) const;
-  LayoutUnit ChildIntrinsicLogicalHeight(const LayoutBox& child) const;
-  LayoutUnit ChildIntrinsicLogicalWidth(const LayoutBox& child) const;
+  LayoutUnit ChildUnstretchedLogicalHeight(const LayoutBox& child) const;
+  LayoutUnit ChildUnstretchedLogicalWidth(const LayoutBox& child) const;
   LayoutUnit MainAxisExtentForChild(const LayoutBox& child) const;
   LayoutUnit MainAxisContentExtentForChildIncludingScrollbar(
       const LayoutBox& child) const;
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index 12eb0c3..c5558e9 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -512,12 +512,11 @@
     return StyleRef().ContainsStyle();
   }
   inline bool ShouldApplyContentContainment() const {
-    return ShouldApplyPaintContainment() && ShouldApplyLayoutContainment() &&
-           ShouldApplyStyleContainment();
+    return ShouldApplyPaintContainment() && ShouldApplyLayoutContainment();
   }
   inline bool ShouldApplyStrictContainment() const {
     return ShouldApplyPaintContainment() && ShouldApplyLayoutContainment() &&
-           ShouldApplyStyleContainment() && ShouldApplySizeContainment();
+           ShouldApplySizeContainment();
   }
 
  private:
diff --git a/third_party/blink/renderer/core/loader/frame_load_request.cc b/third_party/blink/renderer/core/loader/frame_load_request.cc
index 78c15ae60..4872223 100644
--- a/third_party/blink/renderer/core/loader/frame_load_request.cc
+++ b/third_party/blink/renderer/core/loader/frame_load_request.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/core/events/current_input_event.h"
 #include "third_party/blink/renderer/core/fileapi/public_url_manager.h"
+#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 
@@ -63,6 +64,11 @@
       origin_document->GetPublicURLManager().Resolve(
           resource_request.Url(), MakeRequest(&blob_url_token_->data));
     }
+
+    if (ContentSecurityPolicy::ShouldBypassMainWorld(origin_document)) {
+      should_check_main_world_content_security_policy_ =
+          kDoNotCheckContentSecurityPolicy;
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/core/loader/frame_load_request.h b/third_party/blink/renderer/core/loader/frame_load_request.h
index 3c4e77d..e90111d 100644
--- a/third_party/blink/renderer/core/loader/frame_load_request.h
+++ b/third_party/blink/renderer/core/loader/frame_load_request.h
@@ -119,22 +119,11 @@
     return should_check_main_world_content_security_policy_;
   }
 
-  // Sets the BlobURLToken that should be used when fetching the resource. This
+  // The BlobURLToken that should be used when fetching the resource. This
   // is needed for blob URLs, because the blob URL might be revoked before the
   // actual fetch happens, which would result in incorrect failures to fetch.
   // The token lets the browser process securely resolves the blob URL even
   // after the url has been revoked.
-  // FrameFetchRequest initializes this in its constructor, but in some cases
-  // FrameFetchRequest is created asynchronously rather than when a navigation
-  // is scheduled, so in those cases NavigationScheduler needs to override the
-  // blob FrameLoadRequest might have found.
-  void SetBlobURLToken(mojom::blink::BlobURLTokenPtr blob_url_token) {
-    DCHECK(blob_url_token);
-    blob_url_token_ = base::MakeRefCounted<
-        base::RefCountedData<mojom::blink::BlobURLTokenPtr>>(
-        std::move(blob_url_token));
-  }
-
   mojom::blink::BlobURLTokenPtr GetBlobURLToken() const {
     if (!blob_url_token_)
       return nullptr;
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index cb526e66..5ba9b15 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -417,7 +417,7 @@
   // progress.
   DCHECK((document_loader_ && document_loader_->SentDidFinishLoad()) ||
          !HasProvisionalNavigation());
-  if (!document_loader_ || !document_loader_->SentDidFinishLoad() ||
+  if ((document_loader_ && !document_loader_->SentDidFinishLoad()) ||
       HasProvisionalNavigation()) {
     return;
   }
diff --git a/third_party/blink/renderer/core/loader/navigation_scheduler.cc b/third_party/blink/renderer/core/loader/navigation_scheduler.cc
index 2529329..2b8edc57 100644
--- a/third_party/blink/renderer/core/loader/navigation_scheduler.cc
+++ b/third_party/blink/renderer/core/loader/navigation_scheduler.cc
@@ -117,63 +117,6 @@
   }
 };
 
-class ScheduledFrameNavigation final : public ScheduledNavigation {
- public:
-  static ScheduledFrameNavigation* Create(Document* origin_document,
-                                          const KURL& url,
-                                          WebFrameLoadType frame_load_type,
-                                          base::TimeTicks input_timestamp) {
-    return MakeGarbageCollected<ScheduledFrameNavigation>(
-        origin_document, url, frame_load_type, input_timestamp);
-  }
-
-  ScheduledFrameNavigation(Document* origin_document,
-                           const KURL& url,
-                           WebFrameLoadType frame_load_type,
-                           base::TimeTicks input_timestamp)
-      : ScheduledNavigation(ClientNavigationReason::kFrameNavigation,
-                            0.0,
-                            origin_document,
-                            url,
-                            frame_load_type,
-                            input_timestamp),
-        should_check_main_world_content_security_policy_(
-            kCheckContentSecurityPolicy) {
-    if (ContentSecurityPolicy::ShouldBypassMainWorld(origin_document)) {
-      should_check_main_world_content_security_policy_ =
-          kDoNotCheckContentSecurityPolicy;
-    }
-
-    if (origin_document && url.ProtocolIs("blob") &&
-        BlobUtils::MojoBlobURLsEnabled()) {
-      origin_document->GetPublicURLManager().Resolve(
-          Url(), MakeRequest(&blob_url_token_));
-    }
-  }
-
-  void Fire(LocalFrame* frame) override {
-    std::unique_ptr<UserGestureIndicator> gesture_indicator =
-        CreateUserGestureIndicator();
-    FrameLoadRequest request(OriginDocument(), ResourceRequest(Url()), "_self",
-                             should_check_main_world_content_security_policy_);
-    request.SetClientRedirectReason(GetReason());
-    request.SetInputStartTime(InputTimestamp());
-
-    if (blob_url_token_) {
-      mojom::blink::BlobURLTokenPtr token_clone;
-      blob_url_token_->Clone(MakeRequest(&token_clone));
-      request.SetBlobURLToken(std::move(token_clone));
-    }
-
-    frame->Loader().StartNavigation(request, LoadType());
-  }
-
- private:
-  mojom::blink::BlobURLTokenPtr blob_url_token_;
-  ContentSecurityPolicyDisposition
-      should_check_main_world_content_security_policy_;
-};
-
 NavigationScheduler::NavigationScheduler(LocalFrame* frame) : frame_(frame) {}
 
 NavigationScheduler::~NavigationScheduler() {
@@ -207,31 +150,11 @@
     if (delay <= 1)
       frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
     Schedule(MakeGarbageCollected<ScheduledRedirect>(
-                 delay, frame_->GetDocument(), url, http_refresh_type,
-                 frame_load_type, InputTimestamp()),
-             kDoNotCancelParsing);
+        delay, frame_->GetDocument(), url, http_refresh_type, frame_load_type,
+        InputTimestamp()));
   }
 }
 
-bool NavigationScheduler::MustReplaceCurrentItem(LocalFrame* target_frame) {
-  // Non-user navigation before the page has finished firing onload should not
-  // create a new back/forward item. See https://webkit.org/b/42861 for the
-  // original motivation for this.
-  if (!target_frame->GetDocument()->LoadEventFinished() &&
-      !LocalFrame::HasTransientUserActivation(target_frame))
-    return true;
-
-  // Navigation of a subframe during loading of an ancestor frame does not
-  // create a new back/forward item. The definition of "during load" is any time
-  // before all handlers for the load event have been run. See
-  // https://bugs.webkit.org/show_bug.cgi?id=14957 for the original motivation
-  // for this.
-  Frame* parent_frame = target_frame->Tree().Parent();
-  auto* parent_local_frame = DynamicTo<LocalFrame>(parent_frame);
-  return parent_local_frame &&
-         !parent_local_frame->Loader().AllAncestorsAreComplete();
-}
-
 base::TimeTicks NavigationScheduler::InputTimestamp() {
   if (const WebInputEvent* input_event = CurrentInputEvent::Get()) {
     return input_event->TimeStamp();
@@ -239,33 +162,6 @@
   return base::TimeTicks();
 }
 
-void NavigationScheduler::ScheduleFrameNavigation(
-    Document* origin_document,
-    const KURL& url,
-    WebFrameLoadType frame_load_type) {
-  if (!ShouldScheduleNavigation(url))
-    return;
-
-  if (MustReplaceCurrentItem(frame_))
-    frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
-
-  base::TimeTicks input_timestamp = InputTimestamp();
-  if (url.HasFragmentIdentifier() &&
-      EqualIgnoringFragmentIdentifier(frame_->GetDocument()->Url(), url)) {
-    FrameLoadRequest request(origin_document, ResourceRequest(url), "_self");
-    request.SetInputStartTime(input_timestamp);
-    if (frame_load_type == WebFrameLoadType::kReplaceCurrentItem) {
-      request.SetClientRedirectReason(ClientNavigationReason::kFrameNavigation);
-    }
-    frame_->Loader().StartNavigation(request, frame_load_type);
-    return;
-  }
-
-  Schedule(ScheduledFrameNavigation::Create(origin_document, url,
-                                            frame_load_type, input_timestamp),
-           kCancelParsing);
-}
-
 void NavigationScheduler::NavigateTask() {
   if (!frame_->GetPage())
     return;
@@ -279,8 +175,7 @@
   probe::FrameClearedScheduledNavigation(frame_);
 }
 
-void NavigationScheduler::Schedule(ScheduledNavigation* redirect,
-                                   CancelParsingPolicy cancel_parsing_policy) {
+void NavigationScheduler::Schedule(ScheduledNavigation* redirect) {
   DCHECK(frame_->GetPage());
 
   // In a back/forward navigation, we sometimes restore history state to
@@ -299,14 +194,6 @@
 
   Cancel();
   redirect_ = redirect;
-
-  // Most navigations are guaranteed to transition documents if they reach this
-  // point. JS urls aren't, and refresh headers are delayed. Don't immediately
-  // cancel parsing for those.
-  if (cancel_parsing_policy == kCancelParsing &&
-      !redirect->Url().ProtocolIsJavaScript()) {
-    frame_->GetDocument()->CancelParsing();
-  }
   StartTimer();
 }
 
diff --git a/third_party/blink/renderer/core/loader/navigation_scheduler.h b/third_party/blink/renderer/core/loader/navigation_scheduler.h
index 294156f..23e70c9 100644
--- a/third_party/blink/renderer/core/loader/navigation_scheduler.h
+++ b/third_party/blink/renderer/core/loader/navigation_scheduler.h
@@ -61,22 +61,17 @@
   bool IsNavigationScheduledWithin(double interval_in_seconds) const;
 
   void ScheduleRedirect(double delay, const KURL&, Document::HttpRefreshType);
-  void ScheduleFrameNavigation(Document*, const KURL&, WebFrameLoadType);
 
   void StartTimer();
   void Cancel();
 
-  static bool MustReplaceCurrentItem(LocalFrame* target_frame);
-
   void Trace(blink::Visitor*);
 
  private:
   bool ShouldScheduleNavigation(const KURL&) const;
 
   void NavigateTask();
-
-  enum CancelParsingPolicy { kCancelParsing, kDoNotCancelParsing };
-  void Schedule(ScheduledNavigation*, CancelParsingPolicy);
+  void Schedule(ScheduledNavigation*);
 
   base::TimeTicks InputTimestamp();
 
diff --git a/third_party/blink/renderer/core/page/spatial_navigation_controller.cc b/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
index 5c60e62..53ae3fd1 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
+++ b/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
@@ -177,17 +177,14 @@
     return false;
 
   if (event->type() == event_type_names::kKeydown) {
-    interest_element->SetActive(true);
-  } else if (event->type() == event_type_names::kKeyup) {
-    interest_element->SetActive(false);
     if (RuntimeEnabledFeatures::FocuslessSpatialNavigationEnabled()) {
       interest_element->focus(FocusParams(SelectionBehaviorOnFocus::kReset,
                                           kWebFocusTypeSpatialNavigation,
                                           nullptr));
-      // We need enter to activate links, etc. The click should be after the
-      // focus in case the site transfers focus upon clicking.
-      interest_element->DispatchSimulatedClick(event);
     }
+    interest_element->SetActive(true);
+  } else if (event->type() == event_type_names::kKeyup) {
+    interest_element->SetActive(false);
   }
 
   return true;
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 130dfc5e4..cbabbc3 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -1951,7 +1951,6 @@
   bool ContainsStyle() const { return Contain() & kContainsStyle; }
   bool ContainsLayout() const { return Contain() & kContainsLayout; }
   bool ContainsSize() const { return Contain() & kContainsSize; }
-  bool ContainsContent() const { return Contain() & kContainsContent; }
 
   // Display utility functions.
   bool IsDisplayReplacedType() const {
diff --git a/third_party/blink/renderer/core/style/computed_style_constants.h b/third_party/blink/renderer/core/style/computed_style_constants.h
index 8be0d3e..713c969 100644
--- a/third_party/blink/renderer/core/style/computed_style_constants.h
+++ b/third_party/blink/renderer/core/style/computed_style_constants.h
@@ -170,9 +170,8 @@
   kContainsStyle = 0x2,
   kContainsPaint = 0x4,
   kContainsSize = 0x8,
-  kContainsStrict =
-      kContainsLayout | kContainsStyle | kContainsPaint | kContainsSize,
-  kContainsContent = kContainsLayout | kContainsStyle | kContainsPaint,
+  kContainsStrict = kContainsLayout | kContainsPaint | kContainsSize,
+  kContainsContent = kContainsLayout | kContainsPaint,
 };
 inline Containment operator|(Containment a, Containment b) {
   return Containment(int(a) | int(b));
diff --git a/third_party/blink/renderer/core/testing/null_execution_context.cc b/third_party/blink/renderer/core/testing/null_execution_context.cc
index 90e2f413..b887edb7 100644
--- a/third_party/blink/renderer/core/testing/null_execution_context.cc
+++ b/third_party/blink/renderer/core/testing/null_execution_context.cc
@@ -8,6 +8,8 @@
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/frame/dom_timer.h"
+#include "third_party/blink/renderer/platform/scheduler/public/dummy_schedulers.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 
 namespace blink {
@@ -15,7 +17,10 @@
 NullExecutionContext::NullExecutionContext()
     : ExecutionContext(v8::Isolate::GetCurrent()),
       tasks_need_pause_(false),
-      is_secure_context_(true) {}
+      is_secure_context_(true),
+      scheduler_(scheduler::CreateDummyFrameScheduler()) {}
+
+NullExecutionContext::~NullExecutionContext() {}
 
 void NullExecutionContext::SetIsSecureContext(bool is_secure_context) {
   is_secure_context_ = is_secure_context;
@@ -35,7 +40,7 @@
 }
 
 FrameOrWorkerScheduler* NullExecutionContext::GetScheduler() {
-  return nullptr;
+  return scheduler_.get();
 }
 
 scoped_refptr<base::SingleThreadTaskRunner> NullExecutionContext::GetTaskRunner(
diff --git a/third_party/blink/renderer/core/testing/null_execution_context.h b/third_party/blink/renderer/core/testing/null_execution_context.h
index 3564a56..680102b 100644
--- a/third_party/blink/renderer/core/testing/null_execution_context.h
+++ b/third_party/blink/renderer/core/testing/null_execution_context.h
@@ -24,6 +24,7 @@
 
  public:
   NullExecutionContext();
+  ~NullExecutionContext() override;
 
   void SetURL(const KURL& url) { url_ = url; }
 
@@ -76,6 +77,11 @@
   bool is_secure_context_;
 
   KURL url_;
+
+  // A dummy scheduler to ensure that the callers of
+  // ExecutionContext::GetScheduler don't have to check for whether it's null or
+  // not.
+  std::unique_ptr<FrameOrWorkerScheduler> scheduler_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/devtools/front_end/common/UIString.js b/third_party/blink/renderer/devtools/front_end/common/UIString.js
index 7bd56dd5..8845ed7 100644
--- a/third_party/blink/renderer/devtools/front_end/common/UIString.js
+++ b/third_party/blink/renderer/devtools/front_end/common/UIString.js
@@ -84,6 +84,8 @@
   }
 };
 
+/** @type {!WeakMap<!Array<string>, string>} */
+Common._substitutionStrings = new WeakMap();
 
 /**
  * @param {!Array<string>|string} strings
@@ -93,13 +95,10 @@
 self.ls = function(strings, vararg) {
   if (typeof strings === 'string')
     return strings;
-  const values = Array.prototype.slice.call(arguments, 1);
-  if (!values.length)
-    return strings[0];
-  let result = '';
-  for (let i = 0; i < values.length; i++) {
-    result += strings[i];
-    result += '' + values[i];
+  let substitutionString = Common._substitutionStrings.get(strings);
+  if (!substitutionString) {
+    substitutionString = strings.join('%s');
+    Common._substitutionStrings.set(strings, substitutionString);
   }
-  return result + strings[values.length];
+  return Common.UIString(substitutionString, ...Array.prototype.slice.call(arguments, 1));
 };
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index 80d501c..c1fb2a5f3 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -144,6 +144,7 @@
     "//third_party/blink/renderer/modules/sensor",
     "//third_party/blink/renderer/modules/service_worker",
     "//third_party/blink/renderer/modules/shapedetection",
+    "//third_party/blink/renderer/modules/sms",
     "//third_party/blink/renderer/modules/speech",
     "//third_party/blink/renderer/modules/srcobject",
     "//third_party/blink/renderer/modules/storage",
diff --git a/third_party/blink/renderer/modules/event_target_modules_names.json5 b/third_party/blink/renderer/modules/event_target_modules_names.json5
index f788c13..e7999d7 100644
--- a/third_party/blink/renderer/modules/event_target_modules_names.json5
+++ b/third_party/blink/renderer/modules/event_target_modules_names.json5
@@ -57,6 +57,7 @@
     "ServiceWorkerContainer",
     "ServiceWorkerGlobalScope",
     "ServiceWorkerRegistration",
+    "SMSReceiver",
     "SpeechRecognition",
     "SpeechSynthesis",
     "SpeechSynthesisUtterance",
diff --git a/third_party/blink/renderer/modules/installedapp/installed_app_controller.cc b/third_party/blink/renderer/modules/installedapp/installed_app_controller.cc
index d35156e9..4e6839e 100644
--- a/third_party/blink/renderer/modules/installedapp/installed_app_controller.cc
+++ b/third_party/blink/renderer/modules/installedapp/installed_app_controller.cc
@@ -9,7 +9,6 @@
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/manifest/manifest.h"
 #include "third_party/blink/public/platform/web_string.h"
-#include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/modules/manifest/manifest_manager.h"
@@ -64,7 +63,7 @@
     std::unique_ptr<AppInstalledCallbacks> callbacks,
     const WebURL& /*url*/,
     const Manifest& manifest) {
-  WTF::Vector<mojom::blink::RelatedApplicationPtr> mojo_related_apps;
+  Vector<mojom::blink::RelatedApplicationPtr> mojo_related_apps;
   for (const Manifest::RelatedApplication& related_application :
        manifest.related_applications) {
     mojom::blink::RelatedApplicationPtr converted_application(
@@ -81,9 +80,9 @@
 
   if (!provider_) {
     // See https://bit.ly/2S0zRAS for task types.
-    GetSupplementable()->GetInterfaceProvider().GetInterface(
-        mojo::MakeRequest(&provider_, GetExecutionContext()->GetTaskRunner(
-                                          blink::TaskType::kMiscPlatformAPI)));
+    GetSupplementable()->GetInterfaceProvider().GetInterface(mojo::MakeRequest(
+        &provider_,
+        GetExecutionContext()->GetTaskRunner(TaskType::kMiscPlatformAPI)));
     // TODO(mgiuca): Set a connection error handler. This requires a refactor to
     // work like NavigatorShare.cpp (retain a persistent list of clients to
     // reject all of their promises).
@@ -97,21 +96,18 @@
 }
 
 void InstalledAppController::OnFilterInstalledApps(
-    std::unique_ptr<blink::AppInstalledCallbacks> callbacks,
-    WTF::Vector<mojom::blink::RelatedApplicationPtr> result) {
-  WTF::Vector<blink::WebRelatedApplication> applications;
+    std::unique_ptr<AppInstalledCallbacks> callbacks,
+    Vector<mojom::blink::RelatedApplicationPtr> result) {
+  HeapVector<Member<RelatedApplication>> applications;
   for (const auto& res : result) {
-    blink::WebRelatedApplication app;
-    app.platform = res->platform;
-    app.url = res->url;
-    app.id = res->id;
+    auto* app = MakeGarbageCollected<RelatedApplication>(res->platform,
+                                                         res->url, res->id);
     applications.push_back(app);
   }
-  callbacks->OnSuccess(
-      blink::WebVector<blink::WebRelatedApplication>(applications));
+  callbacks->OnSuccess(applications);
 }
 
-void InstalledAppController::Trace(blink::Visitor* visitor) {
+void InstalledAppController::Trace(Visitor* visitor) {
   Supplement<LocalFrame>::Trace(visitor);
   ContextLifecycleObserver::Trace(visitor);
 }
diff --git a/third_party/blink/renderer/modules/installedapp/installed_app_controller.h b/third_party/blink/renderer/modules/installedapp/installed_app_controller.h
index c5c8516..3e3a5c0 100644
--- a/third_party/blink/renderer/modules/installedapp/installed_app_controller.h
+++ b/third_party/blink/renderer/modules/installedapp/installed_app_controller.h
@@ -11,24 +11,23 @@
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/public/mojom/installedapp/installed_app_provider.mojom-blink.h"
 #include "third_party/blink/public/mojom/installedapp/related_application.mojom-blink.h"
-#include "third_party/blink/public/platform/modules/installedapp/web_related_application.h"
-#include "third_party/blink/public/platform/web_callbacks.h"
-#include "third_party/blink/public/platform/web_vector.h"
+#include "third_party/blink/renderer/bindings/core/v8/callback_promise_adapter.h"
 #include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/modules/installedapp/related_application.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/supplementable.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
 class WebURL;
-template <typename T>
-class WebVector;
 struct Manifest;
 
 using AppInstalledCallbacks =
-    WebCallbacks<const WebVector<WebRelatedApplication>&, void>;
+    CallbackPromiseAdapter<HeapVector<Member<RelatedApplication>>, void>;
 
 class MODULES_EXPORT InstalledAppController final
     : public GarbageCollectedFinalized<InstalledAppController>,
@@ -49,7 +48,7 @@
   static void ProvideTo(LocalFrame&);
   static InstalledAppController* From(LocalFrame&);
 
-  void Trace(blink::Visitor*) override;
+  void Trace(Visitor*) override;
 
  private:
   // Callback for the result of GetInstalledRelatedApps.
@@ -66,8 +65,8 @@
   void ContextDestroyed(ExecutionContext*) override;
 
   // Callback from the InstalledAppProvider mojo service.
-  void OnFilterInstalledApps(std::unique_ptr<blink::AppInstalledCallbacks>,
-                             WTF::Vector<mojom::blink::RelatedApplicationPtr>);
+  void OnFilterInstalledApps(std::unique_ptr<AppInstalledCallbacks>,
+                             Vector<mojom::blink::RelatedApplicationPtr>);
 
   // Handle to the InstalledApp mojo service.
   mojom::blink::InstalledAppProviderPtr provider_;
diff --git a/third_party/blink/renderer/modules/installedapp/navigator_installed_app.cc b/third_party/blink/renderer/modules/installedapp/navigator_installed_app.cc
index 17b5465..e8a382a 100644
--- a/third_party/blink/renderer/modules/installedapp/navigator_installed_app.cc
+++ b/third_party/blink/renderer/modules/installedapp/navigator_installed_app.cc
@@ -6,7 +6,6 @@
 
 #include <memory>
 
-#include "third_party/blink/public/platform/modules/installedapp/web_related_application.h"
 #include "third_party/blink/renderer/bindings/core/v8/callback_promise_adapter.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
@@ -51,23 +50,6 @@
       script_state);
 }
 
-class RelatedAppArray {
-  STATIC_ONLY(RelatedAppArray);
-
- public:
-  using WebType = const WebVector<WebRelatedApplication>&;
-
-  static HeapVector<Member<RelatedApplication>> Take(
-      ScriptPromiseResolver*,
-      const WebVector<WebRelatedApplication>& web_info) {
-    HeapVector<Member<RelatedApplication>> applications;
-    for (const auto& web_application : web_info)
-      applications.push_back(MakeGarbageCollected<RelatedApplication>(
-          web_application.platform, web_application.url, web_application.id));
-    return applications;
-  }
-};
-
 ScriptPromise NavigatorInstalledApp::getInstalledRelatedApps(
     ScriptState* script_state) {
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
@@ -92,7 +74,8 @@
   }
 
   app_controller->GetInstalledRelatedApps(
-      std::make_unique<CallbackPromiseAdapter<RelatedAppArray, void>>(
+      std::make_unique<
+          CallbackPromiseAdapter<HeapVector<Member<RelatedApplication>>, void>>(
           resolver));
   return promise;
 }
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc
index 96659965..faadf9c 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_capturer_source_test.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/run_loop.h"
 #include "media/base/bind_to_current_loop.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -99,7 +98,7 @@
     *capture_time_ = capture_time;
     metadata_->Clear();
     metadata_->MergeMetadataFrom(frame->metadata());
-    base::ResetAndReturn(&got_frame_cb_).Run();
+    std::move(got_frame_cb_).Run();
   }
 
  private:
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_track.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_track.cc
index b6b0f29..a62302663 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_track.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_track.cc
@@ -215,7 +215,7 @@
   // Wrap |black_frame_| so we get a fresh timestamp we can modify. Frames
   // returned from this function may still be in use.
   scoped_refptr<media::VideoFrame> wrapped_black_frame =
-      media::VideoFrame::WrapVideoFrame(black_frame_, black_frame_->format(),
+      media::VideoFrame::WrapVideoFrame(*black_frame_, black_frame_->format(),
                                         black_frame_->visible_rect(),
                                         black_frame_->natural_size());
   if (!wrapped_black_frame)
diff --git a/third_party/blink/renderer/modules/mediastream/video_track_adapter.cc b/third_party/blink/renderer/modules/mediastream/video_track_adapter.cc
index a45f52f..5284e636 100644
--- a/third_party/blink/renderer/modules/mediastream/video_track_adapter.cc
+++ b/third_party/blink/renderer/modules/mediastream/video_track_adapter.cc
@@ -324,7 +324,7 @@
         media::ComputeLetterboxRegion(frame->visible_rect(), desired_size);
 
     video_frame = media::VideoFrame::WrapVideoFrame(
-        frame, frame->format(), region_in_frame, desired_size);
+        *frame, frame->format(), region_in_frame, desired_size);
     if (!video_frame) {
       PostFrameDroppedToMainTaskRunner(
           media::VideoCaptureFrameDropReason::
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index bf16c78a..1fbe31c 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -326,6 +326,8 @@
           "speech/speech_synthesis_event.idl",
           "speech/speech_synthesis_utterance.idl",
           "speech/speech_synthesis_voice.idl",
+          "sms/sms.idl",
+          "sms/sms_receiver.idl",
           "storage/storage.idl",
           "storage/storage_event.idl",
           "vr/vr_display.idl",
@@ -745,6 +747,7 @@
           "shapedetection/barcode_detector_options.idl",
           "shapedetection/face_detector_options.idl",
           "shapedetection/landmark.idl",
+          "sms/sms_receiver_options.idl",
           "speech/speech_recognition_error_init.idl",
           "speech/speech_recognition_event_init.idl",
           "speech/speech_synthesis_error_event_init.idl",
diff --git a/third_party/blink/renderer/modules/sms/BUILD.gn b/third_party/blink/renderer/modules/sms/BUILD.gn
new file mode 100644
index 0000000..ab4552d
--- /dev/null
+++ b/third_party/blink/renderer/modules/sms/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/blink/renderer/modules/modules.gni")
+
+blink_modules_sources("sms") {
+  sources = [
+    "sms.cc",
+    "sms.h",
+    "sms_receiver.cc",
+    "sms_receiver.h",
+  ]
+}
diff --git a/third_party/blink/renderer/modules/sms/DEPS b/third_party/blink/renderer/modules/sms/DEPS
new file mode 100644
index 0000000..2ee6cb9
--- /dev/null
+++ b/third_party/blink/renderer/modules/sms/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+    "+mojo/public/cpp/bindings/binding.h",
+    "-third_party/blink/renderer/modules",
+    "+third_party/blink/renderer/modules/event_modules.h",
+    "+third_party/blink/renderer/modules/event_target_modules.h",
+    "+third_party/blink/renderer/modules/modules_export.h",
+    "+third_party/blink/renderer/modules/sms",
+]
diff --git a/third_party/blink/renderer/modules/sms/OWNERS b/third_party/blink/renderer/modules/sms/OWNERS
new file mode 100644
index 0000000..ad9d6f7
--- /dev/null
+++ b/third_party/blink/renderer/modules/sms/OWNERS
@@ -0,0 +1 @@
+file://content/browser/sms/OWNERS
diff --git a/third_party/blink/renderer/modules/sms/sms.cc b/third_party/blink/renderer/modules/sms/sms.cc
new file mode 100644
index 0000000..9bdcc1a
--- /dev/null
+++ b/third_party/blink/renderer/modules/sms/sms.cc
@@ -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.
+
+#include "third_party/blink/renderer/modules/sms/sms.h"
+
+#include <utility>
+
+namespace blink {
+
+SMS::SMS(const WTF::String& content) : content_(content) {}
+
+SMS::~SMS() = default;
+
+const String& SMS::content() const {
+  return content_;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/sms/sms.h b/third_party/blink/renderer/modules/sms/sms.h
new file mode 100644
index 0000000..0ae9730b
--- /dev/null
+++ b/third_party/blink/renderer/modules/sms/sms.h
@@ -0,0 +1,35 @@
+// 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_SMS_SMS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SMS_SMS_H_
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "third_party/blink/public/mojom/sms/sms_manager.mojom-blink.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+
+namespace blink {
+
+class SMS final : public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  // |content| is the raw content of the SMS message.
+  explicit SMS(const WTF::String& content);
+
+  ~SMS() override;
+
+  // Sms IDL interface.
+  const String& content() const;
+
+ private:
+  const String content_;
+
+  DISALLOW_COPY_AND_ASSIGN(SMS);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SMS_SMS_H_
diff --git a/third_party/blink/renderer/modules/sms/sms.idl b/third_party/blink/renderer/modules/sms/sms.idl
new file mode 100644
index 0000000..0fa301f
--- /dev/null
+++ b/third_party/blink/renderer/modules/sms/sms.idl
@@ -0,0 +1,13 @@
+// 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.
+
+// https://github.com/samuelgoto/sms-receiver
+
+[
+  SecureContext,
+  Exposed=(Window,DedicatedWorker),
+  RuntimeEnabled=SmsRetrieval]
+interface SMS {
+  readonly attribute DOMString content;
+};
diff --git a/third_party/blink/renderer/modules/sms/sms_receiver.cc b/third_party/blink/renderer/modules/sms/sms_receiver.cc
new file mode 100644
index 0000000..6383ffb
--- /dev/null
+++ b/third_party/blink/renderer/modules/sms/sms_receiver.cc
@@ -0,0 +1,108 @@
+// 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 <utility>
+
+#include "third_party/blink/renderer/modules/sms/sms_receiver.h"
+
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/mojom/sms/sms_manager.mojom-blink.h"
+#include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/modules/sms/sms.h"
+#include "third_party/blink/renderer/modules/sms/sms_receiver_options.h"
+#include "third_party/blink/renderer/platform/bindings/name_client.h"
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+namespace blink {
+namespace {
+
+const uint32_t kDefaultTimeoutSeconds = 60;
+
+}  // namespace
+
+SMSReceiver* SMSReceiver::Create(ScriptState* script_state,
+                                 const SMSReceiverOptions* options,
+                                 ExceptionState& exception_state) {
+  int32_t timeout_seconds =
+      options->hasTimeout() ? options->timeout() : kDefaultTimeoutSeconds;
+
+  if (timeout_seconds <= 0) {
+    exception_state.ThrowTypeError("Invalid timeout");
+    return nullptr;
+  }
+
+  base::TimeDelta timeout = base::TimeDelta::FromSeconds(timeout_seconds);
+
+  return MakeGarbageCollected<SMSReceiver>(ExecutionContext::From(script_state),
+                                           timeout);
+}
+
+// static
+SMSReceiver* SMSReceiver::Create(ScriptState* script_state,
+                                 ExceptionState& exception_state) {
+  return Create(script_state, SMSReceiverOptions::Create(), exception_state);
+}
+
+SMSReceiver::SMSReceiver(ExecutionContext* context, base::TimeDelta timeout)
+    : ContextClient(context), timeout_(timeout) {}
+
+SMSReceiver::~SMSReceiver() = default;
+
+const AtomicString& SMSReceiver::InterfaceName() const {
+  return event_target_names::kSMSReceiver;
+}
+
+ExecutionContext* SMSReceiver::GetExecutionContext() const {
+  return ContextClient::GetExecutionContext();
+}
+
+bool SMSReceiver::HasPendingActivity() const {
+  // This object should be considered active as long as there are registered
+  // event listeners.
+  return GetExecutionContext() && HasEventListeners();
+}
+
+ScriptPromise SMSReceiver::start(ScriptState* script_state) {
+  // Validate options.
+  ExecutionContext* context = ExecutionContext::From(script_state);
+  DCHECK(context->IsContextThread());
+
+  StartMonitoring();
+
+  return ScriptPromise::CastUndefined(script_state);
+}
+
+void SMSReceiver::StartMonitoring() {
+  // See https://bit.ly/2S0zRAS for task types.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+      GetExecutionContext()->GetTaskRunner(TaskType::kInternalIPC);
+
+  if (!service_) {
+    GetExecutionContext()->GetInterfaceProvider()->GetInterface(
+        mojo::MakeRequest(&service_, task_runner));
+  }
+
+  service_->GetNextMessage(timeout_, WTF::Bind(&SMSReceiver::OnGetNextMessage,
+                                               WrapPersistent(this)));
+}
+
+void SMSReceiver::OnGetNextMessage(const WTF::String& sms) {
+  sms_ = MakeGarbageCollected<SMS>(std::move(sms));
+  DispatchEvent(*Event::Create(event_type_names::kChange));
+}
+
+SMS* SMSReceiver::sms() const {
+  return sms_;
+}
+
+void SMSReceiver::Trace(Visitor* visitor) {
+  EventTargetWithInlineData::Trace(visitor);
+  ActiveScriptWrappable::Trace(visitor);
+  ContextClient::Trace(visitor);
+  visitor->Trace(sms_);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/sms/sms_receiver.h b/third_party/blink/renderer/modules/sms/sms_receiver.h
new file mode 100644
index 0000000..ea06bb62
--- /dev/null
+++ b/third_party/blink/renderer/modules/sms/sms_receiver.h
@@ -0,0 +1,70 @@
+// 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_SMS_SMS_RECEIVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SMS_SMS_RECEIVER_H_
+
+#include "base/macros.h"
+#include "third_party/blink/public/mojom/sms/sms_manager.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/core/dom/events/event_target.h"
+#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
+#include "third_party/blink/renderer/modules/event_modules.h"
+#include "third_party/blink/renderer/modules/event_target_modules.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+
+namespace blink {
+
+class SMS;
+class SMSReceiverOptions;
+
+class SMSReceiver final : public EventTargetWithInlineData,
+                          public ActiveScriptWrappable<SMSReceiver>,
+                          public ContextClient {
+  USING_GARBAGE_COLLECTED_MIXIN(SMSReceiver);
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static SMSReceiver* Create(ScriptState*,
+                             const SMSReceiverOptions*,
+                             ExceptionState&);
+  static SMSReceiver* Create(ScriptState*, ExceptionState&);
+
+  SMSReceiver(ExecutionContext*, base::TimeDelta threshold);
+
+  ~SMSReceiver() override;
+
+  // EventTarget implementation.
+  const AtomicString& InterfaceName() const override;
+  ExecutionContext* GetExecutionContext() const override;
+
+  // ActiveScriptWrappable implementation.
+  bool HasPendingActivity() const final;
+
+  // SMSReceiver IDL interface.
+  ScriptPromise start(ScriptState*);
+  void stop();
+  SMS* sms() const;
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(change, kChange)
+
+  void OnGetNextMessage(const WTF::String&);
+
+  void Trace(blink::Visitor*) override;
+
+ private:
+  Member<SMS> sms_;
+
+  const base::TimeDelta timeout_;
+
+  void StartMonitoring();
+
+  mojom::blink::SmsManagerPtr service_;
+
+  DISALLOW_COPY_AND_ASSIGN(SMSReceiver);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SMS_SMS_RECEIVER_H_
diff --git a/third_party/blink/renderer/modules/sms/sms_receiver.idl b/third_party/blink/renderer/modules/sms/sms_receiver.idl
new file mode 100644
index 0000000..320bc1f
--- /dev/null
+++ b/third_party/blink/renderer/modules/sms/sms_receiver.idl
@@ -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.
+
+// https://github.com/samuelgoto/sms-receiver
+
+[
+  SecureContext,
+  Exposed=(Window,DedicatedWorker),
+  ActiveScriptWrappable,
+  Constructor(optional SMSReceiverOptions options),
+  ConstructorCallWith=ScriptState,
+  RaisesException=Constructor,
+  RuntimeEnabled=SmsRetrieval
+] interface SMSReceiver : EventTarget {
+  readonly attribute SMS sms;
+  attribute EventHandler onchange;
+  [CallWith=ScriptState] Promise<void> start();
+};
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/sms/sms_receiver_options.idl b/third_party/blink/renderer/modules/sms/sms_receiver_options.idl
new file mode 100644
index 0000000..6b61a5f
--- /dev/null
+++ b/third_party/blink/renderer/modules/sms/sms_receiver_options.idl
@@ -0,0 +1,9 @@
+// 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.
+
+// https://github.com/samuelgoto/sms-receiver
+
+dictionary SMSReceiverOptions {
+  unsigned long timeout;
+};
diff --git a/third_party/blink/renderer/modules/webshare/navigator_share.cc b/third_party/blink/renderer/modules/webshare/navigator_share.cc
index 1eef69fd..ad2db6f 100644
--- a/third_party/blink/renderer/modules/webshare/navigator_share.cc
+++ b/third_party/blink/renderer/modules/webshare/navigator_share.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/core/fileapi/file.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/navigator.h"
+#include "third_party/blink/renderer/core/frame/use_counter.h"
 #include "third_party/blink/renderer/modules/webshare/share_data.h"
 #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
 #include "third_party/blink/renderer/platform/mojo/mojo_helper.h"
@@ -107,6 +108,10 @@
   if (error == mojom::blink::ShareError::OK) {
     resolver_->Resolve();
   } else {
+    if (error == mojom::blink::ShareError::CANCELED) {
+      UseCounter::Count(ExecutionContext::From(resolver_->GetScriptState()),
+                        WebFeature::kWebShareCancelled);
+    }
     resolver_->Reject(DOMException::Create(
         (error == mojom::blink::ShareError::PERMISSION_DENIED)
             ? DOMExceptionCode::kNotAllowedError
@@ -199,7 +204,7 @@
 
   WTF::Vector<mojom::blink::SharedFilePtr> files;
   uint64_t total_bytes = 0;
-  if (share_data->hasFiles()) {
+  if (HasFiles(*share_data)) {
     files.ReserveInitialCapacity(share_data->files().size());
     for (const blink::Member<blink::File>& file : share_data->files()) {
       total_bytes += file->size();
@@ -213,6 +218,9 @@
           DOMExceptionCode::kNotAllowedError, "Permission denied");
       return ScriptPromise::RejectWithDOMException(script_state, error);
     }
+    UseCounter::Count(*doc, WebFeature::kWebShareContainingFiles);
+  } else {
+    UseCounter::Count(*doc, WebFeature::kWebShareWithoutFiles);
   }
 
   service_->Share(
diff --git a/third_party/blink/renderer/modules/webshare/navigator_share_test.cc b/third_party/blink/renderer/modules/webshare/navigator_share_test.cc
index 82c4466..678b8ec 100644
--- a/third_party/blink/renderer/modules/webshare/navigator_share_test.cc
+++ b/third_party/blink/renderer/modules/webshare/navigator_share_test.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/use_counter.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/modules/webshare/share_data.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
@@ -28,17 +29,20 @@
 // A mock ShareService used to intercept calls to the mojo methods.
 class MockShareService : public ShareService {
  public:
-  MockShareService() : binding_(this) {}
+  MockShareService() : binding_(this), error_(mojom::ShareError::OK) {}
   ~MockShareService() override = default;
 
   void Bind(mojo::ScopedMessagePipeHandle handle) {
     binding_.Bind(mojom::blink::ShareServiceRequest(std::move(handle)));
   }
 
+  void set_error(mojom::ShareError value) { error_ = value; }
+
   const WTF::String& title() const { return title_; }
   const WTF::String& text() const { return text_; }
   const KURL& url() const { return url_; }
   const WTF::Vector<SharedFilePtr>& files() const { return files_; }
+  mojom::ShareError error() const { return error_; }
 
  private:
   void Share(const WTF::String& title,
@@ -56,7 +60,7 @@
       files_.push_back(entry->Clone());
     }
 
-    std::move(callback).Run(mojom::ShareError::OK);
+    std::move(callback).Run(error_);
   }
 
   mojo::Binding<ShareService> binding_;
@@ -64,6 +68,7 @@
   WTF::String text_;
   KURL url_;
   WTF::Vector<SharedFilePtr> files_;
+  mojom::ShareError error_;
 };
 
 class NavigatorShareTest : public testing::Test {
@@ -90,13 +95,13 @@
     ScriptPromise promise =
         NavigatorShare::share(GetScriptState(), *navigator, &share_data);
     test::RunPendingTasks();
-    EXPECT_EQ(v8::Promise::kFulfilled,
+    EXPECT_EQ(mock_share_service_.error() == mojom::ShareError::OK
+                  ? v8::Promise::kFulfilled
+                  : v8::Promise::kRejected,
               promise.V8Value().As<v8::Promise>()->State());
   }
 
-  const MockShareService& mock_share_service() const {
-    return mock_share_service_;
-  }
+  MockShareService& mock_share_service() { return mock_share_service_; }
 
  protected:
   void SetUp() override {
@@ -135,6 +140,8 @@
   EXPECT_EQ(mock_share_service().text(), message);
   EXPECT_EQ(mock_share_service().url(), KURL(url));
   EXPECT_EQ(mock_share_service().files().size(), 0U);
+  EXPECT_TRUE(
+      UseCounter::IsCounted(GetDocument(), WebFeature::kWebShareWithoutFiles));
 }
 
 TEST_F(NavigatorShareTest, ShareFile) {
@@ -162,6 +169,19 @@
   EXPECT_EQ(mock_share_service().files()[0]->blob->GetType(), content_type);
   EXPECT_EQ(mock_share_service().files()[0]->blob->size(),
             file_contents.length());
+  EXPECT_TRUE(UseCounter::IsCounted(GetDocument(),
+                                    WebFeature::kWebShareContainingFiles));
+}
+
+TEST_F(NavigatorShareTest, CancelShare) {
+  const String title = "Subject";
+  ShareData share_data;
+  share_data.setTitle(title);
+
+  mock_share_service().set_error(mojom::blink::ShareError::CANCELED);
+  Share(share_data);
+  EXPECT_TRUE(
+      UseCounter::IsCounted(GetDocument(), WebFeature::kWebShareCancelled));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr.cc b/third_party/blink/renderer/modules/xr/xr.cc
index aada564d..e764e865 100644
--- a/third_party/blink/renderer/modules/xr/xr.cc
+++ b/third_party/blink/renderer/modules/xr/xr.cc
@@ -74,22 +74,6 @@
   return XRSession::kModeInline;
 }
 
-String sessionModeToString(XRSession::SessionMode mode) {
-  switch (mode) {
-    case XRSession::kModeInline:
-      return "inline";
-    case XRSession::kModeInlineAR:
-      return "legacy-inline-ar";
-    case XRSession::kModeImmersiveVR:
-      return "immersive-vr";
-    case XRSession::kModeImmersiveAR:
-      return "immersive-ar";
-  }
-
-  NOTREACHED();
-  return "";
-}
-
 }  // namespace
 
 XR::PendingSessionQuery::PendingSessionQuery(
@@ -513,7 +497,7 @@
     bool sensorless_session) {
   XRSession* session = MakeGarbageCollected<XRSession>(
       this, client_request ? std::move(client_request) : nullptr, mode,
-      sessionModeToString(mode), blend_mode, sensorless_session);
+      blend_mode, sensorless_session);
   if (display_info)
     session->SetXRDisplayInfo(std::move(display_info));
   sessions_.insert(session);
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index b677287..1bcbbc25 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -111,12 +111,10 @@
     XR* xr,
     device::mojom::blink::XRSessionClientRequest client_request,
     XRSession::SessionMode mode,
-    const String& mode_string,
     EnvironmentBlendMode environment_blend_mode,
     bool sensorless_session)
     : xr_(xr),
       mode_(mode),
-      mode_string_(mode_string),
       environment_integration_(mode == kModeInlineAR ||
                                mode == kModeImmersiveAR),
       client_binding_(this, std::move(client_request)),
diff --git a/third_party/blink/renderer/modules/xr/xr_session.h b/third_party/blink/renderer/modules/xr/xr_session.h
index 88e0f9e0..7c1c559d 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.h
+++ b/third_party/blink/renderer/modules/xr/xr_session.h
@@ -62,18 +62,14 @@
     kBlendModeAlphaBlend
   };
 
-  // TODO(ddorwin): If https://github.com/immersive-web/webxr/issues/513 is
-  // resolved in favor of removing `mode`, remove |mode_string|.
   XRSession(XR*,
             device::mojom::blink::XRSessionClientRequest client_request,
             SessionMode mode,
-            const String& mode_string,
             EnvironmentBlendMode environment_blend_mode,
             bool sensorless_session);
   ~XRSession() override = default;
 
   XR* xr() const { return xr_; }
-  const String& mode() const { return mode_string_; }
   bool environmentIntegration() const { return environment_integration_; }
   const String& environmentBlendMode() const { return blend_mode_string_; }
   XRRenderState* renderState() const { return render_state_; }
@@ -218,7 +214,6 @@
 
   const Member<XR> xr_;
   const SessionMode mode_;
-  const String mode_string_;
   const bool environment_integration_;
   String blend_mode_string_;
   Member<XRRenderState> render_state_;
diff --git a/third_party/blink/renderer/modules/xr/xr_session.idl b/third_party/blink/renderer/modules/xr/xr_session.idl
index dc170961..f5e8c74 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.idl
+++ b/third_party/blink/renderer/modules/xr/xr_session.idl
@@ -22,7 +22,6 @@
     Exposed=Window,
     RuntimeEnabled=WebXR
 ] interface XRSession : EventTarget {
-  readonly attribute XRSessionMode mode;
   readonly attribute XREnvironmentBlendMode environmentBlendMode;
   readonly attribute XRRenderState renderState;
   readonly attribute XRSpace viewerSpace;
diff --git a/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.cc b/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.cc
index 4b5b6942..1ee926e3 100644
--- a/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.cc
+++ b/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.cc
@@ -15,22 +15,7 @@
 
 XRStationaryReferenceSpace::XRStationaryReferenceSpace(XRSession* session,
                                                        Subtype subtype)
-    : XRReferenceSpace(session), subtype_(subtype) {
-  switch (subtype_) {
-    case kSubtypeEyeLevel:
-      subtype_string_ = "eye-level";
-      break;
-    case kSubtypeFloorLevel:
-      subtype_string_ = "floor-level";
-      UpdateFloorLevelTransform();
-      break;
-    case kSubtypePositionDisabled:
-      subtype_string_ = "position-disabled";
-      break;
-    default:
-      NOTREACHED() << "Unknown stationary reference space subtype: " << subtype;
-  }
-}
+    : XRReferenceSpace(session), subtype_(subtype) {}
 
 XRStationaryReferenceSpace::~XRStationaryReferenceSpace() = default;
 
diff --git a/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.h b/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.h
index e57f3284..e4cce49 100644
--- a/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.h
+++ b/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.h
@@ -30,8 +30,6 @@
       const TransformationMatrix& base_input_pose,
       const TransformationMatrix& base_pose) override;
 
-  const String& subtype() const { return subtype_string_; }
-
   void Trace(blink::Visitor*) override;
 
   void OnReset() override;
@@ -41,7 +39,6 @@
 
   unsigned int display_info_id_ = 0;
   Subtype subtype_;
-  String subtype_string_;
   std::unique_ptr<TransformationMatrix> floor_level_transform_;
 };
 
diff --git a/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.idl b/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.idl
index 544e865..c6c192e 100644
--- a/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.idl
+++ b/third_party/blink/renderer/modules/xr/xr_stationary_reference_space.idl
@@ -15,5 +15,4 @@
     Exposed=Window,
     RuntimeEnabled=WebXR
 ] interface XRStationaryReferenceSpace : XRReferenceSpace {
-  readonly attribute XRStationaryReferenceSpaceSubtype subtype;
 };
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer.h b/third_party/blink/renderer/modules/xr/xr_webgl_layer.h
index 49d131a..b43b2ae 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_layer.h
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer.h
@@ -55,9 +55,6 @@
   }
 
   bool antialias() const { return drawing_buffer_->antialias(); }
-  bool depth() const { return drawing_buffer_->depth(); }
-  bool stencil() const { return drawing_buffer_->stencil(); }
-  bool alpha() const { return drawing_buffer_->alpha(); }
   bool ignoreDepthValues() const { return ignore_depth_values_; }
 
   XRViewport* getViewport(XRView*);
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer.idl b/third_party/blink/renderer/modules/xr/xr_webgl_layer.idl
index 9174a5d..12e69650 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_layer.idl
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer.idl
@@ -14,9 +14,6 @@
 ] interface XRWebGLLayer : XRLayer {
   [ImplementedAs=getXRWebGLRenderingContext] readonly attribute XRWebGLRenderingContext context;
   readonly attribute boolean antialias;
-  readonly attribute boolean depth;
-  readonly attribute boolean stencil;
-  readonly attribute boolean alpha;
   readonly attribute boolean ignoreDepthValues;
 
   readonly attribute unsigned long framebufferWidth;
diff --git a/third_party/blink/renderer/platform/exported/mediastream/web_platform_media_stream_source.cc b/third_party/blink/renderer/platform/exported/mediastream/web_platform_media_stream_source.cc
index 9d2aa7a1..1bc617a 100644
--- a/third_party/blink/renderer/platform/exported/mediastream/web_platform_media_stream_source.cc
+++ b/third_party/blink/renderer/platform/exported/mediastream/web_platform_media_stream_source.cc
@@ -4,9 +4,10 @@
 
 #include "third_party/blink/public/platform/modules/mediastream/web_platform_media_stream_source.h"
 
-#include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
+#include <utility>
 
 #include "base/logging.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
 
 namespace blink {
 
@@ -31,7 +32,7 @@
 
 void WebPlatformMediaStreamSource::FinalizeStopSource() {
   if (!stop_callback_.is_null())
-    base::ResetAndReturn(&stop_callback_).Run(Owner());
+    std::move(stop_callback_).Run(Owner());
   if (Owner())
     Owner().SetReadyState(WebMediaStreamSource::kReadyStateEnded);
 }
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 04f2002..53a05210 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1355,6 +1355,10 @@
       status: "stable",
     },
     {
+      name: "SmsRetrieval",
+      status: "experimental",
+    },
+    {
       name: "SpeechSynthesisEventCharLength",
       status: "experimental",
     },
diff --git a/third_party/blink/renderer/platform/scheduler/BUILD.gn b/third_party/blink/renderer/platform/scheduler/BUILD.gn
index 31b463f..3d0dd533a 100644
--- a/third_party/blink/renderer/platform/scheduler/BUILD.gn
+++ b/third_party/blink/renderer/platform/scheduler/BUILD.gn
@@ -12,6 +12,7 @@
     "common/cancelable_closure_holder.cc",
     "common/cancelable_closure_holder.h",
     "common/cooperative_scheduling_manager.cc",
+    "common/dummy_schedulers.cc",
     "common/event_loop.cc",
     "common/features.cc",
     "common/features.h",
@@ -106,6 +107,7 @@
     "main_thread/web_scoped_virtual_time_pauser.cc",
     "public/aggregated_metric_reporter.h",
     "public/cooperative_scheduling_manager.h",
+    "public/dummy_schedulers.h",
     "public/event_loop.h",
     "public/frame_or_worker_scheduler.h",
     "public/frame_scheduler.h",
diff --git a/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc b/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc
new file mode 100644
index 0000000..2fe1a786
--- /dev/null
+++ b/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc
@@ -0,0 +1,262 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/public/dummy_schedulers.h"
+
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+
+class DummyFrameScheduler : public FrameScheduler {
+ public:
+  explicit DummyFrameScheduler(PageScheduler* page_scheduler)
+      : page_scheduler_(page_scheduler) {}
+  ~DummyFrameScheduler() override {}
+
+  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) override {
+    DCHECK(WTF::IsMainThread());
+    return base::ThreadTaskRunnerHandle::Get();
+  }
+
+  PageScheduler* GetPageScheduler() const override { return page_scheduler_; }
+
+  void SetFrameVisible(bool) override {}
+  bool IsFrameVisible() const override { return true; }
+  bool IsPageVisible() const override { return true; }
+  void SetPaused(bool) override {}
+  void SetCrossOrigin(bool) override {}
+  bool IsCrossOrigin() const override { return false; }
+  void SetIsAdFrame() override {}
+  bool IsAdFrame() const override { return false; }
+  void TraceUrlChange(const String&) override {}
+  FrameType GetFrameType() const override { return FrameType::kMainFrame; }
+  WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
+      const String& name,
+      WebScopedVirtualTimePauser::VirtualTaskDuration) override {
+    return WebScopedVirtualTimePauser();
+  }
+  void DidStartProvisionalLoad(bool is_main_frame) override {}
+  void DidCommitProvisionalLoad(bool, FrameScheduler::NavigationType) override {
+  }
+  void OnFirstMeaningfulPaint() override {}
+  bool IsExemptFromBudgetBasedThrottling() const override { return false; }
+  std::unique_ptr<blink::mojom::blink::PauseSubresourceLoadingHandle>
+  GetPauseSubresourceLoadingHandle() override {
+    return nullptr;
+  }
+  std::unique_ptr<WebResourceLoadingTaskRunnerHandle>
+  CreateResourceLoadingTaskRunnerHandle() override {
+    return WebResourceLoadingTaskRunnerHandle::CreateUnprioritized(
+        base::ThreadTaskRunnerHandle::Get());
+  }
+  ukm::SourceId GetUkmSourceId() override { return ukm::kInvalidSourceId; }
+  void OnStartedUsingFeature(SchedulingPolicy::Feature feature,
+                             const SchedulingPolicy& policy) override {}
+  void OnStoppedUsingFeature(SchedulingPolicy::Feature feature,
+                             const SchedulingPolicy& policy) override {}
+  WTF::HashSet<SchedulingPolicy::Feature>
+  GetActiveFeaturesTrackedForBackForwardCacheMetrics() override {
+    return WTF::HashSet<SchedulingPolicy::Feature>();
+  }
+  base::WeakPtr<FrameScheduler> GetWeakPtr() override { return nullptr; }
+
+ private:
+  PageScheduler* page_scheduler_;
+
+  DISALLOW_COPY_AND_ASSIGN(DummyFrameScheduler);
+};
+
+class DummyPageScheduler : public PageScheduler {
+ public:
+  DummyPageScheduler() {}
+  ~DummyPageScheduler() override {}
+
+  std::unique_ptr<FrameScheduler> CreateFrameScheduler(
+      FrameScheduler::Delegate* delegate,
+      BlameContext*,
+      FrameScheduler::FrameType) override {
+    return std::make_unique<DummyFrameScheduler>(this);
+  }
+
+  void SetPageVisible(bool) override {}
+  void SetPageFrozen(bool) override {}
+  void SetKeepActive(bool) override {}
+  bool IsMainFrameLocal() const override { return true; }
+  void SetIsMainFrameLocal(bool) override {}
+  void OnLocalMainFrameNetworkAlmostIdle() override {}
+  base::TimeTicks EnableVirtualTime() override { return base::TimeTicks(); }
+  void DisableVirtualTimeForTesting() override {}
+  bool VirtualTimeAllowedToAdvance() const override { return true; }
+  void SetInitialVirtualTime(base::Time) override {}
+  void SetInitialVirtualTimeOffset(base::TimeDelta) override {}
+  void SetVirtualTimePolicy(VirtualTimePolicy) override {}
+  void GrantVirtualTimeBudget(base::TimeDelta, base::OnceClosure) override {}
+  void SetMaxVirtualTimeTaskStarvationCount(int) override {}
+  void AudioStateChanged(bool is_audio_playing) override {}
+  bool IsAudioPlaying() const override { return false; }
+  bool IsExemptFromBudgetBasedThrottling() const override { return false; }
+  bool OptedOutFromAggressiveThrottlingForTest() const override {
+    return false;
+  }
+  bool RequestBeginMainFrameNotExpected(bool) override { return false; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DummyPageScheduler);
+};
+
+// TODO(altimin,yutak): Merge with SimpleThread in platform.cc.
+class SimpleThread : public Thread {
+ public:
+  explicit SimpleThread(ThreadScheduler* scheduler) : scheduler_(scheduler) {}
+  ~SimpleThread() override {}
+
+  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() const override {
+    return base::ThreadTaskRunnerHandle::Get();
+  }
+
+  ThreadScheduler* Scheduler() override { return scheduler_; }
+
+  bool IsCurrentThread() const { return WTF::IsMainThread(); }
+
+ private:
+  ThreadScheduler* scheduler_;
+
+  DISALLOW_COPY_AND_ASSIGN(SimpleThread);
+};
+
+class DummyThreadScheduler : public ThreadScheduler {
+ public:
+  DummyThreadScheduler() {}
+  ~DummyThreadScheduler() override {}
+
+  void Shutdown() override {}
+
+  scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() override {
+    return base::ThreadTaskRunnerHandle::Get();
+  }
+
+  scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner()
+      override {
+    return base::ThreadTaskRunnerHandle::Get();
+  }
+
+  scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override {
+    return base::ThreadTaskRunnerHandle::Get();
+  }
+
+  scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override {
+    return base::ThreadTaskRunnerHandle::Get();
+  }
+
+  std::unique_ptr<PageScheduler> CreatePageScheduler(
+      PageScheduler::Delegate*) override {
+    return std::make_unique<DummyPageScheduler>();
+  }
+
+  // ThreadScheduler implementation:
+  bool ShouldYieldForHighPriorityWork() override { return false; }
+  bool CanExceedIdleDeadlineIfRequired() const override { return false; }
+  void PostIdleTask(const base::Location&, Thread::IdleTask) override {}
+  void PostNonNestableIdleTask(const base::Location&,
+                               Thread::IdleTask) override {}
+  void AddRAILModeObserver(RAILModeObserver*) override {}
+  void RemoveRAILModeObserver(RAILModeObserver const*) override {}
+  std::unique_ptr<WebThreadScheduler::RendererPauseHandle> PauseScheduler()
+      override {
+    return nullptr;
+  }
+  base::TimeTicks MonotonicallyIncreasingVirtualTime() override {
+    return base::TimeTicks::Now();
+  }
+  void AddTaskObserver(base::MessageLoop::TaskObserver*) override {}
+  void RemoveTaskObserver(base::MessageLoop::TaskObserver*) override {}
+  NonMainThreadSchedulerImpl* AsNonMainThreadScheduler() override {
+    return nullptr;
+  }
+  void SetV8Isolate(v8::Isolate* isolate) override {}
+};
+
+class DummyWebThreadScheduler : public WebThreadScheduler,
+                                public DummyThreadScheduler {
+ public:
+  DummyWebThreadScheduler() {}
+  ~DummyWebThreadScheduler() override {}
+
+  // WebThreadScheduler implementation:
+  void Shutdown() override {}
+
+  scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override {
+    DCHECK(WTF::IsMainThread());
+    return base::ThreadTaskRunnerHandle::Get();
+  }
+
+  scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner()
+      override {
+    DCHECK(WTF::IsMainThread());
+    return base::ThreadTaskRunnerHandle::Get();
+  }
+
+  scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override {
+    DCHECK(WTF::IsMainThread());
+    return base::ThreadTaskRunnerHandle::Get();
+  }
+
+  scoped_refptr<base::SingleThreadTaskRunner> InputTaskRunner() override {
+    DCHECK(WTF::IsMainThread());
+    return base::ThreadTaskRunnerHandle::Get();
+  }
+
+  scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override {
+    DCHECK(WTF::IsMainThread());
+    return base::ThreadTaskRunnerHandle::Get();
+  }
+
+  scoped_refptr<base::SingleThreadTaskRunner> CleanupTaskRunner() override {
+    DCHECK(WTF::IsMainThread());
+    return base::ThreadTaskRunnerHandle::Get();
+  }
+
+  scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() override {
+    DCHECK(WTF::IsMainThread());
+    return base::ThreadTaskRunnerHandle::Get();
+  }
+
+  std::unique_ptr<Thread> CreateMainThread() override {
+    return std::make_unique<SimpleThread>(this);
+  }
+
+  std::unique_ptr<PageScheduler> CreatePageScheduler(
+      PageScheduler::Delegate*) override {
+    return std::make_unique<DummyPageScheduler>();
+  }
+};
+
+}  // namespace
+
+std::unique_ptr<FrameScheduler> CreateDummyFrameScheduler() {
+  return std::make_unique<DummyFrameScheduler>(nullptr);
+}
+
+std::unique_ptr<PageScheduler> CreateDummyPageScheduler() {
+  return std::make_unique<DummyPageScheduler>();
+}
+
+std::unique_ptr<ThreadScheduler> CreateDummyThreadScheduler() {
+  return std::make_unique<DummyThreadScheduler>();
+}
+
+std::unique_ptr<WebThreadScheduler> CreateDummyWebThreadScheduler() {
+  return std::make_unique<DummyWebThreadScheduler>();
+}
+
+}  // namespace scheduler
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/public/dummy_schedulers.h b/third_party/blink/renderer/platform/scheduler/public/dummy_schedulers.h
new file mode 100644
index 0000000..83a775a
--- /dev/null
+++ b/third_party/blink/renderer/platform/scheduler/public/dummy_schedulers.h
@@ -0,0 +1,43 @@
+// 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_PLATFORM_SCHEDULER_PUBLIC_DUMMY_SCHEDULERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_DUMMY_SCHEDULERS_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+class FrameScheduler;
+class PageScheduler;
+class ThreadScheduler;
+
+namespace scheduler {
+class WebThreadScheduler;
+
+// These methods create a simple implementations of the core scheduler classes.
+// They are useful in the situation when you want to return a non-null scheduler
+// (to ensure that your callers don't have to check for this) but don't have one
+// available.
+//
+// The actual implementation is no-op, with two exceptions:
+// - It returns a valid task runner (default one).
+// - Creating new schedulers (e.g. DummyPageScheduler's CreateFrameScheduler
+//   method returns a DummyFrameScheduler.
+//
+// Overall, their usage is discouraged except in the following two cases:
+// - Detached frames (should be fixed with frame:document lifetime refactoring).
+// - Tests
+
+PLATFORM_EXPORT std::unique_ptr<FrameScheduler> CreateDummyFrameScheduler();
+PLATFORM_EXPORT std::unique_ptr<PageScheduler> CreateDummyPageScheduler();
+PLATFORM_EXPORT std::unique_ptr<ThreadScheduler> CreateDummyThreadScheduler();
+PLATFORM_EXPORT std::unique_ptr<WebThreadScheduler>
+CreateDummyWebThreadScheduler();
+
+}  // namespace scheduler
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_DUMMY_SCHEDULERS_H_
diff --git a/third_party/blink/renderer/platform/scheduler/test/renderer_scheduler_test_support.cc b/third_party/blink/renderer/platform/scheduler/test/renderer_scheduler_test_support.cc
index 9eb78c6..9e8c92f 100644
--- a/third_party/blink/renderer/platform/scheduler/test/renderer_scheduler_test_support.cc
+++ b/third_party/blink/renderer/platform/scheduler/test/renderer_scheduler_test_support.cc
@@ -12,8 +12,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "third_party/blink/public/platform/scheduler/test/web_mock_thread_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
-#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
-#include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/dummy_schedulers.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/wtf/wtf.h"
@@ -23,233 +22,10 @@
 
 namespace {
 
-// TODO(altimin,yutak): Merge with SimpleThread in platform.cc.
-class SimpleThread : public Thread {
- public:
-  explicit SimpleThread(ThreadScheduler* scheduler) : scheduler_(scheduler) {}
-  ~SimpleThread() override {}
-
-  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() const override {
-    return base::ThreadTaskRunnerHandle::Get();
-  }
-
-  ThreadScheduler* Scheduler() override { return scheduler_; }
-
-  bool IsCurrentThread() const { return WTF::IsMainThread(); }
-
- private:
-  ThreadScheduler* scheduler_;
-
-  DISALLOW_COPY_AND_ASSIGN(SimpleThread);
-};
-
-class SimpleFrameScheduler : public FrameScheduler {
- public:
-  explicit SimpleFrameScheduler(PageScheduler* page_scheduler)
-      : page_scheduler_(page_scheduler) {}
-  ~SimpleFrameScheduler() override {}
-
-  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) override {
-    DCHECK(WTF::IsMainThread());
-    return base::ThreadTaskRunnerHandle::Get();
-  }
-
-  PageScheduler* GetPageScheduler() const override { return page_scheduler_; }
-
-  void SetFrameVisible(bool) override {}
-  bool IsFrameVisible() const override { return true; }
-  bool IsPageVisible() const override { return true; }
-  void SetPaused(bool) override {}
-  void SetCrossOrigin(bool) override {}
-  bool IsCrossOrigin() const override { return false; }
-  void SetIsAdFrame() override {}
-  bool IsAdFrame() const override { return false; }
-  void TraceUrlChange(const String&) override {}
-  FrameType GetFrameType() const override { return FrameType::kMainFrame; }
-  WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
-      const String& name,
-      WebScopedVirtualTimePauser::VirtualTaskDuration) override {
-    return WebScopedVirtualTimePauser();
-  }
-  void DidStartProvisionalLoad(bool is_main_frame) override {}
-  void DidCommitProvisionalLoad(bool, FrameScheduler::NavigationType) override {
-  }
-  void OnFirstMeaningfulPaint() override {}
-  bool IsExemptFromBudgetBasedThrottling() const override { return false; }
-  std::unique_ptr<blink::mojom::blink::PauseSubresourceLoadingHandle>
-  GetPauseSubresourceLoadingHandle() override {
-    return nullptr;
-  }
-  std::unique_ptr<WebResourceLoadingTaskRunnerHandle>
-  CreateResourceLoadingTaskRunnerHandle() override {
-    return WebResourceLoadingTaskRunnerHandle::CreateUnprioritized(
-        base::ThreadTaskRunnerHandle::Get());
-  }
-  ukm::SourceId GetUkmSourceId() override { return ukm::kInvalidSourceId; }
-  void OnStartedUsingFeature(SchedulingPolicy::Feature feature,
-                             const SchedulingPolicy& policy) override {}
-  void OnStoppedUsingFeature(SchedulingPolicy::Feature feature,
-                             const SchedulingPolicy& policy) override {}
-  WTF::HashSet<SchedulingPolicy::Feature>
-  GetActiveFeaturesTrackedForBackForwardCacheMetrics() override {
-    return WTF::HashSet<SchedulingPolicy::Feature>();
-  }
-  base::WeakPtr<FrameScheduler> GetWeakPtr() override { return nullptr; }
-
- private:
-  PageScheduler* page_scheduler_;
-
-  DISALLOW_COPY_AND_ASSIGN(SimpleFrameScheduler);
-};
-
-class SimplePageScheduler : public PageScheduler {
- public:
-  SimplePageScheduler() {}
-  ~SimplePageScheduler() override {}
-
-  std::unique_ptr<FrameScheduler> CreateFrameScheduler(
-      FrameScheduler::Delegate* delegate,
-      BlameContext*,
-      FrameScheduler::FrameType) override {
-    return std::make_unique<SimpleFrameScheduler>(this);
-  }
-
-  void SetPageVisible(bool) override {}
-  void SetPageFrozen(bool) override {}
-  void SetKeepActive(bool) override {}
-  bool IsMainFrameLocal() const override { return true; }
-  void SetIsMainFrameLocal(bool) override {}
-  void OnLocalMainFrameNetworkAlmostIdle() override {}
-  base::TimeTicks EnableVirtualTime() override { return base::TimeTicks(); }
-  void DisableVirtualTimeForTesting() override {}
-  bool VirtualTimeAllowedToAdvance() const override { return true; }
-  void SetInitialVirtualTime(base::Time) override {}
-  void SetInitialVirtualTimeOffset(base::TimeDelta) override {}
-  void SetVirtualTimePolicy(VirtualTimePolicy) override {}
-  void GrantVirtualTimeBudget(base::TimeDelta, base::OnceClosure) override {}
-  void SetMaxVirtualTimeTaskStarvationCount(int) override {}
-  void AudioStateChanged(bool is_audio_playing) override {}
-  bool IsAudioPlaying() const override { return false; }
-  bool IsExemptFromBudgetBasedThrottling() const override { return false; }
-  bool OptedOutFromAggressiveThrottlingForTest() const override {
-    return false;
-  }
-  bool RequestBeginMainFrameNotExpected(bool) override { return false; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SimplePageScheduler);
-};
-
-class SimpleThreadScheduler : public ThreadScheduler {
- public:
-  SimpleThreadScheduler() {}
-  ~SimpleThreadScheduler() override {}
-
-  void Shutdown() override {}
-
-  scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() override {
-    return base::ThreadTaskRunnerHandle::Get();
-  }
-
-  scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner()
-      override {
-    return base::ThreadTaskRunnerHandle::Get();
-  }
-
-  scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override {
-    return base::ThreadTaskRunnerHandle::Get();
-  }
-
-  scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override {
-    return base::ThreadTaskRunnerHandle::Get();
-  }
-
-  std::unique_ptr<PageScheduler> CreatePageScheduler(
-      PageScheduler::Delegate*) override {
-    return std::make_unique<SimplePageScheduler>();
-  }
-
-  // ThreadScheduler implementation:
-  bool ShouldYieldForHighPriorityWork() override { return false; }
-  bool CanExceedIdleDeadlineIfRequired() const override { return false; }
-  void PostIdleTask(const base::Location&, Thread::IdleTask) override {}
-  void PostNonNestableIdleTask(const base::Location&,
-                               Thread::IdleTask) override {}
-  void AddRAILModeObserver(RAILModeObserver*) override {}
-  void RemoveRAILModeObserver(RAILModeObserver const*) override {}
-  std::unique_ptr<WebThreadScheduler::RendererPauseHandle> PauseScheduler()
-      override {
-    return nullptr;
-  }
-  base::TimeTicks MonotonicallyIncreasingVirtualTime() override {
-    return base::TimeTicks::Now();
-  }
-  void AddTaskObserver(base::MessageLoop::TaskObserver*) override {}
-  void RemoveTaskObserver(base::MessageLoop::TaskObserver*) override {}
-  NonMainThreadSchedulerImpl* AsNonMainThreadScheduler() override {
-    return nullptr;
-  }
-  void SetV8Isolate(v8::Isolate* isolate) override {}
-};
-
-class SimpleMainThreadScheduler : public WebThreadScheduler,
-                                  public SimpleThreadScheduler {
- public:
-  SimpleMainThreadScheduler() {}
-  ~SimpleMainThreadScheduler() override {}
-
-  // WebThreadScheduler implementation:
-  void Shutdown() override {}
-
-  scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override {
-    DCHECK(WTF::IsMainThread());
-    return base::ThreadTaskRunnerHandle::Get();
-  }
-
-  scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner()
-      override {
-    DCHECK(WTF::IsMainThread());
-    return base::ThreadTaskRunnerHandle::Get();
-  }
-
-  scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override {
-    DCHECK(WTF::IsMainThread());
-    return base::ThreadTaskRunnerHandle::Get();
-  }
-
-  scoped_refptr<base::SingleThreadTaskRunner> InputTaskRunner() override {
-    DCHECK(WTF::IsMainThread());
-    return base::ThreadTaskRunnerHandle::Get();
-  }
-
-  scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override {
-    DCHECK(WTF::IsMainThread());
-    return base::ThreadTaskRunnerHandle::Get();
-  }
-
-  scoped_refptr<base::SingleThreadTaskRunner> CleanupTaskRunner() override {
-    DCHECK(WTF::IsMainThread());
-    return base::ThreadTaskRunnerHandle::Get();
-  }
-
-  scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() override {
-    DCHECK(WTF::IsMainThread());
-    return base::ThreadTaskRunnerHandle::Get();
-  }
-
-  std::unique_ptr<Thread> CreateMainThread() override {
-    return std::make_unique<SimpleThread>(this);
-  }
-
-  std::unique_ptr<PageScheduler> CreatePageScheduler(
-      PageScheduler::Delegate*) override {
-    return std::make_unique<SimplePageScheduler>();
-  }
-};
-
 class SimpleMockMainThreadScheduler : public WebMockThreadScheduler {
  public:
-  SimpleMockMainThreadScheduler() {}
+  SimpleMockMainThreadScheduler()
+      : simple_thread_scheduler_(CreateDummyWebThreadScheduler()) {}
   ~SimpleMockMainThreadScheduler() override {}
 
   scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override {
@@ -265,17 +41,17 @@
   }
 
   std::unique_ptr<Thread> CreateMainThread() override {
-    return std::make_unique<SimpleThread>(&simple_thread_scheduler_);
+    return simple_thread_scheduler_->CreateMainThread();
   }
 
  private:
-  SimpleThreadScheduler simple_thread_scheduler_;
+  std::unique_ptr<WebThreadScheduler> simple_thread_scheduler_;
 };
 
 }  // namespace
 
 std::unique_ptr<WebThreadScheduler> CreateWebMainThreadSchedulerForTests() {
-  return std::make_unique<SimpleMainThreadScheduler>();
+  return CreateDummyWebThreadScheduler();
 }
 
 std::unique_ptr<WebMockThreadScheduler>
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 3f64039..fdd4554 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -542,7 +542,6 @@
 
 # [css-contain]
 
-crbug.com/947730 external/wpt/css/css-contain/contain-content-011.html [ Failure ]
 crbug.com/880802 external/wpt/css/css-contain/contain-layout-017.html [ Failure ]
 crbug.com/671132 external/wpt/css/css-contain/contain-layout-baseline-005.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-contain/contain-layout-breaks-002.html [ Failure ]
@@ -552,7 +551,6 @@
 crbug.com/882367 external/wpt/css/css-contain/contain-paint-clip-015.html [ Failure ]
 crbug.com/882367 external/wpt/css/css-contain/contain-paint-clip-016.html [ Failure ]
 crbug.com/855261 external/wpt/css/css-contain/contain-size-grid-002.html [ Failure ]
-crbug.com/947730 external/wpt/css/css-contain/contain-strict-011.html [ Failure ]
 crbug.com/869296 external/wpt/css/css-contain/contain-style-counters-004.html [ Failure ]
 crbug.com/882383 external/wpt/css/css-contain/counter-scoping-001.html [ Failure ]
 crbug.com/882383 external/wpt/css/css-contain/counter-scoping-002.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/parsing/contain-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-contain/parsing/contain-computed-expected.txt
deleted file mode 100644
index 4b2f1cf..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-contain/parsing/contain-computed-expected.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-This is a testharness.js-based test.
-PASS Property contain value 'none' computes to 'none'
-PASS Property contain value 'strict' computes to 'strict'
-PASS Property contain value 'content' computes to 'content'
-PASS Property contain value 'size' computes to 'size'
-PASS Property contain value 'layout' computes to 'layout'
-PASS Property contain value 'style' computes to 'style'
-PASS Property contain value 'paint' computes to 'paint'
-PASS Property contain value 'size layout' computes to 'size layout'
-PASS Property contain value 'style paint' computes to 'style paint'
-FAIL Property contain value 'layout style paint' computes to 'layout style paint' assert_equals: expected "layout style paint" but got "content"
-FAIL Property contain value 'size layout style paint' computes to 'size layout style paint' assert_equals: expected "size layout style paint" but got "strict"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-007.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-007.html
new file mode 100644
index 0000000..01e8f001
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/align-items-007.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="author" title="Google LLC" href="http://www.google.com" />
+<link rel="help" href="https://drafts.csswg.org/css-flexbox/#cross-sizing" />
+<title>css-flexbox: Tests that we size items in a wrapping column flexbox as fit-content</title>
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht" />
+<meta name="assert" content="The image should fit wholly within the flexbox." />
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+
+<!-- This makes sure that we only see green if the flex items are sized correctly -->
+<div style="position: absolute; width: 100px; height: 100px; background: green;"></div>
+
+<div style="display: flex; flex-direction: column; width: 100px; height: 100px; line-height: 20px; align-items: center; background: green;">
+  <img src="../support/red-rect.svg">
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/support/red-rect.svg b/third_party/blink/web_tests/external/wpt/css/support/red-rect.svg
new file mode 100644
index 0000000..3fe10a2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/support/red-rect.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
+              "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100">
+    <rect width="200" height="100" style="fill:red;" />
+</svg>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-iframe-element/src-repeated-in-ancestor-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-iframe-element/src-repeated-in-ancestor-expected.txt
deleted file mode 100644
index e97bed39..0000000
--- a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-iframe-element/src-repeated-in-ancestor-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-PASS different path name
-FAIL same path name, no document fragment assert_unreached: Should not navigate Reached unreachable code
-FAIL same path name, different document fragment assert_unreached: Should not navigate Reached unreachable code
-FAIL same path name, no document fragement (intermediary browsing context) assert_unreached: Should not navigate Reached unreachable code
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-ignore-opens-during-unload.window-expected.txt b/third_party/blink/web_tests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-ignore-opens-during-unload.window-expected.txt
new file mode 100644
index 0000000..2a74109
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/bailout-side-effects-ignore-opens-during-unload.window-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+PASS document.open bailout should not have any side effects (ignore-opens-during-unload is greater than 0 during unload event)
+FAIL document.open bailout should not have any side effects (ignore-opens-during-unload is greater than 0 during beforeunload event) assert_not_equals: document nodes should not be cleared (ignore-opens-during-unload counter is greater than 0 during beforeunload event) got disallowed value 0
+PASS document.open bailout should not have any side effects (ignore-opens-during-unload is greater than 0 during pagehide event)
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html/webappapis/scripting/events/compile-event-handler-settings-objects-expected.txt b/third_party/blink/web_tests/external/wpt/html/webappapis/scripting/events/compile-event-handler-settings-objects-expected.txt
new file mode 100644
index 0000000..27f68210
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/webappapis/scripting/events/compile-event-handler-settings-objects-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+PASS The Function instance must be created in the Realm of the node document
+PASS The entry settings object while executing the compiled callback via Web IDL's invoke must be that of the node document
+FAIL The incumbent settings object while executing the compiled callback via Web IDL's invoke must be that of the node document assert_equals: expected "iframe" but got "parent frame"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webxr/xrSession_mode.https.html b/third_party/blink/web_tests/external/wpt/webxr/xrSession_mode.https.html
deleted file mode 100644
index a1d0660..0000000
--- a/third_party/blink/web_tests/external/wpt/webxr/xrSession_mode.https.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <script src=/resources/testharness.js></script>
-  <script src=/resources/testharnessreport.js></script>
-  <script src="resources/webxr_util.js"></script>
-  <canvas></canvas>
-
-  <script>
-    xr_promise_test("Requested session has it's mode set",
-     (t) => {
-      return XRTest.simulateDeviceConnection({ supportsImmersive:true })
-        .then( (controller) => new Promise((resolve) => {
-          XRTest.simulateUserActivation( () => {
-            resolve(navigator.xr.requestSession('immersive-vr').then( (session) => {
-              assert_equals(session.mode, 'immersive-vr');
-            }));
-          });
-        }));
-    });
-  </script>
-</body>
-
diff --git a/third_party/blink/web_tests/fast/css/containment/contain-parse-and-apply-expected.txt b/third_party/blink/web_tests/fast/css/containment/contain-parse-and-apply-expected.txt
index 582980e..caae4fab 100644
--- a/third_party/blink/web_tests/fast/css/containment/contain-parse-and-apply-expected.txt
+++ b/third_party/blink/web_tests/fast/css/containment/contain-parse-and-apply-expected.txt
@@ -19,8 +19,8 @@
 PASS computedContainValue(children[i]) is "none"
 PASS computedContainValue(children[i]) is "none"
 PASS computedContainValue(children[i]) is "none"
-PASS getContainValue('#test') is "strict"
-PASS getContainValue('#test') is "layout paint"
+PASS getContainValue('#test') is "size layout style paint"
+PASS getContainValue('#test') is "content"
 PASS getContainValue('#test') is "strict"
 PASS getContainValue('#test') is "none"
 PASS getContainValue('#test') is "none"
diff --git a/third_party/blink/web_tests/fast/css/containment/contain-parse-and-apply.html b/third_party/blink/web_tests/fast/css/containment/contain-parse-and-apply.html
index 1e05cff0..12bbad2 100644
--- a/third_party/blink/web_tests/fast/css/containment/contain-parse-and-apply.html
+++ b/third_party/blink/web_tests/fast/css/containment/contain-parse-and-apply.html
@@ -44,10 +44,10 @@
 var test = document.querySelector('#test');
 
 test.style.setProperty("contain", "layout paint style size");
-shouldBeEqualToString("getContainValue('#test')", "strict");
+shouldBeEqualToString("getContainValue('#test')", "size layout style paint");
 
 test.style.setProperty("contain", "layout paint");
-shouldBeEqualToString("getContainValue('#test')", "layout paint");
+shouldBeEqualToString("getContainValue('#test')", "content");
 
 test.style.setProperty("contain", "strict");
 shouldBeEqualToString("getContainValue('#test')", "strict");
@@ -78,4 +78,4 @@
 
 test.style.setProperty("contain", "strict none");
 shouldBeEqualToString("getContainValue('#test')", "style paint");
-</script>
\ No newline at end of file
+</script>
diff --git a/third_party/blink/web_tests/fast/forms/number/number-spinbutton-crash-on-detach.html b/third_party/blink/web_tests/fast/forms/number/number-spinbutton-crash-on-detach.html
index 0c6c4c80..35776b3e 100644
--- a/third_party/blink/web_tests/fast/forms/number/number-spinbutton-crash-on-detach.html
+++ b/third_party/blink/web_tests/fast/forms/number/number-spinbutton-crash-on-detach.html
@@ -15,9 +15,7 @@
     testRunner.waitUntilDone();
 }
 window.onload = function() {
-    var iframe = document.querySelector('iframe');
-    iframe.src = '';
-    iframe.srcdoc = '<script>gc(); alert("alert"); parent.finish();</' + 'script>';
+    document.querySelector('iframe').srcdoc = '<script>gc(); alert("alert"); parent.finish();</' + 'script>';
 };
 
 function finish() {
diff --git a/third_party/blink/web_tests/fast/frames/detach-frame-nested-on-commit-no-crash.html b/third_party/blink/web_tests/fast/frames/detach-frame-nested-on-commit-no-crash.html
index ac88ef0f..8193b19 100644
--- a/third_party/blink/web_tests/fast/frames/detach-frame-nested-on-commit-no-crash.html
+++ b/third_party/blink/web_tests/fast/frames/detach-frame-nested-on-commit-no-crash.html
@@ -9,11 +9,13 @@
 
 function maybeStart()
 {
-    window.secondFrame.contentWindow.onunload = function() {
-        document.documentElement.removeChild(window.bodyEl);
-        setTimeout(finishJSTest, 10);
-  };
-  window.firstFrame.src = 'about:blank';
+    setTimeout(function() {
+        window.secondFrame.contentWindow.onunload = function() {
+            document.documentElement.removeChild(window.bodyEl);
+            setTimeout(finishJSTest, 10);
+        };
+        window.firstFrame.src = 'about:blank';
+    }, 0);
 }
 function runTest()
 {
diff --git a/third_party/blink/web_tests/fast/history/history-back-initial-vs-final-url.html b/third_party/blink/web_tests/fast/history/history-back-initial-vs-final-url.html
index 75576bf..3cc8ea1 100644
--- a/third_party/blink/web_tests/fast/history/history-back-initial-vs-final-url.html
+++ b/third_party/blink/web_tests/fast/history/history-back-initial-vs-final-url.html
@@ -41,8 +41,10 @@
 // may cause the second history entry to replace the current one instead of
 // being appended. history.back() failed for this reason.
 // See https://crbug.com/862580
-window.onload = function() {
-  document.querySelector("iframe").src = "resources/frame-initial-url.html"
+if (!sessionStorage.didNav) {
+  window.onload = function() {
+    document.querySelector("iframe").src = "resources/frame-initial-url.html"
+  }
 }
 
 </script>
diff --git a/third_party/blink/web_tests/fast/history/history-length-append-subframe-with-hash-expected.txt b/third_party/blink/web_tests/fast/history/history-length-append-subframe-with-hash-expected.txt
index d6c5a02..c8ca082 100644
--- a/third_party/blink/web_tests/fast/history/history-length-append-subframe-with-hash-expected.txt
+++ b/third_party/blink/web_tests/fast/history/history-length-append-subframe-with-hash-expected.txt
@@ -9,6 +9,6 @@
 
 ============== Back Forward List ==============
 curr->  (file test):fast/history/history-length-append-subframe-with-hash.html#wentBack
-            about:blank (in frame "<!--framePath //<!--frame0-->-->")
+            about:blank/#identifier (in frame "<!--framePath //<!--frame0-->-->")
         (file test):fast/history/resources/back-on-load.html
 ===============================================
diff --git a/third_party/blink/web_tests/http/tests/devtools/localization/basic-l10n-test-expected.txt b/third_party/blink/web_tests/http/tests/devtools/localization/basic-l10n-test-expected.txt
new file mode 100644
index 0000000..7678158
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/localization/basic-l10n-test-expected.txt
@@ -0,0 +1,21 @@
+Tests that the the localization functions work as expected.
+
+
+Test string is returned as is
+Test string with a placeholder %s is returned as is
+Test %s string with multiple placeholders %s is returned as is
+Test unicode character  %
+
+Test string is returned as is
+Test string with a placeholder is returned with a substitution
+Test string with multiple placeholders is returned with substitutions
+Test numeric placeholder: -99
+Test numeric formatted placeholder and unicode character: 88.15 %
+
+Test string is returned as is
+Test string with a placeholder is returned with a substitution.
+Test string with placeholders is returned with substitutions.
+Test numeric placeholder: -99
+Test numeric placeholder and unicode character 88.149 %
+Test calling ls as a function
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/localization/basic-l10n-test.js b/third_party/blink/web_tests/http/tests/devtools/localization/basic-l10n-test.js
new file mode 100644
index 0000000..b60422f2
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/localization/basic-l10n-test.js
@@ -0,0 +1,33 @@
+// 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 that the the localization functions work as expected.\n`);
+
+  // Verify localize returns an empty string.
+  TestRunner.addResult(Common.localize(''));
+  TestRunner.addResult(Common.localize('Test string is returned as is'));
+  TestRunner.addResult(Common.localize('Test string with a placeholder %s is returned as is'));
+  TestRunner.addResult(Common.localize('Test %s string with multiple placeholders %s is returned as is'));
+  TestRunner.addResult(Common.localize('Test unicode character \xa0%'));
+
+  // Verify UIString returns an empty string.
+  TestRunner.addResult(Common.UIString(''));
+  TestRunner.addResult(Common.UIString('Test string is returned as is'));
+  TestRunner.addResult(Common.UIString('Test string with a %s is returned with a substitution', 'placeholder'));
+  TestRunner.addResult(Common.UIString('%s string with multiple %s is returned with substitutions', 'Test', 'placeholders'));
+  TestRunner.addResult(Common.UIString('Test numeric placeholder: %d', -99));
+  TestRunner.addResult(Common.UIString('Test numeric formatted placeholder and unicode character: %.2f\xa0%%', 88.149));
+
+  // Verify ls returns an empty string.
+  TestRunner.addResult(ls``);
+  TestRunner.addResult(ls`Test string is returned as is`);
+  TestRunner.addResult(ls`Test string with a ${'placeholder'} is returned with a substitution.`);
+  TestRunner.addResult(ls`${'Test'} string with ${'placeholders'} is returned with substitutions.`);
+  TestRunner.addResult(ls`Test numeric placeholder: ${-99}`);
+  TestRunner.addResult(ls`Test numeric placeholder and unicode character ${88.149}\xa0%`);
+  TestRunner.addResult(ls('Test calling ls as a function'));
+
+  TestRunner.completeTest();
+})();
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/loading/redirect-methods-expected.txt b/third_party/blink/web_tests/http/tests/loading/redirect-methods-expected.txt
index c99a057..12e806f 100644
--- a/third_party/blink/web_tests/http/tests/loading/redirect-methods-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/redirect-methods-expected.txt
@@ -39,10 +39,10 @@
 frame "1" - didFinishDocumentLoadForFrame
 frame "1" - didHandleOnloadEventsForFrame
 frame "1" - didFinishLoadForFrame
-frame "0" - didHandleOnloadEventsForFrame
-frame "0" - didFinishLoadForFrame
 frame "1" - BeginNavigation request to 'http://127.0.0.1:8000/loading/resources/redirect-methods-form.html', http method GET
 frame "1" - DidStartNavigation
+frame "0" - didHandleOnloadEventsForFrame
+frame "0" - didFinishLoadForFrame
 frame "1" - ReadyToCommitNavigation
 http://127.0.0.1:8000/loading/resources/redirect-methods-form.html - didReceiveResponse <NSURLResponse http://127.0.0.1:8000/loading/resources/redirect-methods-form.html, http status code 200>
 frame "1" - didCommitLoadForFrame
@@ -66,10 +66,10 @@
 frame "2" - didFinishDocumentLoadForFrame
 frame "2" - didHandleOnloadEventsForFrame
 frame "2" - didFinishLoadForFrame
-frame "1" - didHandleOnloadEventsForFrame
-frame "1" - didFinishLoadForFrame
 frame "2" - BeginNavigation request to 'http://127.0.0.1:8000/loading/resources/redirect-methods-form.html', http method GET
 frame "2" - DidStartNavigation
+frame "1" - didHandleOnloadEventsForFrame
+frame "1" - didFinishLoadForFrame
 frame "2" - ReadyToCommitNavigation
 http://127.0.0.1:8000/loading/resources/redirect-methods-form.html - didReceiveResponse <NSURLResponse http://127.0.0.1:8000/loading/resources/redirect-methods-form.html, http status code 200>
 frame "2" - didCommitLoadForFrame
@@ -93,10 +93,10 @@
 frame "3" - didFinishDocumentLoadForFrame
 frame "3" - didHandleOnloadEventsForFrame
 frame "3" - didFinishLoadForFrame
-frame "2" - didHandleOnloadEventsForFrame
-frame "2" - didFinishLoadForFrame
 frame "3" - BeginNavigation request to 'http://127.0.0.1:8000/loading/resources/redirect-methods-form.html', http method GET
 frame "3" - DidStartNavigation
+frame "2" - didHandleOnloadEventsForFrame
+frame "2" - didFinishLoadForFrame
 frame "3" - ReadyToCommitNavigation
 http://127.0.0.1:8000/loading/resources/redirect-methods-form.html - didReceiveResponse <NSURLResponse http://127.0.0.1:8000/loading/resources/redirect-methods-form.html, http status code 200>
 frame "3" - didCommitLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/misc/javascript-url-stop-loaders-expected.txt b/third_party/blink/web_tests/http/tests/misc/javascript-url-stop-loaders-expected.txt
deleted file mode 100644
index 2a6b550..0000000
--- a/third_party/blink/web_tests/http/tests/misc/javascript-url-stop-loaders-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-
-This test checks whether loading a JavaScript URL cancels loaders. If it fails, it will give an assertion failure in Debug builds.
diff --git a/third_party/blink/web_tests/http/tests/misc/javascript-url-stop-loaders.html b/third_party/blink/web_tests/http/tests/misc/javascript-url-stop-loaders.html
deleted file mode 100644
index 1063e87..0000000
--- a/third_party/blink/web_tests/http/tests/misc/javascript-url-stop-loaders.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="/js-test-resources/js-test.js"></script>
-<script type="text/javascript">
-var str = "<script type='text/javascript' src='http://127.0.0.1:8000/misc/resources/hang-connection.php'><" + "/script>";
-
-function loadFirst()
-{
-    document.getElementById("ad").src = "javascript: window.parent.str";
-}
-
-function finish()
-{
-    gc();
-    if (window.testRunner)
-        testRunner.notifyDone();    
-}
-
-function loadSecond()
-{
-    document.getElementById("ad").src = "javascript: '<!DOCTYPE html><script>window.parent.finish();<" + "/script>'";
-}
-
-if (window.testRunner) {
-    testRunner.waitUntilDone();
-    testRunner.dumpAsText();
-}
-
-setTimeout("loadFirst();", 0);
-setTimeout("loadSecond();", 0);
-</script>
-</head>
-<body>
-<p>This test checks whether loading a JavaScript URL cancels loaders. If it fails, it will give an assertion failure in Debug builds.</p>
-<iframe id="ad"></iframe>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/misc/resource-timing-iframe-restored-from-history.html b/third_party/blink/web_tests/http/tests/misc/resource-timing-iframe-restored-from-history.html
index 4ab9492..a3a14d2 100644
--- a/third_party/blink/web_tests/http/tests/misc/resource-timing-iframe-restored-from-history.html
+++ b/third_party/blink/web_tests/http/tests/misc/resource-timing-iframe-restored-from-history.html
@@ -21,8 +21,10 @@
     }
 }
 
-window.onload = function() {
-  document.querySelector("iframe").src = "resources/frame-initial-url.html";
+if (!sessionStorage.didNav) {
+  window.onload = function() {
+    document.querySelector("iframe").src = "resources/frame-initial-url.html";
+  }
 }
 
 </script>
diff --git a/third_party/blink/web_tests/http/tests/security/isolatedWorld/bypass-main-world-csp-iframes-expected.txt b/third_party/blink/web_tests/http/tests/security/isolatedWorld/bypass-main-world-csp-iframes-expected.txt
index 7e5f81e..2944dcf7 100644
--- a/third_party/blink/web_tests/http/tests/security/isolatedWorld/bypass-main-world-csp-iframes-expected.txt
+++ b/third_party/blink/web_tests/http/tests/security/isolatedWorld/bypass-main-world-csp-iframes-expected.txt
@@ -1,6 +1,6 @@
 ALERT: Running test #6
 
-CONSOLE ERROR: Refused to frame 'http://127.0.0.1:8000/security/isolatedWorld/resources/iframe.php?test=6' because it violates the following Content Security Policy directive: "frame-src 'none'".
+CONSOLE ERROR: line 28: Refused to frame 'http://127.0.0.1:8000/security/isolatedWorld/resources/iframe.php?test=6' because it violates the following Content Security Policy directive: "frame-src 'none'".
 
 ALERT: Running test #5
 
@@ -12,14 +12,16 @@
 ALERT: iframe loaded in test #4
 ALERT: Running test #3
 
+CONSOLE ERROR: line 28: Refused to frame 'http://127.0.0.1:8000/security/isolatedWorld/resources/iframe.php?test=3' because it violates the following Content Security Policy directive: "frame-src 'none'".
+
 ALERT: iframe loaded in test #3.5
 ALERT: Running test #2
 
-CONSOLE ERROR: Refused to frame 'http://127.0.0.1:8000/security/isolatedWorld/resources/iframe.php?test=2.5' because it violates the following Content Security Policy directive: "frame-src 'none'".
+CONSOLE ERROR: line 28: Refused to frame 'http://127.0.0.1:8000/security/isolatedWorld/resources/iframe.php?test=2.5' because it violates the following Content Security Policy directive: "frame-src 'none'".
 
 ALERT: Running test #1
 
-CONSOLE ERROR: Refused to frame 'http://127.0.0.1:8000/security/isolatedWorld/resources/iframe.php?test=1' because it violates the following Content Security Policy directive: "frame-src 'none'".
+CONSOLE ERROR: line 28: Refused to frame 'http://127.0.0.1:8000/security/isolatedWorld/resources/iframe.php?test=1' because it violates the following Content Security Policy directive: "frame-src 'none'".
 
 ALERT: Running test #0
 
diff --git a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-does-not-submit.html b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-does-not-submit.html
deleted file mode 100644
index b4e3cd5..0000000
--- a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-does-not-submit.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<!DOCTYPE html>
-<script src="../../../../../resources/testharness.js"></script>
-<script src="../../../../../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/third_party/blink/public/mojom/page/spatial_navigation.mojom.js"></script>
-<script src="../../../../../fast/spatial-navigation/resources/mock-snav-service.js"></script>
-<script src="../../../../../fast/spatial-navigation/resources/snav-testharness.js"></script>
-
-<form action="javascript:fail();">
-<input type="text" id="first"></input>
-<input type="submit" value="Submit" />
-</form>
-
-<script>
-  function fail() {
-    assert_false(true, "Form should not have been submitted.");
-  }
-  promise_test(async () => {
-    let first = document.getElementById("first");
-
-    snav.triggerMove("Down");
-    await snavCallback();
-
-    assert_false(mockSnavService.canExitFocus,
-                "Should not be able to exit focus.");
-    assert_true(mockSnavService.canSelectElement,
-                "Should be able to select element.");
-    assert_false(mockSnavService.hasNextFormElement,
-                "Should not be able to move to next form element.");
-
-    eventSender.keyDown('Enter');
-    assert_equals(document.activeElement,
-                  first,
-                  "'second' should be focused.");
-
-    await snav.rAF();
-
-    assert_true(mockSnavService.canExitFocus,
-                "Should be able to exit focus.");
-    assert_true(mockSnavService.canSelectElement,
-                "Should be able to select element.");
-    assert_true(mockSnavService.hasNextFormElement,
-                 "Should be able to move to next form element.");
-  }, 'Form does not submit when passing spat nav focus.');
-</script>
diff --git a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-state-on-click.html b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-state-on-click.html
index 09564259..3fb47bba 100644
--- a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-state-on-click.html
+++ b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-state-on-click.html
@@ -12,46 +12,58 @@
 </form>
 
 <script>
-  function notCalled() {
-    assert_false(true, "Should not be called");
-  };
-  mockSnavService.callback = notCalled;
-
-  promise_test(async () => {
+  async_test((t) => {
     let first = document.getElementById("first");
     let second = document.getElementById("second");
 
-    await snav.rAF();
+    mockSnavService.callback = notCalled;
 
-    snav.triggerMove('Down');
+    async function runTest() {
+      console.error('0');
+      await snav.rAF();
 
-    await snavCallback();
-    assert_equals(window.internals.interestedElement,
-                  first,
-                  "'first' should be interested.");
-    assert_not_equals(document.activeElement,
-                      first,
-                      "'first' should not yet be focused.");
-    assert_false(mockSnavService.canExitFocus,
-                "Should be able to exit focus.");
-    assert_true(mockSnavService.canSelectElement,
-                "Should be able to select element.");
-    assert_false(mockSnavService.hasNextFormElement,
-                 "Should be able to move to next form element.");
+      snav.triggerMove('Down');
+      await snavCallback();
+      assert_equals(window.internals.interestedElement,
+                    first,
+                    "'first' should be interested.");
+      assert_not_equals(document.activeElement,
+                        first,
+                        "'first' should not yet be focused.");
+      assert_false(mockSnavService.canExitFocus,
+                  "Should be able to exit focus.");
+      assert_true(mockSnavService.canSelectElement,
+                  "Should be able to select element.");
+      assert_false(mockSnavService.hasNextFormElement,
+                   "Should be able to move to next form element.");
 
-    eventSender.keyDown('Enter');
-    await snavCallback();
-    assert_true(mockSnavService.canExitFocus,
-                "Should be able to exit focus.");
-    assert_true(mockSnavService.canSelectElement,
-                "Should be able to select element.");
-    assert_true(mockSnavService.hasNextFormElement,
-                "Should be able to move to next form element.");
-    assert_equals(window.internals.interestedElement,
-                  first,
-                  "'first' should be interested.");
-    assert_equals(document.activeElement,
-                  first,
-                  "'first' should be focused.");
+      eventSender.keyDown('Enter');
+      await snavCallback();
+      assert_true(mockSnavService.canExitFocus,
+                  "Should be able to exit focus.");
+      assert_true(mockSnavService.canSelectElement,
+                  "Should be able to select element.");
+      assert_true(mockSnavService.hasNextFormElement,
+                  "Should be able to move to next form element.");
+      assert_equals(window.internals.interestedElement,
+                    first,
+                    "'first' should be interested.");
+      assert_equals(document.activeElement,
+                    first,
+                    "'first' should be focused.");
+
+      t.done();
+    };
+
+    t.step_timeout(() => {
+      runTest();
+    }, 0);
+
+    function notCalled() {
+      t.step_timeout(() => {
+        assert_false(true, "Should not be called");
+      }, 0);
+    };
+
   }, 'Spat Nav state updates correctly when clicking on form elements.');
 </script>
diff --git a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-state.html b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-state.html
index 6501c24..5925dc8c 100644
--- a/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-state.html
+++ b/third_party/blink/web_tests/virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-state.html
@@ -13,64 +13,72 @@
 </form>
 
 <script>
-  function notCalled() {
-    assert_false(true, "Should not be called");
-  };
-
-  let initialState = snavCallback();
-  promise_test(async () => {
-
+  async_test((t) => {
     let first = document.getElementById("first");
     let second = document.getElementById("second");
     let third = document.getElementById("third");
 
-    await snav.rAF();
+    let initialState = snavCallback();
 
-    assert_equals(document.activeElement,
-                  first,
-                  "'first' should start focused.");
-    assert_equals(window.internals.interestedElement,
-                  first,
-                  "'first' should start interested.");
+    async function runTest() {
+      assert_equals(document.activeElement,
+                    first,
+                    "'first' should start focused.");
+      assert_equals(window.internals.interestedElement,
+                    first,
+                    "'first' should start interested.");
 
-    await initialState;
-    assert_true(mockSnavService.canExitFocus,
-                "Should be able to exit focus.");
-    assert_true(mockSnavService.canSelectElement,
-                "Should be able to select element.");
-    assert_true(mockSnavService.hasNextFormElement,
-                "Should be able to move to next form element.");
+      await initialState;
+      assert_true(mockSnavService.canExitFocus,
+                  "Should be able to exit focus.");
+      assert_true(mockSnavService.canSelectElement,
+                  "Should be able to select element.");
+      assert_true(mockSnavService.hasNextFormElement,
+                  "Should be able to move to next form element.");
 
-    mockSnavService.callback = notCalled;
-    eventSender.keyDown('Tab');
-    assert_equals(document.activeElement,
-                  second,
-                  "'second' should be focused.");
+      mockSnavService.callback = notCalled;
+      eventSender.keyDown('Tab');
+      assert_equals(document.activeElement,
+                    second,
+                    "'second' should be focused.");
 
-    await snav.rAF();
+      await snav.rAF();
 
-    eventSender.keyDown('Tab');
-    assert_equals(document.activeElement,
-                  third,
-                  "'third' should be focused.");
+      eventSender.keyDown('Tab');
+      assert_equals(document.activeElement,
+                    third,
+                    "'third' should be focused.");
 
-    await snavCallback();
-    assert_true(mockSnavService.canExitFocus,
-                "Should be able to exit focus.");
-    assert_true(mockSnavService.canSelectElement,
-                "Should be able to select element.");
-    assert_false(mockSnavService.hasNextFormElement,
-                 "Should be able to move to next form element.");
+      await snavCallback();
+      assert_true(mockSnavService.canExitFocus,
+                  "Should be able to exit focus.");
+      assert_true(mockSnavService.canSelectElement,
+                  "Should be able to select element.");
+      assert_false(mockSnavService.hasNextFormElement,
+                   "Should be able to move to next form element.");
 
-    eventSender.keyDown('Escape');
+      eventSender.keyDown('Escape');
 
-    await snavCallback();
-    assert_false(mockSnavService.canExitFocus,
-                 "Should be able to exit focus.");
-    assert_true(mockSnavService.canSelectElement,
-                "Should be able to select element.");
-    assert_false(mockSnavService.hasNextFormElement,
-                 "Should be able to move to next form element.");
+      await snavCallback();
+      assert_false(mockSnavService.canExitFocus,
+                   "Should be able to exit focus.");
+      assert_true(mockSnavService.canSelectElement,
+                  "Should be able to select element.");
+      assert_false(mockSnavService.hasNextFormElement,
+                   "Should be able to move to next form element.");
+
+      t.done();
+    };
+
+    t.step_timeout(() => {
+      runTest();
+    }, 0);
+
+    function notCalled() {
+      t.step_timeout(() => {
+        assert_false(true, "Should not be called");
+      }, 0);
+    };
 
   }, 'Spat Nav state updates correctly for form elements.');
 </script>
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 499ee06..2207f33 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -1104,6 +1104,17 @@
 [Worker]     method formData
 [Worker]     method json
 [Worker]     method text
+[Worker] interface SMS
+[Worker]     attribute @@toStringTag
+[Worker]     getter content
+[Worker]     method constructor
+[Worker] interface SMSReceiver : EventTarget
+[Worker]     attribute @@toStringTag
+[Worker]     getter onchange
+[Worker]     getter sms
+[Worker]     method constructor
+[Worker]     method start
+[Worker]     setter onchange
 [Worker] interface Serial : EventTarget
 [Worker]     attribute @@toStringTag
 [Worker]     getter onconnect
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index f12bfe2..ff8cf7d 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -6079,6 +6079,17 @@
     method formData
     method json
     method text
+interface SMS
+    attribute @@toStringTag
+    getter content
+    method constructor
+interface SMSReceiver : EventTarget
+    attribute @@toStringTag
+    getter onchange
+    getter sms
+    method constructor
+    method start
+    setter onchange
 interface SVGAElement : SVGGraphicsElement
     attribute @@toStringTag
     getter href
@@ -10626,7 +10637,6 @@
 interface XRSession : EventTarget
     attribute @@toStringTag
     getter environmentBlendMode
-    getter mode
     getter onblur
     getter onend
     getter onfocus
@@ -10660,7 +10670,6 @@
     method constructor
 interface XRStationaryReferenceSpace : XRReferenceSpace
     attribute @@toStringTag
-    getter subtype
     method constructor
 interface XRUnboundedReferenceSpace : XRReferenceSpace
     attribute @@toStringTag
@@ -10685,15 +10694,12 @@
 interface XRWebGLLayer : XRLayer
     static method getNativeFramebufferScaleFactor
     attribute @@toStringTag
-    getter alpha
     getter antialias
     getter context
-    getter depth
     getter framebuffer
     getter framebufferHeight
     getter framebufferWidth
     getter ignoreDepthValues
-    getter stencil
     method constructor
     method getViewport
     method requestViewportScaling
diff --git a/third_party/blink/web_tests/xr/resources/xr-test-utils.js b/third_party/blink/web_tests/xr/resources/xr-test-utils.js
index 7774739..bb92f1b3 100644
--- a/third_party/blink/web_tests/xr/resources/xr-test-utils.js
+++ b/third_party/blink/web_tests/xr/resources/xr-test-utils.js
@@ -2,7 +2,7 @@
 // performs tests. If func returns a promise, test will only pass if the promise
 // resolves.
 function xr_session_promise_test(
-    func, deviceOptions, sessionOptions, name, properties) {
+    func, deviceOptions, sessionModes, name, properties) {
   if (document.getElementById('webgl-canvas') ||
       document.getElementById('webgl2-canvas')) {
     webglCanvasSetup();
@@ -21,13 +21,13 @@
           }
         })
         .then(() => new Promise((resolve, reject) => {
-          // Run the test with each set of sessionOptions from the array one
+          // Run the test with each of sessionModes from the array one
           // at a time.
           function nextSessionTest(i) {
             // Check if it's time to break the loop.
-            if (i == sessionOptions.length) {
-              if (sessionOptions.length == 0) {
-                reject('No option for the session. Test Did not run.');
+            if (i == sessionModes.length) {
+              if (sessionModes.length == 0) {
+                reject('No modes specified. Test did not run.');
               } else {
                 resolve();
               }
@@ -36,11 +36,12 @@
 
             // Perform the session request in a user gesture.
             runWithUserGesture(() => {
-              let nextOptions = sessionOptions[i];
+              let nextMode = sessionModes[i];
               let testSession = null;
-              navigator.xr.requestSession(nextOptions)
+              navigator.xr.requestSession(nextMode)
                   .then((session) => {
                     testSession = session;
+                    testSession.mode = nextMode;
                     return func(session, t, fakeDeviceController);
                   })
                   .then(() => {
@@ -54,19 +55,9 @@
                   })
                   .then(() => nextSessionTest(++i))
                   .catch((err) => {
-                    let optionsString = '{';
-                    let firstOption = true;
-                    for (let option in nextOptions) {
-                      if (!firstOption) {
-                        optionsString += ',';
-                      }
-                      optionsString += ` ${option}: ${nextOptions[option]}`;
-                      firstOption = false;
-                    }
-                    optionsString += ' }';
                     reject(
-                        `Test failed while running with the following options:
-                        ${optionsString} ${err}`);
+                        `Test failed while running with the following XRSessionMode:
+                        ${nextMode} ${err}`);
                   });
             });
           }
diff --git a/third_party/libcxx-pretty-printers/README.chromium b/third_party/libcxx-pretty-printers/README.chromium
index 2edbcb2..b65151b9 100644
--- a/third_party/libcxx-pretty-printers/README.chromium
+++ b/third_party/libcxx-pretty-printers/README.chromium
@@ -1,6 +1,6 @@
 Name: libcxx-pretty-printers
-URL: https://github.com/tanderson-google/libcxx-pretty-printers
-Version: unknown
+URL: https://github.com/koutheir/libcxx-pretty-printers
+Version: b04941d7f9fbc29d2a2e1c56af765001eebc61e3
 License: GPL v3
 License File: LICENSE
 Security Critical: no
@@ -8,11 +8,6 @@
 Description:
 GDB Pretty Printers for libc++ of Clang/LLVM
 
-Local patches:
-This is a fork of [1] which is a fork of [2] which is a fork of [3] which was
-originally a fork of some pretty-printers for libstdc++.  printers.py no longer
-tracks any of these upstreams.
-
-[1] https://github.com/tanderson-google/libcxx-pretty-printers
-[2] https://github.com/LeszekSwirski/libcxx-pretty-printers
-[3] https://github.com/koutheir/libcxx-pretty-printers
+Updating: Copy libcxx-pretty-printers/src/libcxx/v1/printers.py to
+third_party/libcxx-pretty-printers/printers.py.  Copy the LICENSE if it changed.
+Update the version in this file.
diff --git a/third_party/webxr_test_pages/webxr-samples/input-selection.html b/third_party/webxr_test_pages/webxr-samples/input-selection.html
index f5ea025..80fcf3f6 100644
--- a/third_party/webxr_test_pages/webxr-samples/input-selection.html
+++ b/third_party/webxr_test_pages/webxr-samples/input-selection.html
@@ -95,6 +95,113 @@
       let floorPosition = [0, -floorSize / 2 + 0.01, 0];
       let floorNode = null;
 
+      let controllerTable = {};
+      let boxTable = {};
+      let movableBoxes = {};
+      let loggedGamepadInfo = {};
+
+      class XRControllerState {
+        constructor(button_count) {
+          this.buttons = [];
+          for (let i = 0; i < button_count; ++i) {
+            this.buttons.push(false);
+          }
+        }
+
+        // returns array of buttons to fire events for
+        update(gamepad) {
+          let result = [];
+          for (let i = 0; i < this.buttons.length; ++i) {
+            let old_state = this.buttons[i];
+            let new_state = gamepad.buttons[i].pressed;
+            if (old_state && !new_state) {
+              // Button which was previously pressed is not pressed anymore.
+              result.push(i);
+            }
+            this.buttons[i] = new_state;
+          }
+          return result;
+        }
+      }
+
+      class GamepadBoxSet {
+        constructor(button_count, x, z) {
+          this.box_list = [];
+          this.box_state = [];
+          for (let y = 1; y <= button_count; ++y) {
+            addBox(x, y, z, 1, 0, 0, this.box_list);
+            this.box_state.push(true);
+          }
+        }
+
+        toggle(box_index) {
+          let box = this.box_list[box_index];
+          let uniforms = box.renderPrimitive.uniforms;
+          if (this.box_state[box_index]) {
+            uniforms.baseColorFactor.value = [0, 1, 0, 1];
+          } else {
+            uniforms.baseColorFactor.value = [1, 0, 0, 1];
+          }
+          this.box_state[box_index] = !this.box_state[box_index];
+        }
+      }
+
+      class MovableBox {
+        constructor(x, y, z, inv_speed) {
+          this.box_list = [];
+          addBox(x, y, z, 0, 0, 1, this.box_list);
+          this.inv_speed = inv_speed;
+        }
+
+        update(dx, dz) {
+          vec3.add(this.box_list[0].position, this.box_list[0].position, [dx / this.inv_speed, 0, dz / this.inv_speed]);
+        }
+      }
+
+      function ProcessGamepad(gamepad, input_source) {
+        let hand = input_source.handedness;
+        if (!(hand in loggedGamepadInfo)) {
+          loggedGamepadInfo[hand] = true;
+          console.log(
+            "Detected gamepad with:\n" +
+            "hand = " + hand + "\n" +
+            "id = " + gamepad.id + "\n" +
+            "mapping = " + gamepad.mapping + "\n");
+        }
+        if (!(hand in movableBoxes)) {
+          let x = 2;
+          let z = 2;
+          if (hand == "right") {
+            x = -2;
+            z = -2;
+          }
+          movableBoxes[hand] = new MovableBox(x, 2, z, 100);
+        }
+        if (!(hand in boxTable)) {
+          let x = 1;
+          let z = 1;
+          if (hand == "right") {
+            x = -1;
+            z = -1;
+          }
+          boxTable[hand] = new GamepadBoxSet(gamepad.buttons.length, x, z);
+        }
+        if (!(hand in controllerTable)) {
+          controllerTable[hand] = new XRControllerState(gamepad.buttons.length);
+        }
+        let buttons_fired = controllerTable[hand].update(gamepad);
+        for (let button_index of buttons_fired) {
+          boxTable[hand].toggle(button_index);
+        }
+        for (let i = 0; i < gamepad.axes.length; i += 2) {
+          let dx = gamepad.axes[i];
+          let dy = gamepad.axes[i + 1];
+          if (dx != 0 || dy != 0) {
+            movableBoxes[hand].update(dx, -dy);
+          }
+        }
+      }
+
       function initXR() {
         xrButton = new XRDeviceButton({
           onRequestSession: onRequestSession,
@@ -117,6 +224,25 @@
         fallbackHelper.emulateStage = true;
       }
 
+      function addBox(x, y, z, r, g, b, box_list) {
+        let boxBuilder = new BoxBuilder();
+        boxBuilder.pushCube([0, 0, 0], 0.4);
+        let boxPrimitive = boxBuilder.finishPrimitive(renderer);
+        let boxMaterial = new PbrMaterial();
+        boxMaterial.baseColorFactor.value = [r, g, b, 1.0];
+        let boxRenderPrimitive = renderer.createRenderPrimitive(boxPrimitive, boxMaterial);
+        let boxNode = new Node();
+        boxNode.addRenderPrimitive(boxRenderPrimitive);
+        // Marks the node as one that needs to be checked when hit testing.
+        boxNode.selectable = true;
+        box_list.push({
+          node: boxNode,
+          renderPrimitive: boxRenderPrimitive,
+          position: [x, y, z]
+        });
+        scene.addNode(boxNode);
+      }
+
       function initGL() {
         if (gl)
           return;
@@ -131,29 +257,9 @@
         scene.inputRenderer.setControllerMesh(new Gltf2Node({url: '../media/gltf/controller/controller.gltf'}));
 
         // Create several boxes to use for hit testing.
-        let boxBuilder = new BoxBuilder();
-        boxBuilder.pushCube([0, 0, 0], 0.4);
-        let boxPrimitive = boxBuilder.finishPrimitive(renderer);
-
-        function addBox(x, y, z, r, g, b) {
-          let boxMaterial = new PbrMaterial();
-          boxMaterial.baseColorFactor.value = [r, g, b, 1.0];
-          let boxRenderPrimitive = renderer.createRenderPrimitive(boxPrimitive, boxMaterial);
-          let boxNode = new Node();
-          boxNode.addRenderPrimitive(boxRenderPrimitive);
-          // Marks the node as one that needs to be checked when hit testing.
-          boxNode.selectable = true;
-          boxes.push({
-            node: boxNode,
-            renderPrimitive: boxRenderPrimitive,
-            position: [x, y, z]
-          });
-          scene.addNode(boxNode);
-        }
-
-        addBox(-1.0, 1.6, -1.3, 1.0, 0.0, 0.0);
-        addBox(0.0, 1.7, -1.5, 0.0, 1.0, 0.0);
-        addBox(1.0, 1.6, -1.3, 0.0, 0.0, 1.0);
+        addBox(-1.0, 1.6, -1.3, 1.0, 0.0, 0.0, boxes);
+        addBox(0.0, 1.7, -1.5, 0.0, 1.0, 0.0, boxes);
+        addBox(1.0, 1.6, -1.3, 0.0, 0.0, 1.0, boxes);
 
         addFloorBox();
       }
@@ -200,27 +306,17 @@
         });
 
         // Same logic for establishing a reference space as in room-scale.html
-        session.requestReferenceSpace({ type: 'bounded' }).then((refSpace) => {
-          if (!boundsRenderer) {
-            boundsRenderer = new BoundsRenderer(refSpace);
-            scene.addNode(boundsRenderer);
-          } else {
-            boundsRenderer.boundedRefSpace = refSpace;
-          }
+        session.requestReferenceSpace({ type: 'stationary', subtype: 'floor-level' }).then((refSpace) => {
           return refSpace;
-        }).catch(() => {
-          console.log('Falling back to floor-level reference space');
-          return session.requestReferenceSpace({ type: 'stationary', subtype: 'floor-level' }).catch((e) => {
-            if (!session.mode.startsWith('immersive')) {
-              console.log('Falling back to identity reference space');
-              return session.requestReferenceSpace({ type: 'identity' }).then((refSpace) => {
-                refSpace.originOffset = new XRRigidTransform({y: -1.6});
-                return refSpace;
-              });
-            } else {
-              throw e;
-            }
-          });
+        }, (e) => {
+          if (!session.mode.startsWith('immersive')) {
+            // If we're in inline mode, our underlying platform may not support
+            // the stationary reference space, but an identity space is guaranteed.
+            console.log("falling back to identity reference space");
+            return session.requestReferenceSpace({ type: 'identity' });
+          } else {
+            throw e;
+          }
         }).then((refSpace) => {
           if (session.mode.startsWith('immersive')) {
             xrImmersiveRefSpace = refSpace;
@@ -358,13 +454,32 @@
         scene.startFrame();
         session.requestAnimationFrame(onXRFrame);
 
-        // Update the matrix for each box
-        for (let box of boxes) {
-          let node = box.node;
-          mat4.identity(node.matrix);
-          mat4.translate(node.matrix, node.matrix, box.position);
-          mat4.rotateX(node.matrix, node.matrix, time/1000);
-          mat4.rotateY(node.matrix, node.matrix, time/1500);
+        function updateBoxes(box_list, rotate) {
+          // Update the matrix for each box
+          for (let box of box_list) {
+            let node = box.node;
+            mat4.identity(node.matrix);
+            mat4.translate(node.matrix, node.matrix, box.position);
+            if (rotate) {
+              mat4.rotateX(node.matrix, node.matrix, time/1000);
+              mat4.rotateY(node.matrix, node.matrix, time/1500);
+            }
+          }
+        }
+
+        for (let source of session.getInputSources()) {
+          let gamepad = source.gamepad;
+          if (gamepad) {
+            ProcessGamepad(gamepad, source);
+          }
+        }
+
+        updateBoxes(boxes, true);
+        for (let hand in boxTable) {
+          updateBoxes(boxTable[hand].box_list, false);
+        }
+        for (let hand in movableBoxes) {
+          updateBoxes(movableBoxes[hand].box_list, false);
         }
 
         // In this sample and most samples after it we'll use a helper function
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids
index 5c8eb8c..a1610f3 100644
--- a/tools/gritsettings/resource_ids
+++ b/tools/gritsettings/resource_ids
@@ -368,7 +368,7 @@
   },
 
   "net/base/net_resources.grd": {
-    "includes": [24860],
+    "includes": [24890],
   },
 
   "remoting/resources/remoting_strings.grd": {
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index be2e5cb..b75db81 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -22932,6 +22932,9 @@
   <int value="2869" label="V8RTCQuicTransport_SendDatagram_Method"/>
   <int value="2870" label="V8RTCQuicTransport_ReceiveDatagrams_Method"/>
   <int value="2871" label="CSSValueContainStyle"/>
+  <int value="2872" label="WebShareContainingFiles"/>
+  <int value="2873" label="WebShareWithoutFiles"/>
+  <int value="2874" label="WebShareCancelled"/>
 </enum>
 
 <enum name="FeaturePolicyFeature">
diff --git a/ui/aura/window_event_dispatcher.cc b/ui/aura/window_event_dispatcher.cc
index 0766de1e..5ba3862 100644
--- a/ui/aura/window_event_dispatcher.cc
+++ b/ui/aura/window_event_dispatcher.cc
@@ -6,6 +6,8 @@
 
 #include <stddef.h>
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -235,7 +237,7 @@
               held_event_factory_.GetWeakPtr()));
     } else {
       if (did_dispatch_held_move_event_callback_)
-        base::ResetAndReturn(&did_dispatch_held_move_event_callback_).Run();
+        std::move(did_dispatch_held_move_event_callback_).Run();
     }
   }
   TRACE_EVENT_ASYNC_END0("ui", "WindowEventDispatcher::HoldPointerMoves", this);
@@ -821,7 +823,7 @@
 ui::EventDispatchDetails WindowEventDispatcher::DispatchHeldEvents() {
   if (!held_repostable_event_ && !held_move_event_) {
     if (did_dispatch_held_move_event_callback_)
-      base::ResetAndReturn(&did_dispatch_held_move_event_callback_).Run();
+      std::move(did_dispatch_held_move_event_callback_).Run();
     return DispatchDetails();
   }
 
@@ -866,7 +868,7 @@
       observer.OnWindowEventDispatcherDispatchedHeldEvents(this);
     }
     if (did_dispatch_held_move_event_callback_)
-      base::ResetAndReturn(&did_dispatch_held_move_event_callback_).Run();
+      std::move(did_dispatch_held_move_event_callback_).Run();
   }
 
   return dispatch_details;
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 098da250..317ac69 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -44,6 +44,7 @@
 
   deps = [
     "//base",
+    "//net",
   ]
 
   defines = [ "UI_DATA_PACK_IMPLEMENTATION" ]
diff --git a/ui/base/resource/data_pack.cc b/ui/base/resource/data_pack.cc
index f6cbde5..da3771f 100644
--- a/ui/base/resource/data_pack.cc
+++ b/ui/base/resource/data_pack.cc
@@ -21,6 +21,7 @@
 #include "base/synchronization/lock.h"
 #include "base/sys_byteorder.h"
 #include "build/build_config.h"
+#include "net/filter/gzip_header.h"
 
 // For details of the file layout, see
 // http://dev.chromium.org/developers/design-documents/linuxresourcesandlocalizedstrings
@@ -84,6 +85,14 @@
   }
 }
 
+bool HasGzipHeader(base::StringPiece* data) {
+  net::GZipHeader header;
+  const char* header_end = nullptr;
+  net::GZipHeader::Status header_status =
+      header.ReadMore(data->data(), data->length(), &header_end);
+  return header_status == net::GZipHeader::COMPLETE_HEADER;
+}
+
 // Convenience class to write data to a file. Usage is the following:
 // 1) Create a new instance, passing a base::FilePath.
 // 2) Call Write() repeatedly to write all desired data to the file.
@@ -380,6 +389,17 @@
   return !!LookupEntryById(resource_id);
 }
 
+bool DataPack::IsGzipped(uint16_t resource_id, bool* is_gzipped) const {
+  DCHECK(is_gzipped);
+  if (!HasResource(resource_id))
+    return false;
+
+  base::StringPiece data;
+  CHECK(GetStringPiece(resource_id, &data));
+  *is_gzipped = HasGzipHeader(&data);
+  return true;
+}
+
 bool DataPack::GetStringPiece(uint16_t resource_id,
                               base::StringPiece* data) const {
   // It won't be hard to make this endian-agnostic, but it's not worth
diff --git a/ui/base/resource/data_pack.h b/ui/base/resource/data_pack.h
index 6d252b0..3a8f66c 100644
--- a/ui/base/resource/data_pack.h
+++ b/ui/base/resource/data_pack.h
@@ -60,6 +60,7 @@
 
   // ResourceHandle implementation:
   bool HasResource(uint16_t resource_id) const override;
+  bool IsGzipped(uint16_t resource_id, bool* is_gzipped) const override;
   bool GetStringPiece(uint16_t resource_id,
                       base::StringPiece* data) const override;
   base::RefCountedStaticMemory* GetStaticMemory(
diff --git a/ui/base/resource/data_pack_literal.cc b/ui/base/resource/data_pack_literal.cc
index 3218f848..7d35c6a2 100644
--- a/ui/base/resource/data_pack_literal.cc
+++ b/ui/base/resource/data_pack_literal.cc
@@ -29,10 +29,15 @@
     0x01, 0x00, 0x28, 0x00, 0x00, 0x00,  // index entry 1
     0x04, 0x00, 0x28, 0x00, 0x00, 0x00,  // index entry 4
     0x06, 0x00, 0x34, 0x00, 0x00, 0x00,  // index entry 6
-    0x00, 0x00, 0x40, 0x00, 0x00, 0x00,  // extra entry for the size of last
+    0x00, 0x00, 0x52, 0x00, 0x00, 0x00,  // extra entry for the size of last
     0x0a, 0x00, 0x01, 0x00,              // alias table
-    't',  'h',  'i',  's',  ' ',  'i',  's', ' ', 'i', 'd', ' ', '4',
-    't',  'h',  'i',  's',  ' ',  'i',  's', ' ', 'i', 'd', ' ', '6'};
+
+    't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'i', 'd', ' ', '4',
+    // "this is id 6" gzipped with
+    // echo "this is id 6" | gzip -f -c | hexdump -C
+    0x1f, 0x8b, 0x08, 0x00, 0x2e, 0x15, 0xc1, 0x5c, 0x00, 0x03, 0x2b, 0xc9,
+    0xc8, 0x2c, 0x56, 0x00, 0xa1, 0x14, 0x05, 0x33, 0x2e, 0x00, 0x10, 0x61,
+    0x1b, 0x8b, 0x0d, 0x00, 0x00, 0x00};
 
 const size_t kSamplePakSizeV5 = sizeof(kSamplePakContentsV5);
 
diff --git a/ui/base/resource/data_pack_unittest.cc b/ui/base/resource/data_pack_unittest.cc
index 9ddeee9a..17bd93d 100644
--- a/ui/base/resource/data_pack_unittest.cc
+++ b/ui/base/resource/data_pack_unittest.cc
@@ -175,7 +175,6 @@
   EXPECT_EQ("this is id 4", data);
   ASSERT_TRUE(pack.HasResource(6));
   ASSERT_TRUE(pack.GetStringPiece(6, &data));
-  EXPECT_EQ("this is id 6", data);
 
   // Try reading zero-length data blobs, just in case.
   ASSERT_TRUE(pack.GetStringPiece(1, &data));
@@ -186,6 +185,16 @@
   // Try looking up an invalid key.
   ASSERT_FALSE(pack.HasResource(140));
   ASSERT_FALSE(pack.GetStringPiece(140, &data));
+
+  bool is_gzipped;
+  ASSERT_TRUE(pack.IsGzipped(1, &is_gzipped));
+  ASSERT_FALSE(is_gzipped);
+  ASSERT_TRUE(pack.IsGzipped(4, &is_gzipped));
+  ASSERT_FALSE(is_gzipped);
+  ASSERT_TRUE(pack.IsGzipped(6, &is_gzipped));
+  ASSERT_TRUE(is_gzipped);
+  ASSERT_TRUE(pack.IsGzipped(10, &is_gzipped));
+  ASSERT_FALSE(is_gzipped);
 }
 
 INSTANTIATE_TEST_SUITE_P(WriteBINARY,
diff --git a/ui/base/resource/resource_bundle.cc b/ui/base/resource/resource_bundle.cc
index ada0da2..ab883f7 100644
--- a/ui/base/resource/resource_bundle.cc
+++ b/ui/base/resource/resource_bundle.cc
@@ -555,6 +555,15 @@
   return base::StringPiece();
 }
 
+bool ResourceBundle::IsGzipped(int resource_id) const {
+  bool is_gzipped;
+  for (const auto& pack : data_packs_) {
+    if (pack->IsGzipped(resource_id, &is_gzipped))
+      return is_gzipped;
+  }
+  return false;
+}
+
 base::string16 ResourceBundle::GetLocalizedString(int resource_id) {
 #if DCHECK_IS_ON()
   {
diff --git a/ui/base/resource/resource_bundle.h b/ui/base/resource/resource_bundle.h
index 1f0f0b5..23005f6 100644
--- a/ui/base/resource/resource_bundle.h
+++ b/ui/base/resource/resource_bundle.h
@@ -217,6 +217,10 @@
   // Loads the raw bytes of a scale independent data resource.
   base::RefCountedMemory* LoadDataResourceBytes(int resource_id) const;
 
+  // Whether the |resource_id| is gzipped in this bundle. False is also returned
+  // if the resource is not found.
+  bool IsGzipped(int resource_id) const;
+
   // Loads the raw bytes of a data resource nearest the scale factor
   // |scale_factor| into |bytes|, without doing any processing or
   // interpretation of the resource. Use ResourceHandle::SCALE_FACTOR_NONE
diff --git a/ui/base/resource/resource_bundle_unittest.cc b/ui/base/resource/resource_bundle_unittest.cc
index be36b9a..70b0539 100644
--- a/ui/base/resource/resource_bundle_unittest.cc
+++ b/ui/base/resource/resource_bundle_unittest.cc
@@ -246,6 +246,25 @@
   EXPECT_EQ(string_piece.data(), result.data());
 }
 
+TEST_F(ResourceBundleTest, IsGzipped) {
+  base::ScopedTempDir dir;
+  ASSERT_TRUE(dir.CreateUniqueTempDir());
+  base::FilePath data_path =
+      dir.GetPath().Append(FILE_PATH_LITERAL("sample.pak"));
+  // Dump contents into a pak file and load it.
+  ASSERT_EQ(base::WriteFile(data_path, kSamplePakContentsV5, kSamplePakSizeV5),
+            static_cast<int>(kSamplePakSizeV5));
+  ResourceBundle* resource_bundle = CreateResourceBundle(nullptr);
+  resource_bundle->AddDataPackFromPath(data_path, SCALE_FACTOR_100P);
+
+  ASSERT_FALSE(resource_bundle->IsGzipped(1));
+  ASSERT_FALSE(resource_bundle->IsGzipped(4));
+  ASSERT_TRUE(resource_bundle->IsGzipped(6));
+  ASSERT_FALSE(resource_bundle->IsGzipped(10));
+  // Ask for a non-existent resource ID.
+  ASSERT_FALSE(resource_bundle->IsGzipped(200));
+}
+
 TEST_F(ResourceBundleTest, DelegateGetLocalizedString) {
   MockResourceBundleDelegate delegate;
   ResourceBundle* resource_bundle = CreateResourceBundle(&delegate);
diff --git a/ui/base/resource/resource_data_dll_win.cc b/ui/base/resource/resource_data_dll_win.cc
index 12c54c99..1539715 100644
--- a/ui/base/resource/resource_data_dll_win.cc
+++ b/ui/base/resource/resource_data_dll_win.cc
@@ -28,6 +28,10 @@
                                               &data_size);
 }
 
+bool ResourceDataDLL::IsGzipped(uint16_t resource_id, bool* is_gzipped) const {
+  return false;
+}
+
 bool ResourceDataDLL::GetStringPiece(uint16_t resource_id,
                                      base::StringPiece* data) const {
   DCHECK(data);
diff --git a/ui/base/resource/resource_data_dll_win.h b/ui/base/resource/resource_data_dll_win.h
index c5f0e60..1e21a828 100644
--- a/ui/base/resource/resource_data_dll_win.h
+++ b/ui/base/resource/resource_data_dll_win.h
@@ -21,6 +21,7 @@
 
   // ResourceHandle implementation:
   bool HasResource(uint16_t resource_id) const override;
+  bool IsGzipped(uint16_t resource_id, bool* is_gzipped) const override;
   bool GetStringPiece(uint16_t resource_id,
                       base::StringPiece* data) const override;
   base::RefCountedStaticMemory* GetStaticMemory(
diff --git a/ui/base/resource/resource_handle.h b/ui/base/resource/resource_handle.h
index 055b037..1ebd500 100644
--- a/ui/base/resource/resource_handle.h
+++ b/ui/base/resource/resource_handle.h
@@ -31,6 +31,10 @@
   // Returns true if the DataPack contains a resource with id |resource_id|.
   virtual bool HasResource(uint16_t resource_id) const = 0;
 
+  // Whether a specific |resource_id| is gzipped. Returns false if
+  // !HasResource(resource_id). |is_gzipped| cannot be nullptr.
+  virtual bool IsGzipped(uint16_t resource_id, bool* is_gzipped) const = 0;
+
   // Get resource by id |resource_id|, filling in |data|.
   // The data is owned by the DataPack object and should not be modified.
   // Returns false if the resource id isn't found.
diff --git a/ui/display/manager/display_manager.cc b/ui/display/manager/display_manager.cc
index e755be6..4cf951234 100644
--- a/ui/display/manager/display_manager.cc
+++ b/ui/display/manager/display_manager.cc
@@ -2156,7 +2156,7 @@
 void DisplayManager::CreateMirrorWindowIfAny() {
   if (software_mirroring_display_list_.empty() || !delegate_) {
     if (!created_mirror_window_.is_null())
-      base::ResetAndReturn(&created_mirror_window_).Run();
+      std::move(created_mirror_window_).Run();
     return;
   }
   DisplayInfoList list;
@@ -2164,7 +2164,7 @@
     list.push_back(GetDisplayInfo(display.id()));
   delegate_->CreateOrUpdateMirroringDisplay(list);
   if (!created_mirror_window_.is_null())
-    base::ResetAndReturn(&created_mirror_window_).Run();
+    std::move(created_mirror_window_).Run();
 }
 
 void DisplayManager::ApplyDisplayLayout(DisplayLayout* layout,
diff --git a/ui/file_manager/file_manager/background/js/crostini.js b/ui/file_manager/file_manager/background/js/crostini.js
index 0f1acb2..38187ac 100644
--- a/ui/file_manager/file_manager/background/js/crostini.js
+++ b/ui/file_manager/file_manager/background/js/crostini.js
@@ -243,6 +243,14 @@
     return false;
   }
 
+  // Special case to disallow PluginVm sharing on /MyFiles/PluginVm and
+  // subfolders since it gets shared by default.
+  if (vmName === CrostiniImpl.PLUGIN_VM &&
+      root === VolumeManagerCommon.RootType.DOWNLOADS &&
+      entry.fullPath.split('/')[1] === CrostiniImpl.PLUGIN_VM) {
+    return false;
+  }
+
   return CrostiniImpl.VALID_ROOT_TYPES_FOR_SHARE.has(root) ||
       (loadTimeData.getBoolean('DRIVE_FS_ENABLED') &&
        CrostiniImpl.VALID_DRIVE_FS_ROOT_TYPES_FOR_SHARE.has(root));
diff --git a/ui/file_manager/file_manager/test/plugin_vm.js b/ui/file_manager/file_manager/test/plugin_vm.js
index 858c0e0..298f016 100644
--- a/ui/file_manager/file_manager/test/plugin_vm.js
+++ b/ui/file_manager/file_manager/test/plugin_vm.js
@@ -13,7 +13,6 @@
     ['#delete', false],
     ['#zip-selection', true],
     ['#share-with-linux', true],
-    ['#manage-plugin-vm-sharing', true],
     ['#new-folder', true],
   ];
 
@@ -26,7 +25,6 @@
     ['#delete', true],
     ['#zip-selection', true],
     ['#share-with-linux', true],
-    ['#manage-plugin-vm-sharing', true],
     ['#new-folder', true],
   ];
 
@@ -63,13 +61,6 @@
   await test.waitForFiles(test.TestEntryInfo.getExpectedRows(
       [test.ENTRIES.pluginVm, test.ENTRIES.linuxFiles]));
 
-  // Register /PluginVm as shared.
-  const pluginVmEntry =
-      mockVolumeManager
-          .getCurrentProfileVolumeInfo(VolumeManagerCommon.VolumeType.DOWNLOADS)
-          .fileSystem.entries['/PluginVm'];
-  fileManager.crostini.registerSharedPath('PluginVm', pluginVmEntry);
-
   // Verify folder icon.
   await test.waitForElement(iconFolder);
 
diff --git a/ui/views/mus/remote_view/remote_view_provider.cc b/ui/views/mus/remote_view/remote_view_provider.cc
index a4c2fd3..2b8d9d5 100644
--- a/ui/views/mus/remote_view/remote_view_provider.cc
+++ b/ui/views/mus/remote_view/remote_view_provider.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/scoped_observer.h"
 #include "ui/aura/mus/embed_root.h"
@@ -40,7 +39,7 @@
     DCHECK(!on_destroyed_.is_null());
 
     window_observer_.RemoveAll();
-    base::ResetAndReturn(&on_destroyed_).Run();
+    std::move(on_destroyed_).Run();
   }
 
  private:
diff --git a/ui/webui/resources/cr_elements/cr_slider/cr_slider.html b/ui/webui/resources/cr_elements/cr_slider/cr_slider.html
index e33cbdd..18d329e 100644
--- a/ui/webui/resources/cr_elements/cr_slider/cr_slider.html
+++ b/ui/webui/resources/cr_elements/cr_slider/cr_slider.html
@@ -1,7 +1,6 @@
 <link rel="import" href="../../html/polymer.html">
 
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-behaviors/paper-ripple-behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout.html">
 <link rel="import" href="../../html/cr.html">
 <link rel="import" href="../../html/event_tracker.html">
 <link rel="import" href="../hidden_style_css.html">
@@ -12,7 +11,6 @@
     <style include="cr-hidden-style">
       :host {
         --cr-slider-active-color: var(--google-blue-600);
-        --cr-slider-bar-height: 2px;
         --cr-slider-container-color: rgba(var(--google-blue-600-rgb), .24);
         --cr-slider-container-disabled-color:
             rgba(var(--google-grey-600-rgb), .24);
@@ -27,7 +25,9 @@
 
         -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
         cursor: default;
+        height: 32px;
         outline: none;
+        padding: 0 16px;
         user-select: none;
       }
 
@@ -51,12 +51,7 @@
         touch-action: none;
       }
 
-      #container {
-        height: 32px;
-        position: relative;
-      }
-
-      #barContainer,
+      #container,
       #bar {
         /* Using border instead of background-color to address pixel rounding
            at low zoom levels (e.g. 33%). The browser will round border widths
@@ -65,22 +60,58 @@
         border-top-width: 2px;
       }
 
-      #barContainer {
+      #container {
         border-top-color: var(--cr-slider-container-color);
-        height: var(--cr-slider-bar-height);
-        margin: 0 16px;
-        position: absolute;
-        top: 15px;
-        width: calc(100% - 32px);
+        position: relative;
+        top: 16px;
       }
 
-      #barContainer div {
-        top: calc(-1 * var(--cr-slider-bar-height));
+      #container > div {
+        position: absolute;
+      }
+
+      #markers,
+      #bar {
+        top: -2px;
+      }
+
+      #markers {
+        display: flex;
+        flex-direction: row;
+        left: 0;
+        pointer-events: none;
+        right: 0;
+      }
+
+      .active-marker,
+      .inactive-marker {
+        flex: 1;
+      }
+
+      #markers::before,
+      #markers::after,
+      .active-marker::after,
+      .inactive-marker::after {
+        border-radius: 50%;
+        content: '';
+        display: block;
+        height: 2px;
+        margin-inline-start: -1px;
+        width: 2px;
+      }
+
+      #markers::before,
+      .active-marker::after {
+        background-color: var(--cr-slider-marker-active-color);
+      }
+
+      #markers::after,
+      .inactive-marker::after {
+        background-color: var(--cr-slider-marker-color);
       }
 
       #bar {
         border-top-color: var(--cr-slider-active-color);
-        position: absolute;
         width: 0;
       }
 
@@ -88,27 +119,50 @@
         transition: width var(--cr-slider-position-transition);
       }
 
-      #knobContainer {
-        margin-inline-start: 12px;
-        position: absolute;
-        top: 11px;
-        width: calc(100% - 32px);
+      #knobAndLabel {
+        top: -1px;
+      }
+
+      :host([transiting_]) #knobAndLabel {
+        transition: margin-inline-start var(--cr-slider-position-transition);
       }
 
       #knob {
         background-color: var(--cr-slider-knob-color);
-        border: 0;
         border-radius: 50%;
         box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4);
         height: 10px;
-        margin-inline-start: 0;
         outline: none;
-        position: absolute;
+        transform: translate(-50%, -50%);
         width: 10px;
       }
 
-      :host([transiting_]) #knob {
-        transition: margin-inline-start var(--cr-slider-position-transition);
+      :host([is-rtl_]) #knob {
+        transform: translate(50%, -50%);
+      }
+
+      #label {
+        background: var(--cr-slider-knob-color);
+        border-radius: 14px;
+        bottom: 22px;
+        color: white;  /* Same for dark and light mode. */
+        font-size: 12px;
+        line-height: 1.5em;
+        opacity: 0;
+        padding: 0 8px;
+        position: absolute;
+        transform: translateX(-50%);
+        transition: opacity 80ms ease-in-out;
+        white-space: nowrap;
+      }
+
+      :host([is-rtl_]) #label {
+        transform: translateX(50%);
+      }
+
+      :host(:hover) #label,
+      :host([hold-down_]) #label {
+        opacity: 1;
       }
 
       paper-ripple {
@@ -127,76 +181,11 @@
         right: -11px;
       }
 
-      #markers {
-        left: 0;
-        pointer-events: none;
-        position: absolute;
-        right: 0;
-        @apply --layout-horizontal;
-      }
-
-      .active-marker,
-      .inactive-marker {
-        @apply --layout-flex;
-      }
-      #markers::before,
-      #markers::after,
-      .active-marker::after,
-      .inactive-marker::after {
-        border-radius: 50%;
-        content: '';
-        display: block;
-        height: 2px;
-        margin-left: -1px;
-        width: 2px;
-      }
-
-      #markers::before,
-      .active-marker::after {
-        background-color: var(--cr-slider-marker-active-color);
-      }
-
-      #markers::after,
-      .inactive-marker::after {
-        background-color: var(--cr-slider-marker-color);
-      }
-
-      #labelContainer {
-        cursor: default;
-        margin-inline-start: 1px;
-        opacity: 0;
-        transition: opacity 80ms ease-in-out;
-        user-select: none;
-        width: calc(100% - 32px);
-      }
-
-      #container:hover #labelContainer,
-      .hover #labelContainer,
-      :host([hold-down_]) #labelContainer {
-        opacity: 1;
-      }
-
-      #label {
-        background: var(--cr-slider-knob-color);
-        border-radius: 14px;
-        bottom: 28px;
-        color: white;  /* Same for dark and light mode. */
-        font-size: 12px;
-        line-height: 1.5em;
-        padding: 0 8px;
-        position: absolute;
-        white-space: nowrap;
-      }
-
-      :host([transiting_]) #label {
-        transition: margin-inline-start var(--cr-slider-position-transition);
-      }
-
       :host([disabled_]) {
         pointer-events: none;
       }
 
-      :host([disabled_]) #barContainer {
+      :host([disabled_]) #container {
         border-top-color: var(--cr-slider-container-disabled-color);
       }
 
@@ -209,10 +198,6 @@
         background-color: var(--cr-slider-marker-disabled-color);
       }
 
-      :host([disabled_]) #knobContainer {
-        margin-inline-start: 10px;
-        top: 9px;
-      }
       :host([disabled_]) #knob {
         background-color: var(--cr-slider-disabled-color);
         border: 2px solid var(--cr-slider-knob-disabled-color);
@@ -220,20 +205,15 @@
       }
     </style>
     <div id="container">
-      <div id="barContainer">
-        <div id="bar"></div>
-        <div id="markers" hidden$="[[!markerCount]]">
-          <template is="dom-repeat" items="[[getMarkers_(markerCount)]]">
-            <div class$="[[getMarkerClass_(index, value, min, max,
-                                           markerCount)]]"></div>
-          </template>
-        </div>
+      <div id="bar"></div>
+      <div id="markers" hidden$="[[!markerCount]]">
+        <template is="dom-repeat" items="[[getMarkers_(markerCount)]]">
+          <div class$="[[getMarkerClass_(index, value, min, max,
+                                         markerCount)]]"></div>
+        </template>
       </div>
-      <div id="knobContainer">
-        <div id="knob" on-transitionend="onKnobTransitionEnd_"
-            on-keydown="onKnobKeydown_"></div>
-      </div>
-      <div id="labelContainer" aria-label="[[label_]]">
+      <div id="knobAndLabel" on-transitionend="onTransitionEnd_">
+        <div id="knob" on-keydown="onKnobKeydown_"></div>
         <div id="label">[[label_]]</div>
       </div>
     </div>
diff --git a/ui/webui/resources/cr_elements/cr_slider/cr_slider.js b/ui/webui/resources/cr_elements/cr_slider/cr_slider.js
index 9578da2..b0f8474 100644
--- a/ui/webui/resources/cr_elements/cr_slider/cr_slider.js
+++ b/ui/webui/resources/cr_elements/cr_slider/cr_slider.js
@@ -35,6 +35,20 @@
   }
 
   /**
+   * @param {!(cr_slider.SliderTick|number)} tick
+   * @return {number}
+   */
+  function getAriaValue(tick) {
+    if (Number.isFinite(/** @type {number} */ (tick))) {
+      return /** @type {number} */ (tick);
+    } else if (tick.ariaValue != undefined) {
+      return /** @type {number} */ (tick.ariaValue);
+    } else {
+      return tick.value;
+    }
+  }
+
+  /**
    * The following are the events emitted from cr-slider.
    *
    * cr-slider-value-changed: fired when updating slider via the UI.
@@ -159,8 +173,7 @@
 
     observers: [
       'onTicksChanged_(ticks.*)',
-      'updateLabelAndAria_(value, min, max)',
-      'updateKnobAndBar_(value, min, max)',
+      'updateUi_(ticks.*, value, min, max)',
       'updateValue_(value, min, max)',
     ],
 
@@ -327,11 +340,6 @@
       }
     },
 
-    /** @private */
-    onKnobTransitionEnd_: function() {
-      this.transiting_ = false;
-    },
-
     /**
      * When the left-mouse button is pressed, the knob location is updated and
      * dragging starts.
@@ -388,51 +396,33 @@
     },
 
     /** @private */
-    updateKnobAndBar_: function() {
-      const percent = `${this.getRatio() * 100}%`;
-      this.$.bar.style.width = percent;
-      this.$.knob.style.marginInlineStart = percent;
+    onTransitionEnd_: function() {
+      this.transiting_ = false;
     },
 
     /** @private */
-    updateLabelAndAria_: function() {
+    updateUi_: function() {
+      const percent = `${this.getRatio() * 100}%`;
+      this.$.bar.style.width = percent;
+      this.$.knobAndLabel.style.marginInlineStart = percent;
+
       const ticks = this.ticks;
-      const index = this.value;
-      if (!ticks || ticks.length == 0 || index >= ticks.length ||
-          !Number.isInteger(index) || !this.snaps) {
-        this.setAttribute('aria-valuetext', index);
+      const value = this.value;
+      if (ticks && ticks.length > 0 && value >= 0 && value < ticks.length &&
+          Number.isInteger(value)) {
+        const tick = ticks[this.value];
+        this.label_ = Number.isFinite(tick) ? '' : tick.label;
+        const ariaValueNow = getAriaValue(tick);
+        this.setAttribute('aria-valuetext', this.label_ || ariaValueNow);
+        this.setAttribute('aria-valuenow', ariaValueNow);
+        this.setAttribute('aria-valuemin', getAriaValue(ticks[0]));
+        this.setAttribute('aria-valuemax', getAriaValue(ticks.slice(-1)[0]));
+      } else {
+        this.setAttribute('aria-valuetext', value);
+        this.setAttribute('aria-valuenow', value);
         this.setAttribute('aria-valuemin', this.min);
         this.setAttribute('aria-valuemax', this.max);
-        this.setAttribute('aria-valuenow', index);
-        return;
       }
-      const tick = ticks[index];
-      this.label_ = Number.isFinite(tick) ? '' : tick.label;
-
-      // Update label location after it has been rendered.
-      setTimeout(() => {
-        const label = this.$.label;
-        const parentWidth = label.parentElement.offsetWidth;
-        const labelWidth = label.offsetWidth;
-        // The left and right margin are 16px.
-        const margin = 16;
-        const knobLocation = parentWidth * this.getRatio() + margin;
-        const offsetStart = knobLocation - (labelWidth / 2);
-        label.style.marginInlineStart = `${Math.round(offsetStart)}px`;
-      });
-
-      const ariaValues = [tick, ticks[0], ticks[ticks.length - 1]].map(t => {
-        if (Number.isFinite(t)) {
-          return t;
-        }
-        return Number.isFinite(t.ariaValue) ? t.ariaValue : t.value;
-      });
-      this.setAttribute(
-          'aria-valuetext',
-          this.label_.length > 0 ? this.label_ : ariaValues[0]);
-      this.setAttribute('aria-valuenow', ariaValues[0]);
-      this.setAttribute('aria-valuemin', ariaValues[1]);
-      this.setAttribute('aria-valuemax', ariaValues[2]);
     },
 
     /**
@@ -462,7 +452,7 @@
      * @private
      */
     updateValueFromClientX_: function(clientX) {
-      const rect = this.$.barContainer.getBoundingClientRect();
+      const rect = this.$.container.getBoundingClientRect();
       let ratio = (clientX - rect.left) / rect.width;
       if (this.isRtl_) {
         ratio = 1 - ratio;