diff --git a/BUILD.gn b/BUILD.gn
index 012bd5d..d7058700 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -156,7 +156,6 @@
       "//content/test:content_perftests",
       "//content/test:content_unittests",
       "//gpu:gpu_unittests",
-      "//gpu/ipc/service:gpu_ipc_service_unittests",
       "//ipc:ipc_tests",
       "//media:media_unittests",
       "//media/midi:midi_unittests",
@@ -179,8 +178,7 @@
     ]
   }
 
-  # TODO(https://crbug.com/812268): Fix Windows build, and enable it.
-  if (!is_ios && !is_android && !is_win) {
+  if (!is_ios && !is_android) {
     deps += [
       "//components/cronet:cronet_tests",
       "//components/cronet:cronet_unittests",
diff --git a/DEPS b/DEPS
index 2ee78fc..568c1e88 100644
--- a/DEPS
+++ b/DEPS
@@ -79,11 +79,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '103e7243718a0ba9d4ede671b9c1ac7207d68786',
+  'skia_revision': '2ef4525daf153fe8927727e2131a35d520e90269',
   # 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': '7ab45c02324d2c2221d51fc4d3db0fe537724d30',
+  'v8_revision': '6e9cb6944b56cc88ed142e1779cc53aa2ce37db2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -91,7 +91,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '822a84b1959f9c1bb615ad4dff2569c58fb3b225',
+  'angle_revision': '84fdc62c61609d5b9b8fe7c77ce85522837549c8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -103,7 +103,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '3ae75c2f2e5759fcf8218e59fa380c26b98aa6b6',
+  'pdfium_revision': '5098b252848734ac69e0851f7bd116d93311a57e',
   # 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.
@@ -139,7 +139,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'bffbf166f004d3d44f13cc4672674ca580e09a49',
+  'catapult_revision': '767f070f1df4536e1d5be94ac04153a553b9c5f2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -408,7 +408,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '44123785ca9a78989d225628ee44d5bc1825439c',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '44e40f3f36712c6ba20055407e05b5f6d7f807bc',
       'condition': 'checkout_linux',
   },
 
@@ -433,7 +433,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'dbbf350a34ab7991e635f91a70bd2d211eb921be',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'ee04b9a2f4890af563a21a69871d20fd6a586ce4',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -873,7 +873,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '3c1cb0203b6cfc10389e85a350b2ea6ca29d01ce',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'dc4737bbdacc7ff662af0f0c33ac9bb8a5ddb28b', # commit position 21742
+    Var('webrtc_git') + '/src.git' + '@' + 'bb50ce5bb6d580b243b0fde532c596d6f4248083', # commit position 21742
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 040d7c2..81780c5 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -860,7 +860,7 @@
         onContainerViewChanged();
     }
 
-    private void createContentViewCore(Context context, ViewAndroidDelegate viewDelegate,
+    private void createContentViewCore(ViewAndroidDelegate viewDelegate,
             InternalAccessDelegate internalDispatcher, WebContents webContents,
             WindowAndroid windowAndroid) {
         mContentViewCore = ContentViewCore.create(mContext, PRODUCT_VERSION, webContents,
@@ -870,7 +870,7 @@
                 new AwActionModeCallback(mContext, this, controller.getActionModeCallbackHelper()));
         if (mAutofillProvider != null) {
             controller.setNonSelectionActionModeCallback(
-                    new AutofillActionModeCallback(context, mAutofillProvider));
+                    new AutofillActionModeCallback(mContext, mAutofillProvider));
         }
         controller.setSelectionClient(SelectionClient.createSmartSelectionClient(webContents));
 
@@ -1133,7 +1133,7 @@
         mWindowAndroid = getWindowAndroid(mContext);
         mViewAndroidDelegate =
                 new AwViewAndroidDelegate(mContainerView, mContentsClient, mScrollOffsetManager);
-        createContentViewCore(mContext, mViewAndroidDelegate, mInternalAccessAdapter, webContents,
+        createContentViewCore(mViewAndroidDelegate, mInternalAccessAdapter, webContents,
                 mWindowAndroid.getWindowAndroid());
         nativeSetJavaPeers(mNativeAwContents, this, mWebContentsDelegate, mContentsClientBridge,
                 mIoThreadClient, mInterceptNavigationDelegate, mAutofillProvider);
diff --git a/ash/accessibility/accessibility_controller.cc b/ash/accessibility/accessibility_controller.cc
index 464702f..f2a00027 100644
--- a/ash/accessibility/accessibility_controller.cc
+++ b/ash/accessibility/accessibility_controller.cc
@@ -318,10 +318,9 @@
     client_->PlaySpokenFeedbackToggleCountdown(tick_count);
 }
 
-void AccessibilityController::NotifyAccessibilityStatusChanged(
-    AccessibilityNotificationVisibility notify) {
+void AccessibilityController::NotifyAccessibilityStatusChanged() {
   for (auto& observer : observers_)
-    observer.OnAccessibilityStatusChanged(notify);
+    observer.OnAccessibilityStatusChanged();
 }
 
 void AccessibilityController::SetClient(
@@ -342,7 +341,8 @@
   braille_display_connected_ = connected;
   if (braille_display_connected_)
     SetSpokenFeedbackEnabled(true, A11Y_NOTIFICATION_SHOW);
-  NotifyAccessibilityStatusChanged(A11Y_NOTIFICATION_SHOW);
+  NotifyAccessibilityStatusChanged();
+  NotifyShowAccessibilityNotification();
 }
 
 void AccessibilityController::SetFocusHighlightRect(
@@ -466,7 +466,7 @@
 
   autoclick_enabled_ = enabled;
 
-  NotifyAccessibilityStatusChanged(A11Y_NOTIFICATION_NONE);
+  NotifyAccessibilityStatusChanged();
 
   if (Shell::GetAshConfig() == Config::MASH) {
     if (!connector_)  // Null in tests.
@@ -511,7 +511,7 @@
 
   caret_highlight_enabled_ = enabled;
 
-  NotifyAccessibilityStatusChanged(A11Y_NOTIFICATION_NONE);
+  NotifyAccessibilityStatusChanged();
   UpdateAccessibilityHighlightingFromPrefs();
 }
 
@@ -525,7 +525,7 @@
 
   cursor_highlight_enabled_ = enabled;
 
-  NotifyAccessibilityStatusChanged(A11Y_NOTIFICATION_NONE);
+  NotifyAccessibilityStatusChanged();
   UpdateAccessibilityHighlightingFromPrefs();
 }
 
@@ -543,7 +543,7 @@
 
   focus_highlight_enabled_ = enabled;
 
-  NotifyAccessibilityStatusChanged(A11Y_NOTIFICATION_NONE);
+  NotifyAccessibilityStatusChanged();
   UpdateAccessibilityHighlightingFromPrefs();
 }
 
@@ -557,7 +557,7 @@
 
   high_contrast_enabled_ = enabled;
 
-  NotifyAccessibilityStatusChanged(A11Y_NOTIFICATION_NONE);
+  NotifyAccessibilityStatusChanged();
 
   // Under mash the UI service (window server) handles high contrast mode.
   if (Shell::GetAshConfig() == Config::MASH) {
@@ -589,7 +589,7 @@
   large_cursor_enabled_ = enabled;
   large_cursor_size_in_dip_ = size;
 
-  NotifyAccessibilityStatusChanged(A11Y_NOTIFICATION_NONE);
+  NotifyAccessibilityStatusChanged();
 
   ShellPort::Get()->SetCursorSize(
       large_cursor_enabled_ ? ui::CursorSize::kLarge : ui::CursorSize::kNormal);
@@ -606,7 +606,7 @@
 
   mono_audio_enabled_ = enabled;
 
-  NotifyAccessibilityStatusChanged(A11Y_NOTIFICATION_NONE);
+  NotifyAccessibilityStatusChanged();
   chromeos::CrasAudioHandler::Get()->SetOutputMonoEnabled(enabled);
 }
 
@@ -620,7 +620,10 @@
 
   spoken_feedback_enabled_ = enabled;
 
-  NotifyAccessibilityStatusChanged(spoken_feedback_notification_);
+  NotifyAccessibilityStatusChanged();
+  if (spoken_feedback_notification_ != A11Y_NOTIFICATION_NONE)
+    NotifyShowAccessibilityNotification();
+
   // TODO(warx): ChromeVox loading/unloading requires browser process started,
   // thus it is still handled on Chrome side.
 
@@ -656,7 +659,7 @@
 
   select_to_speak_enabled_ = enabled;
 
-  NotifyAccessibilityStatusChanged(A11Y_NOTIFICATION_NONE);
+  NotifyAccessibilityStatusChanged();
 }
 
 void AccessibilityController::UpdateStickyKeysFromPref() {
@@ -669,7 +672,7 @@
 
   sticky_keys_enabled_ = enabled;
 
-  NotifyAccessibilityStatusChanged(A11Y_NOTIFICATION_NONE);
+  NotifyAccessibilityStatusChanged();
 
   Shell::Get()->sticky_keys_controller()->Enable(enabled);
 }
@@ -684,7 +687,7 @@
 
   virtual_keyboard_enabled_ = enabled;
 
-  NotifyAccessibilityStatusChanged(A11Y_NOTIFICATION_NONE);
+  NotifyAccessibilityStatusChanged();
 
   keyboard::SetAccessibilityKeyboardEnabled(enabled);
 
@@ -706,4 +709,9 @@
     Shell::Get()->DestroyKeyboard();
 }
 
+void AccessibilityController::NotifyShowAccessibilityNotification() {
+  for (auto& observer : observers_)
+    observer.ShowAccessibilityNotification();
+}
+
 }  // namespace ash
diff --git a/ash/accessibility/accessibility_controller.h b/ash/accessibility/accessibility_controller.h
index 38cef0a..3efb8d5e 100644
--- a/ash/accessibility/accessibility_controller.h
+++ b/ash/accessibility/accessibility_controller.h
@@ -128,8 +128,7 @@
 
   // Public because a11y features like screen magnifier are managed outside of
   // this controller.
-  void NotifyAccessibilityStatusChanged(
-      AccessibilityNotificationVisibility notify);
+  void NotifyAccessibilityStatusChanged();
 
   // mojom::AccessibilityController:
   void SetClient(mojom::AccessibilityControllerClientPtr client) override;
@@ -164,6 +163,8 @@
   void UpdateVirtualKeyboardFromPref();
   void UpdateAccessibilityHighlightingFromPrefs();
 
+  void NotifyShowAccessibilityNotification();
+
   service_manager::Connector* connector_ = nullptr;
   std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
 
diff --git a/ash/accessibility/accessibility_controller_unittest.cc b/ash/accessibility/accessibility_controller_unittest.cc
index fe5ac81..40f1a4b 100644
--- a/ash/accessibility/accessibility_controller_unittest.cc
+++ b/ash/accessibility/accessibility_controller_unittest.cc
@@ -26,17 +26,9 @@
   ~TestAccessibilityObserver() override = default;
 
   // AccessibilityObserver:
-  void OnAccessibilityStatusChanged(
-      AccessibilityNotificationVisibility notify) override {
-    if (notify == A11Y_NOTIFICATION_NONE) {
-      ++notification_none_changed_;
-    } else if (notify == A11Y_NOTIFICATION_SHOW) {
-      ++notification_show_changed_;
-    }
-  }
+  void OnAccessibilityStatusChanged() override { ++status_changed_count_; }
 
-  int notification_none_changed_ = 0;
-  int notification_show_changed_ = 0;
+  int status_changed_count_ = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestAccessibilityObserver);
@@ -101,15 +93,15 @@
 
   TestAccessibilityObserver observer;
   controller->AddObserver(&observer);
-  EXPECT_EQ(0, observer.notification_none_changed_);
+  EXPECT_EQ(0, observer.status_changed_count_);
 
   controller->SetAutoclickEnabled(true);
   EXPECT_TRUE(controller->IsAutoclickEnabled());
-  EXPECT_EQ(1, observer.notification_none_changed_);
+  EXPECT_EQ(1, observer.status_changed_count_);
 
   controller->SetAutoclickEnabled(false);
   EXPECT_FALSE(controller->IsAutoclickEnabled());
-  EXPECT_EQ(2, observer.notification_none_changed_);
+  EXPECT_EQ(2, observer.status_changed_count_);
 
   controller->RemoveObserver(&observer);
 }
@@ -121,15 +113,15 @@
 
   TestAccessibilityObserver observer;
   controller->AddObserver(&observer);
-  EXPECT_EQ(0, observer.notification_none_changed_);
+  EXPECT_EQ(0, observer.status_changed_count_);
 
   controller->SetCaretHighlightEnabled(true);
   EXPECT_TRUE(controller->IsCaretHighlightEnabled());
-  EXPECT_EQ(1, observer.notification_none_changed_);
+  EXPECT_EQ(1, observer.status_changed_count_);
 
   controller->SetCaretHighlightEnabled(false);
   EXPECT_FALSE(controller->IsCaretHighlightEnabled());
-  EXPECT_EQ(2, observer.notification_none_changed_);
+  EXPECT_EQ(2, observer.status_changed_count_);
 
   controller->RemoveObserver(&observer);
 }
@@ -141,15 +133,15 @@
 
   TestAccessibilityObserver observer;
   controller->AddObserver(&observer);
-  EXPECT_EQ(0, observer.notification_none_changed_);
+  EXPECT_EQ(0, observer.status_changed_count_);
 
   controller->SetCursorHighlightEnabled(true);
   EXPECT_TRUE(controller->IsCursorHighlightEnabled());
-  EXPECT_EQ(1, observer.notification_none_changed_);
+  EXPECT_EQ(1, observer.status_changed_count_);
 
   controller->SetCursorHighlightEnabled(false);
   EXPECT_FALSE(controller->IsCursorHighlightEnabled());
-  EXPECT_EQ(2, observer.notification_none_changed_);
+  EXPECT_EQ(2, observer.status_changed_count_);
 
   controller->RemoveObserver(&observer);
 }
@@ -161,15 +153,15 @@
 
   TestAccessibilityObserver observer;
   controller->AddObserver(&observer);
-  EXPECT_EQ(0, observer.notification_none_changed_);
+  EXPECT_EQ(0, observer.status_changed_count_);
 
   controller->SetFocusHighlightEnabled(true);
   EXPECT_TRUE(controller->IsFocusHighlightEnabled());
-  EXPECT_EQ(1, observer.notification_none_changed_);
+  EXPECT_EQ(1, observer.status_changed_count_);
 
   controller->SetFocusHighlightEnabled(false);
   EXPECT_FALSE(controller->IsFocusHighlightEnabled());
-  EXPECT_EQ(2, observer.notification_none_changed_);
+  EXPECT_EQ(2, observer.status_changed_count_);
 
   controller->RemoveObserver(&observer);
 }
@@ -181,15 +173,15 @@
 
   TestAccessibilityObserver observer;
   controller->AddObserver(&observer);
-  EXPECT_EQ(0, observer.notification_none_changed_);
+  EXPECT_EQ(0, observer.status_changed_count_);
 
   controller->SetHighContrastEnabled(true);
   EXPECT_TRUE(controller->IsHighContrastEnabled());
-  EXPECT_EQ(1, observer.notification_none_changed_);
+  EXPECT_EQ(1, observer.status_changed_count_);
 
   controller->SetHighContrastEnabled(false);
   EXPECT_FALSE(controller->IsHighContrastEnabled());
-  EXPECT_EQ(2, observer.notification_none_changed_);
+  EXPECT_EQ(2, observer.status_changed_count_);
 
   controller->RemoveObserver(&observer);
 }
@@ -201,15 +193,15 @@
 
   TestAccessibilityObserver observer;
   controller->AddObserver(&observer);
-  EXPECT_EQ(0, observer.notification_none_changed_);
+  EXPECT_EQ(0, observer.status_changed_count_);
 
   controller->SetLargeCursorEnabled(true);
   EXPECT_TRUE(controller->IsLargeCursorEnabled());
-  EXPECT_EQ(1, observer.notification_none_changed_);
+  EXPECT_EQ(1, observer.status_changed_count_);
 
   controller->SetLargeCursorEnabled(false);
   EXPECT_FALSE(controller->IsLargeCursorEnabled());
-  EXPECT_EQ(2, observer.notification_none_changed_);
+  EXPECT_EQ(2, observer.status_changed_count_);
 
   controller->RemoveObserver(&observer);
 }
@@ -238,15 +230,15 @@
 
   TestAccessibilityObserver observer;
   controller->AddObserver(&observer);
-  EXPECT_EQ(0, observer.notification_none_changed_);
+  EXPECT_EQ(0, observer.status_changed_count_);
 
   controller->SetMonoAudioEnabled(true);
   EXPECT_TRUE(controller->IsMonoAudioEnabled());
-  EXPECT_EQ(1, observer.notification_none_changed_);
+  EXPECT_EQ(1, observer.status_changed_count_);
 
   controller->SetMonoAudioEnabled(false);
   EXPECT_FALSE(controller->IsMonoAudioEnabled());
-  EXPECT_EQ(2, observer.notification_none_changed_);
+  EXPECT_EQ(2, observer.status_changed_count_);
 
   controller->RemoveObserver(&observer);
 }
@@ -258,18 +250,15 @@
 
   TestAccessibilityObserver observer;
   controller->AddObserver(&observer);
-  EXPECT_EQ(0, observer.notification_none_changed_);
-  EXPECT_EQ(0, observer.notification_show_changed_);
+  EXPECT_EQ(0, observer.status_changed_count_);
 
   controller->SetSpokenFeedbackEnabled(true, A11Y_NOTIFICATION_SHOW);
   EXPECT_TRUE(controller->IsSpokenFeedbackEnabled());
-  EXPECT_EQ(0, observer.notification_none_changed_);
-  EXPECT_EQ(1, observer.notification_show_changed_);
+  EXPECT_EQ(1, observer.status_changed_count_);
 
   controller->SetSpokenFeedbackEnabled(false, A11Y_NOTIFICATION_NONE);
   EXPECT_FALSE(controller->IsSpokenFeedbackEnabled());
-  EXPECT_EQ(1, observer.notification_none_changed_);
-  EXPECT_EQ(1, observer.notification_show_changed_);
+  EXPECT_EQ(2, observer.status_changed_count_);
 
   controller->RemoveObserver(&observer);
 }
@@ -281,19 +270,19 @@
 
   TestAccessibilityObserver observer;
   controller->AddObserver(&observer);
-  EXPECT_EQ(0, observer.notification_none_changed_);
+  EXPECT_EQ(0, observer.status_changed_count_);
 
   StickyKeysController* sticky_keys_controller =
       Shell::Get()->sticky_keys_controller();
   controller->SetStickyKeysEnabled(true);
   EXPECT_TRUE(sticky_keys_controller->enabled_for_test());
   EXPECT_TRUE(controller->IsStickyKeysEnabled());
-  EXPECT_EQ(1, observer.notification_none_changed_);
+  EXPECT_EQ(1, observer.status_changed_count_);
 
   controller->SetStickyKeysEnabled(false);
   EXPECT_FALSE(sticky_keys_controller->enabled_for_test());
   EXPECT_FALSE(controller->IsStickyKeysEnabled());
-  EXPECT_EQ(2, observer.notification_none_changed_);
+  EXPECT_EQ(2, observer.status_changed_count_);
 
   controller->RemoveObserver(&observer);
 }
@@ -305,17 +294,17 @@
 
   TestAccessibilityObserver observer;
   controller->AddObserver(&observer);
-  EXPECT_EQ(0, observer.notification_none_changed_);
+  EXPECT_EQ(0, observer.status_changed_count_);
 
   controller->SetVirtualKeyboardEnabled(true);
   EXPECT_TRUE(keyboard::GetAccessibilityKeyboardEnabled());
   EXPECT_TRUE(controller->IsVirtualKeyboardEnabled());
-  EXPECT_EQ(1, observer.notification_none_changed_);
+  EXPECT_EQ(1, observer.status_changed_count_);
 
   controller->SetVirtualKeyboardEnabled(false);
   EXPECT_FALSE(keyboard::GetAccessibilityKeyboardEnabled());
   EXPECT_FALSE(controller->IsVirtualKeyboardEnabled());
-  EXPECT_EQ(2, observer.notification_none_changed_);
+  EXPECT_EQ(2, observer.status_changed_count_);
 
   controller->RemoveObserver(&observer);
 }
diff --git a/ash/accessibility/accessibility_observer.h b/ash/accessibility/accessibility_observer.h
index ce79d0c9..aad7fe47 100644
--- a/ash/accessibility/accessibility_observer.h
+++ b/ash/accessibility/accessibility_observer.h
@@ -6,7 +6,6 @@
 #define ASH_ACCESSIBILITY_ACCESSIBILITY_OBSERVER_H_
 
 #include "ash/ash_export.h"
-#include "ash/public/cpp/accessibility_types.h"
 
 namespace ash {
 
@@ -14,12 +13,13 @@
  public:
   virtual ~AccessibilityObserver() {}
 
-  // Notifies when accessibility status changes. Used to:
-  // * Show the accessibility menu when any feature is enabled.
-  // * Show notifications when spoken feedback or braille is enabled.
-  // TODO(jamescook): Split into two methods.
-  virtual void OnAccessibilityStatusChanged(
-      AccessibilityNotificationVisibility notify) = 0;
+  // Called when any accessibility status changes.
+  virtual void OnAccessibilityStatusChanged() {}
+
+  // Called when notification should be shown for accessibility status changes,
+  // used for spoken feedback or braille is enabled.
+  // TODO(warx): move this to AccessibilityController.
+  virtual void ShowAccessibilityNotification() {}
 };
 
 }  // namespace ash
diff --git a/ash/accessibility/touch_exploration_manager.cc b/ash/accessibility/touch_exploration_manager.cc
index 0b1e8ad9..09aa7a58 100644
--- a/ash/accessibility/touch_exploration_manager.cc
+++ b/ash/accessibility/touch_exploration_manager.cc
@@ -56,8 +56,7 @@
   Shell::Get()->RemoveShellObserver(this);
 }
 
-void TouchExplorationManager::OnAccessibilityStatusChanged(
-    AccessibilityNotificationVisibility notify) {
+void TouchExplorationManager::OnAccessibilityStatusChanged() {
   UpdateTouchExplorationState();
 }
 
diff --git a/ash/accessibility/touch_exploration_manager.h b/ash/accessibility/touch_exploration_manager.h
index fbc947c..13967dc4 100644
--- a/ash/accessibility/touch_exploration_manager.h
+++ b/ash/accessibility/touch_exploration_manager.h
@@ -50,8 +50,7 @@
   ~TouchExplorationManager() override;
 
   // AccessibilityObserver overrides:
-  void OnAccessibilityStatusChanged(
-      AccessibilityNotificationVisibility notify) override;
+  void OnAccessibilityStatusChanged() override;
 
   // TouchExplorationControllerDelegate overrides:
   void SetOutputLevel(int volume) override;
diff --git a/ash/app_list/model/search/search_box_model.cc b/ash/app_list/model/search/search_box_model.cc
index 9d9db819..0bea09d 100644
--- a/ash/app_list/model/search/search_box_model.cc
+++ b/ash/app_list/model/search/search_box_model.cc
@@ -12,7 +12,7 @@
 
 namespace app_list {
 
-SearchBoxModel::SearchBoxModel() : is_tablet_mode_(false) {}
+SearchBoxModel::SearchBoxModel() = default;
 
 SearchBoxModel::~SearchBoxModel() = default;
 
@@ -50,13 +50,21 @@
     observer.SelectionModelChanged();
 }
 
-void SearchBoxModel::SetTabletMode(bool started) {
-  if (started == is_tablet_mode_)
+void SearchBoxModel::SetTabletMode(bool is_tablet_mode) {
+  if (is_tablet_mode == is_tablet_mode_)
     return;
-  is_tablet_mode_ = started;
+  is_tablet_mode_ = is_tablet_mode;
   UpdateAccessibleName();
 }
 
+void SearchBoxModel::SetSearchEngineIsGoogle(bool is_google) {
+  if (is_google == search_engine_is_google_)
+    return;
+  search_engine_is_google_ = is_google;
+  for (auto& observer : observers_)
+    observer.SearchEngineChanged();
+}
+
 void SearchBoxModel::Update(const base::string16& text,
                             bool initiated_by_user) {
   if (text_ == text)
diff --git a/ash/app_list/model/search/search_box_model.h b/ash/app_list/model/search/search_box_model.h
index d8bc76c2..6633b3f 100644
--- a/ash/app_list/model/search/search_box_model.h
+++ b/ash/app_list/model/search/search_box_model.h
@@ -49,7 +49,11 @@
     return selection_model_;
   }
 
-  void SetTabletMode(bool started);
+  void SetTabletMode(bool is_tablet_mode);
+  bool is_tablet_mode() const { return is_tablet_mode_; }
+
+  void SetSearchEngineIsGoogle(bool is_google);
+  bool search_engine_is_google() const { return search_engine_is_google_; }
 
   // Sets/gets the text for the search box's Textfield and the voice search
   // flag.
@@ -67,7 +71,8 @@
   base::string16 accessible_name_;
   gfx::SelectionModel selection_model_;
   base::string16 text_;
-  bool is_tablet_mode_;
+  bool search_engine_is_google_ = false;
+  bool is_tablet_mode_ = false;
 
   base::ObserverList<SearchBoxModelObserver> observers_;
 
diff --git a/ash/app_list/model/search/search_box_model_observer.h b/ash/app_list/model/search/search_box_model_observer.h
index 5b7dbcf2..538d98085 100644
--- a/ash/app_list/model/search/search_box_model_observer.h
+++ b/ash/app_list/model/search/search_box_model_observer.h
@@ -20,6 +20,9 @@
   // Invoked when text or voice search flag is changed.
   virtual void Update() = 0;
 
+  // Invoked when the search engine is changed.
+  virtual void SearchEngineChanged() = 0;
+
  protected:
   virtual ~SearchBoxModelObserver() {}
 };
diff --git a/ash/app_list/model/search/search_model.cc b/ash/app_list/model/search/search_model.cc
index d1a2f8a2..8eb238a0 100644
--- a/ash/app_list/model/search/search_model.cc
+++ b/ash/app_list/model/search/search_model.cc
@@ -28,9 +28,12 @@
 
 SearchModel::~SearchModel() {}
 
-void SearchModel::SetTabletMode(bool started) {
-  is_tablet_mode_ = started;
-  search_box_->SetTabletMode(started);
+void SearchModel::SetTabletMode(bool is_tablet_mode) {
+  search_box_->SetTabletMode(is_tablet_mode);
+}
+
+void SearchModel::SetSearchEngineIsGoogle(bool is_google) {
+  search_box_->SetSearchEngineIsGoogle(is_google);
 }
 
 std::vector<SearchResult*> SearchModel::FilterSearchResultsByDisplayType(
diff --git a/ash/app_list/model/search/search_model.h b/ash/app_list/model/search/search_model.h
index de7accaa..bf6125a5 100644
--- a/ash/app_list/model/search/search_model.h
+++ b/ash/app_list/model/search/search_model.h
@@ -29,13 +29,13 @@
   ~SearchModel();
 
   // Whether tablet mode is active. Controlled by AppListView.
-  void SetTabletMode(bool started);
-  bool tablet_mode() const { return is_tablet_mode_; }
+  void SetTabletMode(bool is_tablet_mode);
+  bool tablet_mode() const { return search_box_->is_tablet_mode(); }
 
-  void SetSearchEngineIsGoogle(bool is_google) {
-    search_engine_is_google_ = is_google;
+  void SetSearchEngineIsGoogle(bool is_google);
+  bool search_engine_is_google() const {
+    return search_box_->search_engine_is_google();
   }
-  bool search_engine_is_google() const { return search_engine_is_google_; }
 
   // Filters the given |results| by |display_type|. The returned list is
   // truncated to |max_results|.
@@ -57,9 +57,6 @@
  private:
   std::unique_ptr<SearchBoxModel> search_box_;
   std::unique_ptr<SearchResults> results_;
-  bool search_engine_is_google_ = false;
-  // Whether tablet mode is active. Controlled by the AppListView.
-  bool is_tablet_mode_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(SearchModel);
 };
diff --git a/ash/frame/caption_buttons/caption_button_model.h b/ash/frame/caption_buttons/caption_button_model.h
index 60a6bf7e..f303c7c 100644
--- a/ash/frame/caption_buttons/caption_button_model.h
+++ b/ash/frame/caption_buttons/caption_button_model.h
@@ -20,6 +20,10 @@
 
   // Returns true if |type| is enabled.
   virtual bool IsEnabled(CaptionButtonIcon type) const = 0;
+
+  // In zoom mode, the maximize/restore button will be repalced
+  // with zoom/unzoom button.
+  virtual bool InZoomMode() const = 0;
 };
 
 }  // namespace ash
diff --git a/ash/frame/caption_buttons/caption_button_types.h b/ash/frame/caption_buttons/caption_button_types.h
index ece7d8e9c..e3854c04 100644
--- a/ash/frame/caption_buttons/caption_button_types.h
+++ b/ash/frame/caption_buttons/caption_button_types.h
@@ -17,6 +17,7 @@
   CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
   CAPTION_BUTTON_ICON_BACK,
   CAPTION_BUTTON_ICON_LOCATION,
+  CAPTION_BUTTON_ICON_MENU,
   CAPTION_BUTTON_ICON_COUNT
 };
 
diff --git a/ash/frame/caption_buttons/frame_caption_button_container_view.cc b/ash/frame/caption_buttons/frame_caption_button_container_view.cc
index 358c03a..65d18ca 100644
--- a/ash/frame/caption_buttons/frame_caption_button_container_view.cc
+++ b/ash/frame/caption_buttons/frame_caption_button_container_view.cc
@@ -130,7 +130,9 @@
         return frame_->widget_delegate()->CanResize();
       case CAPTION_BUTTON_ICON_CLOSE:
         return true;
+      // No back or menu button by default.
       case CAPTION_BUTTON_ICON_BACK:
+      case CAPTION_BUTTON_ICON_MENU:
         return false;
       case CAPTION_BUTTON_ICON_LOCATION:
       case CAPTION_BUTTON_ICON_COUNT:
@@ -141,6 +143,7 @@
     return false;
   }
   bool IsEnabled(CaptionButtonIcon type) const override { return true; }
+  bool InZoomMode() const override { return false; }
 
  private:
   views::Widget* frame_;
@@ -180,6 +183,11 @@
     tablet_mode_animation_->Reset(1.0f);
 
   // Insert the buttons left to right.
+  menu_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MENU);
+  menu_button_->SetAccessibleName(
+      l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MENU));
+  AddChildView(menu_button_);
+
   minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE);
   minimize_button_->SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE));
@@ -209,7 +217,7 @@
     const gfx::VectorIcon& icon_definition) {
   button_icon_map_[icon] = &icon_definition;
 
-  FrameCaptionButton* buttons[] = {minimize_button_, size_button_,
+  FrameCaptionButton* buttons[] = {menu_button_, minimize_button_, size_button_,
                                    close_button_};
   for (size_t i = 0; i < arraysize(buttons); ++i) {
     if (buttons[i]->icon() == icon)
@@ -219,12 +227,14 @@
 }
 
 void FrameCaptionButtonContainerView::SetPaintAsActive(bool paint_as_active) {
+  menu_button_->set_paint_as_active(paint_as_active);
   minimize_button_->set_paint_as_active(paint_as_active);
   size_button_->set_paint_as_active(paint_as_active);
   close_button_->set_paint_as_active(paint_as_active);
 }
 
 void FrameCaptionButtonContainerView::SetUseLightImages(bool light) {
+  menu_button_->set_use_light_images(light);
   minimize_button_->set_use_light_images(light);
   size_button_->set_use_light_images(light);
   close_button_->set_use_light_images(light);
@@ -245,6 +255,9 @@
   } else if (minimize_button_->visible() &&
              ConvertPointToViewAndHitTest(this, minimize_button_, point)) {
     return HTMINBUTTON;
+  } else if (menu_button_->visible() &&
+             ConvertPointToViewAndHitTest(this, menu_button_, point)) {
+    return HTMENU;
   }
   return HTNOWHERE;
 }
@@ -270,9 +283,12 @@
       model_->IsEnabled(CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE));
   minimize_button_->SetVisible(model_->IsVisible(CAPTION_BUTTON_ICON_MINIMIZE));
   minimize_button_->SetEnabled(model_->IsEnabled(CAPTION_BUTTON_ICON_MINIMIZE));
+  menu_button_->SetVisible(model_->IsVisible(CAPTION_BUTTON_ICON_MENU));
+  menu_button_->SetEnabled(model_->IsEnabled(CAPTION_BUTTON_ICON_MENU));
 }
 
 void FrameCaptionButtonContainerView::SetButtonSize(const gfx::Size& size) {
+  menu_button_->SetPreferredSize(size);
   minimize_button_->SetPreferredSize(size);
   size_button_->SetPreferredSize(size);
   close_button_->SetPreferredSize(size);
@@ -417,6 +433,7 @@
 void FrameCaptionButtonContainerView::SetButtonsToNormal(Animate animate) {
   SetButtonIcons(CAPTION_BUTTON_ICON_MINIMIZE, CAPTION_BUTTON_ICON_CLOSE,
                  animate);
+  menu_button_->SetState(views::Button::STATE_NORMAL);
   minimize_button_->SetState(views::Button::STATE_NORMAL);
   size_button_->SetState(views::Button::STATE_NORMAL);
   close_button_->SetState(views::Button::STATE_NORMAL);
@@ -438,7 +455,7 @@
   gfx::Point position(position_in_screen);
   views::View::ConvertPointFromScreen(this, &position);
 
-  FrameCaptionButton* buttons[] = {minimize_button_, size_button_,
+  FrameCaptionButton* buttons[] = {menu_button_, minimize_button_, size_button_,
                                    close_button_};
   int min_squared_distance = INT_MAX;
   FrameCaptionButton* closest_button = NULL;
@@ -463,7 +480,7 @@
 void FrameCaptionButtonContainerView::SetHoveredAndPressedButtons(
     const FrameCaptionButton* to_hover,
     const FrameCaptionButton* to_press) {
-  FrameCaptionButton* buttons[] = {minimize_button_, size_button_,
+  FrameCaptionButton* buttons[] = {menu_button_, minimize_button_, size_button_,
                                    close_button_};
   for (size_t i = 0; i < arraysize(buttons); ++i) {
     FrameCaptionButton* button = buttons[i];
diff --git a/ash/frame/caption_buttons/frame_caption_button_container_view.h b/ash/frame/caption_buttons/frame_caption_button_container_view.h
index ff72a5d..fbfe54d 100644
--- a/ash/frame/caption_buttons/frame_caption_button_container_view.h
+++ b/ash/frame/caption_buttons/frame_caption_button_container_view.h
@@ -63,6 +63,10 @@
       return container_view_->close_button_;
     }
 
+    FrameCaptionButton* menu_button() const {
+      return container_view_->menu_button_;
+    }
+
    private:
     FrameCaptionButtonContainerView* container_view_;
 
@@ -142,6 +146,7 @@
 
   // The buttons. In the normal button style, at most one of |minimize_button_|
   // and |size_button_| is visible.
+  FrameCaptionButton* menu_button_ = nullptr;
   FrameCaptionButton* minimize_button_ = nullptr;
   FrameCaptionButton* size_button_ = nullptr;
   FrameCaptionButton* close_button_ = nullptr;
diff --git a/ash/frame/caption_buttons/frame_size_button.cc b/ash/frame/caption_buttons/frame_size_button.cc
index 3d4dae82..2405a5b 100644
--- a/ash/frame/caption_buttons/frame_size_button.cc
+++ b/ash/frame/caption_buttons/frame_size_button.cc
@@ -207,6 +207,7 @@
       case CAPTION_BUTTON_ICON_CLOSE:
       case CAPTION_BUTTON_ICON_BACK:
       case CAPTION_BUTTON_ICON_LOCATION:
+      case CAPTION_BUTTON_ICON_MENU:
       case CAPTION_BUTTON_ICON_COUNT:
         NOTREACHED();
         break;
diff --git a/ash/frame/custom_frame_view_ash_unittest.cc b/ash/frame/custom_frame_view_ash_unittest.cc
index 499d17e..e2566fd2 100644
--- a/ash/frame/custom_frame_view_ash_unittest.cc
+++ b/ash/frame/custom_frame_view_ash_unittest.cc
@@ -11,6 +11,7 @@
 #include "ash/frame/caption_buttons/frame_caption_button.h"
 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
 #include "ash/frame/header_view.h"
+#include "ash/public/cpp/vector_icons/vector_icons.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
@@ -424,6 +425,8 @@
   TestButtonModel() = default;
   ~TestButtonModel() override = default;
 
+  void set_zoom_mode(bool zoom_mode) { zoom_mode_ = zoom_mode; }
+
   void SetVisible(CaptionButtonIcon type, bool visible) {
     if (visible)
       visible_buttons_.insert(type);
@@ -445,10 +448,12 @@
   bool IsEnabled(CaptionButtonIcon type) const override {
     return enabled_buttons_.count(type);
   }
+  bool InZoomMode() const override { return zoom_mode_; }
 
  private:
   base::flat_set<CaptionButtonIcon> visible_buttons_;
   base::flat_set<CaptionButtonIcon> enabled_buttons_;
+  bool zoom_mode_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(TestButtonModel);
 };
@@ -566,6 +571,7 @@
 
   EXPECT_FALSE(test_api.minimize_button()->visible());
   EXPECT_FALSE(test_api.size_button()->visible());
+  EXPECT_FALSE(test_api.menu_button()->visible());
 
   // Back button
   model_ptr->SetVisible(CAPTION_BUTTON_ICON_BACK, true);
@@ -596,6 +602,31 @@
   model_ptr->SetEnabled(CAPTION_BUTTON_ICON_MINIMIZE, true);
   custom_frame_view->SizeConstraintsChanged();
   EXPECT_TRUE(test_api.minimize_button()->enabled());
+
+  // menu button
+  model_ptr->SetVisible(CAPTION_BUTTON_ICON_MENU, true);
+  custom_frame_view->SizeConstraintsChanged();
+  EXPECT_TRUE(test_api.menu_button()->visible());
+  EXPECT_FALSE(test_api.menu_button()->enabled());
+
+  model_ptr->SetEnabled(CAPTION_BUTTON_ICON_MENU, true);
+  custom_frame_view->SizeConstraintsChanged();
+  EXPECT_TRUE(test_api.menu_button()->enabled());
+
+// The addresses in library and in the main binary differ in
+// comoponent build.
+#if !defined(COMPONENT_BUILD)
+  // zoom button
+  EXPECT_EQ(&kWindowControlMaximizeIcon,
+            test_api.size_button()->icon_definition_for_test());
+  model_ptr->set_zoom_mode(true);
+  custom_frame_view->SizeConstraintsChanged();
+  EXPECT_EQ(&ash::kWindowControlZoomIcon,
+            test_api.size_button()->icon_definition_for_test());
+  widget->Maximize();
+  EXPECT_EQ(&ash::kWindowControlDezoomIcon,
+            test_api.size_button()->icon_definition_for_test());
+#endif
 }
 
 namespace {
diff --git a/ash/frame/default_frame_header.cc b/ash/frame/default_frame_header.cc
index 0a803eb..52fb5451 100644
--- a/ash/frame/default_frame_header.cc
+++ b/ash/frame/default_frame_header.cc
@@ -324,6 +324,9 @@
 
   UpdateSizeButtonImages();
 
+  caption_button_container_->SetButtonImage(CAPTION_BUTTON_ICON_MENU,
+                                            kWindowControlMenuIcon);
+
   caption_button_container_->SetButtonImage(CAPTION_BUTTON_ICON_CLOSE,
                                             kWindowControlCloseIcon);
 
@@ -339,9 +342,14 @@
   // buttons as it would cause mismatch beteen window state and size button.
   if (frame_->IsMinimized())
     return;
+  bool use_zoom_icons = caption_button_container_->model()->InZoomMode();
+  const gfx::VectorIcon& restore_icon =
+      use_zoom_icons ? kWindowControlDezoomIcon : kWindowControlRestoreIcon;
+  const gfx::VectorIcon& maximize_icon =
+      use_zoom_icons ? kWindowControlZoomIcon : kWindowControlMaximizeIcon;
   const gfx::VectorIcon& icon = frame_->IsMaximized() || frame_->IsFullscreen()
-                                    ? kWindowControlRestoreIcon
-                                    : kWindowControlMaximizeIcon;
+                                    ? restore_icon
+                                    : maximize_icon;
   caption_button_container_->SetButtonImage(
       CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE, icon);
 }
diff --git a/ash/ime/ime_controller.cc b/ash/ime/ime_controller.cc
index f36e39b..615178b 100644
--- a/ash/ime/ime_controller.cc
+++ b/ash/ime/ime_controller.cc
@@ -162,12 +162,13 @@
     client_->SetCapsLockEnabled(caps_enabled);
 }
 
-void ImeController::OverrideKeyboardKeyset(ash::mojom::ImeKeyset keyset) {
+void ImeController::OverrideKeyboardKeyset(
+    chromeos::input_method::mojom::ImeKeyset keyset) {
   OverrideKeyboardKeyset(keyset, base::DoNothing());
 }
 
 void ImeController::OverrideKeyboardKeyset(
-    ash::mojom::ImeKeyset keyset,
+    chromeos::input_method::mojom::ImeKeyset keyset,
     mojom::ImeControllerClient::OverrideKeyboardKeysetCallback callback) {
   if (client_)
     client_->OverrideKeyboardKeyset(keyset, std::move(callback));
diff --git a/ash/ime/ime_controller.h b/ash/ime/ime_controller.h
index 96a35402..c08f4935 100644
--- a/ash/ime/ime_controller.h
+++ b/ash/ime/ime_controller.h
@@ -71,9 +71,9 @@
   void SwitchImeById(const std::string& ime_id, bool show_message);
   void ActivateImeMenuItem(const std::string& key);
   void SetCapsLockEnabled(bool caps_enabled);
-  void OverrideKeyboardKeyset(mojom::ImeKeyset keyset);
+  void OverrideKeyboardKeyset(chromeos::input_method::mojom::ImeKeyset keyset);
   void OverrideKeyboardKeyset(
-      mojom::ImeKeyset keyset,
+      chromeos::input_method::mojom::ImeKeyset keyset,
       mojom::ImeControllerClient::OverrideKeyboardKeysetCallback callback);
 
   // Returns true if the switch is allowed and the keystroke should be
diff --git a/ash/ime/test_ime_controller_client.cc b/ash/ime/test_ime_controller_client.cc
index db341ce2..5852fbc9 100644
--- a/ash/ime/test_ime_controller_client.cc
+++ b/ash/ime/test_ime_controller_client.cc
@@ -44,7 +44,7 @@
 }
 
 void TestImeControllerClient::OverrideKeyboardKeyset(
-    ash::mojom::ImeKeyset keyset,
+    chromeos::input_method::mojom::ImeKeyset keyset,
     OverrideKeyboardKeysetCallback callback) {
   last_keyset_ = keyset;
   std::move(callback).Run();
diff --git a/ash/ime/test_ime_controller_client.h b/ash/ime/test_ime_controller_client.h
index 710421a..fc621b45 100644
--- a/ash/ime/test_ime_controller_client.h
+++ b/ash/ime/test_ime_controller_client.h
@@ -24,7 +24,7 @@
   void SwitchImeById(const std::string& id, bool show_message) override;
   void ActivateImeMenuItem(const std::string& key) override;
   void SetCapsLockEnabled(bool enabled) override;
-  void OverrideKeyboardKeyset(mojom::ImeKeyset keyset,
+  void OverrideKeyboardKeyset(chromeos::input_method::mojom::ImeKeyset keyset,
                               OverrideKeyboardKeysetCallback callback) override;
 
   int next_ime_count_ = 0;
@@ -33,7 +33,8 @@
   int set_caps_lock_count_ = 0;
   std::string last_switch_ime_id_;
   bool last_show_message_ = false;
-  mojom::ImeKeyset last_keyset_ = ash::mojom::ImeKeyset::kNone;
+  chromeos::input_method::mojom::ImeKeyset last_keyset_ =
+      chromeos::input_method::mojom::ImeKeyset::kNone;
 
  private:
   mojo::Binding<mojom::ImeControllerClient> binding_;
diff --git a/ash/keyboard/keyboard_ui.cc b/ash/keyboard/keyboard_ui.cc
index 16b64a4..3e99020a 100644
--- a/ash/keyboard/keyboard_ui.cc
+++ b/ash/keyboard/keyboard_ui.cc
@@ -10,8 +10,6 @@
 #include "ash/accessibility/accessibility_observer.h"
 #include "ash/keyboard/keyboard_ui_observer.h"
 #include "ash/shell.h"
-#include "ash/system/tray/system_tray_notifier.h"
-#include "ash/system/tray_accessibility.h"
 #include "ui/keyboard/keyboard_controller.h"
 
 namespace ash {
@@ -44,8 +42,7 @@
   }
 
   // AccessibilityObserver:
-  void OnAccessibilityStatusChanged(
-      AccessibilityNotificationVisibility notify) override {
+  void OnAccessibilityStatusChanged() override {
     bool enabled = IsEnabled();
     if (enabled_ == enabled)
       return;
diff --git a/ash/magnifier/docked_magnifier_controller.cc b/ash/magnifier/docked_magnifier_controller.cc
index 0ebae3c..aca7d4a 100644
--- a/ash/magnifier/docked_magnifier_controller.cc
+++ b/ash/magnifier/docked_magnifier_controller.cc
@@ -523,8 +523,7 @@
 
   // Update the green checkmark status in the accessibility menu in the system
   // tray.
-  shell->accessibility_controller()->NotifyAccessibilityStatusChanged(
-      A11Y_NOTIFICATION_NONE);
+  shell->accessibility_controller()->NotifyAccessibilityStatusChanged();
 
   // We use software composited mouse cursor so that it can be mirrored into the
   // magnifier viewport.
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index 7ad5a76..6b97231 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -34,6 +34,9 @@
 const base::Feature kLockScreenNotifications{"LockScreenNotifications",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kModeSpecificPowerButton{"ModeSpecificPowerButton",
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
+
 bool IsDisplayMoveWindowAccelsEnabled() {
   return base::FeatureList::IsEnabled(kDisplayMoveWindowAccels);
 }
@@ -64,5 +67,9 @@
   return base::FeatureList::IsEnabled(kLockScreenNotifications);
 }
 
+bool IsModeSpecificPowerButtonEnabled() {
+  return base::FeatureList::IsEnabled(kModeSpecificPowerButton);
+}
+
 }  // namespace features
 }  // namespace ash
diff --git a/ash/public/cpp/ash_features.h b/ash/public/cpp/ash_features.h
index ef0640f..14e23c3 100644
--- a/ash/public/cpp/ash_features.h
+++ b/ash/public/cpp/ash_features.h
@@ -50,6 +50,11 @@
 // Enables notifications on the lock screen.
 ASH_PUBLIC_EXPORT extern const base::Feature kLockScreenNotifications;
 
+// Enables mode-specific power button behavior.
+// TODO(derat): Remove this after we make a decision about whether to enable it
+// by default: https://crbug.com/819276
+ASH_PUBLIC_EXPORT extern const base::Feature kModeSpecificPowerButton;
+
 ASH_PUBLIC_EXPORT bool IsDisplayMoveWindowAccelsEnabled();
 
 ASH_PUBLIC_EXPORT bool IsDockedMagnifierEnabled();
@@ -64,6 +69,8 @@
 
 ASH_PUBLIC_EXPORT bool IsLockScreenNotificationsEnabled();
 
+ASH_PUBLIC_EXPORT bool IsModeSpecificPowerButtonEnabled();
+
 }  // namespace features
 }  // namespace ash
 
diff --git a/ash/public/cpp/shelf_model.cc b/ash/public/cpp/shelf_model.cc
index f2cd1394..de834ba0 100644
--- a/ash/public/cpp/shelf_model.cc
+++ b/ash/public/cpp/shelf_model.cc
@@ -299,7 +299,8 @@
   }
 }
 
-ShelfItemDelegate* ShelfModel::GetShelfItemDelegate(const ShelfID& shelf_id) {
+ShelfItemDelegate* ShelfModel::GetShelfItemDelegate(
+    const ShelfID& shelf_id) const {
   auto it = id_to_item_delegate_map_.find(shelf_id);
   if (it != id_to_item_delegate_map_.end())
     return it->second.get();
diff --git a/ash/public/cpp/shelf_model.h b/ash/public/cpp/shelf_model.h
index 1f098a5..f39bfb44 100644
--- a/ash/public/cpp/shelf_model.h
+++ b/ash/public/cpp/shelf_model.h
@@ -103,7 +103,7 @@
                             std::unique_ptr<ShelfItemDelegate> item_delegate);
 
   // Returns ShelfItemDelegate for |shelf_id|, or nullptr if none exists.
-  ShelfItemDelegate* GetShelfItemDelegate(const ShelfID& shelf_id);
+  ShelfItemDelegate* GetShelfItemDelegate(const ShelfID& shelf_id) const;
 
   void AddObserver(ShelfModelObserver* observer);
   void RemoveObserver(ShelfModelObserver* observer);
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn
index 78e5bbe..24f3d35 100644
--- a/ash/public/interfaces/BUILD.gn
+++ b/ash/public/interfaces/BUILD.gn
@@ -65,6 +65,7 @@
     "//services/preferences/public/mojom",
     "//skia/public/interfaces",
     "//ui/accessibility:ax_enums_mojo",
+    "//ui/base/ime/chromeos/public/interfaces",
     "//ui/events/mojo:interfaces",
     "//ui/gfx/geometry/mojo",
     "//ui/gfx/image/mojo:interfaces",
diff --git a/ash/public/interfaces/ime_controller.mojom b/ash/public/interfaces/ime_controller.mojom
index 5fa7b5d8..6a22b8f 100644
--- a/ash/public/interfaces/ime_controller.mojom
+++ b/ash/public/interfaces/ime_controller.mojom
@@ -5,6 +5,7 @@
 module ash.mojom;
 
 import "ash/public/interfaces/ime_info.mojom";
+import "ui/base/ime/chromeos/public/interfaces/ime_keyset.mojom";
 
 // Interface for ash client (e.g. Chrome) to send input method info to ash.
 interface ImeController {
@@ -47,16 +48,6 @@
                                    bool is_voice_enabled);
 };
 
-// Used by the virtual keyboard to represent different key layouts for
-// different purposes. 'kNone' represents the default key layout.
-// Used in UMA, so this enum should not be reordered.
-enum ImeKeyset {
-  kNone = 0,
-  kEmoji = 1,
-  kHandwriting = 2,
-  kVoice = 3,
-};
-
 // Interface for ash to send input method requests to its client (e.g. Chrome).
 interface ImeControllerClient {
   // Switches to the next input method. Does nothing if only one input method
@@ -90,5 +81,5 @@
   // Overrides the keyboard keyset (emoji, handwriting or voice). If keyset is
   // 'kNone', we switch to the default keyset. Because this is asynchronous,
   // any code that needs the keyset to be updated first must use the callback.
-  OverrideKeyboardKeyset(ImeKeyset keyset) => ();
+  OverrideKeyboardKeyset(chromeos.input_method.mojom.ImeKeyset keyset) => ();
 };
diff --git a/ash/shell.cc b/ash/shell.cc
index d34c13c..836698e0 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -1017,8 +1017,12 @@
   // The order in which event filters are added is significant.
 
   // ui::UserActivityDetector passes events to observers, so let them get
-  // rewritten first.
-  user_activity_detector_.reset(new ui::UserActivityDetector);
+  // rewritten first. The detector observses the platform event source directly
+  // in the Classic configuration, but in Mus and Mash configurations, the
+  // detector instead relies on reports of events from the Window Server.
+  const bool observe_event_source = config == Config::CLASSIC;
+  user_activity_detector_ =
+      std::make_unique<ui::UserActivityDetector>(observe_event_source);
 
   overlay_filter_.reset(new OverlayEventFilter);
   AddPreTargetHandler(overlay_filter_.get());
diff --git a/ash/system/ime/tray_ime_chromeos.cc b/ash/system/ime/tray_ime_chromeos.cc
index 07d80c2c..e3e7a70 100644
--- a/ash/system/ime/tray_ime_chromeos.cc
+++ b/ash/system/ime/tray_ime_chromeos.cc
@@ -175,8 +175,7 @@
   Update();
 }
 
-void TrayIME::OnAccessibilityStatusChanged(
-    AccessibilityNotificationVisibility notify) {
+void TrayIME::OnAccessibilityStatusChanged() {
   Update();
 }
 
diff --git a/ash/system/ime/tray_ime_chromeos.h b/ash/system/ime/tray_ime_chromeos.h
index d12c0b7..f82e33d9 100644
--- a/ash/system/ime/tray_ime_chromeos.h
+++ b/ash/system/ime/tray_ime_chromeos.h
@@ -40,8 +40,7 @@
   void OnKeyboardSuppressionChanged(bool suppressed) override;
 
   // Overridden from AccessibilityObserver:
-  void OnAccessibilityStatusChanged(
-      AccessibilityNotificationVisibility notify) override;
+  void OnAccessibilityStatusChanged() override;
 
  private:
   friend class TrayIMETest;
diff --git a/ash/system/ime_menu/ime_menu_tray.cc b/ash/system/ime_menu/ime_menu_tray.cc
index 22590fe6..3bb44a8 100644
--- a/ash/system/ime_menu/ime_menu_tray.cc
+++ b/ash/system/ime_menu/ime_menu_tray.cc
@@ -177,13 +177,14 @@
     // The |keyset| will be used for drawing input view keyset in IME
     // extensions. ImeMenuTray::ShowKeyboardWithKeyset() will deal with
     // the |keyset| string to generate the right input view url.
-    mojom::ImeKeyset keyset = mojom::ImeKeyset::kNone;
+    using chromeos::input_method::mojom::ImeKeyset;
+    ImeKeyset keyset = ImeKeyset::kNone;
     if (sender == emoji_button_)
-      keyset = mojom::ImeKeyset::kEmoji;
+      keyset = ImeKeyset::kEmoji;
     else if (sender == voice_button_)
-      keyset = mojom::ImeKeyset::kVoice;
+      keyset = ImeKeyset::kVoice;
     else if (sender == handwriting_button_)
-      keyset = mojom::ImeKeyset::kHandwriting;
+      keyset = ImeKeyset::kHandwriting;
     else
       NOTREACHED();
 
@@ -338,7 +339,8 @@
   SetIsActive(true);
 }
 
-void ImeMenuTray::ShowKeyboardWithKeyset(ash::mojom::ImeKeyset keyset) {
+void ImeMenuTray::ShowKeyboardWithKeyset(
+    chromeos::input_method::mojom::ImeKeyset keyset) {
   CloseBubble();
 
   // This will override the url ref of the keyboard to make it shown with
@@ -463,7 +465,8 @@
 }
 
 void ImeMenuTray::OnKeyboardClosed() {
-  ime_controller_->OverrideKeyboardKeyset(ash::mojom::ImeKeyset::kNone);
+  ime_controller_->OverrideKeyboardKeyset(
+      chromeos::input_method::mojom::ImeKeyset::kNone);
   keyboard::KeyboardController* keyboard_controller =
       keyboard::KeyboardController::GetInstance();
   if (keyboard_controller)
@@ -490,7 +493,8 @@
 
   // If the the IME menu has overriding the input view url, we should write it
   // back to normal keyboard when hiding the input view.
-  ime_controller_->OverrideKeyboardKeyset(ash::mojom::ImeKeyset::kNone);
+  ime_controller_->OverrideKeyboardKeyset(
+      chromeos::input_method::mojom::ImeKeyset::kNone);
   show_keyboard_ = false;
 
   // If the keyboard is forced to be shown by IME menu for once, we need to
diff --git a/ash/system/ime_menu/ime_menu_tray.h b/ash/system/ime_menu/ime_menu_tray.h
index 1bb430d..5cbf7997a 100644
--- a/ash/system/ime_menu/ime_menu_tray.h
+++ b/ash/system/ime_menu/ime_menu_tray.h
@@ -13,6 +13,7 @@
 #include "ash/system/tray/tray_bubble_wrapper.h"
 #include "ash/system/virtual_keyboard/virtual_keyboard_observer.h"
 #include "base/macros.h"
+#include "ui/base/ime/chromeos/public/interfaces/ime_keyset.mojom.h"
 #include "ui/keyboard/keyboard_controller_observer.h"
 #include "ui/views/bubble/tray_bubble_view.h"
 
@@ -38,7 +39,7 @@
 
   // Shows the virtual keyboard with the given keyset: emoji, handwriting or
   // voice.
-  void ShowKeyboardWithKeyset(mojom::ImeKeyset keyset);
+  void ShowKeyboardWithKeyset(chromeos::input_method::mojom::ImeKeyset keyset);
 
   // Returns true if the menu should show emoji, handwriting and voice buttons
   // on the bottom. Otherwise, the menu will show a 'Customize...' bottom row
diff --git a/ash/system/ime_menu/ime_menu_tray_unittest.cc b/ash/system/ime_menu/ime_menu_tray_unittest.cc
index b3b4563..5d67485c 100644
--- a/ash/system/ime_menu/ime_menu_tray_unittest.cc
+++ b/ash/system/ime_menu/ime_menu_tray_unittest.cc
@@ -285,7 +285,8 @@
   accessibility_controller->SetVirtualKeyboardEnabled(true);
   EXPECT_TRUE(accessibility_controller->IsVirtualKeyboardEnabled());
 
-  GetTray()->ShowKeyboardWithKeyset(mojom::ImeKeyset::kEmoji);
+  GetTray()->ShowKeyboardWithKeyset(
+      chromeos::input_method::mojom::ImeKeyset::kEmoji);
   // The menu should be hidden.
   EXPECT_FALSE(IsBubbleShown());
   // The virtual keyboard should be enabled.
@@ -306,7 +307,8 @@
 
   TestImeControllerClient client;
   ime_controller->SetClient(client.CreateInterfacePtr());
-  GetTray()->ShowKeyboardWithKeyset(mojom::ImeKeyset::kEmoji);
+  GetTray()->ShowKeyboardWithKeyset(
+      chromeos::input_method::mojom::ImeKeyset::kEmoji);
   // Keyboard is shown asynchronously through Mojo
   ime_controller->FlushMojoForTesting();
   // The virtual keyboard should be enabled.
diff --git a/ash/system/power/power_button_controller.cc b/ash/system/power/power_button_controller.cc
index 964ed7fa..83f7849b 100644
--- a/ash/system/power/power_button_controller.cc
+++ b/ash/system/power/power_button_controller.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "ash/accelerators/accelerator_controller.h"
+#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller.h"
@@ -150,7 +151,7 @@
 
   if (down) {
     show_menu_animation_done_ = false;
-    if (turn_screen_off_for_tap_) {
+    if (ShouldTurnScreenOffForTap()) {
       force_off_on_button_up_ = true;
 
       // When the system resumes in response to the power button being pressed,
@@ -174,7 +175,7 @@
     screen_off_when_power_button_down_ = !display_controller_->IsScreenOn();
     display_controller_->SetBacklightsForcedOff(false);
 
-    if (!turn_screen_off_for_tap_) {
+    if (!ShouldTurnScreenOffForTap()) {
       StartPowerMenuAnimation();
     } else {
       base::TimeDelta timeout = screen_off_when_power_button_down_
@@ -208,7 +209,7 @@
 
     // Cancel the menu animation if it's still ongoing when the button is
     // released on a clamshell device.
-    if (!turn_screen_off_for_tap_ && !show_menu_animation_done_) {
+    if (!ShouldTurnScreenOffForTap() && !show_menu_animation_done_) {
       static_cast<PowerButtonMenuScreenView*>(menu_widget_->GetContentsView())
           ->ScheduleShowHideAnimation(false);
     }
@@ -339,10 +340,12 @@
 }
 
 void PowerButtonController::OnTabletModeStarted() {
+  in_tablet_mode_ = true;
   StopTimersAndDismissMenu();
 }
 
 void PowerButtonController::OnTabletModeEnded() {
+  in_tablet_mode_ = false;
   StopTimersAndDismissMenu();
 }
 
@@ -356,6 +359,12 @@
     lock_button_down_ = false;
 }
 
+bool PowerButtonController::ShouldTurnScreenOffForTap() const {
+  return features::IsModeSpecificPowerButtonEnabled()
+             ? in_tablet_mode_
+             : default_turn_screen_off_for_tap_;
+}
+
 void PowerButtonController::StopTimersAndDismissMenu() {
   shutdown_timer_.Stop();
   power_button_menu_timer_.Stop();
@@ -399,7 +408,7 @@
 
 void PowerButtonController::InitTabletPowerButtonMembers() {
   if (!force_clamshell_power_button_)
-    turn_screen_off_for_tap_ = true;
+    default_turn_screen_off_for_tap_ = true;
 
   if (!screenshot_controller_) {
     screenshot_controller_ =
diff --git a/ash/system/power/power_button_controller.h b/ash/system/power/power_button_controller.h
index 08b704dd..1d9bb9c5 100644
--- a/ash/system/power/power_button_controller.h
+++ b/ash/system/power/power_button_controller.h
@@ -120,7 +120,7 @@
   void SuspendImminent(power_manager::SuspendImminent::Reason reason) override;
   void SuspendDone(const base::TimeDelta& sleep_duration) override;
 
-  // Initializes |turn_screen_off_for_tap_| and |screenshot_controller_|
+  // Initializes |default_turn_screen_off_for_tap_| and |screenshot_controller_|
   // according to the tablet mode switch in |result|.
   void OnGetSwitchStates(
       base::Optional<chromeos::PowerManagerClient::SwitchStates> result);
@@ -146,6 +146,10 @@
  private:
   friend class PowerButtonControllerTestApi;
 
+  // Returns true if the screen should be turned off in response to the power
+  // button being tapped.
+  bool ShouldTurnScreenOffForTap() const;
+
   // Stops |power_button_menu_timer_|, |shutdown_timer_| and dismisses the power
   // button menu.
   void StopTimersAndDismissMenu();
@@ -162,7 +166,7 @@
   void ProcessCommandLine();
 
   // Initializes tablet power button behavior related members
-  // |turn_screen_off_for_tap_| and |screenshot_controller_|.
+  // |default_turn_screen_off_for_tap_| and |screenshot_controller_|.
   void InitTabletPowerButtonMembers();
 
   // Locks the screen if the "Show lock screen when waking from sleep" pref is
@@ -180,6 +184,9 @@
   bool power_button_down_ = false;
   bool lock_button_down_ = false;
 
+  // True if the device is curently in tablet mode (per TabletModeController).
+  bool in_tablet_mode_ = false;
+
   // Has the screen brightness been reduced to 0%?
   bool brightness_is_zero_ = false;
 
@@ -205,8 +212,10 @@
   // True if the device has tablet mode switch.
   bool has_tablet_mode_switch_ = false;
 
-  // True if should turn screen off when tapping the power button.
-  bool turn_screen_off_for_tap_ = false;
+  // True if we should turn screen off when the power button is tapped.
+  // This may be overridden by a feature; use ShouldTurnScreenOffForTap() to
+  // get the actual desired behavior.
+  bool default_turn_screen_off_for_tap_ = false;
 
   // True if the screen was off when the power button was pressed.
   bool screen_off_when_power_button_down_ = false;
diff --git a/ash/system/power/power_button_controller_test_api.cc b/ash/system/power/power_button_controller_test_api.cc
index e50c320..8a76204 100644
--- a/ash/system/power/power_button_controller_test_api.cc
+++ b/ash/system/power/power_button_controller_test_api.cc
@@ -74,10 +74,6 @@
          GetPowerButtonMenuView()->sign_out_item_for_testing();
 }
 
-bool PowerButtonControllerTestApi::ShouldTurnScreenOffForTap() const {
-  return controller_->turn_screen_off_for_tap_;
-}
-
 PowerButtonScreenshotController*
 PowerButtonControllerTestApi::GetScreenshotController() {
   return controller_->screenshot_controller_.get();
@@ -99,7 +95,7 @@
 
 void PowerButtonControllerTestApi::SetTurnScreenOffForTap(
     bool turn_screen_off_for_tap) {
-  controller_->turn_screen_off_for_tap_ = turn_screen_off_for_tap;
+  controller_->default_turn_screen_off_for_tap_ = turn_screen_off_for_tap;
 }
 
 void PowerButtonControllerTestApi::SetShowMenuAnimationDone(
diff --git a/ash/system/power/power_button_controller_test_api.h b/ash/system/power/power_button_controller_test_api.h
index 8c07d08..65c6d70 100644
--- a/ash/system/power/power_button_controller_test_api.h
+++ b/ash/system/power/power_button_controller_test_api.h
@@ -61,9 +61,6 @@
   // True if |controller_|'s menu has a sign out item.
   bool MenuHasSignOutItem() const;
 
-  // True if should turn screen off when tapping the power button.
-  bool ShouldTurnScreenOffForTap() const;
-
   PowerButtonScreenshotController* GetScreenshotController();
 
   void SetPowerButtonType(PowerButtonController::ButtonType button_type);
diff --git a/ash/system/power/power_button_controller_unittest.cc b/ash/system/power/power_button_controller_unittest.cc
index 7b9d536c..cd88c93 100644
--- a/ash/system/power/power_button_controller_unittest.cc
+++ b/ash/system/power/power_button_controller_unittest.cc
@@ -9,6 +9,7 @@
 #include "ash/display/screen_orientation_controller.h"
 #include "ash/display/screen_orientation_controller_test_api.h"
 #include "ash/media_controller.h"
+#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/config.h"
 #include "ash/session/session_controller.h"
@@ -25,6 +26,7 @@
 #include "base/command_line.h"
 #include "base/json/json_writer.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "chromeos/dbus/fake_power_manager_client.h"
 #include "chromeos/dbus/fake_session_manager_client.h"
@@ -180,7 +182,6 @@
 TEST_F(PowerButtonControllerTest, TappingPowerButtonOfClamshell) {
   // Should not turn the screen off when screen is on.
   InitPowerButtonControllerMembers(PowerManagerClient::TabletMode::UNSUPPORTED);
-  EXPECT_FALSE(turn_screen_off_for_tap_);
   EXPECT_FALSE(power_manager_client_->backlights_forced_off());
   PressPowerButton();
   power_button_test_api_->SetShowMenuAnimationDone(false);
@@ -217,7 +218,6 @@
 TEST_F(PowerButtonControllerTest, TappingPowerButtonOfTablet) {
   // Should turn screen off if screen is on and power button menu will not be
   // shown.
-  EXPECT_TRUE(turn_screen_off_for_tap_);
   PressPowerButton();
   // Showing power menu animation hasn't started as power menu timer is running.
   EXPECT_TRUE(power_button_test_api_->PowerButtonMenuTimerIsRunning());
@@ -249,7 +249,6 @@
   ForceClamshellPowerButton();
   SetTabletModeSwitchState(PowerManagerClient::TabletMode::ON);
   AdvanceClockToAvoidIgnoring();
-  EXPECT_FALSE(turn_screen_off_for_tap_);
   EXPECT_FALSE(power_manager_client_->backlights_forced_off());
   PressPowerButton();
   power_button_test_api_->SetShowMenuAnimationDone(false);
@@ -263,6 +262,32 @@
   EXPECT_FALSE(power_button_test_api_->IsMenuOpened());
 }
 
+// Tests that the mode-specific power button feature causes power button taps to
+// turn the screen off while in tablet mode but not in laptop mode.
+TEST_F(PowerButtonControllerTest, ModeSpecificPowerButton) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(features::kModeSpecificPowerButton);
+
+  // While the device is in tablet mode, tapping the power button should turn
+  // the display off (and then back on if pressed a second time).
+  power_button_controller_->OnTabletModeStarted();
+  PressPowerButton();
+  ReleasePowerButton();
+  EXPECT_TRUE(power_manager_client_->backlights_forced_off());
+  PressPowerButton();
+  ReleasePowerButton();
+  EXPECT_FALSE(power_manager_client_->backlights_forced_off());
+
+  // In laptop mode, tapping the power button shouldn't turn the screen off.
+  // Instead, we should start showing the power menu animation.
+  power_button_controller_->OnTabletModeEnded();
+  AdvanceClockToAvoidIgnoring();
+  PressPowerButton();
+  EXPECT_TRUE(power_button_test_api_->IsMenuOpened());
+  ReleasePowerButton();
+  EXPECT_FALSE(power_manager_client_->backlights_forced_off());
+}
+
 // Tests that release power button after menu is opened but before trigger
 // shutdown will not turn screen off.
 TEST_F(PowerButtonControllerTest, ReleasePowerButtonAfterShowPowerButtonMenu) {
diff --git a/ash/system/power/power_button_test_base.cc b/ash/system/power/power_button_test_base.cc
index b73fbab..adc2802 100644
--- a/ash/system/power/power_button_test_base.cc
+++ b/ash/system/power/power_button_test_base.cc
@@ -71,12 +71,9 @@
   if (initial_tablet_mode_switch_state !=
       chromeos::PowerManagerClient::TabletMode::UNSUPPORTED) {
     SetTabletModeSwitchState(initial_tablet_mode_switch_state);
-    turn_screen_off_for_tap_ =
-        power_button_test_api_->ShouldTurnScreenOffForTap();
     screenshot_controller_ = power_button_test_api_->GetScreenshotController();
   } else {
-    turn_screen_off_for_tap_ = false;
-    power_button_test_api_->SetTurnScreenOffForTap(turn_screen_off_for_tap_);
+    power_button_test_api_->SetTurnScreenOffForTap(false);
     screenshot_controller_ = nullptr;
   }
 }
@@ -88,8 +85,6 @@
           chromeos::PowerManagerClient::LidState::OPEN,
           tablet_mode_switch_state});
 
-  turn_screen_off_for_tap_ =
-      power_button_test_api_->ShouldTurnScreenOffForTap();
   screenshot_controller_ = power_button_test_api_->GetScreenshotController();
 }
 
diff --git a/ash/system/power/power_button_test_base.h b/ash/system/power/power_button_test_base.h
index d37feb03..0f80968 100644
--- a/ash/system/power/power_button_test_base.h
+++ b/ash/system/power/power_button_test_base.h
@@ -46,13 +46,13 @@
   // Initializes |power_button_controller_| and other members that point at
   // objects owned by it. If |initial_tablet_mode_switch_state| is not
   // UNSUPPORTED, tablet mode switch will be set and PowerButtonController will
-  // set |turn_screen_off_for_tap_| to true and create
+  // set |default_turn_screen_off_for_tap_| to true and create
   // PowerButtonScreenshotController on getting the switch.
   void InitPowerButtonControllerMembers(chromeos::PowerManagerClient::TabletMode
                                             initial_tablet_mode_switch_state);
 
-  // Sets the tablet mode switch state. And then PowerButtonController will
-  // initialize |turn_screen_off_for_tap_| and |screenshot_controller_| if the
+  // Sets the tablet mode switch state. PowerButtonController will initialize
+  // |default_turn_screen_off_for_tap_| and |screenshot_controller_| if the
   // switch state is not UNSUPPORTED.
   void SetTabletModeSwitchState(
       chromeos::PowerManagerClient::TabletMode tablet_mode_switch_state);
@@ -96,9 +96,6 @@
   // Simulate that shutdown sound duration callback is done.
   void ShutdownSoundPlayed();
 
-  // True if should turn screen off when tapping the power button.
-  bool turn_screen_off_for_tap_ = false;
-
   // Ownership is passed on to chromeos::DBusThreadManager.
   chromeos::FakePowerManagerClient* power_manager_client_ = nullptr;
   chromeos::FakeSessionManagerClient* session_manager_client_ = nullptr;
diff --git a/ash/system/tray_accessibility.cc b/ash/system/tray_accessibility.cc
index fd27fd8..95ef416 100644
--- a/ash/system/tray_accessibility.cc
+++ b/ash/system/tray_accessibility.cc
@@ -531,10 +531,15 @@
   SetTrayIconVisible(GetInitialVisibility());
 }
 
-void TrayAccessibility::OnAccessibilityStatusChanged(
-    AccessibilityNotificationVisibility notify) {
+void TrayAccessibility::OnAccessibilityStatusChanged() {
   SetTrayIconVisible(GetInitialVisibility());
 
+  if (detailed_menu_)
+    detailed_menu_->OnAccessibilityStatusChanged();
+}
+
+// TODO(warx): Move ShowAccessibilityNotification() to AccessibilityController.
+void TrayAccessibility::ShowAccessibilityNotification() {
   uint32_t accessibility_state = GetAccessibilityState();
   // We'll get an extra notification if a braille display is connected when
   // spoken feedback wasn't already enabled.  This is because the braille
@@ -544,9 +549,6 @@
   if (accessibility_state == previous_accessibility_state_)
     return;
 
-  if (detailed_menu_)
-    detailed_menu_->OnAccessibilityStatusChanged();
-
   message_center::MessageCenter* message_center =
       message_center::MessageCenter::Get();
   message_center->RemoveNotification(kNotificationId, false /* by_user */);
@@ -558,9 +560,7 @@
       (A11Y_SPOKEN_FEEDBACK | A11Y_BRAILLE_DISPLAY_CONNECTED);
   previous_accessibility_state_ = accessibility_state;
 
-  // Shows notification if |notify| is true and the spoken feedback is being
-  // enabled or if a braille display is connected.
-  if (notify != A11Y_NOTIFICATION_SHOW || being_enabled == A11Y_NONE)
+  if (being_enabled == A11Y_NONE)
     return;
 
   base::string16 text;
diff --git a/ash/system/tray_accessibility.h b/ash/system/tray_accessibility.h
index c228e9a..776959ee 100644
--- a/ash/system/tray_accessibility.h
+++ b/ash/system/tray_accessibility.h
@@ -122,8 +122,8 @@
   void UpdateAfterLoginStatusChange(LoginStatus status) override;
 
   // Overridden from AccessibilityObserver.
-  void OnAccessibilityStatusChanged(
-      AccessibilityNotificationVisibility notify) override;
+  void OnAccessibilityStatusChanged() override;
+  void ShowAccessibilityNotification() override;
 
   views::View* default_;
   tray::AccessibilityDetailedView* detailed_menu_;
diff --git a/ash/wm/overview/window_grid.cc b/ash/wm/overview/window_grid.cc
index 4e279ed..6e28039f 100644
--- a/ash/wm/overview/window_grid.cc
+++ b/ash/wm/overview/window_grid.cc
@@ -297,8 +297,14 @@
 
 void WindowGrid::PositionWindows(bool animate,
                                  WindowSelectorItem* ignored_item) {
-  if (window_selector_->IsShuttingDown() || window_list_.empty())
+  if (window_selector_->IsShuttingDown())
     return;
+  if (window_list_.empty()) {
+    if (IsNewOverviewUi())
+      ShowNoRecentsWindowMessage(/*visible=*/true);
+    return;
+  }
+
   DCHECK(shield_widget_.get());
   // Keep the background shield widget covering the whole screen.
   aura::Window* widget_window = shield_widget_->GetNativeWindow();
diff --git a/ash/wm/overview/window_selector.cc b/ash/wm/overview/window_selector.cc
index 7896dff0..54deec11 100644
--- a/ash/wm/overview/window_selector.cc
+++ b/ash/wm/overview/window_selector.cc
@@ -314,8 +314,6 @@
       }
       window_grid->PrepareForOverview();
       window_grid->PositionWindows(/*animate=*/true);
-      if (num_items_ == 0 && IsNewOverviewUi())
-        window_grid->ShowNoRecentsWindowMessage(true);
       // Reset |should_animate_when_entering_| in order to animate during
       // overview mode, such as dragging animations.
       if (IsNewOverviewAnimationsEnabled())
@@ -433,20 +431,16 @@
   if (IsNewOverviewUi()) {
     // If there are no longer any items on any of the grids, shutdown,
     // otherwise the empty grids will remain blurred but will have no items.
-    bool other_grids_has_items = false;
-    for (const auto& grid : grid_list_) {
-      other_grids_has_items = !grid->empty();
-      if (other_grids_has_items)
-        break;
-    }
-    if (!other_grids_has_items) {
-      // Shutdown all grids if no grids have any items. Set |index| to -1 so
-      // that it does not attempt to select any items.
-      for (const auto& grid : grid_list_)
-        grid->Shutdown();
-
+    if (IsEmpty()) {
+      // Shutdown all grids if no grids have any items and split view mode is
+      // not active. Set |index| to -1 so that it does not attempt to select any
+      // items.
       index = -1;
-      grid_list_.clear();
+      if (!Shell::Get()->IsSplitViewModeActive()) {
+        for (const auto& grid : grid_list_)
+          grid->Shutdown();
+        grid_list_.clear();
+      }
     } else {
       for (auto iter = grid_list_.begin(); iter != grid_list_.end(); ++iter) {
         if (grid == (*iter).get()) {
@@ -477,6 +471,8 @@
   }
   if (grid_list_.empty())
     CancelSelection();
+  else
+    PositionWindows(/*animate=*/false);
 }
 
 void WindowSelector::IncrementSelection(int increment) {
@@ -589,8 +585,6 @@
     if (grid->GetWindowSelectorItemContaining(item->GetWindow())) {
       grid->RemoveItem(item);
       --num_items_;
-      if (grid->empty())
-        OnGridEmpty(grid.get());
       break;
     }
   }
@@ -645,10 +639,7 @@
                                     const ui::KeyEvent& key_event) {
   // Do not do anything with the events if none of the window grids have windows
   // in them.
-  bool empty_grids = true;
-  for (std::unique_ptr<WindowGrid>& grid : grid_list_)
-    empty_grids &= grid->empty();
-  if (empty_grids)
+  if (IsEmpty())
     return true;
 
   if (key_event.type() != ui::ET_KEY_PRESSED)
@@ -941,4 +932,12 @@
     split_view_drag_indicators_->OnDisplayBoundsChanged();
 }
 
+bool WindowSelector::IsEmpty() {
+  for (const auto& grid : grid_list_) {
+    if (!grid->empty())
+      return false;
+  }
+  return true;
+}
+
 }  // namespace ash
diff --git a/ash/wm/overview/window_selector.h b/ash/wm/overview/window_selector.h
index 3a9fcae..09d50b1 100644
--- a/ash/wm/overview/window_selector.h
+++ b/ash/wm/overview/window_selector.h
@@ -202,6 +202,9 @@
   // Called when the display area for the overview window grids changed.
   void OnDisplayBoundsChanged();
 
+  // Returns true if all its window grids don't have any window item.
+  bool IsEmpty();
+
   // Tracks observed windows.
   std::set<aura::Window*> observed_windows_;
 
diff --git a/ash/wm/overview/window_selector_controller.cc b/ash/wm/overview/window_selector_controller.cc
index 86ec056..789547fb 100644
--- a/ash/wm/overview/window_selector_controller.cc
+++ b/ash/wm/overview/window_selector_controller.cc
@@ -87,26 +87,29 @@
 }
 
 bool WindowSelectorController::ToggleOverview() {
+  auto windows = Shell::Get()->mru_window_tracker()->BuildMruWindowList();
+
+  // Hidden windows will be removed by ShouldExcludeWindowFromOverview so we
+  // must copy them out first.
+  std::vector<aura::Window*> hide_windows(windows.size());
+  auto end = std::copy_if(windows.begin(), windows.end(), hide_windows.begin(),
+                          ShouldHideWindowInOverview);
+  hide_windows.resize(end - hide_windows.begin());
+
+  end = std::remove_if(windows.begin(), windows.end(),
+                       ShouldExcludeWindowFromOverview);
+  windows.resize(end - windows.begin());
+
   if (IsSelecting()) {
+    // Do not allow ending overview if we're in single split mode.
+    if (windows.empty() && Shell::Get()->IsSplitViewModeActive())
+      return true;
     OnSelectionEnded();
   } else {
     // Don't start overview if window selection is not allowed.
     if (!CanSelect())
       return false;
 
-    auto windows = Shell::Get()->mru_window_tracker()->BuildMruWindowList();
-
-    // Hidden windows will be removed by ShouldExcludeWindowFromOverview so we
-    // must copy them out first.
-    std::vector<aura::Window*> hide_windows(windows.size());
-    auto end = std::copy_if(windows.begin(), windows.end(),
-                            hide_windows.begin(), ShouldHideWindowInOverview);
-    hide_windows.resize(end - hide_windows.begin());
-
-    end = std::remove_if(windows.begin(), windows.end(),
-                         ShouldExcludeWindowFromOverview);
-    windows.resize(end - windows.begin());
-
     // Don't enter overview with no windows to select from.
     if (!IsNewOverviewUi() && windows.empty())
       return false;
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc
index 0cfe4d2..4a671f5 100644
--- a/ash/wm/overview/window_selector_unittest.cc
+++ b/ash/wm/overview/window_selector_unittest.cc
@@ -3262,9 +3262,9 @@
 }
 
 // Tests that if there is only one window in the MRU window list in the overview
-// mode, snapping the window to one side of the screen will end the overview
-// mode since there is no more window left in the overview window grid.
-TEST_F(SplitViewWindowSelectorTest, EmptyWindowsListExitOverview) {
+// mode, snapping the window to one side of the screen will not end the overview
+// mode even if there is no more window left in the overview window grid.
+TEST_F(SplitViewWindowSelectorTest, EmptyWindowsListNotExitOverview) {
   const gfx::Rect bounds(400, 400);
   std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
 
@@ -3277,9 +3277,68 @@
       GetWindowItemForWindow(grid_index, window1.get());
   DragWindowTo(selector_item1, gfx::Point(0, 0));
 
+  // Test that overview mode is active in this single window case.
   EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true);
   EXPECT_EQ(split_view_controller()->state(),
             SplitViewController::LEFT_SNAPPED);
+  EXPECT_TRUE(window_selector_controller()->IsSelecting());
+
+  // Create a new window should exit the overview mode.
+  std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
+  wm::ActivateWindow(window2.get());
+  EXPECT_FALSE(window_selector_controller()->IsSelecting());
+  EXPECT_EQ(split_view_controller()->state(),
+            SplitViewController::BOTH_SNAPPED);
+  // If there are only 2 snapped windows, close one of them should enter
+  // overview mode.
+  window2.reset();
+  EXPECT_TRUE(window_selector_controller()->IsSelecting());
+
+  // If there are more than 2 windows in overview
+  std::unique_ptr<aura::Window> window3(CreateWindow(bounds));
+  std::unique_ptr<aura::Window> window4(CreateWindow(bounds));
+  wm::ActivateWindow(window3.get());
+  wm::ActivateWindow(window4.get());
+  EXPECT_FALSE(window_selector_controller()->IsSelecting());
+  EXPECT_EQ(split_view_controller()->state(),
+            SplitViewController::BOTH_SNAPPED);
+  ToggleOverview();
+  EXPECT_TRUE(window_selector_controller()->IsSelecting());
+  window3.reset();
+  EXPECT_TRUE(window_selector_controller()->IsSelecting());
+  window4.reset();
+  EXPECT_TRUE(window_selector_controller()->IsSelecting());
+
+  // Test that if there is only 1 snapped window, and no window in the overview
+  // grid, ToggleOverview() can't end overview.
+  ToggleOverview();
+  EXPECT_TRUE(window_selector_controller()->IsSelecting());
+
+  EndSplitView();
+  EXPECT_FALSE(Shell::Get()->IsSplitViewModeActive());
+  EXPECT_TRUE(window_selector_controller()->IsSelecting());
+
+  // Test that ToggleOverview() can end overview if we're not in split view
+  // mode.
+  ToggleOverview();
+  EXPECT_FALSE(window_selector_controller()->IsSelecting());
+
+  // Now enter overview and split view again. Test that exiting tablet mode can
+  // end split view and overview correctly.
+  ToggleOverview();
+  selector_item1 = GetWindowItemForWindow(grid_index, window1.get());
+  DragWindowTo(selector_item1, gfx::Point(0, 0));
+  EXPECT_TRUE(Shell::Get()->IsSplitViewModeActive());
+  EXPECT_TRUE(window_selector_controller()->IsSelecting());
+  Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(false);
+  EXPECT_FALSE(Shell::Get()->IsSplitViewModeActive());
+  EXPECT_FALSE(window_selector_controller()->IsSelecting());
+
+  // Test that closing all windows in overview can end overview if we're not in
+  // split view mode.
+  ToggleOverview();
+  EXPECT_TRUE(window_selector_controller()->IsSelecting());
+  window1.reset();
   EXPECT_FALSE(window_selector_controller()->IsSelecting());
 }
 
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index fb9d5ca1..88b1769d 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -12,6 +12,7 @@
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/screen_util.h"
+#include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/toast/toast_data.h"
@@ -670,7 +671,8 @@
   // directly.
   if ((left_window_ && !CanSnap(left_window_)) ||
       (right_window_ && !CanSnap(right_window_))) {
-    EndSplitView();
+    if (!Shell::Get()->session_controller()->IsUserSessionBlocked())
+      EndSplitView();
     return;
   }
 
diff --git a/ash/wm/splitview/split_view_controller_unittest.cc b/ash/wm/splitview/split_view_controller_unittest.cc
index 08fae23c..7801e3a 100644
--- a/ash/wm/splitview/split_view_controller_unittest.cc
+++ b/ash/wm/splitview/split_view_controller_unittest.cc
@@ -7,6 +7,8 @@
 #include "ash/display/screen_orientation_controller.h"
 #include "ash/display/screen_orientation_controller_test_api.h"
 #include "ash/screen_util.h"
+#include "ash/session/session_controller.h"
+#include "ash/session/test_session_controller_client.h"
 #include "ash/shell.h"
 #include "ash/system/overview/overview_button_tray.h"
 #include "ash/system/status_area_widget.h"
@@ -39,6 +41,9 @@
   // test::AshTestBase:
   void SetUp() override {
     AshTestBase::SetUp();
+    // Avoid TabletModeController::OnGetSwitchStates() from disabling tablet
+    // mode.
+    base::RunLoop().RunUntilIdle();
     Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true);
   }
 
@@ -1177,4 +1182,35 @@
   EXPECT_LE(divider_position(), 0.5f * workarea_bounds.width());
 }
 
+// Test that if display configuration changes in lock screen, the split view
+// mode doesn't end.
+TEST_F(SplitViewControllerTest, DoNotEndSplitViewInLockScreen) {
+  display::test::DisplayManagerTestApi(display_manager())
+      .SetFirstDisplayAsInternalDisplay();
+  UpdateDisplay("800x400");
+  const gfx::Rect bounds(0, 0, 200, 300);
+  std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
+  std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
+  split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT);
+  split_view_controller()->SnapWindow(window2.get(),
+                                      SplitViewController::RIGHT);
+  EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive());
+  EXPECT_EQ(split_view_controller()->state(),
+            SplitViewController::BOTH_SNAPPED);
+
+  // Now lock the screen.
+  Shell::Get()->session_controller()->LockScreenAndFlushForTest();
+  // Change display configuration. Split view mode is still active.
+  UpdateDisplay("400x800");
+  EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive());
+  EXPECT_EQ(split_view_controller()->state(),
+            SplitViewController::BOTH_SNAPPED);
+
+  // Now unlock the screen.
+  GetSessionControllerClient()->UnlockScreen();
+  EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive());
+  EXPECT_EQ(split_view_controller()->state(),
+            SplitViewController::BOTH_SNAPPED);
+}
+
 }  // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_window_state.cc b/ash/wm/tablet_mode/tablet_mode_window_state.cc
index 8aa7394..f3047172 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_state.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_state.cc
@@ -86,7 +86,6 @@
     return screen_util::GetDisplayBoundsInParent(state_object->window());
 
   if (state_object->GetStateType() == mojom::WindowStateType::LEFT_SNAPPED) {
-    DCHECK(CanSnap(state_object));
     return Shell::Get()
         ->split_view_controller()
         ->GetSnappedWindowBoundsInParent(state_object->window(),
@@ -94,7 +93,6 @@
   }
 
   if (state_object->GetStateType() == mojom::WindowStateType::RIGHT_SNAPPED) {
-    DCHECK(CanSnap(state_object));
     return Shell::Get()
         ->split_view_controller()
         ->GetSnappedWindowBoundsInParent(state_object->window(),
diff --git a/ash/wm/workspace/backdrop_controller.cc b/ash/wm/workspace/backdrop_controller.cc
index 0af7260..91ed3a54 100644
--- a/ash/wm/workspace/backdrop_controller.cc
+++ b/ash/wm/workspace/backdrop_controller.cc
@@ -164,8 +164,7 @@
   Shell::Get()->split_view_controller()->RemoveObserver(this);
 }
 
-void BackdropController::OnAccessibilityStatusChanged(
-    AccessibilityNotificationVisibility notify) {
+void BackdropController::OnAccessibilityStatusChanged() {
   UpdateBackdrop();
 }
 
diff --git a/ash/wm/workspace/backdrop_controller.h b/ash/wm/workspace/backdrop_controller.h
index 9e5b460..e764a3a 100644
--- a/ash/wm/workspace/backdrop_controller.h
+++ b/ash/wm/workspace/backdrop_controller.h
@@ -71,8 +71,7 @@
   void OnSplitViewModeEnded() override;
 
   // AccessibilityObserver:
-  void OnAccessibilityStatusChanged(
-      AccessibilityNotificationVisibility notify) override;
+  void OnAccessibilityStatusChanged() override;
 
   // SplitViewController::Observer:
   void OnSplitViewStateChanged(SplitViewController::State previous_state,
diff --git a/base/android/java/src/org/chromium/base/ContextUtils.java b/base/android/java/src/org/chromium/base/ContextUtils.java
index acfe103..075c4a9a 100644
--- a/base/android/java/src/org/chromium/base/ContextUtils.java
+++ b/base/android/java/src/org/chromium/base/ContextUtils.java
@@ -134,9 +134,6 @@
             throw new RuntimeException("Global application context cannot be set to null.");
         }
         sApplicationContext = appContext;
-
-        // TODO(agrieve): Remove when we stop supporting JB.
-        getProcessName(); // Prime the cache for getProcessName().
     }
 
     /**
@@ -171,6 +168,8 @@
 
     /** @return The name of the current process. E.g. "org.chromium.chrome:privileged_process0". */
     public static String getProcessName() {
+        // Once we drop support JB, this method can be simplified to not cache sProcessName and call
+        // ActivityThread.currentProcessName().
         if (sProcessName != null) {
             return sProcessName;
         }
diff --git a/base/android/proguard/chromium_apk.flags b/base/android/proguard/chromium_apk.flags
index 7b4f75bb..82509977 100644
--- a/base/android/proguard/chromium_apk.flags
+++ b/base/android/proguard/chromium_apk.flags
@@ -48,8 +48,9 @@
 #          5          | 1:56 |  5770504  |         486432
 -optimizationpasses 3
 
-# Horizontal class merging marginally increases dex size (as of Mar 2018).
--optimizations !class/merging/horizontal
+# Class merging messes up stacktraces beyond the point of them being
+# deobfuscatable. If turned on, it would give us a 2% reduction in .dex size.
+-optimizations !class/merging/*
 
 # Allowing Proguard to change modifiers. This change shrinks the .dex size by
 # ~1%, and reduces the method count by ~4%.
diff --git a/base/base_switches.cc b/base/base_switches.cc
index 9f33d0bf..051ab72 100644
--- a/base/base_switches.cc
+++ b/base/base_switches.cc
@@ -21,25 +21,6 @@
 // Comma-separated list of feature names to enable. See also kDisableFeatures.
 const char kEnableFeatures[] = "enable-features";
 
-// Makes memory allocators keep track of their allocations and context, so a
-// detailed breakdown of memory usage can be presented in chrome://tracing when
-// the memory-infra category is enabled.
-const char kEnableHeapProfiling[]           = "enable-heap-profiling";
-
-// Report pseudo allocation traces. Pseudo traces are derived from currently
-// active trace events.
-const char kEnableHeapProfilingModePseudo[] = "";
-
-// Report native (walk the stack) allocation traces. By default pseudo stacks
-// derived from trace events are reported.
-const char kEnableHeapProfilingModeNative[] = "native";
-
-// Report per-task heap usage and churn in the task profiler.
-// Does not keep track of individual allocations unlike the default and native
-// mode. Keeps only track of summarized churn stats in the task profiler
-// (chrome://profiler).
-const char kEnableHeapProfilingTaskProfiler[] = "task-profiler";
-
 // Generates full memory crash dump.
 const char kFullMemoryCrashReport[]         = "full-memory-crash-report";
 
diff --git a/base/base_switches.h b/base/base_switches.h
index e5b9453..48fdbdc 100644
--- a/base/base_switches.h
+++ b/base/base_switches.h
@@ -16,10 +16,6 @@
 extern const char kDisableLowEndDeviceMode[];
 extern const char kEnableCrashReporter[];
 extern const char kEnableFeatures[];
-extern const char kEnableHeapProfiling[];
-extern const char kEnableHeapProfilingModePseudo[];
-extern const char kEnableHeapProfilingModeNative[];
-extern const char kEnableHeapProfilingTaskProfiler[];
 extern const char kEnableLowEndDeviceMode[];
 extern const char kForceFieldTrials[];
 extern const char kFullMemoryCrashReport[];
diff --git a/base/json/json_parser.cc b/base/json/json_parser.cc
index 3e77b99..4a3677c7 100644
--- a/base/json/json_parser.cc
+++ b/base/json/json_parser.cc
@@ -506,7 +506,7 @@
       string.Convert();
 
       // Read past the escape '\' and ensure there's a character following.
-      if (!CanConsume(1)) {
+      if (!CanConsume(2)) {
         ReportError(JSONReader::JSON_INVALID_ESCAPE, 0);
         return false;
       }
diff --git a/base/json/json_parser_unittest.cc b/base/json/json_parser_unittest.cc
index eabcc381..13173ad 100644
--- a/base/json/json_parser_unittest.cc
+++ b/base/json/json_parser_unittest.cc
@@ -441,6 +441,8 @@
       "nul",
       "\"\\x2",
       "\"\\u123",
+      "\"\\",
+      "\"\\/",
       // clang-format on
   };
 
diff --git a/base/message_loop/message_pump_libevent.cc b/base/message_loop/message_pump_libevent.cc
index dc5efc6..f12c30e 100644
--- a/base/message_loop/message_pump_libevent.cc
+++ b/base/message_loop/message_pump_libevent.cc
@@ -7,7 +7,7 @@
 #include <errno.h>
 #include <unistd.h>
 
-#include <memory>
+#include <utility>
 
 #include "base/auto_reset.h"
 #include "base/compiler_specific.h"
@@ -45,11 +45,7 @@
 
 MessagePumpLibevent::FileDescriptorWatcher::FileDescriptorWatcher(
     const Location& from_here)
-    : event_(nullptr),
-      pump_(nullptr),
-      watcher_(nullptr),
-      was_destroyed_(nullptr),
-      created_from_location_(from_here) {}
+    : created_from_location_(from_here) {}
 
 MessagePumpLibevent::FileDescriptorWatcher::~FileDescriptorWatcher() {
   if (event_) {
@@ -62,29 +58,28 @@
 }
 
 bool MessagePumpLibevent::FileDescriptorWatcher::StopWatchingFileDescriptor() {
-  event* e = ReleaseEvent();
-  if (e == nullptr)
+  std::unique_ptr<event> e = ReleaseEvent();
+  if (!e)
     return true;
 
   // event_del() is a no-op if the event isn't active.
-  int rv = event_del(e);
-  delete e;
+  int rv = event_del(e.get());
   pump_ = nullptr;
   watcher_ = nullptr;
   return (rv == 0);
 }
 
-void MessagePumpLibevent::FileDescriptorWatcher::Init(event* e) {
+void MessagePumpLibevent::FileDescriptorWatcher::Init(
+    std::unique_ptr<event> e) {
   DCHECK(e);
   DCHECK(!event_);
 
-  event_ = e;
+  event_ = std::move(e);
 }
 
-event* MessagePumpLibevent::FileDescriptorWatcher::ReleaseEvent() {
-  struct event* e = event_;
-  event_ = nullptr;
-  return e;
+std::unique_ptr<event>
+MessagePumpLibevent::FileDescriptorWatcher::ReleaseEvent() {
+  return std::move(event_);
 }
 
 void MessagePumpLibevent::FileDescriptorWatcher::OnFileCanReadWithoutBlocking(
@@ -189,12 +184,9 @@
     return false;
   }
 
-  // Transfer ownership of evt to controller.
-  controller->Init(evt.release());
-
+  controller->Init(std::move(evt));
   controller->set_watcher(delegate);
   controller->set_pump(this);
-
   return true;
 }
 
diff --git a/base/message_loop/message_pump_libevent.h b/base/message_loop/message_pump_libevent.h
index cee4ad36..1be1922 100644
--- a/base/message_loop/message_pump_libevent.h
+++ b/base/message_loop/message_pump_libevent.h
@@ -5,6 +5,8 @@
 #ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_LIBEVENT_H_
 #define BASE_MESSAGE_LOOP_MESSAGE_PUMP_LIBEVENT_H_
 
+#include <memory>
+
 #include "base/compiler_specific.h"
 #include "base/location.h"
 #include "base/macros.h"
@@ -54,12 +56,11 @@
     friend class MessagePumpLibevent;
     friend class MessagePumpLibeventTest;
 
-    // Called by MessagePumpLibevent, ownership of |e| is transferred to this
-    // object.
-    void Init(event* e);
+    // Called by MessagePumpLibevent.
+    void Init(std::unique_ptr<event> e);
 
-    // Used by MessagePumpLibevent to take ownership of event_.
-    event* ReleaseEvent();
+    // Used by MessagePumpLibevent to take ownership of |event_|.
+    std::unique_ptr<event> ReleaseEvent();
 
     void set_pump(MessagePumpLibevent* pump) { pump_ = pump; }
     MessagePumpLibevent* pump() const { return pump_; }
@@ -69,12 +70,12 @@
     void OnFileCanReadWithoutBlocking(int fd, MessagePumpLibevent* pump);
     void OnFileCanWriteWithoutBlocking(int fd, MessagePumpLibevent* pump);
 
-    event* event_;
-    MessagePumpLibevent* pump_;
-    Watcher* watcher_;
+    std::unique_ptr<event> event_;
+    MessagePumpLibevent* pump_ = nullptr;
+    Watcher* watcher_ = nullptr;
     // If this pointer is non-NULL, the pointee is set to true in the
     // destructor.
-    bool* was_destroyed_;
+    bool* was_destroyed_ = nullptr;
 
     const Location created_from_location_;
 
diff --git a/base/strings/string_tokenizer_fuzzer.cc b/base/strings/string_tokenizer_fuzzer.cc
index 00d6cb0..917041b 100644
--- a/base/strings/string_tokenizer_fuzzer.cc
+++ b/base/strings/string_tokenizer_fuzzer.cc
@@ -17,24 +17,25 @@
 
 // Entry point for LibFuzzer.
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  if (size < 1) {
+  uint8_t size_t_bytes = sizeof(size_t);
+  if (size < size_t_bytes + 1) {
     return 0;
   }
 
+  // Calculate pattern size based on remaining bytes, otherwise fuzzing is
+  // inefficient with bailouts in most cases.
+  size_t pattern_size =
+      *reinterpret_cast<const size_t*>(data) % (size - size_t_bytes);
+
+  std::string pattern(reinterpret_cast<const char*>(data + size_t_bytes),
+                      pattern_size);
+  std::string input(
+      reinterpret_cast<const char*>(data + size_t_bytes + pattern_size),
+      size - pattern_size - size_t_bytes);
+
   // Allow quote_chars and options to be set. Otherwise full coverage
   // won't be possible since IsQuote, FullGetNext and other functions
   // won't be called.
-  size_t pattern_size = data[0];
-
-  if (pattern_size > size - 1) {
-    return 0;
-  }
-
-  std::string pattern(reinterpret_cast<const char*>(data + 1), pattern_size);
-
-  std::string input(reinterpret_cast<const char*>(data + 1 + pattern_size),
-                    size - pattern_size - 1);
-
   base::StringTokenizer t(input, pattern);
   GetAllTokens(t);
 
diff --git a/base/strings/utf_string_conversion_utils.cc b/base/strings/utf_string_conversion_utils.cc
index 3101a60..f7682c1 100644
--- a/base/strings/utf_string_conversion_utils.cc
+++ b/base/strings/utf_string_conversion_utils.cc
@@ -5,6 +5,7 @@
 #include "base/strings/utf_string_conversion_utils.h"
 
 #include "base/third_party/icu/icu_utf.h"
+#include "build/build_config.h"
 
 namespace base {
 
@@ -121,7 +122,10 @@
 }
 
 // Instantiate versions we know callers will need.
+#if !defined(OS_WIN)
+// wchar_t and char16 are the same thing on Windows.
 template void PrepareForUTF8Output(const wchar_t*, size_t, std::string*);
+#endif
 template void PrepareForUTF8Output(const char16*, size_t, std::string*);
 
 template<typename STRING>
@@ -142,7 +146,10 @@
 }
 
 // Instantiate versions we know callers will need.
+#if !defined(OS_WIN)
+// std::wstring and string16 are the same thing on Windows.
 template void PrepareForUTF16Or32Output(const char*, size_t, std::wstring*);
+#endif
 template void PrepareForUTF16Or32Output(const char*, size_t, string16*);
 
 }  // namespace base
diff --git a/base/test/simple_test_tick_clock.cc b/base/test/simple_test_tick_clock.cc
index d454646..7ee3401d 100644
--- a/base/test/simple_test_tick_clock.cc
+++ b/base/test/simple_test_tick_clock.cc
@@ -12,7 +12,7 @@
 
 SimpleTestTickClock::~SimpleTestTickClock() = default;
 
-TimeTicks SimpleTestTickClock::NowTicks() {
+TimeTicks SimpleTestTickClock::NowTicks() const {
   AutoLock lock(lock_);
   return now_ticks_;
 }
diff --git a/base/test/simple_test_tick_clock.h b/base/test/simple_test_tick_clock.h
index f2f7581..923eba4 100644
--- a/base/test/simple_test_tick_clock.h
+++ b/base/test/simple_test_tick_clock.h
@@ -21,7 +21,7 @@
   SimpleTestTickClock();
   ~SimpleTestTickClock() override;
 
-  TimeTicks NowTicks() override;
+  TimeTicks NowTicks() const override;
 
   // Advances the clock by |delta|, which must not be negative.
   void Advance(TimeDelta delta);
@@ -31,7 +31,7 @@
 
  private:
   // Protects |now_ticks_|.
-  Lock lock_;
+  mutable Lock lock_;
 
   TimeTicks now_ticks_;
 };
diff --git a/base/test/test_mock_time_task_runner.cc b/base/test/test_mock_time_task_runner.cc
index 9529038..e22fd104 100644
--- a/base/test/test_mock_time_task_runner.cc
+++ b/base/test/test_mock_time_task_runner.cc
@@ -27,7 +27,7 @@
       : task_runner_(std::move(task_runner)) {}
 
   // TickClock:
-  TimeTicks NowTicks() override { return task_runner_->NowTicks(); }
+  TimeTicks NowTicks() const override { return task_runner_->NowTicks(); }
 
  private:
   scoped_refptr<const TestMockTimeTaskRunner> task_runner_;
@@ -413,7 +413,7 @@
   // doesn't need an extra kick on nested runs.
 }
 
-TimeTicks TestMockTimeTaskRunner::MockClock::NowTicks() {
+TimeTicks TestMockTimeTaskRunner::MockClock::NowTicks() const {
   return task_runner_->NowTicks();
 }
 
diff --git a/base/test/test_mock_time_task_runner.h b/base/test/test_mock_time_task_runner.h
index 0ad8247..87e02c1 100644
--- a/base/test/test_mock_time_task_runner.h
+++ b/base/test/test_mock_time_task_runner.h
@@ -209,7 +209,7 @@
         : task_runner_(task_runner) {}
 
     // TickClock:
-    TimeTicks NowTicks() override;
+    TimeTicks NowTicks() const override;
 
     // Clock:
     Time Now() override;
diff --git a/base/time/default_tick_clock.cc b/base/time/default_tick_clock.cc
index 96d4d86..27b1671 100644
--- a/base/time/default_tick_clock.cc
+++ b/base/time/default_tick_clock.cc
@@ -10,7 +10,7 @@
 
 DefaultTickClock::~DefaultTickClock() = default;
 
-TimeTicks DefaultTickClock::NowTicks() {
+TimeTicks DefaultTickClock::NowTicks() const {
   return TimeTicks::Now();
 }
 
diff --git a/base/time/default_tick_clock.h b/base/time/default_tick_clock.h
index dce2538..df8037f 100644
--- a/base/time/default_tick_clock.h
+++ b/base/time/default_tick_clock.h
@@ -17,7 +17,7 @@
   ~DefaultTickClock() override;
 
   // Simply returns TimeTicks::Now().
-  TimeTicks NowTicks() override;
+  TimeTicks NowTicks() const override;
 
   // Returns a shared instance of DefaultTickClock. This is thread-safe.
   static DefaultTickClock* GetInstance();
diff --git a/base/time/tick_clock.h b/base/time/tick_clock.h
index f7aba537..dc57354a 100644
--- a/base/time/tick_clock.h
+++ b/base/time/tick_clock.h
@@ -32,7 +32,7 @@
   // assume that NowTicks() is monotonic (but not strictly monotonic).
   // In other words, the returned TimeTicks will never decrease with
   // time, although they might "stand still".
-  virtual TimeTicks NowTicks() = 0;
+  virtual TimeTicks NowTicks() const = 0;
 };
 
 }  // namespace base
diff --git a/base/trace_event/heap_profiler_allocation_context_tracker.h b/base/trace_event/heap_profiler_allocation_context_tracker.h
index 9bd656d..da03b7f6 100644
--- a/base/trace_event/heap_profiler_allocation_context_tracker.h
+++ b/base/trace_event/heap_profiler_allocation_context_tracker.h
@@ -15,11 +15,25 @@
 namespace base {
 namespace trace_event {
 
-// The allocation context tracker keeps track of thread-local context for heap
-// profiling. It includes a pseudo stack of trace events. On every allocation
-// the tracker provides a snapshot of its context in the form of an
-// |AllocationContext| that is to be stored together with the allocation
-// details.
+// AllocationContextTracker is a thread-local object. Its main purpose is to
+// keep track of a pseudo stack of trace events. Chrome has been instrumented
+// with lots of `TRACE_EVENT` macros. These trace events push their name to a
+// thread-local stack when they go into scope, and pop when they go out of
+// scope, if all of the following conditions have been met:
+//
+//  * A trace is being recorded.
+//  * The category of the event is enabled in the trace config.
+//  * Heap profiling is enabled (with the `--enable-heap-profiling` flag).
+//
+// This means that allocations that occur before tracing is started will not
+// have backtrace information in their context.
+//
+// AllocationContextTracker also keeps track of some thread state not related to
+// trace events. See |AllocationContext|.
+//
+// A thread-local instance of the context tracker is initialized lazily when it
+// is first accessed. This might be because a trace event pushed or popped, or
+// because `GetContextSnapshot()` was called when an allocation occurred
 class BASE_EXPORT AllocationContextTracker {
  public:
   enum class CaptureMode : int32_t {
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index 71f4ce4..d98576f 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -201,44 +201,6 @@
   g_memory_dump_manager_for_testing = nullptr;
 }
 
-// static
-HeapProfilingMode MemoryDumpManager::GetHeapProfilingModeFromCommandLine() {
-  if (!CommandLine::InitializedForCurrentProcess() ||
-      !CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableHeapProfiling)) {
-    return kHeapProfilingModeDisabled;
-  }
-#if BUILDFLAG(USE_ALLOCATOR_SHIM) && !defined(OS_NACL)
-  std::string profiling_mode =
-      CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-          switches::kEnableHeapProfiling);
-  if (profiling_mode == switches::kEnableHeapProfilingTaskProfiler)
-    return kHeapProfilingModeTaskProfiler;
-  if (profiling_mode == switches::kEnableHeapProfilingModePseudo)
-    return kHeapProfilingModePseudo;
-  if (profiling_mode == switches::kEnableHeapProfilingModeNative)
-    return kHeapProfilingModeNative;
-#endif  // BUILDFLAG(USE_ALLOCATOR_SHIM) && !defined(OS_NACL)
-  return kHeapProfilingModeInvalid;
-}
-
-void MemoryDumpManager::EnableHeapProfilingIfNeeded() {
-#if BUILDFLAG(USE_ALLOCATOR_SHIM) && !defined(OS_NACL)
-  HeapProfilingMode profiling_mode = GetHeapProfilingModeFromCommandLine();
-  if (IsHeapProfilingModeEnabled(profiling_mode)) {
-    EnableHeapProfiling(profiling_mode);
-  } else {
-    if (profiling_mode == kHeapProfilingModeInvalid) {
-      // Heap profiling is misconfigured, disable it permanently.
-      EnableHeapProfiling(kHeapProfilingModeDisabled);
-    }
-  }
-#else
-  // Heap profiling is unsupported, disable it permanently.
-  EnableHeapProfiling(kHeapProfilingModeDisabled);
-#endif  // BUILDFLAG(USE_ALLOCATOR_SHIM) && !defined(OS_NACL)
-}
-
 bool MemoryDumpManager::EnableHeapProfiling(HeapProfilingMode profiling_mode) {
   AutoLock lock(lock_);
 #if BUILDFLAG(USE_ALLOCATOR_SHIM) && !defined(OS_NACL)
@@ -329,7 +291,6 @@
     request_dump_function_ = request_dump_function;
     is_coordinator_ = is_coordinator;
   }
-  EnableHeapProfilingIfNeeded();
 
 // Enable the core dump providers.
 #if defined(MALLOC_MEMORY_TRACING_SUPPORTED)
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h
index d6237fd..072a7d6 100644
--- a/base/trace_event/memory_dump_manager.h
+++ b/base/trace_event/memory_dump_manager.h
@@ -131,15 +131,6 @@
   void CreateProcessDump(const MemoryDumpRequestArgs& args,
                          const ProcessMemoryDumpCallback& callback);
 
-  // Returns the heap profiling mode configured on the command-line, if any.
-  // If heap profiling is configured but not supported by this binary, or if an
-  // invalid mode is specified, then kHeapProfilingInvalid is returned.
-  static HeapProfilingMode GetHeapProfilingModeFromCommandLine();
-
-  // Enable heap profiling if supported, and kEnableHeapProfiling command line
-  // is specified.
-  void EnableHeapProfilingIfNeeded();
-
   // Enable heap profiling with specified |profiling_mode|.
   // Use kHeapProfilingModeDisabled to disable, but it can't be re-enabled then.
   // Returns true if mode has been *changed* to the desired |profiling_mode|.
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index de98f1d..e92045ed 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -961,54 +961,6 @@
 }
 #endif  //  BUILDFLAG(USE_ALLOCATOR_SHIM) && !defined(OS_NACL)
 
-TEST_F(MemoryDumpManagerTest, EnableHeapProfilingIfNeeded) {
-  MockMemoryDumpProvider mdp1;
-  MemoryDumpProvider::Options supported_options;
-  supported_options.supports_heap_profiling = true;
-  RegisterDumpProvider(&mdp1, ThreadTaskRunnerHandle::Get(), supported_options);
-
-  // Should be noop.
-  mdm_->EnableHeapProfilingIfNeeded();
-  ASSERT_EQ(AllocationContextTracker::CaptureMode::DISABLED,
-            AllocationContextTracker::capture_mode());
-  mdm_->EnableHeapProfilingIfNeeded();
-  ASSERT_EQ(AllocationContextTracker::CaptureMode::DISABLED,
-            AllocationContextTracker::capture_mode());
-
-#if BUILDFLAG(USE_ALLOCATOR_SHIM) && !defined(OS_NACL)
-  testing::InSequence sequence;
-  EXPECT_CALL(mdp1, OnHeapProfilingEnabled(true)).Times(1);
-  EXPECT_CALL(mdp1, OnHeapProfilingEnabled(false)).Times(1);
-
-  CommandLine* cmdline = CommandLine::ForCurrentProcess();
-  cmdline->AppendSwitchASCII(switches::kEnableHeapProfiling, "");
-  mdm_->EnableHeapProfilingIfNeeded();
-  RunLoop().RunUntilIdle();
-  ASSERT_EQ(AllocationContextTracker::CaptureMode::PSEUDO_STACK,
-            AllocationContextTracker::capture_mode());
-  EXPECT_TRUE(mdm_->EnableHeapProfiling(kHeapProfilingModeDisabled));
-  RunLoop().RunUntilIdle();
-  ASSERT_EQ(AllocationContextTracker::CaptureMode::DISABLED,
-            AllocationContextTracker::capture_mode());
-  EXPECT_FALSE(mdm_->EnableHeapProfiling(kHeapProfilingModeBackground));
-  ASSERT_EQ(AllocationContextTracker::CaptureMode::DISABLED,
-            AllocationContextTracker::capture_mode());
-#endif  //  BUILDFLAG(USE_ALLOCATOR_SHIM) && !defined(OS_NACL)
-}
-
-TEST_F(MemoryDumpManagerTest, EnableHeapProfilingIfNeededUnsupported) {
-#if BUILDFLAG(USE_ALLOCATOR_SHIM) && !defined(OS_NACL)
-  ASSERT_EQ(mdm_->GetHeapProfilingMode(), kHeapProfilingModeDisabled);
-  CommandLine* cmdline = CommandLine::ForCurrentProcess();
-  cmdline->AppendSwitchASCII(switches::kEnableHeapProfiling, "unsupported");
-  mdm_->EnableHeapProfilingIfNeeded();
-  EXPECT_EQ(mdm_->GetHeapProfilingMode(), kHeapProfilingModeInvalid);
-#else
-  mdm_->EnableHeapProfilingIfNeeded();
-  EXPECT_EQ(mdm_->GetHeapProfilingMode(), kHeapProfilingModeInvalid);
-#endif  //  BUILDFLAG(USE_ALLOCATOR_SHIM) && !defined(OS_NACL)
-}
-
 // Mock MDP class that tests if the number of OnMemoryDump() calls are expected.
 // It is implemented without gmocks since EXPECT_CALL implementation is slow
 // when there are 1000s of instances, as required in
diff --git a/base/trace_event/memory_infra_background_whitelist.cc b/base/trace_event/memory_infra_background_whitelist.cc
index 22330ce..0ef47e4 100644
--- a/base/trace_event/memory_infra_background_whitelist.cc
+++ b/base/trace_event/memory_infra_background_whitelist.cc
@@ -127,54 +127,100 @@
     "net/url_request_context",
     "net/url_request_context/app_request",
     "net/url_request_context/app_request/0x?",
+    "net/url_request_context/app_request/0x?/cookie_monster",
+    "net/url_request_context/app_request/0x?/cookie_monster/cookies",
+    "net/url_request_context/app_request/0x?/cookie_monster/"
+    "tasks_pending_global",
+    "net/url_request_context/app_request/0x?/cookie_monster/"
+    "tasks_pending_for_key",
     "net/url_request_context/app_request/0x?/http_cache",
     "net/url_request_context/app_request/0x?/http_cache/memory_backend",
     "net/url_request_context/app_request/0x?/http_cache/simple_backend",
     "net/url_request_context/app_request/0x?/http_network_session",
     "net/url_request_context/extensions",
     "net/url_request_context/extensions/0x?",
+    "net/url_request_context/extensions/0x?/cookie_monster",
+    "net/url_request_context/extensions/0x?/cookie_monster/cookies",
+    "net/url_request_context/extensions/0x?/cookie_monster/"
+    "tasks_pending_global",
+    "net/url_request_context/extensions/0x?/cookie_monster/"
+    "tasks_pending_for_key",
     "net/url_request_context/extensions/0x?/http_cache",
     "net/url_request_context/extensions/0x?/http_cache/memory_backend",
     "net/url_request_context/extensions/0x?/http_cache/simple_backend",
     "net/url_request_context/extensions/0x?/http_network_session",
     "net/url_request_context/isolated_media",
     "net/url_request_context/isolated_media/0x?",
+    "net/url_request_context/isolated_media/0x?/cookie_monster",
+    "net/url_request_context/isolated_media/0x?/cookie_monster/cookies",
+    "net/url_request_context/isolated_media/0x?/cookie_monster/"
+    "tasks_pending_global",
+    "net/url_request_context/isolated_media/0x?/cookie_monster/"
+    "tasks_pending_for_key",
     "net/url_request_context/isolated_media/0x?/http_cache",
     "net/url_request_context/isolated_media/0x?/http_cache/memory_backend",
     "net/url_request_context/isolated_media/0x?/http_cache/simple_backend",
     "net/url_request_context/isolated_media/0x?/http_network_session",
     "net/url_request_context/main",
     "net/url_request_context/main/0x?",
+    "net/url_request_context/main/0x?/cookie_monster",
+    "net/url_request_context/main/0x?/cookie_monster/cookies",
+    "net/url_request_context/main/0x?/cookie_monster/tasks_pending_global",
+    "net/url_request_context/main/0x?/cookie_monster/tasks_pending_for_key",
     "net/url_request_context/main/0x?/http_cache",
     "net/url_request_context/main/0x?/http_cache/memory_backend",
     "net/url_request_context/main/0x?/http_cache/simple_backend",
     "net/url_request_context/main/0x?/http_network_session",
     "net/url_request_context/main_media",
     "net/url_request_context/main_media/0x?",
+    "net/url_request_context/main_media/0x?/cookie_monster",
+    "net/url_request_context/main_media/0x?/cookie_monster/cookies",
+    "net/url_request_context/main_media/0x?/cookie_monster/"
+    "tasks_pending_global",
+    "net/url_request_context/main_media/0x?/cookie_monster/"
+    "tasks_pending_for_key",
     "net/url_request_context/main_media/0x?/http_cache",
     "net/url_request_context/main_media/0x?/http_cache/memory_backend",
     "net/url_request_context/main_media/0x?/http_cache/simple_backend",
     "net/url_request_context/main_media/0x?/http_network_session",
     "net/url_request_context/proxy",
     "net/url_request_context/proxy/0x?",
+    "net/url_request_context/proxy/0x?/cookie_monster",
+    "net/url_request_context/proxy/0x?/cookie_monster/cookies",
+    "net/url_request_context/proxy/0x?/cookie_monster/tasks_pending_global",
+    "net/url_request_context/proxy/0x?/cookie_monster/tasks_pending_for_key",
     "net/url_request_context/proxy/0x?/http_cache",
     "net/url_request_context/proxy/0x?/http_cache/memory_backend",
     "net/url_request_context/proxy/0x?/http_cache/simple_backend",
     "net/url_request_context/proxy/0x?/http_network_session",
     "net/url_request_context/safe_browsing",
     "net/url_request_context/safe_browsing/0x?",
+    "net/url_request_context/safe_browsing/0x?/cookie_monster",
+    "net/url_request_context/safe_browsing/0x?/cookie_monster/cookies",
+    "net/url_request_context/safe_browsing/0x?/cookie_monster/"
+    "tasks_pending_global",
+    "net/url_request_context/safe_browsing/0x?/cookie_monster/"
+    "tasks_pending_for_key",
     "net/url_request_context/safe_browsing/0x?/http_cache",
     "net/url_request_context/safe_browsing/0x?/http_cache/memory_backend",
     "net/url_request_context/safe_browsing/0x?/http_cache/simple_backend",
     "net/url_request_context/safe_browsing/0x?/http_network_session",
     "net/url_request_context/system",
     "net/url_request_context/system/0x?",
+    "net/url_request_context/system/0x?/cookie_monster",
+    "net/url_request_context/system/0x?/cookie_monster/cookies",
+    "net/url_request_context/system/0x?/cookie_monster/tasks_pending_global",
+    "net/url_request_context/system/0x?/cookie_monster/tasks_pending_for_key",
     "net/url_request_context/system/0x?/http_cache",
     "net/url_request_context/system/0x?/http_cache/memory_backend",
     "net/url_request_context/system/0x?/http_cache/simple_backend",
     "net/url_request_context/system/0x?/http_network_session",
     "net/url_request_context/unknown",
     "net/url_request_context/unknown/0x?",
+    "net/url_request_context/unknown/0x?/cookie_monster",
+    "net/url_request_context/unknown/0x?/cookie_monster/cookies",
+    "net/url_request_context/unknown/0x?/cookie_monster/tasks_pending_global",
+    "net/url_request_context/unknown/0x?/cookie_monster/tasks_pending_for_key",
     "net/url_request_context/unknown/0x?/http_cache",
     "net/url_request_context/unknown/0x?/http_cache/memory_backend",
     "net/url_request_context/unknown/0x?/http_cache/simple_backend",
diff --git a/base/trace_event/trace_config.cc b/base/trace_event/trace_config.cc
index 5926c954..624a29c 100644
--- a/base/trace_event/trace_config.cc
+++ b/base/trace_event/trace_config.cc
@@ -182,6 +182,23 @@
   return category_filter_.IsCategoryGroupEnabled(category_group_name);
 }
 
+// static
+std::string TraceConfig::TraceRecordModeToStr(TraceRecordMode record_mode) {
+  switch (record_mode) {
+    case RECORD_UNTIL_FULL:
+      return kRecordUntilFull;
+    case RECORD_CONTINUOUSLY:
+      return kRecordContinuously;
+    case RECORD_AS_MUCH_AS_POSSIBLE:
+      return kRecordAsMuchAsPossible;
+    case ECHO_TO_CONSOLE:
+      return kTraceToConsole;
+    default:
+      NOTREACHED();
+  }
+  return kRecordUntilFull;
+}
+
 TraceConfig::TraceConfig() {
   InitializeDefault();
 }
@@ -193,24 +210,8 @@
 
 TraceConfig::TraceConfig(StringPiece category_filter_string,
                          TraceRecordMode record_mode) {
-  std::string trace_options_string;
-  switch (record_mode) {
-    case RECORD_UNTIL_FULL:
-      trace_options_string = kRecordUntilFull;
-      break;
-    case RECORD_CONTINUOUSLY:
-      trace_options_string = kRecordContinuously;
-      break;
-    case RECORD_AS_MUCH_AS_POSSIBLE:
-      trace_options_string = kRecordAsMuchAsPossible;
-      break;
-    case ECHO_TO_CONSOLE:
-      trace_options_string = kTraceToConsole;
-      break;
-    default:
-      NOTREACHED();
-  }
-  InitializeFromStrings(category_filter_string, trace_options_string);
+  InitializeFromStrings(category_filter_string,
+                        TraceConfig::TraceRecordModeToStr(record_mode));
 }
 
 TraceConfig::TraceConfig(const DictionaryValue& config) {
@@ -470,23 +471,8 @@
 
 std::unique_ptr<DictionaryValue> TraceConfig::ToDict() const {
   auto dict = std::make_unique<DictionaryValue>();
-  switch (record_mode_) {
-    case RECORD_UNTIL_FULL:
-      dict->SetString(kRecordModeParam, kRecordUntilFull);
-      break;
-    case RECORD_CONTINUOUSLY:
-      dict->SetString(kRecordModeParam, kRecordContinuously);
-      break;
-    case RECORD_AS_MUCH_AS_POSSIBLE:
-      dict->SetString(kRecordModeParam, kRecordAsMuchAsPossible);
-      break;
-    case ECHO_TO_CONSOLE:
-      dict->SetString(kRecordModeParam, kTraceToConsole);
-      break;
-    default:
-      NOTREACHED();
-  }
-
+  dict->SetString(kRecordModeParam,
+                  TraceConfig::TraceRecordModeToStr(record_mode_));
   dict->SetBoolean(kEnableSystraceParam, enable_systrace_);
   dict->SetBoolean(kEnableArgumentFilterParam, enable_argument_filter_);
 
diff --git a/base/trace_event/trace_config.h b/base/trace_event/trace_config.h
index 5460489..decd54d1 100644
--- a/base/trace_event/trace_config.h
+++ b/base/trace_event/trace_config.h
@@ -120,6 +120,8 @@
   };
   typedef std::vector<EventFilterConfig> EventFilters;
 
+  static std::string TraceRecordModeToStr(TraceRecordMode record_mode);
+
   TraceConfig();
 
   // Create TraceConfig object from category filter and trace options strings.
diff --git a/build/android/gyp/javac.py b/build/android/gyp/javac.py
index 35c6d48..3aa95ef 100755
--- a/build/android/gyp/javac.py
+++ b/build/android/gyp/javac.py
@@ -10,6 +10,7 @@
 import shutil
 import re
 import sys
+import zipfile
 
 from util import build_utils
 from util import md5_check
@@ -94,10 +95,10 @@
   'MissingOverride',
   'NarrowingCompoundAssignment',
   'ParameterName',
+  'ReferenceEquality',
   'StaticGuardedByInstance',
   'StaticQualifiedUsingExpression',
   'UseCorrectAssertInTests',
-  'ReferenceEquality',
 ]
 
 
@@ -249,6 +250,22 @@
       info_file.write('{},{}\n'.format(fully_qualified_name, path))
 
 
+def _FullJavaNameFromClassFilePath(path):
+  # Input:  base/android/java/src/org/chromium/Foo.class
+  # Output: base.android.java.src.org.chromium.Foo
+  if not path.endswith('.class'):
+    return ''
+  path = os.path.splitext(path)[0]
+  parts = []
+  while path:
+    # Use split to be platform independent.
+    head, tail = os.path.split(path)
+    path = head
+    parts.append(tail)
+  parts.reverse()  # Package comes first
+  return '.'.join(parts)
+
+
 def _CreateInfoFile(java_files, options, srcjar_files):
   """Writes a .jar.info file.
 
@@ -277,7 +294,21 @@
   # Collect all the info files for transitive dependencies of the apk.
   if options.apk_jar_info_path:
     for jar_path in options.full_classpath:
-      info_data.update(_ParseInfoFile(jar_path + '.info'))
+      # android_java_prebuilt adds jar files in the src directory (relative to
+      #     the output directory, usually ../../third_party/example.jar).
+      # android_aar_prebuilt collects jar files in the aar file and uses the
+      #     java_prebuilt rule to generate gen/example/classes.jar files.
+      # We scan these prebuilt jars to parse each class path for the FQN. This
+      #     allows us to later map these classes back to their respective src
+      #     directories.
+      if jar_path.startswith('..') or jar_path.endswith('classes.jar'):
+        with zipfile.ZipFile(jar_path) as zip_info:
+          for path in zip_info.namelist():
+            fully_qualified_name = _FullJavaNameFromClassFilePath(path)
+            if fully_qualified_name:
+              info_data[fully_qualified_name] = jar_path
+      else:
+        info_data.update(_ParseInfoFile(jar_path + '.info'))
     _WriteInfoFile(options.apk_jar_info_path, info_data, srcjar_files)
 
 
diff --git a/build/fuchsia/runner_v2/exe_runner.py b/build/fuchsia/runner_v2/exe_runner.py
index 17b1cb6..5fa910d 100755
--- a/build/fuchsia/runner_v2/exe_runner.py
+++ b/build/fuchsia/runner_v2/exe_runner.py
@@ -27,8 +27,8 @@
 
   with GetDeploymentTargetForArgs(args) as target:
     target.Start()
-    RunPackage(args.output_directory, target, args.package,
-               args.package_name, args.child_args, args.package_manifest)
+    return RunPackage(args.output_directory, target, args.package,
+                      args.package_name, args.child_args, args.package_manifest)
 
 
 if __name__ == '__main__':
diff --git a/build/fuchsia/runner_v2/test_runner.py b/build/fuchsia/runner_v2/test_runner.py
index a939d6b4..9bf31f2 100755
--- a/build/fuchsia/runner_v2/test_runner.py
+++ b/build/fuchsia/runner_v2/test_runner.py
@@ -100,8 +100,9 @@
     if args.enable_test_server:
       test_server, forwarder = SetupTestServer(target, test_concurrency)
 
-    RunPackage(args.output_directory, target, args.package, args.package_name,
-               child_args, args.package_manifest)
+    returncode = RunPackage(args.output_directory, target, args.package,
+                            args.package_name, child_args,
+                            args.package_manifest)
 
     if forwarder:
       forwarder.terminate()
@@ -110,6 +111,8 @@
     if args.test_launcher_summary_output:
       target.GetFile(TEST_RESULT_PATH, args.test_launcher_summary_output)
 
+    return returncode
+
 
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/cc/resources/display_resource_provider.cc b/cc/resources/display_resource_provider.cc
index 5938413..8fc95b2 100644
--- a/cc/resources/display_resource_provider.cc
+++ b/cc/resources/display_resource_provider.cc
@@ -80,6 +80,12 @@
       continue;
 
     const viz::internal::Resource* resource = LockForRead(id);
+    // TODO(ericrk): We should never fail LockForRead, but we appear to be
+    // doing so on Android in rare cases. Handle this gracefully until a better
+    // solution can be found. https://crbug.com/811858
+    if (!resource)
+      return;
+
     DCHECK(resource->wants_promotion_hint);
 
     // Insist that this is backed by a GPU texture.
@@ -121,8 +127,11 @@
 
 #endif
 bool DisplayResourceProvider::IsOverlayCandidate(viz::ResourceId id) {
-  viz::internal::Resource* resource = GetResource(id);
-  return resource->is_overlay_candidate;
+  viz::internal::Resource* resource = TryGetResource(id);
+  // TODO(ericrk): We should never fail TryGetResource, but we appear to
+  // be doing so on Android in rare cases. Handle this gracefully until a
+  // better solution can be found. https://crbug.com/811858
+  return resource && resource->is_overlay_candidate;
 }
 
 viz::ResourceType DisplayResourceProvider::GetResourceType(viz::ResourceId id) {
@@ -135,7 +144,12 @@
 }
 
 void DisplayResourceProvider::WaitSyncToken(viz::ResourceId id) {
-  viz::internal::Resource* resource = GetResource(id);
+  viz::internal::Resource* resource = TryGetResource(id);
+  // TODO(ericrk): We should never fail TryGetResource, but we appear to
+  // be doing so on Android in rare cases. Handle this gracefully until a
+  // better solution can be found. https://crbug.com/811858
+  if (!resource)
+    return;
   WaitSyncTokenInternal(resource);
 #if defined(OS_ANDROID)
   // Now that the resource is synced, we may send it a promotion hint.  We could
@@ -453,7 +467,12 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   GLES2Interface* gl = ContextGL();
   ResourceMap::iterator it = resources_.find(resource_id);
-  DCHECK(it != resources_.end());
+  // TODO(ericrk): We should never fail to find resource_id, but we appear to
+  // be doing so on Android in rare cases. Handle this gracefully until a
+  // better solution can be found. https://crbug.com/811858
+  if (it == resources_.end())
+    return GL_TEXTURE_2D;
+
   viz::internal::Resource* resource = &it->second;
   DCHECK(resource->lock_for_read_count);
   // TODO(xing.xu): remove locked_for_write.
@@ -544,6 +563,12 @@
     : resource_provider_(resource_provider), resource_id_(resource_id) {
   const viz::internal::Resource* resource =
       resource_provider->LockForRead(resource_id);
+  // TODO(ericrk): We should never fail LockForRead, but we appear to be
+  // doing so on Android in rare cases. Handle this gracefully until a better
+  // solution can be found. https://crbug.com/811858
+  if (!resource)
+    return;
+
   texture_id_ = resource->gl_id;
   target_ = resource->target;
   size_ = resource->size;
@@ -552,7 +577,13 @@
 
 const viz::internal::Resource* DisplayResourceProvider::LockForRead(
     viz::ResourceId id) {
-  viz::internal::Resource* resource = GetResource(id);
+  // TODO(ericrk): We should never fail TryGetResource, but we appear to be
+  // doing so on Android in rare cases. Handle this gracefully until a better
+  // solution can be found. https://crbug.com/811858
+  viz::internal::Resource* resource = TryGetResource(id);
+  if (!resource)
+    return nullptr;
+
   // TODO(xing.xu): remove locked_for_write.
   DCHECK(!resource->locked_for_write)
       << "locked for write: " << resource->locked_for_write;
@@ -600,7 +631,11 @@
 void DisplayResourceProvider::UnlockForRead(viz::ResourceId id) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   ResourceMap::iterator it = resources_.find(id);
-  CHECK(it != resources_.end());
+  // TODO(ericrk): We should never fail to find id, but we appear to be
+  // doing so on Android in rare cases. Handle this gracefully until a better
+  // solution can be found. https://crbug.com/811858
+  if (it == resources_.end())
+    return;
 
   viz::internal::Resource* resource = &it->second;
   DCHECK_GT(resource->lock_for_read_count, 0);
@@ -660,6 +695,7 @@
     : resource_provider_(resource_provider), resource_id_(resource_id) {
   const viz::internal::Resource* resource =
       resource_provider->LockForRead(resource_id);
+  DCHECK(resource);
   if (resource_provider_->resource_sk_image_.find(resource_id) !=
       resource_provider_->resource_sk_image_.end()) {
     // Use cached sk_image.
@@ -704,6 +740,7 @@
     : resource_provider_(resource_provider), resource_id_(resource_id) {
   const viz::internal::Resource* resource =
       resource_provider->LockForRead(resource_id);
+  DCHECK(resource);
   resource_provider->PopulateSkBitmapWithResource(&sk_bitmap_, resource);
 }
 
diff --git a/cc/resources/display_resource_provider.h b/cc/resources/display_resource_provider.h
index 48288bd..25e9463c 100644
--- a/cc/resources/display_resource_provider.h
+++ b/cc/resources/display_resource_provider.h
@@ -83,8 +83,8 @@
     DisplayResourceProvider* const resource_provider_;
     const viz::ResourceId resource_id_;
 
-    GLuint texture_id_;
-    GLenum target_;
+    GLuint texture_id_ = 0;
+    GLenum target_ = GL_TEXTURE_2D;
     gfx::Size size_;
     gfx::ColorSpace color_space_;
 
diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc
index ab6c358..fcc3b61 100644
--- a/cc/resources/resource_provider.cc
+++ b/cc/resources/resource_provider.cc
@@ -149,11 +149,19 @@
 
 viz::internal::Resource* ResourceProvider::GetResource(viz::ResourceId id) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  // TODO(ericrk): Changing the DCHECKs in this function to CHECKs to debug
-  // https://crbug.com/811858.
-  CHECK(id);
+  DCHECK(id);
   ResourceMap::iterator it = resources_.find(id);
-  CHECK(it != resources_.end());
+  DCHECK(it != resources_.end());
+  return &it->second;
+}
+
+viz::internal::Resource* ResourceProvider::TryGetResource(viz::ResourceId id) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (!id)
+    return nullptr;
+  ResourceMap::iterator it = resources_.find(id);
+  if (it == resources_.end())
+    return nullptr;
   return &it->second;
 }
 
diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h
index fa07aae..31e46d23 100644
--- a/cc/resources/resource_provider.h
+++ b/cc/resources/resource_provider.h
@@ -92,6 +92,11 @@
                                           viz::internal::Resource resource);
   viz::internal::Resource* GetResource(viz::ResourceId id);
 
+  // TODO(ericrk): TryGetResource is part of a temporary workaround for cases
+  // where resources which should be available are missing. This version may
+  // return nullptr if a resource is not found. https://crbug.com/811858
+  viz::internal::Resource* TryGetResource(viz::ResourceId id);
+
   void PopulateSkBitmapWithResource(SkBitmap* sk_bitmap,
                                     const viz::internal::Resource* resource);
 
diff --git a/chrome/VERSION b/chrome/VERSION
index a480d249..6a4e3bc 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=67
 MINOR=0
-BUILD=3381
+BUILD=3382
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContentCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContentCoordinator.java
index 286acc04..a6dd8a6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContentCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContentCoordinator.java
@@ -12,11 +12,13 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModel.ClusterListObservable;
 import org.chromium.chrome.browser.modelutil.RecyclerViewModelChangeProcessor;
+import org.chromium.chrome.browser.ntp.ContextMenuManager;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
 import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
+import org.chromium.ui.base.WindowAndroid;
 
 /**
  * Coordinator for the content sub-component. Responsible for communication with the parent
@@ -24,6 +26,8 @@
  */
 class ContentCoordinator {
     private final ContextualSuggestionsModel mModel;
+    private final WindowAndroid mWindowAndroid;
+    private final ContextMenuManager mContextMenuManager;
 
     private SuggestionsRecyclerView mRecyclerView;
     private RecyclerViewModelChangeProcessor<ClusterListObservable, NewTabPageViewHolder>
@@ -37,16 +41,24 @@
      * @param uiDelegate The {@link SuggestionsUiDelegate} used to help construct items in the
      *                   content view.
      * @param model The {@link ContextualSuggestionsModel} for the component.
+     * @param windowAndroid The {@link WindowAndroid} for attaching a context menu listener.
+     * @param closeContextMenuCallback The callback when a context menu is closed.
      */
     ContentCoordinator(Context context, ViewGroup parentView, Profile profile,
-            SuggestionsUiDelegate uiDelegate, ContextualSuggestionsModel model) {
+            SuggestionsUiDelegate uiDelegate, ContextualSuggestionsModel model,
+            WindowAndroid windowAndroid, Runnable closeContextMenuCallback) {
         mModel = model;
+        mWindowAndroid = windowAndroid;
 
         mRecyclerView = (SuggestionsRecyclerView) LayoutInflater.from(context).inflate(
                 R.layout.contextual_suggestions_layout, parentView, false);
 
-        ContextualSuggestionsAdapter adapter = new ContextualSuggestionsAdapter(
-                context, profile, new UiConfig(mRecyclerView), uiDelegate, mModel);
+        mContextMenuManager = new ContextMenuManager(uiDelegate.getNavigationDelegate(),
+                mRecyclerView::setTouchEnabled, closeContextMenuCallback);
+        mWindowAndroid.addContextMenuCloseListener(mContextMenuManager);
+
+        ContextualSuggestionsAdapter adapter = new ContextualSuggestionsAdapter(context, profile,
+                new UiConfig(mRecyclerView), uiDelegate, mModel, mContextMenuManager);
         mRecyclerView.setAdapter(adapter);
 
         mModelChangeProcessor = new RecyclerViewModelChangeProcessor<>(adapter);
@@ -68,5 +80,6 @@
         // The model outlives the content sub-component. Remove the observer so that this object
         // can be garbage collected.
         mModel.mClusterListObservable.removeObserver(mModelChangeProcessor);
+        mWindowAndroid.removeContextMenuCloseListener(mContextMenuManager);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionCardViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionCardViewHolder.java
index fd6d78d..4fce7afe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionCardViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionCardViewHolder.java
@@ -20,6 +20,11 @@
     }
 
     @Override
+    public boolean isItemSupported(@ContextMenuManager.ContextMenuItemId int menuItemId) {
+        return menuItemId != ContextMenuManager.ID_LEARN_MORE && super.isItemSupported(menuItemId);
+    }
+
+    @Override
     public boolean isDismissable() {
         return false;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsAdapter.java
index 941cf67..82d4347 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsAdapter.java
@@ -10,6 +10,7 @@
 
 import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModel.ClusterListObservable;
 import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
+import org.chromium.chrome.browser.ntp.ContextMenuManager;
 import org.chromium.chrome.browser.ntp.cards.ItemViewType;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder;
 import org.chromium.chrome.browser.ntp.snippets.SectionHeaderViewHolder;
@@ -33,9 +34,9 @@
                     return new SectionHeaderViewHolder(mRecyclerView, mUiConfig);
 
                 case ItemViewType.SNIPPET:
-                    // TODO(twellington): Hook up ContextMenuManager.
-                    return new ContextualSuggestionCardViewHolder(mRecyclerView, null, mUiDelegate,
-                            mUiConfig, OfflinePageBridge.getForProfile(mProfile));
+                    return new ContextualSuggestionCardViewHolder(mRecyclerView,
+                            mContextMenuManager, mUiDelegate, mUiConfig,
+                            OfflinePageBridge.getForProfile(mProfile));
 
                 default:
                     assert false;
@@ -54,6 +55,7 @@
     private final UiConfig mUiConfig;
     private final SuggestionsUiDelegate mUiDelegate;
     private final ContextualSuggestionsModel mModel;
+    private final ContextMenuManager mContextMenuManager;
 
     private SuggestionsRecyclerView mRecyclerView;
 
@@ -65,9 +67,11 @@
      * @param uiDelegate The {@link SuggestionsUiDelegate} used to help construct items in the
      *                   content view.
      * @param model The {@link ContextualSuggestionsModel} for the component.
+     * @param contextMenuManager The {@link ContextMenuManager} used to display a context menu.
      */
     ContextualSuggestionsAdapter(Context context, Profile profile, UiConfig uiConfig,
-            SuggestionsUiDelegate uiDelegate, ContextualSuggestionsModel model) {
+            SuggestionsUiDelegate uiDelegate, ContextualSuggestionsModel model,
+            ContextMenuManager contextMenuManager) {
         super(model.mClusterListObservable);
 
         setViewBinder(new ContextualSuggestionsViewBinder());
@@ -76,6 +80,7 @@
         mUiConfig = uiConfig;
         mUiDelegate = uiDelegate;
         mModel = model;
+        mContextMenuManager = contextMenuManager;
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCoordinator.java
index b263a674..2fea67d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCoordinator.java
@@ -77,8 +77,8 @@
         // a toolbar view when suggestions are fist available, and use this method to construct the
         // content view when the sheet is opened.
         mToolbarCoordinator = new ToolbarCoordinator(mActivity, mBottomSheet, mModel);
-        mContentCoordinator =
-                new ContentCoordinator(mActivity, mBottomSheet, mProfile, mUiDelegate, mModel);
+        mContentCoordinator = new ContentCoordinator(mActivity, mBottomSheet, mProfile, mUiDelegate,
+                mModel, mActivity.getWindowAndroid(), mActivity::closeContextMenu);
         mBottomSheetContent = new ContextualSuggestionsBottomSheetContent(
                 mContentCoordinator, mToolbarCoordinator);
         mBottomSheet.showContent(mBottomSheetContent);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
index 57ac584..60f0063 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
@@ -37,6 +37,7 @@
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager.FullscreenListener;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.toolbar.ActionModeController.ActionBarDelegate;
 import org.chromium.chrome.browser.toolbar.ViewShiftingActionBarDelegate;
@@ -672,13 +673,14 @@
 
         assert mTabModelSelector != null;
 
-        int tabLoadStatus;
+        int tabLoadStatus = TabLoadStatus.DEFAULT_PAGE_LOAD;
 
         if (getActiveTab() != null && getActiveTab().isIncognito() == incognito) {
             tabLoadStatus = getActiveTab().loadUrl(params);
         } else {
-            // Do nothing if there is no available tab to load in.
-            return TabLoadStatus.PAGE_LOAD_FAILED;
+            // If no compatible tab is active behind the sheet, open a new one.
+            mTabModelSelector.openNewTab(
+                    params, TabModel.TabLaunchType.FROM_CHROME_UI, getActiveTab(), incognito);
         }
 
         // In all non-native cases, minimize the sheet.
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 44c880bf..6e995c1 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -4878,11 +4878,6 @@
         usage statistics
       </message>
 
-      <!-- Unimplemented Flags Infobar-->
-      <message name="IDS_UNIMPLEMENTED_FLAGS_WARNING_MESSAGE" desc="Message shown when a command-line flag is used that is not implemented by this build. [Keep it short so it fits in the infobar.]">
-        <ph name="BAD_FLAG">$1<ex>--enable-heap-profiling</ex></ph> is not implemented in this build
-      </message>
-
       <!-- Bad Flags Infobar-->
       <message name="IDS_BAD_FLAGS_WARNING_MESSAGE" desc="Message shown when an unsupported command-line flag is used. [Keep it short so it fits in the infobar.]">
         You are using an unsupported command-line flag: <ph name="BAD_FLAG">$1<ex>--no-sandbox</ex></ph>. Stability and security will suffer.
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index ca6194d..a8bcab3 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -38,6 +38,7 @@
     "content_paste.icon",
     "cookie.icon",
     "crashed_tab.icon",
+    "credit_card_20.icon",
     "credit_card.icon",
     "default_touch_favicon.icon",
     "default_touch_favicon_mask.icon",
diff --git a/chrome/app/vector_icons/credit_card_20.icon b/chrome/app/vector_icons/credit_card_20.icon
new file mode 100644
index 0000000..ec5bdd7
--- /dev/null
+++ b/chrome/app/vector_icons/credit_card_20.icon
@@ -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.
+
+// TODO(crbug.com/647286): This is a duplicate of the existing kCreditCardIcon,
+// just sized to 20x20dips for use in Chrome's touch mode. Delete this when
+// vector icons can support multiple icon sizes.
+MOVE_TO, 40, 8,
+H_LINE_TO, 8,
+R_CUBIC_TO, -2.21f, 0, -3.98f, 1.79f, -3.98f, 4,
+LINE_TO, 4, 36,
+R_CUBIC_TO, 0, 2.21f, 1.79f, 4, 4, 4,
+R_H_LINE_TO, 32,
+R_CUBIC_TO, 2.21f, 0, 4, -1.79f, 4, -4,
+V_LINE_TO, 12,
+R_CUBIC_TO, 0, -2.21f, -1.79f, -4, -4, -4,
+CLOSE,
+R_MOVE_TO, 0, 28,
+H_LINE_TO, 8,
+V_LINE_TO, 24,
+R_H_LINE_TO, 32,
+R_V_LINE_TO, 12,
+CLOSE,
+R_MOVE_TO, 0, -20,
+H_LINE_TO, 8,
+R_V_LINE_TO, -4,
+R_H_LINE_TO, 32,
+R_V_LINE_TO, 4,
+CLOSE
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index e7e6342d..8ffb3e5 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -959,16 +959,6 @@
       arraysize(kPersistentMenuItemEnabled), nullptr}};
 #endif  // OS_ANDROID
 
-const FeatureEntry::Choice kEnableHeapProfilingChoices[] = {
-    {flags_ui::kGenericExperimentChoiceDisabled, "", ""},
-    {flag_descriptions::kEnableHeapProfilingModePseudo,
-     switches::kEnableHeapProfiling, switches::kEnableHeapProfilingModePseudo},
-    {flag_descriptions::kEnableHeapProfilingModeNative,
-     switches::kEnableHeapProfiling, switches::kEnableHeapProfilingModeNative},
-    {flag_descriptions::kEnableHeapProfilingTaskProfiler,
-     switches::kEnableHeapProfiling,
-     switches::kEnableHeapProfilingTaskProfiler}};
-
 const FeatureEntry::Choice kEnableOutOfProcessHeapProfilingChoices[] = {
     {flags_ui::kGenericExperimentChoiceDisabled, "", ""},
     {flag_descriptions::kEnableOutOfProcessHeapProfilingModeMinimal,
@@ -3157,10 +3147,6 @@
      flag_descriptions::kSamplingHeapProfilerDescription, kOsAll,
      SINGLE_VALUE_TYPE(switches::kSamplingHeapProfiler)},
 
-    {"enable-heap-profiling", flag_descriptions::kEnableHeapProfilingName,
-     flag_descriptions::kEnableHeapProfilingDescription, kOsAll,
-     MULTI_VALUE_TYPE(kEnableHeapProfilingChoices)},
-
     {"memlog", flag_descriptions::kEnableOutOfProcessHeapProfilingName,
      flag_descriptions::kEnableOutOfProcessHeapProfilingDescription, kOsAll,
      MULTI_VALUE_TYPE(kEnableOutOfProcessHeapProfilingChoices)},
@@ -3575,6 +3561,11 @@
      flag_descriptions::kAshEnablePersistentWindowBoundsName,
      flag_descriptions::kAshEnablePersistentWindowBoundsDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kPersistentWindowBounds)},
+
+    {"ash-enable-mode-specific-power-button",
+     flag_descriptions::kAshEnableModeSpecificPowerButtonName,
+     flag_descriptions::kAshEnableModeSpecificPowerButtonDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::features::kModeSpecificPowerButton)},
 #endif  // OS_CHROMEOS
 
     {"clipboard-content-setting",
diff --git a/chrome/browser/android/data_usage/tab_data_use_entry_unittest.cc b/chrome/browser/android/data_usage/tab_data_use_entry_unittest.cc
index 501e2c2e..d009bd7 100644
--- a/chrome/browser/android/data_usage/tab_data_use_entry_unittest.cc
+++ b/chrome/browser/android/data_usage/tab_data_use_entry_unittest.cc
@@ -41,7 +41,7 @@
   ~SimpleOffsetTestTickClock() override {}
 
   // Returns the spoofed time as Now.
-  base::TimeTicks NowTicks() override {
+  base::TimeTicks NowTicks() const override {
     return base::TimeTicks::UnixEpoch() + now_offset_;
   }
 
diff --git a/chrome/browser/android/vr/vr_shell_gl.cc b/chrome/browser/android/vr/vr_shell_gl.cc
index 251e840..6428be8a 100644
--- a/chrome/browser/android/vr/vr_shell_gl.cc
+++ b/chrome/browser/android/vr/vr_shell_gl.cc
@@ -54,14 +54,14 @@
 namespace vr {
 
 namespace {
-static constexpr float kZNear = 0.1f;
-static constexpr float kZFar = 10000.0f;
+constexpr float kZNear = 0.1f;
+constexpr float kZFar = 10000.0f;
 
 // GVR buffer indices for use with viewport->SetSourceBufferIndex
 // or frame.BindBuffer. We use one for world content (with reprojection)
 // including main VrShell and WebVR content plus world-space UI.
-static constexpr int kFramePrimaryBuffer = 0;
-static constexpr int kFrameWebVrBrowserUiBuffer = 1;
+constexpr int kFramePrimaryBuffer = 0;
+constexpr int kFrameWebVrBrowserUiBuffer = 1;
 
 // When display UI on top of WebVR, we use a seperate buffer. Normally, the
 // buffer is set to recommended size to get best visual (i.e the buffer for
@@ -71,52 +71,57 @@
 // elements. This allows us rendering UI at the same quality with a smaller
 // buffer.
 // Use 2 for now, we can probably make the buffer even smaller.
-static constexpr float kWebVrBrowserUiSizeFactor = 2.f;
+constexpr float kWebVrBrowserUiSizeFactor = 2.f;
 
 // The GVR viewport list has two entries (left eye and right eye) for each
 // GVR buffer.
-static constexpr int kViewportListPrimaryOffset = 0;
-static constexpr int kViewportListWebVrBrowserUiOffset = 2;
+constexpr int kViewportListPrimaryOffset = 0;
+constexpr int kViewportListWebVrBrowserUiOffset = 2;
 
 // Buffer size large enough to handle the current backlog of poses which is
 // 2-3 frames.
-static constexpr unsigned kPoseRingBufferSize = 8;
+constexpr unsigned kPoseRingBufferSize = 8;
 
 // Number of frames to use for sliding averages for pose timings,
 // as used for estimating prediction times.
-static constexpr unsigned kWebVRSlidingAverageSize = 5;
+constexpr unsigned kWebVRSlidingAverageSize = 5;
 
 // Criteria for considering holding the app button in combination with
 // controller movement as a gesture.
-static constexpr float kMinAppButtonGestureAngleRad = 0.25;
+constexpr float kMinAppButtonGestureAngleRad = 0.25;
+
+// Exceeding pressing the appbutton for longer than this threshold will result
+// in a long press.
+constexpr base::TimeDelta kLongPressThreshold =
+    base::TimeDelta::FromMilliseconds(900);
 
 // Timeout for checking for the WebVR rendering GL fence. If the timeout is
 // reached, yield to let other tasks execute before rechecking.
-static constexpr base::TimeDelta kWebVRFenceCheckTimeout =
+constexpr base::TimeDelta kWebVRFenceCheckTimeout =
     base::TimeDelta::FromMicroseconds(2000);
 
 // Polling interval for checking for the WebVR rendering GL fence. Used as
 // an alternative to kWebVRFenceCheckTimeout if the GPU workaround is active.
 // The actual interval may be longer due to PostDelayedTask's resolution.
-static constexpr base::TimeDelta kWebVRFenceCheckPollInterval =
+constexpr base::TimeDelta kWebVRFenceCheckPollInterval =
     base::TimeDelta::FromMicroseconds(500);
 
-static constexpr int kWebVrInitialFrameTimeoutSeconds = 5;
-static constexpr int kWebVrSpinnerTimeoutSeconds = 2;
+constexpr int kWebVrInitialFrameTimeoutSeconds = 5;
+constexpr int kWebVrSpinnerTimeoutSeconds = 2;
 
 // Heuristic time limit to detect overstuffed GVR buffers for a
 // >60fps capable web app.
-static constexpr base::TimeDelta kWebVrSlowAcquireThreshold =
+constexpr base::TimeDelta kWebVrSlowAcquireThreshold =
     base::TimeDelta::FromMilliseconds(2);
 
 // If running too fast, allow dropping frames occasionally to let GVR catch up.
 // Drop at most one frame in MaxDropRate.
-static constexpr int kWebVrUnstuffMaxDropRate = 11;
+constexpr int kWebVrUnstuffMaxDropRate = 11;
 
-static constexpr int kNumSamplesPerPixelBrowserUi = 2;
-static constexpr int kNumSamplesPerPixelWebVr = 1;
+constexpr int kNumSamplesPerPixelBrowserUi = 2;
+constexpr int kNumSamplesPerPixelWebVr = 1;
 
-static constexpr float kRedrawSceneAngleDeltaDegrees = 1.0;
+constexpr float kRedrawSceneAngleDeltaDegrees = 1.0;
 
 gfx::Transform PerspectiveMatrixFromView(const gvr::Rectf& fov,
                                          float z_near,
@@ -736,8 +741,9 @@
 
   HandleControllerAppButtonActivity(controller_direction);
 
-  if (ShouldDrawWebVr())
+  if (ShouldDrawWebVr() && !ShouldSendGesturesToWebVr()) {
     return;
+  }
 
   ControllerModel controller_model;
   controller_->GetTransform(&controller_model.transform);
@@ -766,6 +772,7 @@
   controller_model.touching_touchpad = controller_->IsTouching();
   controller_model.touchpad_touch_position =
       gfx::PointF(controller_->TouchPosX(), controller_->TouchPosY());
+  controller_model.app_button_long_pressed = app_button_long_pressed_;
   controller_model_ = controller_model;
 
   ReticleModel reticle_model;
@@ -794,6 +801,8 @@
   if (controller_->ButtonDownHappened(
           gvr::ControllerButton::GVR_CONTROLLER_BUTTON_APP)) {
     controller_start_direction_ = controller_direction;
+    app_button_down_time_ = base::TimeTicks::Now();
+    app_button_long_pressed_ = false;
   }
 
   if (controller_->ButtonUpHappened(
@@ -820,16 +829,24 @@
         // UI state in the midst of frame rendering.
         base::ThreadTaskRunnerHandle::Get()->PostTask(
             FROM_HERE,
-            base::BindRepeating(&Ui::OnAppButtonGesturePerformed,
+            base::BindRepeating(&Ui::OnAppButtonSwipePerformed,
                                 base::Unretained(ui_.get()), direction));
       }
     }
-    if (direction == PlatformController::kSwipeDirectionNone) {
+    if (direction == PlatformController::kSwipeDirectionNone &&
+        !app_button_long_pressed_) {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
           FROM_HERE, base::BindRepeating(&Ui::OnAppButtonClicked,
                                          base::Unretained(ui_.get())));
     }
   }
+
+  if (!app_button_long_pressed_ &&
+      controller_->ButtonState(
+          gvr::ControllerButton::GVR_CONTROLLER_BUTTON_APP) &&
+      (base::TimeTicks::Now() - app_button_down_time_) > kLongPressThreshold) {
+    app_button_long_pressed_ = true;
+  }
 }
 
 bool VrShellGl::ResizeForWebVR(int16_t frame_index) {
@@ -1294,6 +1311,10 @@
   return web_vr_mode_ && ui_->ShouldRenderWebVr() && webvr_frames_received_ > 0;
 }
 
+bool VrShellGl::ShouldSendGesturesToWebVr() {
+  return ui_->IsAppButtonLongPressed() != app_button_long_pressed_;
+}
+
 void VrShellGl::DrawWebVr() {
   TRACE_EVENT0("gpu", "VrShellGl::DrawWebVr");
   // Don't need face culling, depth testing, blending, etc. Turn it all off.
diff --git a/chrome/browser/android/vr/vr_shell_gl.h b/chrome/browser/android/vr/vr_shell_gl.h
index 1539d0e..182d72b 100644
--- a/chrome/browser/android/vr/vr_shell_gl.h
+++ b/chrome/browser/android/vr/vr_shell_gl.h
@@ -143,6 +143,7 @@
   void DrawFrameSubmitNow(int16_t frame_index, const gfx::Transform& head_pose);
   bool ShouldDrawWebVr();
   void DrawWebVr();
+  bool ShouldSendGesturesToWebVr();
   bool WebVrPoseByteIsValid(int pose_index_byte);
 
   void UpdateController(const RenderInfo& render_info,
@@ -291,6 +292,8 @@
 
   // Attributes for gesture detection while holding app button.
   gfx::Vector3dF controller_start_direction_;
+  base::TimeTicks app_button_down_time_;
+  bool app_button_long_pressed_ = false;
 
   FPSMeter vr_ui_fps_meter_;
   FPSMeter webvr_fps_meter_;
diff --git a/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.cc b/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.cc
index e3d0b35..55d1449 100644
--- a/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.cc
+++ b/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.cc
@@ -263,7 +263,7 @@
                  content::NotificationService::AllBrowserContextsAndSources());
   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
                  content::NotificationService::AllBrowserContextsAndSources());
-  registrar_.Add(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+  registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
                  content::NotificationService::AllBrowserContextsAndSources());
   BrowserList::AddObserver(this);
 }
@@ -689,7 +689,7 @@
       }
       break;
     }
-    case chrome::NOTIFICATION_BROWSER_WINDOW_READY: {
+    case chrome::NOTIFICATION_BROWSER_OPENED: {
       Browser* browser = content::Source<Browser>(source).ptr();
       // Don't keep track of browsers that are not associated with an app.
       const Extension* extension = MaybeGetAppForBrowser(browser);
@@ -745,7 +745,7 @@
 
 // The BrowserWindow may be NULL when this is called.
 // Therefore we listen for the notification
-// chrome::NOTIFICATION_BROWSER_WINDOW_READY and then call OnAppActivated.
+// chrome::NOTIFICATION_BROWSER_OPENED and then call OnAppActivated.
 // If this notification is removed, check that OnBrowserAdded is called after
 // the BrowserWindow is ready.
 void ExtensionAppShimHandler::OnBrowserAdded(Browser* browser) {
diff --git a/chrome/browser/banners/app_banner_manager_desktop.cc b/chrome/browser/banners/app_banner_manager_desktop.cc
index c72f68d4..ae9e24e 100644
--- a/chrome/browser/banners/app_banner_manager_desktop.cc
+++ b/chrome/browser/banners/app_banner_manager_desktop.cc
@@ -27,9 +27,6 @@
 namespace banners {
 
 bool AppBannerManagerDesktop::IsEnabled() {
-  if (gDisableTriggeringForTesting)
-    return false;
-
   return base::FeatureList::IsEnabled(features::kAppBanners) ||
          IsExperimentalAppBannersEnabled();
 }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index b30517a..87aed902 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -274,10 +274,9 @@
 #include "chrome/browser/chrome_browser_main_mac.h"
 #elif defined(OS_CHROMEOS)
 #include "ash/public/interfaces/constants.mojom.h"
-#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.h"
 #include "chrome/browser/chromeos/arc/fileapi/arc_content_file_system_backend_delegate.h"
 #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_backend_delegate.h"
-#include "chrome/browser/chromeos/arc/intent_helper/arc_navigation_throttle.h"
 #include "chrome/browser/chromeos/chrome_browser_main_chromeos.h"
 #include "chrome/browser/chromeos/chrome_service_name.h"
 #include "chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.h"
@@ -3557,18 +3556,10 @@
       throttles.push_back(MergeSessionNavigationThrottle::Create(handle));
     }
 
-    if (arc::IsArcPlayStoreEnabledForProfile(Profile::FromBrowserContext(
-            handle->GetWebContents()->GetBrowserContext())) &&
-        !handle->GetWebContents()->GetBrowserContext()->IsOffTheRecord()) {
-      prerender::PrerenderContents* prerender_contents =
-          prerender::PrerenderContents::FromWebContents(
-              handle->GetWebContents());
-      if (!prerender_contents) {
-        auto url_to_arc_throttle =
-            std::make_unique<arc::ArcNavigationThrottle>(handle);
-        throttles.push_back(std::move(url_to_arc_throttle));
-      }
-    }
+    auto url_to_apps_throttle =
+        chromeos::AppsNavigationThrottle::MaybeCreate(handle);
+    if (url_to_apps_throttle)
+      throttles.push_back(std::move(url_to_apps_throttle));
   }
 #endif
 
diff --git a/chrome/browser/chrome_notification_types.h b/chrome/browser/chrome_notification_types.h
index b814738..74cef07 100644
--- a/chrome/browser/chrome_notification_types.h
+++ b/chrome/browser/chrome_notification_types.h
@@ -44,12 +44,6 @@
   // DEPRECATED: Use BrowserListObserver::OnBrowserAdded()
   NOTIFICATION_BROWSER_OPENED = NOTIFICATION_CHROME_START,
 
-  // This message is sent soon after BROWSER_OPENED, and indicates that
-  // the Browser's |window_| is now non-NULL. The source is a Source<Browser>
-  // containing the affected Browser.  No details are expected.
-  // DEPRECATED: Use BrowserListObserver::OnBrowserAdded()
-  NOTIFICATION_BROWSER_WINDOW_READY,
-
   // This message is sent after a window has been closed.  The source is a
   // Source<Browser> containing the affected Browser.  No details are expected.
   // DEPRECATED: Use BrowserListObserver::OnBrowserRemoved()
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index c93b57e..1d60eda 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -300,6 +300,8 @@
     "app_mode/startup_app_launcher.h",
     "app_mode/startup_app_launcher_update_checker.cc",
     "app_mode/startup_app_launcher_update_checker.h",
+    "apps/intent_helper/apps_navigation_throttle.cc",
+    "apps/intent_helper/apps_navigation_throttle.h",
     "arc/accessibility/arc_accessibility_helper_bridge.cc",
     "arc/accessibility/arc_accessibility_helper_bridge.h",
     "arc/accessibility/ax_tree_source_arc.cc",
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.cc b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
index c351727..52a188b 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_manager.cc
+++ b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
@@ -378,7 +378,7 @@
   // Update system tray menu visibility.
   ash::Shell::Get()
       ->accessibility_controller()
-      ->NotifyAccessibilityStatusChanged(ash::A11Y_NOTIFICATION_NONE);
+      ->NotifyAccessibilityStatusChanged();
 }
 
 void AccessibilityManager::EnableLargeCursor(bool enabled) {
@@ -1070,7 +1070,7 @@
   if (details.notification_type == ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFIER) {
     ash::Shell::Get()
         ->accessibility_controller()
-        ->NotifyAccessibilityStatusChanged(details.notify);
+        ->NotifyAccessibilityStatusChanged();
   }
 }
 
diff --git a/chrome/browser/chromeos/apps/OWNERS b/chrome/browser/chromeos/apps/OWNERS
new file mode 100644
index 0000000..def6133f
--- /dev/null
+++ b/chrome/browser/chromeos/apps/OWNERS
@@ -0,0 +1,2 @@
+benwells@chromium.org
+dominickn@chromium.org
diff --git a/chrome/browser/chromeos/apps/README.md b/chrome/browser/chromeos/apps/README.md
new file mode 100644
index 0000000..9c86caf
--- /dev/null
+++ b/chrome/browser/chromeos/apps/README.md
@@ -0,0 +1,17 @@
+# Chrome OS Apps Platform
+
+Chrome OS features multiple app platforms, such as ARC++ and desktop PWAs,
+that are based on different underlying technologies. UX-wise, it is an
+explicit goal to minimise the differences between them and give users the
+feeling that all apps have as similar properties, management, and features as
+possible.
+
+This directory contains generic code to facilitate app platforms on Chrome OS
+communicating with the browser and each other. This layer consists of
+app-platform-agnostic code, which each app platform's custom implementation
+then plugs into. Example features include:
+
+* `chromeos/apps/intent_helper` - allows installed apps that handle particular
+  URLs to be opened by users from the omnibox. For ARC++, custom code queries
+  the ARC++ container; for desktop PWAs, data is contained in the browser
+  itself.
diff --git a/chrome/browser/chromeos/apps/intent_helper/OWNERS b/chrome/browser/chromeos/apps/intent_helper/OWNERS
new file mode 100644
index 0000000..576b4995
--- /dev/null
+++ b/chrome/browser/chromeos/apps/intent_helper/OWNERS
@@ -0,0 +1,3 @@
+# For ARC++ related code
+djacobo@chromium.org
+yusukes@chromium.org
diff --git a/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.cc b/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.cc
new file mode 100644
index 0000000..767e434
--- /dev/null
+++ b/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.cc
@@ -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.
+
+#include "chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.h"
+
+#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/arc/intent_helper/arc_navigation_throttle.h"
+#include "chrome/browser/prerender/prerender_contents.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/navigation_throttle.h"
+#include "content/public/browser/web_contents.h"
+
+namespace chromeos {
+
+// static
+std::unique_ptr<content::NavigationThrottle>
+AppsNavigationThrottle::MaybeCreate(content::NavigationHandle* handle) {
+  if (arc::IsArcPlayStoreEnabledForProfile(Profile::FromBrowserContext(
+          handle->GetWebContents()->GetBrowserContext())) &&
+      !handle->GetWebContents()->GetBrowserContext()->IsOffTheRecord()) {
+    prerender::PrerenderContents* prerender_contents =
+        prerender::PrerenderContents::FromWebContents(handle->GetWebContents());
+    if (!prerender_contents)
+      return std::make_unique<arc::ArcNavigationThrottle>(handle);
+  }
+
+  return nullptr;
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.h b/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.h
new file mode 100644
index 0000000..183960c
--- /dev/null
+++ b/chrome/browser/chromeos/apps/intent_helper/apps_navigation_throttle.h
@@ -0,0 +1,31 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_APPS_INTENT_HELPER_APPS_NAVIGATION_THROTTLE_H_
+#define CHROME_BROWSER_CHROMEOS_APPS_INTENT_HELPER_APPS_NAVIGATION_THROTTLE_H_
+
+#include <memory>
+
+namespace content {
+class NavigationHandle;
+class NavigationThrottle;
+}  // namespace content
+
+namespace chromeos {
+
+// TODO(crbug.com/824598): make this class inherit from
+// content::NavigationThrottle, and move the throttle functionality from
+// ArcNavigationThrottle into here.
+class AppsNavigationThrottle {
+ public:
+  // Possibly creates a navigation throttle that checks if any installed ARC++
+  // apps can handle the URL being navigated to. The user is prompted if they
+  // wish to open the app or remain in the browser.
+  static std::unique_ptr<content::NavigationThrottle> MaybeCreate(
+      content::NavigationHandle* handle);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_APPS_INTENT_HELPER_APPS_NAVIGATION_THROTTLE_H_
diff --git a/chrome/browser/chromeos/file_manager/gallery_browsertest.cc b/chrome/browser/chromeos/file_manager/gallery_browsertest.cc
index edc2b99..2f6c944 100644
--- a/chrome/browser/chromeos/file_manager/gallery_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/gallery_browsertest.cc
@@ -36,9 +36,8 @@
   StartTest();
 }
 
-// http://crbug.com/804413.
 IN_PROC_BROWSER_TEST_F(GalleryBrowserTestInGuestMode,
-                       DISABLED_OpenSingleImageOnDownloads) {
+                       OpenSingleImageOnDownloads) {
   set_test_case_name("openSingleImageOnDownloads");
   StartTest();
 }
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
index 14bd6cfa..0b8d77c 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
@@ -105,6 +105,19 @@
   return category;
 }
 
+std::string KeysetToString(mojom::ImeKeyset keyset) {
+  switch (keyset) {
+    case mojom::ImeKeyset::kNone:
+      return "";
+    case mojom::ImeKeyset::kEmoji:
+      return "emoji";
+    case mojom::ImeKeyset::kHandwriting:
+      return "hwt";
+    case mojom::ImeKeyset::kVoice:
+      return "voice";
+  }
+}
+
 }  // namespace
 
 // ------------------------ InputMethodManagerImpl::StateImpl
@@ -1262,7 +1275,7 @@
                         is_ime_menu_activated_);
 }
 
-void InputMethodManagerImpl::OverrideKeyboardUrlRef(const std::string& keyset) {
+void InputMethodManagerImpl::OverrideKeyboardKeyset(mojom::ImeKeyset keyset) {
   GURL url = state_->GetInputViewUrl();
 
   // If fails to find ref or tag "id" in the ref, it means the current IME is
@@ -1276,7 +1289,7 @@
   if (i == std::string::npos)
     return;
 
-  if (keyset.empty()) {
+  if (keyset == mojom::ImeKeyset::kNone) {
     // Resets the url as the input method default url and notify the hash
     // changed to VK.
     state_->input_view_url = state_->current_input_method.input_view_url();
@@ -1294,9 +1307,9 @@
   // id like: id=${keyset}.emoji/hwt/voice.
   auto j = overridden_ref.find("&", i + 1);
   if (j == std::string::npos) {
-    overridden_ref += "." + keyset;
+    overridden_ref += "." + KeysetToString(keyset);
   } else {
-    overridden_ref.replace(j, 0, "." + keyset);
+    overridden_ref.replace(j, 0, "." + KeysetToString(keyset));
   }
 
   GURL::Replacements replacements;
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.h b/chrome/browser/chromeos/input_method/input_method_manager_impl.h
index c3b3a2d..62ef8001 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.h
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.h
@@ -190,7 +190,7 @@
       const std::string& engine_id,
       const std::vector<InputMethodManager::MenuItem>& items) override;
   void MaybeNotifyImeMenuActivationChanged() override;
-  void OverrideKeyboardUrlRef(const std::string& keyset) override;
+  void OverrideKeyboardKeyset(mojom::ImeKeyset keyset) override;
   void SetImeMenuFeatureEnabled(ImeMenuFeature feature, bool enabled) override;
   bool GetImeMenuFeatureEnabled(ImeMenuFeature feature) const override;
   void NotifyObserversImeExtraInputStateChange() override;
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc
index 72d80ab..03b95bd 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc
@@ -1281,7 +1281,7 @@
       "chrome-extension://"
       "inputview.html#id=us.compact.qwerty.emoji&language=en-US&passwordLayout="
       "us.compact.qwerty&name=keyboard_us");
-  manager_->OverrideKeyboardUrlRef("emoji");
+  manager_->OverrideKeyboardKeyset(mojom::ImeKeyset::kEmoji);
   EXPECT_EQ(overridden_url_emoji, GetActiveIMEState()->GetInputViewUrl());
 
   // Override the keyboard url ref with 'hwt'.
@@ -1290,7 +1290,7 @@
       "chrome-extension://"
       "inputview.html#id=us.compact.qwerty.hwt&language=en-US&passwordLayout="
       "us.compact.qwerty&name=keyboard_us");
-  manager_->OverrideKeyboardUrlRef("hwt");
+  manager_->OverrideKeyboardKeyset(mojom::ImeKeyset::kHandwriting);
   EXPECT_EQ(overridden_url_hwt, GetActiveIMEState()->GetInputViewUrl());
 
   // Override the keyboard url ref with 'voice'.
@@ -1299,7 +1299,7 @@
       "chrome-extension://"
       "inputview.html#id=us.compact.qwerty.voice&language=en-US"
       "&passwordLayout=us.compact.qwerty&name=keyboard_us");
-  manager_->OverrideKeyboardUrlRef("voice");
+  manager_->OverrideKeyboardKeyset(mojom::ImeKeyset::kVoice);
   EXPECT_EQ(overridden_url_voice, GetActiveIMEState()->GetInputViewUrl());
 }
 
@@ -1310,7 +1310,7 @@
 
   EXPECT_EQ(default_url, GetActiveIMEState()->GetInputViewUrl());
 
-  manager_->OverrideKeyboardUrlRef("emoji");
+  manager_->OverrideKeyboardKeyset(mojom::ImeKeyset::kEmoji);
   EXPECT_EQ(default_url, GetActiveIMEState()->GetInputViewUrl());
 }
 
diff --git a/chrome/browser/chromeos/net/network_portal_detector_impl.cc b/chrome/browser/chromeos/net/network_portal_detector_impl.cc
index 60908fe..b4a3989 100644
--- a/chrome/browser/chromeos/net/network_portal_detector_impl.cc
+++ b/chrome/browser/chromeos/net/network_portal_detector_impl.cc
@@ -417,7 +417,7 @@
   return attempt_start_time_;
 }
 
-base::TimeTicks NetworkPortalDetectorImpl::NowTicks() {
+base::TimeTicks NetworkPortalDetectorImpl::NowTicks() const {
   if (time_ticks_for_testing_.is_null())
     return base::TimeTicks::Now();
   return time_ticks_for_testing_;
diff --git a/chrome/browser/chromeos/net/network_portal_detector_impl.h b/chrome/browser/chromeos/net/network_portal_detector_impl.h
index 5c11b6b7..3740b158 100644
--- a/chrome/browser/chromeos/net/network_portal_detector_impl.h
+++ b/chrome/browser/chromeos/net/network_portal_detector_impl.h
@@ -140,7 +140,7 @@
   // PortalDetectorStrategy::Delegate implementation:
   int NoResponseResultCount() override;
   base::TimeTicks AttemptStartTime() override;
-  base::TimeTicks NowTicks() override;
+  base::TimeTicks NowTicks() const override;
 
   // content::NotificationObserver implementation:
   void Observe(int type,
diff --git a/chrome/browser/chromeos/night_light/night_light_client_unittest.cc b/chrome/browser/chromeos/night_light/night_light_client_unittest.cc
index e3f6c4d..8932175 100644
--- a/chrome/browser/chromeos/night_light/night_light_client_unittest.cc
+++ b/chrome/browser/chromeos/night_light/night_light_client_unittest.cc
@@ -75,7 +75,7 @@
   base::Time Now() override { return fake_now_; }
 
   // base::TickClock:
-  base::TimeTicks NowTicks() override { return fake_now_ticks_; }
+  base::TimeTicks NowTicks() const override { return fake_now_ticks_; }
 
   void set_fake_now(base::Time now) { fake_now_ = now; }
   void set_fake_now_ticks(base::TimeTicks now_ticks) {
diff --git a/chrome/browser/chromeos/power/ml/screen_brightness.proto b/chrome/browser/chromeos/power/ml/screen_brightness_event.proto
similarity index 95%
rename from chrome/browser/chromeos/power/ml/screen_brightness.proto
rename to chrome/browser/chromeos/power/ml/screen_brightness_event.proto
index d7f2e75c..18ff673 100644
--- a/chrome/browser/chromeos/power/ml/screen_brightness.proto
+++ b/chrome/browser/chromeos/power/ml/screen_brightness_event.proto
@@ -8,10 +8,10 @@
 
 option optimize_for = LITE_RUNTIME;
 
-// ScreenBrightness contains features, labels and related information for the
-// Adaptive Screen Brightness project. These will be used for logging and model
-// inference.
-message ScreenBrightness {
+// ScreenBrightnessEvent contains features, labels and related information for
+// the Adaptive Screen Brightness project. These will be used for logging and
+// model inference.
+message ScreenBrightnessEvent {
   message Features {
     // DeviceSpec is fixed per device. It's really important to have these info
     // as perceived brightness varies a lot across different devices.
diff --git a/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc b/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc
index ca186653..2308028 100644
--- a/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc
+++ b/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc
@@ -424,7 +424,7 @@
       "New Display Name");
 
   // Do the same thing for the Automatic and Discovered printers.
-  // Create a configuration for the enterprise printer, which should shift it
+  // Create a configuration for the zeroconf printer, which should shift it
   // into the configured category.
   manager_->UpdateConfiguredPrinter(Printer("Automatic"));
   scoped_task_environment_.RunUntilIdle();
diff --git a/chrome/browser/chromeos/upgrade_detector_chromeos.cc b/chrome/browser/chromeos/upgrade_detector_chromeos.cc
index 6f9552e..e8d5fe5d3 100644
--- a/chrome/browser/chromeos/upgrade_detector_chromeos.cc
+++ b/chrome/browser/chromeos/upgrade_detector_chromeos.cc
@@ -6,8 +6,11 @@
 
 #include <stdint.h>
 
+#include <utility>
+
 #include "base/macros.h"
-#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
+#include "base/time/default_tick_clock.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/update_engine_client.h"
 
@@ -20,64 +23,79 @@
 // be at. Once we reach the highest severity, the timer will stop.
 constexpr base::TimeDelta kNotifyCycleDelta = base::TimeDelta::FromMinutes(20);
 
-constexpr int kHighDaysThreshold = 4;
-constexpr int kElevatedDaysThreshold = 2;
-constexpr int kLowDaysThreshold = 0;
+// The default amount of time it takes for the detector's annoyance level
+// (upgrade_notification_stage()) to reach UPGRADE_ANNOYANCE_HIGH once an
+// upgrade is detected.
+constexpr base::TimeDelta kDefaultHighThreshold = base::TimeDelta::FromDays(4);
 
-}  // namespace
+// The scale factor to determine the elevated annoyance level from the high
+// annoyance level's threshold. The elevated level always hits half-way to the
+// high level.
+constexpr double kElevatedScaleFactor = 0.5;
 
-class UpgradeDetectorChromeos::ChannelsRequester {
+class ChannelsRequester {
  public:
-  typedef base::Callback<void(const std::string&, const std::string&)>
+  typedef base::OnceCallback<void(std::string, std::string)>
       OnChannelsReceivedCallback;
 
-  ChannelsRequester() : weak_factory_(this) {}
-
-  void RequestChannels(const OnChannelsReceivedCallback& callback) {
+  static void Begin(OnChannelsReceivedCallback callback) {
+    ChannelsRequester* instance = new ChannelsRequester(std::move(callback));
     UpdateEngineClient* client =
         DBusThreadManager::Get()->GetUpdateEngineClient();
-    callback_ = callback;
+    // base::Unretained is safe because this instance keeps itself alive until
+    // both callbacks have run.
+    // TODO: use BindOnce here; see https://crbug.com/825993.
     client->GetChannel(true /* get_current_channel */,
                        base::Bind(&ChannelsRequester::SetCurrentChannel,
-                                  weak_factory_.GetWeakPtr()));
+                                  base::Unretained(instance)));
     client->GetChannel(false /* get_current_channel */,
                        base::Bind(&ChannelsRequester::SetTargetChannel,
-                                  weak_factory_.GetWeakPtr()));
+                                  base::Unretained(instance)));
   }
 
  private:
+  explicit ChannelsRequester(OnChannelsReceivedCallback callback)
+      : callback_(std::move(callback)) {}
+
+  ~ChannelsRequester() = default;
+
   void SetCurrentChannel(const std::string& current_channel) {
     DCHECK(!current_channel.empty());
     current_channel_ = current_channel;
-    TriggerCallbackIfReady();
+    TriggerCallbackAndDieIfReady();
   }
 
   void SetTargetChannel(const std::string& target_channel) {
     DCHECK(!target_channel.empty());
     target_channel_ = target_channel;
-    TriggerCallbackIfReady();
+    TriggerCallbackAndDieIfReady();
   }
 
-  void TriggerCallbackIfReady() {
+  void TriggerCallbackAndDieIfReady() {
     if (current_channel_.empty() || target_channel_.empty())
       return;
-    if (!callback_.is_null())
-      callback_.Run(current_channel_, target_channel_);
+    if (!callback_.is_null()) {
+      std::move(callback_).Run(std::move(current_channel_),
+                               std::move(target_channel_));
+    }
+    delete this;
   }
 
+  OnChannelsReceivedCallback callback_;
   std::string current_channel_;
   std::string target_channel_;
 
-  OnChannelsReceivedCallback callback_;
-
-  base::WeakPtrFactory<ChannelsRequester> weak_factory_;
-
   DISALLOW_COPY_AND_ASSIGN(ChannelsRequester);
 };
 
-UpgradeDetectorChromeos::UpgradeDetectorChromeos()
-    : initialized_(false), weak_factory_(this) {
-}
+}  // namespace
+
+UpgradeDetectorChromeos::UpgradeDetectorChromeos(base::TickClock* tick_clock)
+    : UpgradeDetector(tick_clock),
+      high_threshold_(DetermineHighThreshold()),
+      upgrade_notification_timer_(tick_clock),
+      initialized_(false),
+      weak_factory_(this) {}
 
 UpgradeDetectorChromeos::~UpgradeDetectorChromeos() {
 }
@@ -92,26 +110,41 @@
   if (!initialized_)
     return;
   DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
+  // Discard an outstanding request to a ChannelsRequester.
+  weak_factory_.InvalidateWeakPtrs();
+  upgrade_notification_timer_.Stop();
+  initialized_ = false;
 }
 
 base::TimeDelta UpgradeDetectorChromeos::GetHighAnnoyanceLevelDelta() {
-  return base::TimeDelta::FromDays(kHighDaysThreshold - kElevatedDaysThreshold);
+  return high_threshold_ - (high_threshold_ * kElevatedScaleFactor);
 }
 
 base::TimeTicks UpgradeDetectorChromeos::GetHighAnnoyanceDeadline() {
   const base::TimeTicks detected_time = upgrade_detected_time();
   if (detected_time.is_null())
     return detected_time;
-  return detected_time + base::TimeDelta::FromDays(kHighDaysThreshold);
+  return detected_time + high_threshold_;
+}
+
+// static
+base::TimeDelta UpgradeDetectorChromeos::DetermineHighThreshold() {
+  base::TimeDelta custom = GetRelaunchNotificationPeriod();
+  return custom.is_zero() ? kDefaultHighThreshold : custom;
+}
+
+void UpgradeDetectorChromeos::OnRelaunchNotificationPeriodPrefChanged() {
+  high_threshold_ = DetermineHighThreshold();
+  if (!upgrade_detected_time().is_null())
+    NotifyOnUpgrade();
 }
 
 void UpgradeDetectorChromeos::UpdateStatusChanged(
     const UpdateEngineClient::Status& status) {
   if (status.status == UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT) {
-    set_upgrade_detected_time(base::TimeTicks::Now());
+    set_upgrade_detected_time(tick_clock()->NowTicks());
 
-    channels_requester_.reset(new UpgradeDetectorChromeos::ChannelsRequester());
-    channels_requester_->RequestChannels(
+    ChannelsRequester::Begin(
         base::Bind(&UpgradeDetectorChromeos::OnChannelsReceived,
                    weak_factory_.GetWeakPtr()));
   } else if (status.status ==
@@ -127,29 +160,33 @@
 }
 
 void UpgradeDetectorChromeos::NotifyOnUpgrade() {
-  base::TimeDelta delta = base::TimeTicks::Now() - upgrade_detected_time();
-  int64_t days_passed = delta.InDays();
+  base::TimeDelta delta = tick_clock()->NowTicks() - upgrade_detected_time();
 
   // These if statements must be sorted (highest interval first).
-  if (days_passed >= kHighDaysThreshold) {
+  if (delta >= high_threshold_)
     set_upgrade_notification_stage(UPGRADE_ANNOYANCE_HIGH);
-
-    // We can't get any higher, baby.
-    upgrade_notification_timer_.Stop();
-  } else if (days_passed >= kElevatedDaysThreshold) {
+  else if (delta >= high_threshold_ * kElevatedScaleFactor)
     set_upgrade_notification_stage(UPGRADE_ANNOYANCE_ELEVATED);
-  } else if (days_passed >= kLowDaysThreshold) {
+  else
     set_upgrade_notification_stage(UPGRADE_ANNOYANCE_LOW);
-  } else {
-    return;  // Not ready to recommend upgrade.
+
+  // Stop the timer if the highest threshold has been reached. Otherwise, make
+  // sure it is running. It is possible that a change in the notification period
+  // (via administrative policy) has moved the detector from high annoyance back
+  // down to a lower level, in which case the timer must be restarted.
+  if (upgrade_notification_stage() == UPGRADE_ANNOYANCE_HIGH) {
+    upgrade_notification_timer_.Stop();
+  } else if (!upgrade_notification_timer_.IsRunning()) {
+    upgrade_notification_timer_.Start(
+        FROM_HERE, kNotifyCycleDelta, this,
+        &UpgradeDetectorChromeos::NotifyOnUpgrade);
   }
 
   NotifyUpgrade();
 }
 
-void UpgradeDetectorChromeos::OnChannelsReceived(
-    const std::string& current_channel,
-    const std::string& target_channel) {
+void UpgradeDetectorChromeos::OnChannelsReceived(std::string current_channel,
+                                                 std::string target_channel) {
   // As current update engine status is UPDATE_STATUS_UPDATED_NEED_REBOOT
   // and target channel is more stable than current channel, powerwash
   // will be performed after reboot.
@@ -166,7 +203,9 @@
 
 // static
 UpgradeDetectorChromeos* UpgradeDetectorChromeos::GetInstance() {
-  return base::Singleton<UpgradeDetectorChromeos>::get();
+  static base::NoDestructor<UpgradeDetectorChromeos> instance(
+      base::DefaultTickClock::GetInstance());
+  return instance.get();
 }
 
 // static
@@ -176,5 +215,5 @@
 
 // static
 base::TimeDelta UpgradeDetector::GetDefaultHighAnnoyanceThreshold() {
-  return base::TimeDelta::FromDays(kHighDaysThreshold);
+  return kDefaultHighThreshold;
 }
diff --git a/chrome/browser/chromeos/upgrade_detector_chromeos.h b/chrome/browser/chromeos/upgrade_detector_chromeos.h
index dda5185..541d0d3 100644
--- a/chrome/browser/chromeos/upgrade_detector_chromeos.h
+++ b/chrome/browser/chromeos/upgrade_detector_chromeos.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_CHROMEOS_UPGRADE_DETECTOR_CHROMEOS_H_
 #define CHROME_BROWSER_CHROMEOS_UPGRADE_DETECTOR_CHROMEOS_H_
 
+#include <string>
+
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -14,7 +16,8 @@
 
 namespace base {
 template <typename T>
-struct DefaultSingletonTraits;
+class NoDestructor;
+class TickClock;
 }  // namespace base
 
 class UpgradeDetectorChromeos : public UpgradeDetector,
@@ -37,10 +40,15 @@
   base::TimeTicks GetHighAnnoyanceDeadline() override;
 
  private:
-  friend struct base::DefaultSingletonTraits<UpgradeDetectorChromeos>;
-  class ChannelsRequester;
+  friend class base::NoDestructor<UpgradeDetectorChromeos>;
 
-  UpgradeDetectorChromeos();
+  explicit UpgradeDetectorChromeos(base::TickClock* tick_clock);
+
+  // Returns the threshold to reach high annoyance level.
+  static base::TimeDelta DetermineHighThreshold();
+
+  // UpgradeDetector:
+  void OnRelaunchNotificationPeriodPrefChanged() override;
 
   // chromeos::UpdateEngineClient::Observer implementation.
   void UpdateStatusChanged(
@@ -52,16 +60,17 @@
   // user that a new version is available.
   void NotifyOnUpgrade();
 
-  void OnChannelsReceived(const std::string& current_channel,
-                          const std::string& target_channel);
+  void OnChannelsReceived(std::string current_channel,
+                          std::string target_channel);
+
+  // The delta from upgrade detection until high annoyance level is reached.
+  base::TimeDelta high_threshold_;
 
   // After we detect an upgrade we start a recurring timer to see if enough time
   // has passed and we should start notifying the user.
   base::RepeatingTimer upgrade_notification_timer_;
   bool initialized_;
 
-  std::unique_ptr<ChannelsRequester> channels_requester_;
-
   base::WeakPtrFactory<UpgradeDetectorChromeos> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(UpgradeDetectorChromeos);
diff --git a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc
index 59177c3..b484293 100644
--- a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc
+++ b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc
@@ -379,7 +379,7 @@
       LanguageSettingsPrivateDelegateFactory::GetForBrowserContext(
           browser_context());
 
-  return RespondNow(OneArgument(
+  return RespondNow(ArgumentList(
       language_settings_private::GetSpellcheckDictionaryStatuses::Results::
           Create(delegate->GetHunspellDictionaryStatuses())));
 }
@@ -656,4 +656,24 @@
   return RespondNow(NoArguments());
 }
 
+LanguageSettingsPrivateRetryDownloadDictionaryFunction::
+    LanguageSettingsPrivateRetryDownloadDictionaryFunction() = default;
+
+LanguageSettingsPrivateRetryDownloadDictionaryFunction::
+    ~LanguageSettingsPrivateRetryDownloadDictionaryFunction() = default;
+
+ExtensionFunction::ResponseAction
+LanguageSettingsPrivateRetryDownloadDictionaryFunction::Run() {
+  const auto parameters =
+      language_settings_private::RetryDownloadDictionary::Params::Create(
+          *args_);
+  EXTENSION_FUNCTION_VALIDATE(parameters.get());
+
+  LanguageSettingsPrivateDelegate* delegate =
+      LanguageSettingsPrivateDelegateFactory::GetForBrowserContext(
+          browser_context());
+  delegate->RetryDownloadHunspellDictionary(parameters->language_code);
+  return RespondNow(NoArguments());
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.h b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.h
index fe4228da..951f544 100644
--- a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.h
+++ b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.h
@@ -277,6 +277,25 @@
   DISALLOW_COPY_AND_ASSIGN(LanguageSettingsPrivateRemoveInputMethodFunction);
 };
 
+// Implements the languageSettingsPrivate.retryDownloadDictionary method.
+class LanguageSettingsPrivateRetryDownloadDictionaryFunction
+    : public UIThreadExtensionFunction {
+ public:
+  LanguageSettingsPrivateRetryDownloadDictionaryFunction();
+  DECLARE_EXTENSION_FUNCTION("languageSettingsPrivate.retryDownloadDictionary",
+                             LANGUAGESETTINGSPRIVATE_RETRYDOWNLOADDICTIONARY)
+
+ protected:
+  ~LanguageSettingsPrivateRetryDownloadDictionaryFunction() override;
+
+  // ExtensionFunction overrides.
+  ResponseAction Run() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(
+      LanguageSettingsPrivateRetryDownloadDictionaryFunction);
+};
+
 }  // namespace extensions
 
 #endif  // CHROME_BROWSER_EXTENSIONS_API_LANGUAGE_SETTINGS_PRIVATE_LANGUAGE_SETTINGS_PRIVATE_API_H_
diff --git a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc
new file mode 100644
index 0000000..790cc77
--- /dev/null
+++ b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc
@@ -0,0 +1,134 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/language_settings_private/language_settings_private_api.h"
+#include "chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.h"
+#include "chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate_factory.h"
+#include "chrome/browser/extensions/extension_function_test_utils.h"
+#include "chrome/browser/extensions/extension_service_test_base.h"
+#include "chrome/test/base/test_browser_window.h"
+#include "chrome/test/base/testing_profile.h"
+#include "extensions/browser/event_router_factory.h"
+#include "extensions/browser/extension_prefs.h"
+
+namespace extensions {
+
+typedef api::language_settings_private::SpellcheckDictionaryStatus
+    DictionaryStatus;
+
+class MockLanguageSettingsPrivateDelegate
+    : public LanguageSettingsPrivateDelegate {
+ public:
+  explicit MockLanguageSettingsPrivateDelegate(content::BrowserContext* context)
+      : LanguageSettingsPrivateDelegate(context) {}
+  ~MockLanguageSettingsPrivateDelegate() override {}
+
+  // LanguageSettingsPrivateDelegate:
+  std::vector<DictionaryStatus> GetHunspellDictionaryStatuses() override;
+  void RetryDownloadHunspellDictionary(const std::string& language) override;
+
+  std::vector<std::string> retry_download_hunspell_dictionary_called_with() {
+    return retry_download_hunspell_dictionary_called_with_;
+  }
+
+ private:
+  std::vector<std::string> retry_download_hunspell_dictionary_called_with_;
+};
+
+std::vector<DictionaryStatus>
+MockLanguageSettingsPrivateDelegate::GetHunspellDictionaryStatuses() {
+  std::vector<DictionaryStatus> statuses;
+  DictionaryStatus status;
+  status.language_code = "fr";
+  status.is_ready = false;
+  status.is_downloading = std::make_unique<bool>(true);
+  status.download_failed = std::make_unique<bool>(false);
+  statuses.push_back(std::move(status));
+  return statuses;
+}
+
+void MockLanguageSettingsPrivateDelegate::RetryDownloadHunspellDictionary(
+    const std::string& language) {
+  retry_download_hunspell_dictionary_called_with_.push_back(language);
+}
+
+namespace {
+
+std::unique_ptr<KeyedService> BuildEventRouter(
+    content::BrowserContext* profile) {
+  return std::make_unique<EventRouter>(profile, ExtensionPrefs::Get(profile));
+}
+
+std::unique_ptr<KeyedService> BuildLanguageSettingsPrivateDelegate(
+    content::BrowserContext* profile) {
+  return std::make_unique<MockLanguageSettingsPrivateDelegate>(profile);
+}
+
+}  // namespace
+
+class LanguageSettingsPrivateApiTest : public ExtensionServiceTestBase {
+ public:
+  LanguageSettingsPrivateApiTest() = default;
+  ~LanguageSettingsPrivateApiTest() override = default;
+
+ private:
+  void SetUp() override {
+    ExtensionServiceTestBase::SetUp();
+    ExtensionServiceTestBase::InitializeEmptyExtensionService();
+    EventRouterFactory::GetInstance()->SetTestingFactory(profile(),
+                                                         &BuildEventRouter);
+
+    LanguageSettingsPrivateDelegateFactory::GetInstance()->SetTestingFactory(
+        profile(), &BuildLanguageSettingsPrivateDelegate);
+  }
+
+  void TearDown() override { ExtensionServiceTestBase::TearDown(); }
+
+  std::unique_ptr<TestBrowserWindow> browser_window_;
+  std::unique_ptr<Browser> browser_;
+};
+
+TEST_F(LanguageSettingsPrivateApiTest, RetryDownloadHunspellDictionaryTest) {
+  MockLanguageSettingsPrivateDelegate* mock_delegate =
+      static_cast<MockLanguageSettingsPrivateDelegate*>(
+          LanguageSettingsPrivateDelegateFactory::GetForBrowserContext(
+              browser_context()));
+
+  auto function = base::MakeRefCounted<
+      LanguageSettingsPrivateRetryDownloadDictionaryFunction>();
+
+  EXPECT_EQ(
+      0u,
+      mock_delegate->retry_download_hunspell_dictionary_called_with().size());
+  EXPECT_TRUE(
+      api_test_utils::RunFunction(function.get(), "[\"fr\"]", profile()))
+      << function->GetError();
+  EXPECT_EQ(
+      1u,
+      mock_delegate->retry_download_hunspell_dictionary_called_with().size());
+  EXPECT_EQ(
+      "fr",
+      mock_delegate->retry_download_hunspell_dictionary_called_with().front());
+}
+
+TEST_F(LanguageSettingsPrivateApiTest, GetSpellcheckDictionaryStatusesTest) {
+  auto function = base::MakeRefCounted<
+      LanguageSettingsPrivateGetSpellcheckDictionaryStatusesFunction>();
+
+  std::unique_ptr<base::Value> actual =
+      api_test_utils::RunFunctionAndReturnSingleResult(function.get(), "[]",
+                                                       profile());
+  EXPECT_TRUE(actual) << function->GetError();
+
+  base::ListValue expected;
+  auto expected_status = std::make_unique<base::DictionaryValue>();
+  expected_status->SetString("languageCode", "fr");
+  expected_status->SetBoolean("isReady", false);
+  expected_status->SetBoolean("isDownloading", true);
+  expected_status->SetBoolean("downloadFailed", false);
+  expected.Append(std::move(expected_status));
+  EXPECT_EQ(expected, *actual);
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.cc b/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.cc
index ef36126f..9efde56 100644
--- a/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.cc
+++ b/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.cc
@@ -21,6 +21,7 @@
 #include "components/spellcheck/browser/pref_names.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/notification_source.h"
+#include "content/public/browser/storage_partition.h"
 
 namespace extensions {
 
@@ -228,6 +229,20 @@
   listening_spellcheck_ = should_listen;
 }
 
+void LanguageSettingsPrivateDelegate::RetryDownloadHunspellDictionary(
+    const std::string& language) {
+  for (const base::WeakPtr<SpellcheckHunspellDictionary> dictionary :
+       GetHunspellDictionaries()) {
+    if (dictionary && dictionary->GetLanguage() == language) {
+      auto* request_context_getter =
+          content::BrowserContext::GetDefaultStoragePartition(context_)
+              ->GetURLRequestContext();
+      dictionary->RetryDownloadDictionary(request_context_getter);
+      return;
+    }
+  }
+}
+
 void LanguageSettingsPrivateDelegate::OnSpellcheckDictionariesChanged() {
   RefreshDictionaries(listening_spellcheck_, listening_spellcheck_);
   BroadcastDictionariesChangedEvent();
diff --git a/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.h b/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.h
index e151e56..873dec9 100644
--- a/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.h
+++ b/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.h
@@ -38,9 +38,18 @@
   ~LanguageSettingsPrivateDelegate() override;
 
   // Returns the languages and statuses of the enabled spellcheck dictionaries.
-  std::vector<api::language_settings_private::SpellcheckDictionaryStatus>
+  virtual std::vector<
+      api::language_settings_private::SpellcheckDictionaryStatus>
   GetHunspellDictionaryStatuses();
 
+  // Retry downloading the spellcheck dictionary.
+  virtual void RetryDownloadHunspellDictionary(const std::string& language);
+
+  // content::NotificationObserver implementation.
+  void Observe(int type,
+               const content::NotificationSource& source,
+               const content::NotificationDetails& details) override;
+
  protected:
   explicit LanguageSettingsPrivateDelegate(content::BrowserContext* context);
 
@@ -51,11 +60,6 @@
   void OnListenerAdded(const EventListenerInfo& details) override;
   void OnListenerRemoved(const EventListenerInfo& details) override;
 
-  // content::NotificationObserver implementation.
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
-
   // SpellcheckHunspellDictionary::Observer implementation.
   void OnHunspellDictionaryInitialized(const std::string& language) override;
   void OnHunspellDictionaryDownloadBegin(const std::string& language) override;
@@ -95,6 +99,8 @@
   void RemoveDictionaryObservers();
 
   // The hunspell dictionaries that are used for spellchecking.
+  // TODO(aee): Consider replacing with
+  // |SpellcheckService::GetHunspellDictionaries()|.
   WeakDictionaries hunspell_dictionaries_;
 
   // The custom dictionary that is used for spellchecking.
diff --git a/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate_unittest.cc b/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate_unittest.cc
new file mode 100644
index 0000000..c6cc3c9
--- /dev/null
+++ b/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate_unittest.cc
@@ -0,0 +1,119 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/extension_service_test_base.h"
+#include "chrome/browser/spellchecker/spellcheck_factory.h"
+#include "chrome/browser/spellchecker/spellcheck_service.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/spellcheck/browser/pref_names.h"
+#include "components/spellcheck/spellcheck_buildflags.h"
+#include "content/public/browser/notification_service.h"
+#include "extensions/browser/event_router_factory.h"
+#include "extensions/browser/extension_prefs.h"
+
+namespace extensions {
+
+namespace {
+
+std::unique_ptr<KeyedService> BuildEventRouter(
+    content::BrowserContext* profile) {
+  return std::make_unique<EventRouter>(profile, ExtensionPrefs::Get(profile));
+}
+
+std::unique_ptr<KeyedService> BuildSpellcheckService(
+    content::BrowserContext* profile) {
+  return std::make_unique<SpellcheckService>(profile);
+}
+
+}  // namespace
+
+class LanguageSettingsPrivateDelegateTest
+    : public ExtensionServiceTestBase,
+      public SpellcheckHunspellDictionary::Observer {
+ public:
+  LanguageSettingsPrivateDelegateTest() = default;
+  ~LanguageSettingsPrivateDelegateTest() override = default;
+
+  void SetUp() override {
+    ExtensionServiceTestBase::SetUp();
+    ExtensionServiceTestBase::InitializeEmptyExtensionService();
+    EventRouterFactory::GetInstance()->SetTestingFactory(profile(),
+                                                         &BuildEventRouter);
+
+    base::ListValue language_codes;
+    language_codes.AppendString("fr");
+    profile()->GetPrefs()->Set(spellcheck::prefs::kSpellCheckDictionaries,
+                               language_codes);
+
+    SpellcheckServiceFactory::GetInstance()->SetTestingFactory(
+        profile(), &BuildSpellcheckService);
+
+    // Wait until dictionary file is loaded.
+    SpellcheckService* service =
+        SpellcheckServiceFactory::GetForContext(profile());
+    auto* dictionary = service->GetHunspellDictionaries().front().get();
+    dictionary->AddObserver(this);
+    run_loop_ = std::make_unique<base::RunLoop>();
+    run_loop_->Run();
+    run_loop_.reset();
+    dictionary->RemoveObserver(this);
+
+    delegate_.reset(LanguageSettingsPrivateDelegate::Create(browser_context()));
+    delegate_->Observe(chrome::NOTIFICATION_PROFILE_ADDED,
+                       content::NotificationService::AllSources(),
+                       content::NotificationService::NoDetails());
+  }
+
+  void TearDown() override {
+    delegate_.reset();
+    ExtensionServiceTestBase::TearDown();
+  }
+
+  LanguageSettingsPrivateDelegate* delegate() { return delegate_.get(); }
+
+ private:
+  // SpellcheckHunspellDictionary::Observer implementation.
+  void OnHunspellDictionaryInitialized(const std::string& language) override {}
+  void OnHunspellDictionaryDownloadBegin(const std::string& language) override {
+  }
+  void OnHunspellDictionaryDownloadSuccess(
+      const std::string& language) override {}
+  void OnHunspellDictionaryDownloadFailure(
+      const std::string& language) override {
+    if (run_loop_)
+      run_loop_->Quit();
+  }
+
+  std::unique_ptr<LanguageSettingsPrivateDelegate> delegate_;
+  std::unique_ptr<base::RunLoop> run_loop_;
+};
+
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
+TEST_F(LanguageSettingsPrivateDelegateTest,
+       RetryDownloadHunspellDictionaryTest) {
+  auto beforeStatuses = delegate()->GetHunspellDictionaryStatuses();
+  ASSERT_EQ(1u, beforeStatuses.size());
+  ASSERT_EQ("fr", beforeStatuses.front().language_code);
+  ASSERT_FALSE(beforeStatuses.front().is_ready);
+  ASSERT_FALSE(beforeStatuses.front().is_downloading);
+  ASSERT_TRUE(beforeStatuses.front().download_failed &&
+              *beforeStatuses.front().download_failed);
+
+  delegate()->RetryDownloadHunspellDictionary("fr");
+
+  auto afterStatuses = delegate()->GetHunspellDictionaryStatuses();
+  ASSERT_EQ(1u, afterStatuses.size());
+  ASSERT_EQ("fr", afterStatuses.front().language_code);
+  EXPECT_FALSE(afterStatuses.front().is_ready);
+  EXPECT_TRUE(afterStatuses.front().is_downloading &&
+              *afterStatuses.front().is_downloading);
+  EXPECT_FALSE(afterStatuses.front().download_failed);
+}
+
+#endif  // !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/blacklist.cc b/chrome/browser/extensions/blacklist.cc
index 01a230e..3a7e2ef 100644
--- a/chrome/browser/extensions/blacklist.cc
+++ b/chrome/browser/extensions/blacklist.cc
@@ -8,6 +8,7 @@
 #include <iterator>
 
 #include "base/bind.h"
+#include "base/callback_list.h"
 #include "base/lazy_instance.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -19,11 +20,8 @@
 #include "chrome/browser/extensions/blacklist_state_fetcher.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "components/prefs/pref_service.h"
-#include "components/safe_browsing/db/notification_types.h"
 #include "components/safe_browsing/db/util.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_source.h"
 #include "extensions/browser/extension_prefs.h"
 
 using content::BrowserThread;
@@ -53,10 +51,17 @@
 
   void set(scoped_refptr<SafeBrowsingDatabaseManager> instance) {
     instance_ = instance;
+    database_changed_callback_list_.Notify();
+  }
+
+  std::unique_ptr<base::CallbackList<void()>::Subscription>
+  RegisterDatabaseChangedCallback(const base::RepeatingClosure& cb) {
+    return database_changed_callback_list_.Add(cb);
   }
 
  private:
   scoped_refptr<SafeBrowsingDatabaseManager> instance_;
+  base::CallbackList<void()> database_changed_callback_list_;
 };
 
 static base::LazyInstance<LazySafeBrowsingDatabaseManager>::DestructorAtExit
@@ -160,13 +165,14 @@
 }
 
 Blacklist::Blacklist(ExtensionPrefs* prefs) {
-  scoped_refptr<SafeBrowsingDatabaseManager> database_manager =
-      GetDatabaseManager();
-  if (database_manager.get()) {
-    registrar_.Add(
-        this, safe_browsing::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE,
-        content::Source<SafeBrowsingDatabaseManager>(database_manager.get()));
-  }
+  auto& lazy_database_manager = g_database_manager.Get();
+  // Using base::Unretained is safe because when this object goes away, the
+  // subscription will automatically be destroyed.
+  database_changed_subscription_ =
+      lazy_database_manager.RegisterDatabaseChangedCallback(base::BindRepeating(
+          &Blacklist::ObserveNewDatabase, base::Unretained(this)));
+
+  ObserveNewDatabase();
 }
 
 Blacklist::~Blacklist() {
@@ -310,6 +316,10 @@
   return state_fetcher_.release();
 }
 
+void Blacklist::ResetDatabaseUpdatedListenerForTest() {
+  database_updated_subscription_.reset();
+}
+
 void Blacklist::AddObserver(Observer* observer) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   observers_.AddObserver(observer);
@@ -331,10 +341,21 @@
   return g_database_manager.Get().get();
 }
 
-void Blacklist::Observe(int type,
-                        const content::NotificationSource& unused_source,
-                        const content::NotificationDetails& unused_details) {
-  DCHECK_EQ(safe_browsing::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE, type);
+void Blacklist::ObserveNewDatabase() {
+  auto database_manager = GetDatabaseManager();
+  if (database_manager.get()) {
+    // Using base::Unretained is safe because when this object goes away, the
+    // subscription to the callback list will automatically be destroyed.
+    database_updated_subscription_ =
+        database_manager.get()->RegisterDatabaseUpdatedCallback(
+            base::BindRepeating(&Blacklist::NotifyObservers,
+                                base::Unretained(this)));
+  } else {
+    database_updated_subscription_.reset();
+  }
+}
+
+void Blacklist::NotifyObservers() {
   for (auto& observer : observers_)
     observer.OnBlacklistUpdated();
 }
diff --git a/chrome/browser/extensions/blacklist.h b/chrome/browser/extensions/blacklist.h
index 69b4641..15f16f8 100644
--- a/chrome/browser/extensions/blacklist.h
+++ b/chrome/browser/extensions/blacklist.h
@@ -13,13 +13,12 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "base/callback_list.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/safe_browsing/db/database_manager.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
 #include "extensions/browser/blacklist_state.h"
 
 namespace content {
@@ -33,7 +32,6 @@
 
 // The blacklist of extensions backed by safe browsing.
 class Blacklist : public KeyedService,
-                  public content::NotificationObserver,
                   public base::SupportsWeakPtr<Blacklist> {
  public:
   class Observer {
@@ -110,6 +108,9 @@
   // BlacklistStateFetcher.
   BlacklistStateFetcher* ResetBlacklistStateFetcherForTest();
 
+  // Reset the listening for an updated database.
+  void ResetDatabaseUpdatedListenerForTest();
+
   // Adds/removes an observer to the blacklist.
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
@@ -122,10 +123,9 @@
   static scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager>
       GetDatabaseManager();
 
-  // content::NotificationObserver
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
+  void ObserveNewDatabase();
+
+  void NotifyObservers();
 
   void GetBlacklistStateForIDs(const GetBlacklistedIDsCallback& callback,
                                const std::set<std::string>& blacklisted_ids);
@@ -140,7 +140,10 @@
 
   base::ObserverList<Observer> observers_;
 
-  content::NotificationRegistrar registrar_;
+  std::unique_ptr<base::CallbackList<void()>::Subscription>
+      database_updated_subscription_;
+  std::unique_ptr<base::CallbackList<void()>::Subscription>
+      database_changed_subscription_;
 
   // The cached BlacklistState's, received from BlacklistStateFetcher.
   BlacklistStateMap blacklist_state_cache_;
diff --git a/chrome/browser/extensions/bookmark_app_helper.cc b/chrome/browser/extensions/bookmark_app_helper.cc
index b5f2cf6f..3ae3d769 100644
--- a/chrome/browser/extensions/bookmark_app_helper.cc
+++ b/chrome/browser/extensions/bookmark_app_helper.cc
@@ -782,7 +782,7 @@
     return;
   }
 
-  if (banners::AppBannerManager::IsExperimentalAppBannersEnabled() &&
+  if (banners::AppBannerManagerDesktop::IsEnabled() &&
       web_app_info_.open_as_window) {
     banners::AppBannerManagerDesktop::FromWebContents(contents_)->OnInstall(
         false /* is_native app */, blink::kWebDisplayModeStandalone);
diff --git a/chrome/browser/extensions/chrome_process_manager_delegate.cc b/chrome/browser/extensions/chrome_process_manager_delegate.cc
index 41fe5cc..f2292f9 100644
--- a/chrome/browser/extensions/chrome_process_manager_delegate.cc
+++ b/chrome/browser/extensions/chrome_process_manager_delegate.cc
@@ -32,8 +32,7 @@
 namespace extensions {
 
 ChromeProcessManagerDelegate::ChromeProcessManagerDelegate() {
-  registrar_.Add(this,
-                 chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+  registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
                  content::NotificationService::AllSources());
   registrar_.Add(this,
                  chrome::NOTIFICATION_PROFILE_CREATED,
@@ -112,7 +111,7 @@
 
   // There are no browser windows open and the browser process was
   // started to show the app launcher. Background hosts will be loaded later
-  // via NOTIFICATION_BROWSER_WINDOW_READY. http://crbug.com/178260
+  // via NOTIFICATION_BROWSER_OPENED. http://crbug.com/178260
   return chrome::GetBrowserCount(profile) == 0 &&
          base::CommandLine::ForCurrentProcess()->HasSwitch(
              switches::kShowAppList);
@@ -123,9 +122,9 @@
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
   switch (type) {
-    case chrome::NOTIFICATION_BROWSER_WINDOW_READY: {
+    case chrome::NOTIFICATION_BROWSER_OPENED: {
       Browser* browser = content::Source<Browser>(source).ptr();
-      OnBrowserWindowReady(browser);
+      OnBrowserOpened(browser);
       break;
     }
     case chrome::NOTIFICATION_PROFILE_CREATED: {
@@ -143,7 +142,7 @@
   }
 }
 
-void ChromeProcessManagerDelegate::OnBrowserWindowReady(Browser* browser) {
+void ChromeProcessManagerDelegate::OnBrowserOpened(Browser* browser) {
   Profile* profile = browser->profile();
   DCHECK(profile);
 
diff --git a/chrome/browser/extensions/chrome_process_manager_delegate.h b/chrome/browser/extensions/chrome_process_manager_delegate.h
index c03bb4b3..bc670a6 100644
--- a/chrome/browser/extensions/chrome_process_manager_delegate.h
+++ b/chrome/browser/extensions/chrome_process_manager_delegate.h
@@ -40,7 +40,7 @@
 
  private:
   // Notification handlers.
-  void OnBrowserWindowReady(Browser* browser);
+  void OnBrowserOpened(Browser* browser);
   void OnProfileCreated(Profile* profile);
   void OnProfileDestroyed(Profile* profile);
 
diff --git a/chrome/browser/extensions/extension_util.cc b/chrome/browser/extensions/extension_util.cc
index b427811..8c14def 100644
--- a/chrome/browser/extensions/extension_util.cc
+++ b/chrome/browser/extensions/extension_util.cc
@@ -335,6 +335,7 @@
     content::BrowserContext* context,
     const GURL& url,
     base::Optional<LaunchContainer> launch_container_filter) {
+  DCHECK(base::FeatureList::IsEnabled(features::kDesktopPWAWindowing));
   const ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
   for (scoped_refptr<const Extension> app :
        ExtensionRegistry::Get(context)->enabled_extensions()) {
diff --git a/chrome/browser/extensions/favicon_downloader.cc b/chrome/browser/extensions/favicon_downloader.cc
index acb1b6c..69a1ed2 100644
--- a/chrome/browser/extensions/favicon_downloader.cc
+++ b/chrome/browser/extensions/favicon_downloader.cc
@@ -49,11 +49,11 @@
 int FaviconDownloader::DownloadImage(const GURL& url) {
   return web_contents()->DownloadImage(
       url,
-      true,  // is_favicon
-      0,     // no max size
+      true,   // is_favicon
+      0,      // no max size
       false,  // normal cache policy
-      base::Bind(&FaviconDownloader::DidDownloadFavicon,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&FaviconDownloader::DidDownloadFavicon,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 std::vector<content::FaviconURL>
diff --git a/chrome/browser/extensions/test_blacklist.cc b/chrome/browser/extensions/test_blacklist.cc
index 864b638..ec461f41 100644
--- a/chrome/browser/extensions/test_blacklist.cc
+++ b/chrome/browser/extensions/test_blacklist.cc
@@ -77,6 +77,7 @@
 
 void TestBlacklist::Detach() {
   blacklist_->ResetBlacklistStateFetcherForTest();
+  blacklist_->ResetDatabaseUpdatedListenerForTest();
 }
 
 void TestBlacklist::SetBlacklistState(const std::string& extension_id,
diff --git a/chrome/browser/fast_shutdown_browsertest.cc b/chrome/browser/fast_shutdown_browsertest.cc
index b5dfb65..f8ae3b7 100644
--- a/chrome/browser/fast_shutdown_browsertest.cc
+++ b/chrome/browser/fast_shutdown_browsertest.cc
@@ -50,7 +50,7 @@
   EXPECT_EQ("", content::GetCookies(browser()->profile(), url));
 
   content::WindowedNotificationObserver window_observer(
-      chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+      chrome::NOTIFICATION_BROWSER_OPENED,
       content::NotificationService::AllSources());
   ui_test_utils::NavigateToURLWithDisposition(
       browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index c088588e..3f7f644 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -397,12 +397,6 @@
 const char kEnableHDRDescription[] =
     "Enables HDR support on compatible displays.";
 
-const char kEnableHeapProfilingName[] = "Heap profiling";
-const char kEnableHeapProfilingDescription[] = "Enables heap profiling.";
-const char kEnableHeapProfilingModePseudo[] = "Enabled (pseudo mode)";
-const char kEnableHeapProfilingModeNative[] = "Enabled (native mode)";
-const char kEnableHeapProfilingTaskProfiler[] = "Enabled (task mode)";
-
 const char kEnableHttpFormWarningName[] =
     "Show in-form warnings for sensitive fields when the top-level page is not "
     "HTTPS";
@@ -3039,6 +3033,12 @@
 const char kAshEnablePersistentWindowBoundsDescription[] =
     "Enable persistent window bounds in multi-displays scenario.";
 
+const char kAshEnableModeSpecificPowerButtonName[] =
+    "Enable mode-specific power button behavior.";
+const char kAshEnableModeSpecificPowerButtonDescription[] =
+    "Enable mode-specific power button behavior. While in tablet mode, tapping "
+    "the power button turns the display off.";
+
 const char kAshEnableTrilinearFilteringName[] = "Enable trilinear filtering.";
 const char kAshEnableTrilinearFilteringDescription[] =
     "Enable trilinear filtering.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 35872c9..db709be 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -274,12 +274,6 @@
 extern const char kEnableHDRName[];
 extern const char kEnableHDRDescription[];
 
-extern const char kEnableHeapProfilingName[];
-extern const char kEnableHeapProfilingDescription[];
-extern const char kEnableHeapProfilingModePseudo[];
-extern const char kEnableHeapProfilingModeNative[];
-extern const char kEnableHeapProfilingTaskProfiler[];
-
 extern const char kEnableHttpFormWarningName[];
 extern const char kEnableHttpFormWarningDescription[];
 
@@ -1870,6 +1864,9 @@
 extern const char kAshEnablePersistentWindowBoundsName[];
 extern const char kAshEnablePersistentWindowBoundsDescription[];
 
+extern const char kAshEnableModeSpecificPowerButtonName[];
+extern const char kAshEnableModeSpecificPowerButtonDescription[];
+
 extern const char kAshEnableTrilinearFilteringName[];
 extern const char kAshEnableTrilinearFilteringDescription[];
 
diff --git a/chrome/browser/media/android/remote/remote_media_player_manager.cc b/chrome/browser/media/android/remote/remote_media_player_manager.cc
index 53d37e8..c3cec2ce 100644
--- a/chrome/browser/media/android/remote/remote_media_player_manager.cc
+++ b/chrome/browser/media/android/remote/remote_media_player_manager.cc
@@ -105,14 +105,14 @@
     return;
   }
   content::WebContents::ImageDownloadCallback callback =
-      base::Bind(&RemoteMediaPlayerManager::DidDownloadPoster,
-                 weak_ptr_factory_.GetWeakPtr(), player_id);
+      base::BindOnce(&RemoteMediaPlayerManager::DidDownloadPoster,
+                     weak_ptr_factory_.GetWeakPtr(), player_id);
   web_contents()->DownloadImage(
       poster_urls_[player_id],
       false,  // is_favicon, false so that cookies will be used.
       MAX_POSTER_BITMAP_SIZE,  // max_bitmap_size, 0 means no limit.
       false,                   // normal cache policy.
-      callback);
+      std::move(callback));
 }
 
 void RemoteMediaPlayerManager::OnSetPoster(int player_id, const GURL& url) {
diff --git a/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc b/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc
index 0dff51d..9f9d6e289 100644
--- a/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc
+++ b/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc
@@ -26,6 +26,7 @@
           WebFeature::kOpenerNavigationWithoutGesture,
           WebFeature::kUsbRequestDevice, WebFeature::kXMLHttpRequestSynchronous,
           WebFeature::kPaymentHandler,
+          WebFeature::kPaymentRequestShowWithoutGesture,
       }));
   return opt_in_features.count(feature);
 }
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 110ad68..cc37548 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -1141,7 +1141,7 @@
   WebContents* web_contents = GetActiveWebContents();
 
   content::WindowedNotificationObserver observer(
-      chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+      chrome::NOTIFICATION_BROWSER_OPENED,
       content::NotificationService::AllSources());
   content::SimulateMouseClickAt(web_contents, blink::WebInputEvent::kShiftKey,
                                 blink::WebMouseEvent::Button::kLeft,
diff --git a/chrome/browser/profiles/profile_window_browsertest.cc b/chrome/browser/profiles/profile_window_browsertest.cc
index cfe0e5d..2adcf61 100644
--- a/chrome/browser/profiles/profile_window_browsertest.cc
+++ b/chrome/browser/profiles/profile_window_browsertest.cc
@@ -130,7 +130,7 @@
   // does incomplete initialization that would lead to
   // SystemUrlRequestContextGetter being leaked.
   content::WindowedNotificationObserver browser_creation_observer(
-      chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+      chrome::NOTIFICATION_BROWSER_OPENED,
       content::NotificationService::AllSources());
   profiles::SwitchToGuestProfile(ProfileManager::CreateCallback());
 
diff --git a/chrome/browser/profiling_host/profiling_process_host.cc b/chrome/browser/profiling_host/profiling_process_host.cc
index 7fa51cbe..b246e7d 100644
--- a/chrome/browser/profiling_host/profiling_process_host.cc
+++ b/chrome/browser/profiling_host/profiling_process_host.cc
@@ -273,16 +273,15 @@
 ProfilingProcessHost::Mode ProfilingProcessHost::GetModeForStartup() {
   const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
 #if BUILDFLAG(USE_ALLOCATOR_SHIM)
+  if (cmdline->HasSwitch("enable-heap-profiling")) {
+    LOG(ERROR) << "--enable-heap-profiling is no longer supported. Use "
+                  "--memlog instead. See documentation at "
+                  "docs/memory/debugging_memory_issues.md";
+    return Mode::kNone;
+  }
+
   if (cmdline->HasSwitch(switches::kMemlog) ||
       base::FeatureList::IsEnabled(kOOPHeapProfilingFeature)) {
-    if (cmdline->HasSwitch(switches::kEnableHeapProfiling)) {
-      // PartitionAlloc doesn't support chained allocation hooks so we can't
-      // run both heap profilers at the same time.
-      LOG(ERROR) << "--" << switches::kEnableHeapProfiling
-                 << " specified with --" << switches::kMemlog
-                 << "which are not compatible. Memlog will be disabled.";
-      return Mode::kNone;
-    }
 
     std::string mode;
     // Respect the commandline switch above the field trial.
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
index ce3b231..5dcaf79 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -672,6 +672,8 @@
 
 // Verify that "Open link in [App Name]" opens a new App window.
 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest, OpenLinkInBookmarkApp) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(features::kDesktopPWAWindowing);
   InstallTestBookmarkApp(GURL(kAppUrl1));
 
   ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/chrome/browser/resources/md_bookmarks/command_manager.html b/chrome/browser/resources/md_bookmarks/command_manager.html
index ad0e2f1..c5e38f8 100644
--- a/chrome/browser/resources/md_bookmarks/command_manager.html
+++ b/chrome/browser/resources/md_bookmarks/command_manager.html
@@ -28,44 +28,49 @@
         display: none;
       }
     </style>
-    <template is="cr-lazy-render" id="dropdown">
-      <cr-action-menu on-mousedown="onMenuMousedown_">
-        <template is="dom-repeat" items="[[menuCommands_]]" as="command">
-          <button slot="item" class="dropdown-item"
-              command$="[[command]]"
-              hidden$="[[!isCommandVisible_(command, menuIds_)]]"
-              disabled$="[[!isCommandEnabled_(command, menuIds_)]]"
-              on-click="onCommandClick_">
-            <span class="label">
-              [[getCommandLabel_(command, menuIds_)]]
-            </span>
-            <span class="sublabel">
-              [[getCommandSublabel_(command, menuIds_)]]
-            </span>
-          </button>
-          <hr hidden$="[[!showDividerAfter_(command, menuIds_)]]"
-              aria-hidden="true">
-          </hr>
-        </template>
-      </cr-action-menu>
-    </template>
-    <template is="cr-lazy-render" id="editDialog">
-      <bookmarks-edit-dialog></bookmarks-edit-dialog>
-    </template>
-    <template is="cr-lazy-render" id="openDialog">
-      <dialog is="cr-dialog">
-        <div slot="title">$i18n{openDialogTitle}</div>
-        <div slot="body"></div>
-        <div slot="button-container">
-          <paper-button class="cancel-button" on-click="onOpenCancelTap_">
-            $i18n{cancel}
-          </paper-button>
-          <paper-button class="action-button" on-click="onOpenConfirmTap_">
-            $i18n{openDialogConfirm}
-          </paper-button>
-        </div>
-      </dialog>
-    </template>
+    <cr-lazy-render id="dropdown">
+      <template>
+        <cr-action-menu on-mousedown="onMenuMousedown_">
+          <template is="dom-repeat" items="[[menuCommands_]]" as="command">
+            <button slot="item" class="dropdown-item"
+                command$="[[command]]"
+                hidden$="[[!isCommandVisible_(command, menuIds_)]]"
+                disabled$="[[!isCommandEnabled_(command, menuIds_)]]"
+                on-click="onCommandClick_">
+              <span class="label">
+                [[getCommandLabel_(command, menuIds_)]]
+              </span>
+              <span class="sublabel">
+                [[getCommandSublabel_(command, menuIds_)]]
+              </span>
+            </button>
+            <hr hidden$="[[!showDividerAfter_(command, menuIds_)]]"
+                aria-hidden="true">
+          </template>
+        </cr-action-menu>
+      </template>
+    </cr-lazy-render>
+    <cr-lazy-render id="editDialog">
+      <template>
+        <bookmarks-edit-dialog></bookmarks-edit-dialog>
+      </template>
+    </cr-lazy-render>
+    <cr-lazy-render id="openDialog">
+      <template>
+        <dialog is="cr-dialog">
+          <div slot="title">$i18n{openDialogTitle}</div>
+          <div slot="body"></div>
+          <div slot="button-container">
+            <paper-button class="cancel-button" on-click="onOpenCancelTap_">
+              $i18n{cancel}
+            </paper-button>
+            <paper-button class="action-button" on-click="onOpenConfirmTap_">
+              $i18n{openDialogConfirm}
+            </paper-button>
+          </div>
+        </dialog>
+      </template>
+    </cr-lazy-render>
   </template>
   <script src="chrome://bookmarks/command_manager.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/md_extensions/manager.html b/chrome/browser/resources/md_extensions/manager.html
index 54590401..716f70f 100644
--- a/chrome/browser/resources/md_extensions/manager.html
+++ b/chrome/browser/resources/md_extensions/manager.html
@@ -87,23 +87,29 @@
           apps="[[apps_]]" extensions="[[extensions_]]"
           on-show-install-warnings="onShowInstallWarnings_">
       </extensions-item-list>
-      <template id="details-view" is="cr-lazy-render">
-        <extensions-detail-view delegate="[[delegate]]" slot="view"
-            in-dev-mode="[[inDevMode]]"
-            incognito-available="[[incognitoAvailable_]]"
-            data="[[detailViewItem_]]">
-        </extensions-detail-view>
-      </template>
-      <template id="keyboard-shortcuts" is="cr-lazy-render">
-        <extensions-keyboard-shortcuts delegate="[[delegate]]" slot="view"
-            items="[[extensions_]]">
-        </extensions-keyboard-shortcuts>
-      </template>
-      <template id="error-page" is="cr-lazy-render">
-        <extensions-error-page data="[[errorPageItem_]]" slot="view"
-            delegate="[[delegate]]" slot="view">
-        </extensions-error-page>
-      </template>
+      <cr-lazy-render id="details-view">
+        <template>
+          <extensions-detail-view delegate="[[delegate]]" slot="view"
+              in-dev-mode="[[inDevMode]]"
+              incognito-available="[[incognitoAvailable_]]"
+              data="[[detailViewItem_]]">
+          </extensions-detail-view>
+        </template>
+      </cr-lazy-render>
+      <cr-lazy-render id="keyboard-shortcuts">
+        <template>
+          <extensions-keyboard-shortcuts delegate="[[delegate]]" slot="view"
+              items="[[extensions_]]">
+          </extensions-keyboard-shortcuts>
+        </template>
+      </cr-lazy-render>
+      <cr-lazy-render id="error-page">
+        <template>
+          <extensions-error-page data="[[errorPageItem_]]" slot="view"
+              delegate="[[delegate]]" slot="view">
+          </extensions-error-page>
+        </template>
+      </cr-lazy-render>
     </extensions-view-manager>
     <template is="dom-if" if="[[showOptionsDialog_]]" restamp>
       <extensions-options-dialog id="options-dialog"
diff --git a/chrome/browser/resources/md_extensions/view_manager.js b/chrome/browser/resources/md_extensions/view_manager.js
index 011f357..71a5bc4f 100644
--- a/chrome/browser/resources/md_extensions/view_manager.js
+++ b/chrome/browser/resources/md_extensions/view_manager.js
@@ -80,8 +80,7 @@
       const animationFunction = extensions.viewAnimations.get(animation);
       assert(animationFunction);
 
-      let effectiveView =
-          view.matches('[is=cr-lazy-render]') ? view.get() : view;
+      let effectiveView = view.matches('cr-lazy-render') ? view.get() : view;
 
       effectiveView.classList.add('active');
       effectiveView.dispatchEvent(new CustomEvent('view-enter-start'));
diff --git a/chrome/browser/resources/md_history/app.html b/chrome/browser/resources/md_history/app.html
index 0e148d5..13481a8 100644
--- a/chrome/browser/resources/md_history/app.html
+++ b/chrome/browser/resources/md_history/app.html
@@ -103,15 +103,17 @@
       </iron-pages>
     </div>
 
-    <template is="cr-lazy-render" id="drawer">
-      <cr-drawer heading="$i18n{title}" align="$i18n{textdirection}"
-          swipe-open>
-        <history-side-bar id="drawer-side-bar" class="drawer-content"
-            selected-page="{{selectedPage_}}"
-            show-footer="[[showSidebarFooter]]">
-        </history-side-bar>
-      </cr-drawer>
-    </template>
+    <cr-lazy-render id="drawer">
+      <template>
+        <cr-drawer heading="$i18n{title}" align="$i18n{textdirection}"
+            swipe-open>
+          <history-side-bar id="drawer-side-bar" class="drawer-content"
+              selected-page="{{selectedPage_}}"
+              show-footer="[[showSidebarFooter]]">
+          </history-side-bar>
+        </cr-drawer>
+      </template>
+    </cr-lazy-render>
 
     <iron-media-query query="(max-width: 1023px)"
         query-matches="{{hasDrawer_}}">
diff --git a/chrome/browser/resources/md_history/history_list.html b/chrome/browser/resources/md_history/history_list.html
index e149709..5a86619 100644
--- a/chrome/browser/resources/md_history/history_list.html
+++ b/chrome/browser/resources/md_history/history_list.html
@@ -58,36 +58,40 @@
         lower-threshold="500" on-lower-threshold="onScrollToBottom_">
     </iron-scroll-threshold>
 
-    <template is="cr-lazy-render" id="dialog">
-      <dialog is="cr-dialog">
-        <div slot="title">$i18n{removeSelected}</div>
-        <div slot="body">$i18n{deleteWarning}</div>
-        <div slot="button-container">
-          <paper-button class="cancel-button" on-click="onDialogCancelTap_">
-            $i18n{cancel}
-          </paper-button>
-          <paper-button class="action-button" on-click="onDialogConfirmTap_">
-            $i18n{deleteConfirm}
-          </paper-button>
-        </div>
-      </dialog>
-    </template>
+    <cr-lazy-render id="dialog">
+      <template>
+        <dialog is="cr-dialog">
+          <div slot="title">$i18n{removeSelected}</div>
+          <div slot="body">$i18n{deleteWarning}</div>
+          <div slot="button-container">
+            <paper-button class="cancel-button" on-click="onDialogCancelTap_">
+              $i18n{cancel}
+            </paper-button>
+            <paper-button class="action-button" on-click="onDialogConfirmTap_">
+              $i18n{deleteConfirm}
+            </paper-button>
+          </div>
+        </dialog>
+      </template>
+    </cr-lazy-render>
 
-    <template is="cr-lazy-render" id="sharedMenu">
-      <cr-action-menu>
-        <button id="menuMoreButton" slot="item" class="dropdown-item"
-            hidden="[[!canSearchMoreFromSite_(
-                searchedTerm, actionMenuModel_.item.domain)]]"
-            on-click="onMoreFromSiteTap_">
-          $i18n{moreFromSite}
-        </button>
-        <button id="menuRemoveButton" slot="item" class="dropdown-item"
-            hidden="[[!canDeleteHistory_]]"
-            on-click="onRemoveFromHistoryTap_">
-          $i18n{removeFromHistory}
-        </button>
-      </cr-action-menu>
-    </template>
+    <cr-lazy-render id="sharedMenu">
+      <template>
+        <cr-action-menu>
+          <button id="menuMoreButton" slot="item" class="dropdown-item"
+              hidden="[[!canSearchMoreFromSite_(
+                  searchedTerm, actionMenuModel_.item.domain)]]"
+              on-click="onMoreFromSiteTap_">
+            $i18n{moreFromSite}
+          </button>
+          <button id="menuRemoveButton" slot="item" class="dropdown-item"
+              hidden="[[!canDeleteHistory_]]"
+              on-click="onRemoveFromHistoryTap_">
+            $i18n{removeFromHistory}
+          </button>
+        </cr-action-menu>
+      </template>
+    </cr-lazy-render>
   </template>
   <script src="chrome://history/history_list.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/md_history/synced_device_manager.html b/chrome/browser/resources/md_history/synced_device_manager.html
index cf99f6e..90054b9 100644
--- a/chrome/browser/resources/md_history/synced_device_manager.html
+++ b/chrome/browser/resources/md_history/synced_device_manager.html
@@ -87,18 +87,20 @@
       </paper-button>
     </div>
 
-    <template is="cr-lazy-render" id="menu">
-      <cr-action-menu>
-        <button id="menuOpenButton" slot="item" class="dropdown-item"
-            on-click="onOpenAllTap_">
-          $i18n{openAll}
-        </button>
-        <button id="menuDeleteButton" slot="item" class="dropdown-item"
-            on-click="onDeleteSessionTap_">
-          $i18n{deleteSession}
-        </button>
-      </cr-action-menu>
-    </template>
+    <cr-lazy-render id="menu">
+      <template>
+        <cr-action-menu>
+          <button id="menuOpenButton" slot="item" class="dropdown-item"
+              on-click="onOpenAllTap_">
+            $i18n{openAll}
+          </button>
+          <button id="menuDeleteButton" slot="item" class="dropdown-item"
+              on-click="onDeleteSessionTap_">
+            $i18n{deleteSession}
+          </button>
+        </cr-action-menu>
+      </template>
+    </cr-lazy-render>
   </template>
   <script src="chrome://history/synced_device_manager.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/pdf/pdf.js b/chrome/browser/resources/pdf/pdf.js
index 029f5b2..0f03224 100644
--- a/chrome/browser/resources/pdf/pdf.js
+++ b/chrome/browser/resources/pdf/pdf.js
@@ -699,12 +699,10 @@
         this.sendDocumentLoadedMessage_();
         break;
       case 'setScrollPosition':
-        var position = this.viewport_.position;
-        if (message.data.x !== undefined)
-          position.x = message.data.x;
-        if (message.data.y !== undefined)
-          position.y = message.data.y;
-        this.viewport_.position = position;
+        this.viewport_.scrollTo(/** @type {!PartialPoint} */ (message.data));
+        break;
+      case 'scrollBy':
+        this.viewport_.scrollBy(/** @type {!Point} */ (message.data));
         break;
       case 'cancelStreamUrl':
         chrome.mimeHandlerPrivate.abortStream();
diff --git a/chrome/browser/resources/pdf/viewport.js b/chrome/browser/resources/pdf/viewport.js
index 709b646..ddf93124 100644
--- a/chrome/browser/resources/pdf/viewport.js
+++ b/chrome/browser/resources/pdf/viewport.js
@@ -3,6 +3,22 @@
 // found in the LICENSE file.
 
 /**
+ * @typedef {{
+ *   x: number,
+ *   y: number
+ * }}
+ */
+let Point;
+
+/**
+ * @typedef {{
+ *   x: number | undefined,
+ *   y: number | undefined
+ * }}
+ */
+let PartialPoint;
+
+/**
  * Returns the height of the intersection of two rectangles.
  * @param {Object} rect1 the first rect
  * @param {Object} rect2 the second rect
@@ -254,7 +270,7 @@
 
   /**
    * Scroll the viewport to the specified position.
-   * @type {Object} position the position to scroll to.
+   * @type {Object} position The position to scroll to.
    */
   set position(position) {
     this.window_.scrollTo(position.x, position.y + this.topToolbarHeight_);
@@ -906,5 +922,38 @@
     return (
         this.fittingType_ == FittingType.FIT_TO_PAGE ||
         this.fittingType_ == FittingType.FIT_TO_HEIGHT);
+  },
+
+  /**
+   * Scroll the viewport to the specified position.
+   *
+   * @param {!PartialPoint} point The position to which to move the viewport.
+   */
+  scrollTo: function(point) {
+    let changed = false;
+    const newPosition = this.position;
+    if (point.x !== undefined && point.x != newPosition.x) {
+      newPosition.x = point.x;
+      changed = true;
+    }
+    if (point.y !== undefined && point.y != newPosition.y) {
+      newPosition.y = point.y;
+      changed = true;
+    }
+
+    if (changed)
+      this.position = newPosition;
+  },
+
+  /**
+   * Scroll the viewport by the specified delta.
+   *
+   * @param {!Point} delta The delta by which to move the viewport.
+   */
+  scrollBy: function(delta) {
+    const newPosition = this.position;
+    newPosition.x += delta.x;
+    newPosition.y += delta.y;
+    this.scrollTo(newPosition);
   }
 };
diff --git a/chrome/browser/resources/print_preview/new/advanced_options_settings.html b/chrome/browser/resources/print_preview/new/advanced_options_settings.html
index 5ecb91c..77ac579 100644
--- a/chrome/browser/resources/print_preview/new/advanced_options_settings.html
+++ b/chrome/browser/resources/print_preview/new/advanced_options_settings.html
@@ -24,11 +24,13 @@
         </button>
       </div>
     </print-preview-settings-section>
-    <template is="cr-lazy-render" id="advancedDialog">
-      <print-preview-advanced-dialog
-          settings="{{settings}}" destination="[[destination]]">
-      </print-preview-advanced-dialog>
-    </template>
+    <cr-lazy-render id="advancedDialog">
+      <template>
+        <print-preview-advanced-dialog
+            settings="{{settings}}" destination="[[destination]]">
+        </print-preview-advanced-dialog>
+      </template>
+    </cr-lazy-render>
   </template>
   <script src="advanced_options_settings.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/print_preview/new/destination_settings.html b/chrome/browser/resources/print_preview/new/destination_settings.html
index b95e9e8..3c3bc8a6 100644
--- a/chrome/browser/resources/print_preview/new/destination_settings.html
+++ b/chrome/browser/resources/print_preview/new/destination_settings.html
@@ -88,14 +88,16 @@
         </button>
       </div>
     </print-preview-settings-section>
-    <template is="cr-lazy-render" id="destinationDialog">
-      <print-preview-destination-dialog
-          destination-store="[[destinationStore]]"
-          recent-destinations="[[recentDestinations]]"
-          user-info="{{userInfo}}"
-          show-cloud-print-promo="{{showCloudPrintPromo_}}">
-      </print-preview-destination-dialog>
-    </template>
+    <cr-lazy-render id="destinationDialog">
+      <template>
+        <print-preview-destination-dialog
+            destination-store="[[destinationStore]]"
+            recent-destinations="[[recentDestinations]]"
+            user-info="{{userInfo}}"
+            show-cloud-print-promo="{{showCloudPrintPromo_}}">
+        </print-preview-destination-dialog>
+      </template>
+    </cr-lazy-render>
   </template>
   <script src="destination_settings.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/languages_page/languages_page.html b/chrome/browser/resources/settings/languages_page/languages_page.html
index 3e95b3e..85f0d93 100644
--- a/chrome/browser/resources/settings/languages_page/languages_page.html
+++ b/chrome/browser/resources/settings/languages_page/languages_page.html
@@ -265,55 +265,58 @@
           </div>
         </iron-collapse>
 </if>
-        <template is="cr-lazy-render" id="menu">
-          <cr-action-menu
-              class$="[[getMenuClass_(prefs.translate.enabled.value)]]">
+        <cr-lazy-render id="menu">
+          <template>
+            <cr-action-menu
+                class$="[[getMenuClass_(prefs.translate.enabled.value)]]">
 <if expr="chromeos or is_win">
-            <paper-checkbox id="uiLanguageItem" slot="item"
-                class="dropdown-item"
-                checked="[[isProspectiveUILanguage_(
-                    detailLanguage_.language.code,
-                    languages.prospectiveUILanguage)]]"
-                on-change="onUILanguageChange_"
-                disabled="[[disableUILanguageCheckbox_(
-                    detailLanguage_, languages.prospectiveUILanguage)]]">
-                $i18n{displayInThisLanguage}
-            </paper-checkbox>
+              <paper-checkbox id="uiLanguageItem" slot="item"
+                  class="dropdown-item"
+                  checked="[[isProspectiveUILanguage_(
+                      detailLanguage_.language.code,
+                      languages.prospectiveUILanguage)]]"
+                  on-change="onUILanguageChange_"
+                  disabled="[[disableUILanguageCheckbox_(
+                      detailLanguage_, languages.prospectiveUILanguage)]]">
+                  $i18n{displayInThisLanguage}
+              </paper-checkbox>
 </if>
-            <paper-checkbox id="offerTranslations" slot="item"
-                class="dropdown-item"
-                checked="[[detailLanguage_.translateEnabled]]"
-                on-change="onTranslateCheckboxChange_"
-                hidden="[[!prefs.translate.enabled.value]]"
-                disabled="[[disableTranslateCheckbox_(
-                    detailLanguage_.language, languages.translateTarget)]]">
-              $i18n{offerToTranslateInThisLanguage}
-            </paper-checkbox>
-            <hr slot="item">
-            <button slot="item" class="dropdown-item" role="menuitem"
-                on-click="onMoveToTopTap_"
-                hidden="[[isNthLanguage_(
-                    0, detailLanguage_, languages.enabled.*)]]">
-              $i18n{moveToTop}
-            </button>
-            <button slot="item" class="dropdown-item" role="menuitem"
-                on-click="onMoveUpTap_"
-                hidden="[[!showMoveUp_(detailLanguage_, languages.enabled.*)]]">
-              $i18n{moveUp}
-            </button>
-            <button slot="item" class="dropdown-item" role="menuitem"
-                on-click="onMoveDownTap_"
-                hidden="[[!showMoveDown_(
-                    detailLanguage_, languages.enabled.*)]]">
-              $i18n{moveDown}
-            </button>
-            <button slot="item" class="dropdown-item" role="menuitem"
-                on-click="onRemoveLanguageTap_"
-                hidden="[[!detailLanguage_.removable]]">
-              $i18n{removeLanguage}
-            </button>
-          </cr-action-menu>
-        </template>
+              <paper-checkbox id="offerTranslations" slot="item"
+                  class="dropdown-item"
+                  checked="[[detailLanguage_.translateEnabled]]"
+                  on-change="onTranslateCheckboxChange_"
+                  hidden="[[!prefs.translate.enabled.value]]"
+                  disabled="[[disableTranslateCheckbox_(
+                      detailLanguage_.language, languages.translateTarget)]]">
+                $i18n{offerToTranslateInThisLanguage}
+              </paper-checkbox>
+              <hr slot="item">
+              <button slot="item" class="dropdown-item" role="menuitem"
+                  on-click="onMoveToTopTap_"
+                  hidden="[[isNthLanguage_(
+                      0, detailLanguage_, languages.enabled.*)]]">
+                $i18n{moveToTop}
+              </button>
+              <button slot="item" class="dropdown-item" role="menuitem"
+                  on-click="onMoveUpTap_"
+                  hidden="[[!showMoveUp_(detailLanguage_,
+                      languages.enabled.*)]]">
+                $i18n{moveUp}
+              </button>
+              <button slot="item" class="dropdown-item" role="menuitem"
+                  on-click="onMoveDownTap_"
+                  hidden="[[!showMoveDown_(
+                      detailLanguage_, languages.enabled.*)]]">
+                $i18n{moveDown}
+              </button>
+              <button slot="item" class="dropdown-item" role="menuitem"
+                  on-click="onRemoveLanguageTap_"
+                  hidden="[[!detailLanguage_.removable]]">
+                $i18n{removeLanguage}
+              </button>
+            </cr-action-menu>
+          </template>
+        </cr-lazy-render>
       </neon-animatable>
 <if expr="chromeos">
       <template is="dom-if" route-path="/inputMethods">
diff --git a/chrome/browser/resources/settings/on_startup_page/startup_url_entry.html b/chrome/browser/resources/settings/on_startup_page/startup_url_entry.html
index a89df417..42ecafb7 100644
--- a/chrome/browser/resources/settings/on_startup_page/startup_url_entry.html
+++ b/chrome/browser/resources/settings/on_startup_page/startup_url_entry.html
@@ -30,17 +30,19 @@
               focus-row-control focus-type="menu">
           </button>
         </paper-icon-button-light>
-        <template is="cr-lazy-render" id="menu">
-          <cr-action-menu>
-            <button slot="item" class="dropdown-item" on-click="onEditTap_">
-              $i18n{edit}
-            </button>
-            <button slot="item" class="dropdown-item" id="remove"
-                on-click="onRemoveTap_">
-              $i18n{onStartupRemove}
-            </button>
-          </cr-action-menu>
-        </template>
+        <cr-lazy-render id="menu">
+          <template>
+            <cr-action-menu>
+              <button slot="item" class="dropdown-item" on-click="onEditTap_">
+                $i18n{edit}
+              </button>
+              <button slot="item" class="dropdown-item" id="remove"
+                  on-click="onRemoveTap_">
+                $i18n{onStartupRemove}
+              </button>
+            </cr-action-menu>
+          </template>
+        </cr-lazy-render>
       </template>
     </div>
   </template>
diff --git a/chrome/browser/resources/settings/reset_page/reset_page.html b/chrome/browser/resources/settings/reset_page/reset_page.html
index b36d776..f86210f 100644
--- a/chrome/browser/resources/settings/reset_page/reset_page.html
+++ b/chrome/browser/resources/settings/reset_page/reset_page.html
@@ -40,10 +40,13 @@
         </div>
         <!-- Keep a single instance of reset-profile-dialog on purpose, to
           preserve state across show/hide operations. -->
-        <template is="cr-lazy-render" id="resetProfileDialog">
-          <settings-reset-profile-dialog on-close="onResetProfileDialogClose_">
-          </settings-reset-profile-dialog>
-        </template>
+        <cr-lazy-render id="resetProfileDialog">
+          <template>
+            <settings-reset-profile-dialog
+                on-close="onResetProfileDialogClose_">
+            </settings-reset-profile-dialog>
+          </template>
+        </cr-lazy-render>
 <if expr="chromeos">
         <div class="settings-box two-line" id="powerwash" actionable
             on-click="onShowPowerwashDialog_" hidden="[[!allowPowerwash_]]">
@@ -70,8 +73,7 @@
             <div class="start">$i18n{resetCleanupComputerTrigger}</div>
             <paper-icon-button-light class="subpage-arrow">
               <button id="chromeCleanupArrow"
-                  aria-label="$i18n{resetCleanupComputerTrigger}"
-                  aria-describedby="chromeCleanupSecondary"></button>
+                  aria-label="$i18n{resetCleanupComputerTrigger}"></button>
             </paper-icon-button-light>
           </div>
         </template>
diff --git a/chrome/browser/resources/settings/settings_menu/settings_menu.html b/chrome/browser/resources/settings/settings_menu/settings_menu.html
index 56bc5495..23caa191 100644
--- a/chrome/browser/resources/settings/settings_menu/settings_menu.html
+++ b/chrome/browser/resources/settings/settings_menu/settings_menu.html
@@ -65,12 +65,15 @@
         outline: auto 5px -webkit-focus-ring-color;
       }
 
-      #advancedButton > span {
+      #advancedButton > span,
+      #extensionsButton > span {
         flex: 1;
       }
 
-      #advancedButton > iron-icon {
-        -webkit-margin-end: 22px;  /* 24px - 2px from margin for outline. */
+      #advancedButton > iron-icon,
+      #extensionsButton > iron-icon {
+        @apply --cr-icon-height-width;
+        -webkit-margin-end: 14px;  /* 16px - 2px from margin for outline. */
       }
 
       #menuSeparator {
@@ -80,8 +83,9 @@
         margin-top: 8px;
       }
     </style>
-    <iron-selector id="topMenu" selectable="a" attr-for-selected="href"
-        on-iron-activate="onSelectorActivate_" role="navigation">
+    <iron-selector id="topMenu" selectable="a:not([unselectable])"
+        attr-for-selected="href" on-iron-activate="onSelectorActivate_"
+        role="navigation">
 <if expr="chromeos">
       <a href="/internet">
         <iron-icon icon="settings:network-wifi"></iron-icon>
@@ -189,6 +193,11 @@
         </iron-selector>
       </iron-collapse>
       <div id="menuSeparator"></div>
+      <a id="extensionsButton" unselectable href="chrome://extensions"
+          target="_blank" on-click="onExternalLinkClick_">
+        <span>$i18n{extensionsPageTitle}</span>
+        <iron-icon class="cr-icon icon-external" actionable></iron-icon>
+      </a>
       <a id="about-menu" href="/help">$i18n{aboutPageTitle}</a>
     </iron-selector>
   </template>
diff --git a/chrome/browser/resources/settings/settings_menu/settings_menu.js b/chrome/browser/resources/settings/settings_menu/settings_menu.js
index 81160f27..a5db236 100644
--- a/chrome/browser/resources/settings/settings_menu/settings_menu.js
+++ b/chrome/browser/resources/settings/settings_menu/settings_menu.js
@@ -52,7 +52,8 @@
    * @private
    */
   onLinkTap_: function(event) {
-    if (event.target.hasAttribute('href'))
+    if (event.target.tagName == 'A' &&
+        !event.target.hasAttribute('unselectable'))
       event.preventDefault();
   },
 
@@ -87,4 +88,9 @@
   arrowState_: function(opened) {
     return opened ? 'cr:arrow-drop-up' : 'cr:arrow-drop-down';
   },
+
+  /** @private */
+  onExternalLinkClick_: function() {
+    this.fire('external-link-click');
+  },
 });
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chrome/browser/resources/settings/settings_ui/settings_ui.js
index 1b53667..77f59aa 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.js
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.js
@@ -59,7 +59,7 @@
     lastSearchQuery_: {
       type: String,
       value: '',
-    }
+    },
   },
 
   listeners: {
@@ -82,6 +82,10 @@
       this.$.drawerTemplate.if = true;
     });
 
+    this.addEventListener('external-link-click', () => {
+      this.$.drawer.closeDrawer();
+    });
+
     window.addEventListener('popstate', e => {
       this.$.drawer.closeDrawer();
     });
diff --git a/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc b/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
index 533427d1..65aae95 100644
--- a/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
+++ b/chrome/browser/safe_browsing/browser_feature_extractor_unittest.cc
@@ -602,7 +602,8 @@
        features::kSafeBrowsingOriginalUrl,
         "http://www.good.com/")));
   EXPECT_DOUBLE_EQ(1.0, features[features::kSafeBrowsingIsSubresource]);
-  EXPECT_DOUBLE_EQ(3.0, features[features::kSafeBrowsingThreatType]);
+  EXPECT_DOUBLE_EQ(SB_THREAT_TYPE_URL_MALWARE,
+                   features[features::kSafeBrowsingThreatType]);
 }
 
 TEST_F(BrowserFeatureExtractorTest, MalwareFeatures) {
diff --git a/chrome/browser/safe_browsing/local_database_manager.cc b/chrome/browser/safe_browsing/local_database_manager.cc
index 0334eeec..4248918 100644
--- a/chrome/browser/safe_browsing/local_database_manager.cc
+++ b/chrome/browser/safe_browsing/local_database_manager.cc
@@ -35,10 +35,8 @@
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
 #include "components/safe_browsing/common/safebrowsing_switches.h"
-#include "components/safe_browsing/db/notification_types.h"
 #include "components/safe_browsing/db/util.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_service.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "url/url_constants.h"
 
@@ -693,10 +691,7 @@
 void LocalSafeBrowsingDatabaseManager::NotifyDatabaseUpdateFinished(
     bool update_succeeded) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  content::NotificationService::current()->Notify(
-      NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE,
-      content::Source<SafeBrowsingDatabaseManager>(this),
-      content::Details<bool>(&update_succeeded));
+  update_complete_callback_list_.Notify();
 }
 
 LocalSafeBrowsingDatabaseManager::QueuedCheck::QueuedCheck(
diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
index 046eb35..c4fffe4 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
@@ -68,7 +68,6 @@
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/db/database_manager.h"
 #include "components/safe_browsing/db/metadata.pb.h"
-#include "components/safe_browsing/db/notification_types.h"
 #include "components/safe_browsing/db/test_database_manager.h"
 #include "components/safe_browsing/db/util.h"
 #include "components/safe_browsing/db/v4_database.h"
@@ -2114,15 +2113,18 @@
 // and can save cookies.
 IN_PROC_BROWSER_TEST_F(SafeBrowsingDatabaseManagerCookieTest,
                        TestSBUpdateCookies) {
-  content::WindowedNotificationObserver observer(
-      NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE,
-      content::Source<SafeBrowsingDatabaseManager>(
-          sb_factory_->test_safe_browsing_service()->database_manager().get()));
+  base::RunLoop run_loop;
+  auto callback_subscription =
+      sb_factory_->test_safe_browsing_service()
+          ->database_manager()
+          .get()
+          ->RegisterDatabaseUpdatedCallback(run_loop.QuitClosure());
+
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
       base::BindOnce(&SafeBrowsingDatabaseManagerCookieTest::ForceUpdate,
                      base::Unretained(this)));
-  observer.Wait();
+  run_loop.Run();
 }
 
 // Tests the safe browsing blocking page in a browser.
diff --git a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
index 026c161..f8a558f 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
@@ -2152,7 +2152,7 @@
   // Create a new browser in Guest Mode.
   EXPECT_EQ(1U, BrowserList::GetInstance()->size());
   content::WindowedNotificationObserver browser_creation_observer(
-      chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+      chrome::NOTIFICATION_BROWSER_OPENED,
       content::NotificationService::AllSources());
   profiles::SwitchToGuestProfile(ProfileManager::CreateCallback());
   browser_creation_observer.Wait();
diff --git a/chrome/browser/tracing/background_tracing_field_trial_unittest.cc b/chrome/browser/tracing/background_tracing_field_trial_unittest.cc
index 0d7e41a..e025904 100644
--- a/chrome/browser/tracing/background_tracing_field_trial_unittest.cc
+++ b/chrome/browser/tracing/background_tracing_field_trial_unittest.cc
@@ -34,7 +34,7 @@
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kTraceStartup);
     // Normally is runned from ContentMainRunnerImpl::Initialize().
-    tracing::EnableStartupTracingIfNeeded(false);
+    tracing::EnableStartupTracingIfNeeded();
   }
 
   base::FieldTrialList field_trial_list(nullptr);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 3b7b34ca..f5d121fc 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3602,6 +3602,14 @@
         "ash/launcher/arc_app_window_launcher_item_controller.h",
         "ash/launcher/arc_launcher_context_menu.cc",
         "ash/launcher/arc_launcher_context_menu.h",
+        "ash/launcher/crostini_app_window.cc",
+        "ash/launcher/crostini_app_window.h",
+        "ash/launcher/crostini_app_window_shelf_controller.cc",
+        "ash/launcher/crostini_app_window_shelf_controller.h",
+        "ash/launcher/crostini_app_window_shelf_item_controller.cc",
+        "ash/launcher/crostini_app_window_shelf_item_controller.h",
+        "ash/launcher/crostini_shelf_context_menu.cc",
+        "ash/launcher/crostini_shelf_context_menu.h",
         "ash/launcher/launcher_arc_app_updater.cc",
         "ash/launcher/launcher_arc_app_updater.h",
         "views/arc_app_dialog_view.cc",
diff --git a/chrome/browser/ui/app_list/crostini/crostini_util.cc b/chrome/browser/ui/app_list/crostini/crostini_util.cc
index 86c427a..108b202 100644
--- a/chrome/browser/ui/app_list/crostini/crostini_util.cc
+++ b/chrome/browser/ui/app_list/crostini/crostini_util.cc
@@ -9,6 +9,12 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_features.h"
 
+namespace {
+
+constexpr char kCrostiniAppIdPrefix[] = "crostini:";
+
+}  // namespace
+
 bool IsCrostiniAllowed() {
   return base::FeatureList::IsEnabled(features::kCrostini) &&
          virtual_machines::AreVirtualMachinesAllowedByPolicy();
@@ -26,3 +32,13 @@
 bool IsCrostiniRunning() {
   return false;
 }
+
+std::string CreateCrostiniAppId(const std::string& window_app_id) {
+  DCHECK(!IsCrostiniAppId(window_app_id));
+  return kCrostiniAppIdPrefix + window_app_id;
+}
+
+bool IsCrostiniAppId(const std::string& app_id) {
+  return strncmp(app_id.c_str(), kCrostiniAppIdPrefix,
+                 sizeof(kCrostiniAppIdPrefix)) == 0;
+}
diff --git a/chrome/browser/ui/app_list/crostini/crostini_util.h b/chrome/browser/ui/app_list/crostini/crostini_util.h
index c2826b5..95e4b2f 100644
--- a/chrome/browser/ui/app_list/crostini/crostini_util.h
+++ b/chrome/browser/ui/app_list/crostini/crostini_util.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_APP_LIST_CROSTINI_CROSTINI_UTIL_H_
 #define CHROME_BROWSER_UI_APP_LIST_CROSTINI_CROSTINI_UTIL_H_
 
+#include <string>
+
 // Returns true if crostini is allowed to run.
 // Otherwise, returns false, e.g. if crostini is not available on the device,
 // or it is in the flow to set up managed account creation.
@@ -13,4 +15,9 @@
 // Returns true if crostini UI can be shown. Implies crostini is allowed to run.
 bool IsExperimentalCrostiniUIAvailable();
 
+// Returns a Crostini app ID from a Aura window app ID.
+std::string CreateCrostiniAppId(const std::string& window_app_id);
+
+// Returns true if the app_id is a Crostini app ID.
+bool IsCrostiniAppId(const std::string& app_id);
 #endif  // CHROME_BROWSER_UI_APP_LIST_CROSTINI_CROSTINI_UTIL_H_
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index 81d7a12..d6c8987 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -198,8 +198,9 @@
     immersive_handler_factory_ = std::make_unique<ImmersiveHandlerFactoryMus>();
 
     // Enterprise support in the browser can monitor user activity. Connect to
-    // the UI service to monitor activity. The ash process has its own monitor.
-    user_activity_detector_ = std::make_unique<ui::UserActivityDetector>();
+    // the UI service to monitor activity, as this detector cannot observe the
+    // platform event source directly. The ash process has its own detector.
+    user_activity_detector_ = std::make_unique<ui::UserActivityDetector>(false);
     ui::mojom::UserActivityMonitorPtr user_activity_monitor;
     content::ServiceManagerConnection::GetForProcess()
         ->GetConnector()
diff --git a/chrome/browser/ui/ash/ime_controller_client.cc b/chrome/browser/ui/ash/ime_controller_client.cc
index 74a0c18..ee0d23d 100644
--- a/chrome/browser/ui/ash/ime_controller_client.cc
+++ b/chrome/browser/ui/ash/ime_controller_client.cc
@@ -113,25 +113,9 @@
 }
 
 void ImeControllerClient::OverrideKeyboardKeyset(
-    ash::mojom::ImeKeyset keyset,
+    chromeos::input_method::mojom::ImeKeyset keyset,
     OverrideKeyboardKeysetCallback callback) {
-  std::string url_ref;
-  switch (keyset) {
-    case ash::mojom::ImeKeyset::kNone:
-      // kNone resets url_ref to the empty string.
-      break;
-    case ash::mojom::ImeKeyset::kEmoji:
-      url_ref = "emoji";
-      break;
-    case ash::mojom::ImeKeyset::kVoice:
-      url_ref = "voice";
-      break;
-    case ash::mojom::ImeKeyset::kHandwriting:
-      url_ref = "hwt";
-      break;
-  }
-
-  input_method_manager_->OverrideKeyboardUrlRef(url_ref);
+  input_method_manager_->OverrideKeyboardKeyset(keyset);
   std::move(callback).Run();
 }
 
diff --git a/chrome/browser/ui/ash/ime_controller_client.h b/chrome/browser/ui/ash/ime_controller_client.h
index 28caffa..79f4626 100644
--- a/chrome/browser/ui/ash/ime_controller_client.h
+++ b/chrome/browser/ui/ash/ime_controller_client.h
@@ -42,7 +42,7 @@
   void SwitchImeById(const std::string& id, bool show_message) override;
   void ActivateImeMenuItem(const std::string& key) override;
   void SetCapsLockEnabled(bool caps_enabled) override;
-  void OverrideKeyboardKeyset(ash::mojom::ImeKeyset keyset,
+  void OverrideKeyboardKeyset(chromeos::input_method::mojom::ImeKeyset keyset,
                               OverrideKeyboardKeysetCallback callback) override;
 
   // chromeos::input_method::InputMethodManager::Observer:
diff --git a/chrome/browser/ui/ash/ime_controller_client_unittest.cc b/chrome/browser/ui/ash/ime_controller_client_unittest.cc
index 99e8b6ba..f10b24f 100644
--- a/chrome/browser/ui/ash/ime_controller_client_unittest.cc
+++ b/chrome/browser/ui/ash/ime_controller_client_unittest.cc
@@ -120,8 +120,9 @@
   void ActivateInputMethodMenuItem(const std::string& key) override {
     last_activate_menu_item_key_ = key;
   }
-  void OverrideKeyboardUrlRef(const std::string& key_set) override {
-    keyboard_url_ref_ = key_set;
+  void OverrideKeyboardKeyset(
+      chromeos::input_method::mojom::ImeKeyset keyset) override {
+    keyboard_keyset_ = keyset;
   }
 
   InputMethodUtil* GetInputMethodUtil() override { return &util_; }
@@ -135,7 +136,7 @@
   int add_menu_observer_count_ = 0;
   int remove_menu_observer_count_ = 0;
   std::string last_activate_menu_item_key_;
-  std::string keyboard_url_ref_;
+  chromeos::input_method::mojom::ImeKeyset keyboard_keyset_;
   FakeInputMethodDelegate delegate_;
   InputMethodUtil util_;
 
@@ -337,10 +338,11 @@
   ImeControllerClient client(&input_method_manager_);
   bool callback_called = false;
   client.OverrideKeyboardKeyset(
-      ash::mojom::ImeKeyset::kEmoji,
+      chromeos::input_method::mojom::ImeKeyset::kEmoji,
       base::BindLambdaForTesting(
           [&callback_called]() { callback_called = true; }));
-  EXPECT_EQ("emoji", input_method_manager_.keyboard_url_ref_);
+  EXPECT_EQ(chromeos::input_method::mojom::ImeKeyset::kEmoji,
+            input_method_manager_.keyboard_keyset_);
   EXPECT_TRUE(callback_called);
 }
 
diff --git a/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.cc
index adfb374e..a7ca20e 100644
--- a/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.cc
@@ -199,3 +199,32 @@
         shelf_id().app_id);
   }
 }
+
+ash::MenuItemList AppWindowLauncherItemController::GetAppMenuItems(
+    int event_flags) {
+  ash::MenuItemList items;
+  base::string16 app_title = LauncherControllerHelper::GetAppTitle(
+      ChromeLauncherController::instance()->profile(), app_id());
+  for (auto it = windows().begin(); it != windows().end(); ++it) {
+    // TODO(khmel): resolve correct icon here.
+    size_t i = std::distance(windows().begin(), it);
+    aura::Window* window = (*it)->GetNativeWindow();
+    ash::mojom::MenuItemPtr item = ash::mojom::MenuItem::New();
+    item->command_id = base::checked_cast<uint32_t>(i);
+    item->label = (window && !window->GetTitle().empty()) ? window->GetTitle()
+                                                          : app_title;
+    items.push_back(std::move(item));
+  }
+
+  return items;
+}
+
+void AppWindowLauncherItemController::ExecuteCommand(bool from_context_menu,
+                                                     int64_t command_id,
+                                                     int32_t event_flags,
+                                                     int64_t display_id) {
+  if (from_context_menu && ExecuteContextMenuCommand(command_id, event_flags))
+    return;
+
+  ActivateIndexedApp(command_id);
+}
diff --git a/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h b/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h
index aed2c57b..87596af 100644
--- a/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h
+++ b/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h
@@ -46,6 +46,11 @@
                     int64_t display_id,
                     ash::ShelfLaunchSource source,
                     ItemSelectedCallback callback) override;
+  ash::MenuItemList GetAppMenuItems(int event_flags) override;
+  void ExecuteCommand(bool from_context_menu,
+                      int64_t command_id,
+                      int32_t event_flags,
+                      int64_t display_id) override;
   std::unique_ptr<ui::MenuModel> GetContextMenu(int64_t display_id) override;
   void Close() override;
 
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc
index 0143d418..ac647e41 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc
@@ -49,32 +49,3 @@
   arc::SetTaskActive(*task_ids_.begin());
   std::move(callback).Run(ash::SHELF_ACTION_NEW_WINDOW_CREATED, base::nullopt);
 }
-
-ash::MenuItemList ArcAppWindowLauncherItemController::GetAppMenuItems(
-    int event_flags) {
-  ash::MenuItemList items;
-  base::string16 app_title = LauncherControllerHelper::GetAppTitle(
-      ChromeLauncherController::instance()->profile(), app_id());
-  for (auto it = windows().begin(); it != windows().end(); ++it) {
-    // TODO(khmel): resolve correct icon here.
-    size_t i = std::distance(windows().begin(), it);
-    aura::Window* window = (*it)->GetNativeWindow();
-    ash::mojom::MenuItemPtr item = ash::mojom::MenuItem::New();
-    item->command_id = base::checked_cast<uint32_t>(i);
-    item->label = (window && !window->GetTitle().empty()) ? window->GetTitle()
-                                                          : app_title;
-    items.push_back(std::move(item));
-  }
-
-  return items;
-}
-
-void ArcAppWindowLauncherItemController::ExecuteCommand(bool from_context_menu,
-                                                        int64_t command_id,
-                                                        int32_t event_flags,
-                                                        int64_t display_id) {
-  if (from_context_menu && ExecuteContextMenuCommand(command_id, event_flags))
-    return;
-
-  ActivateIndexedApp(command_id);
-}
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.h b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.h
index ab98786c..5dd49776 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.h
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.h
@@ -26,11 +26,6 @@
                     int64_t display_id,
                     ash::ShelfLaunchSource source,
                     ItemSelectedCallback callback) override;
-  ash::MenuItemList GetAppMenuItems(int event_flags) override;
-  void ExecuteCommand(bool from_context_menu,
-                      int64_t command_id,
-                      int32_t event_flags,
-                      int64_t display_id) override;
 
   void AddTaskId(int task_id);
   void RemoveTaskId(int task_id);
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
index 38e1254..aa37cab 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -35,6 +35,7 @@
 #include "chrome/browser/ui/app_list/app_sync_ui_state.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_icon_loader.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
+#include "chrome/browser/ui/app_list/crostini/crostini_util.h"
 #include "chrome/browser/ui/ash/ash_util.h"
 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
 #include "chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h"
@@ -45,6 +46,7 @@
 #include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h"
 #include "chrome/browser/ui/ash/launcher/browser_status_monitor.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.h"
+#include "chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.h"
 #include "chrome/browser/ui/ash/launcher/launcher_arc_app_updater.h"
 #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h"
 #include "chrome/browser/ui/ash/launcher/launcher_extension_app_updater.h"
@@ -257,6 +259,10 @@
   app_window_controllers_.push_back(std::move(extension_app_window_controller));
   app_window_controllers_.push_back(
       std::make_unique<ArcAppWindowLauncherController>(this));
+  if (IsExperimentalCrostiniUIAvailable()) {
+    app_window_controllers_.push_back(
+        std::make_unique<CrostiniAppWindowShelfController>(this));
+  }
 }
 
 ChromeLauncherController::~ChromeLauncherController() {
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
index ed83b5e..da1fc80d 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -1009,7 +1009,7 @@
 IN_PROC_BROWSER_TEST_F(ShelfAppBrowserTest, LaunchMaximized) {
   MaximizeWindow(browser()->window());
   content::WindowedNotificationObserver open_observer(
-      chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+      chrome::NOTIFICATION_BROWSER_OPENED,
       content::NotificationService::AllSources());
   chrome::NewEmptyWindow(browser()->profile());
   open_observer.Wait();
diff --git a/chrome/browser/ui/ash/launcher/crostini_app_window.cc b/chrome/browser/ui/ash/launcher/crostini_app_window.cc
new file mode 100644
index 0000000..3dbb56b
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/crostini_app_window.cc
@@ -0,0 +1,122 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/ash/launcher/crostini_app_window.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.h"
+#include "chrome/browser/ui/ash/launcher/crostini_app_window_shelf_item_controller.h"
+#include "components/exo/shell_surface_base.h"
+#include "extensions/common/constants.h"
+#include "ui/aura/window.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/image/image_skia_operations.h"
+#include "ui/views/widget/native_widget_aura.h"
+#include "ui/views/widget/widget.h"
+
+CrostiniAppWindow::CrostiniAppWindow(const ash::ShelfID& shelf_id,
+                                     views::Widget* widget)
+    : shelf_id_(shelf_id), widget_(widget) {}
+
+void CrostiniAppWindow::SetController(
+    CrostiniAppWindowShelfItemController* controller) {
+  DCHECK(!controller_ || !controller);
+  controller_ = controller;
+}
+
+bool CrostiniAppWindow::IsActive() const {
+  return widget_->IsActive();
+}
+
+bool CrostiniAppWindow::IsMaximized() const {
+  NOTREACHED();
+  return false;
+}
+
+bool CrostiniAppWindow::IsMinimized() const {
+  NOTREACHED();
+  return false;
+}
+
+bool CrostiniAppWindow::IsFullscreen() const {
+  NOTREACHED();
+  return false;
+}
+
+gfx::NativeWindow CrostiniAppWindow::GetNativeWindow() const {
+  return widget_->GetNativeWindow();
+}
+
+gfx::Rect CrostiniAppWindow::GetRestoredBounds() const {
+  NOTREACHED();
+  return gfx::Rect();
+}
+
+ui::WindowShowState CrostiniAppWindow::GetRestoredState() const {
+  NOTREACHED();
+  return ui::SHOW_STATE_NORMAL;
+}
+
+gfx::Rect CrostiniAppWindow::GetBounds() const {
+  NOTREACHED();
+  return gfx::Rect();
+}
+
+void CrostiniAppWindow::Show() {
+  widget_->Show();
+}
+
+void CrostiniAppWindow::ShowInactive() {
+  NOTREACHED();
+}
+
+void CrostiniAppWindow::Hide() {
+  NOTREACHED();
+}
+
+bool CrostiniAppWindow::IsVisible() const {
+  NOTREACHED();
+  return true;
+}
+
+void CrostiniAppWindow::Close() {
+  widget_->Close();
+}
+
+void CrostiniAppWindow::Activate() {
+  widget_->Activate();
+}
+
+void CrostiniAppWindow::Deactivate() {
+  NOTREACHED();
+}
+
+void CrostiniAppWindow::Maximize() {
+  NOTREACHED();
+}
+
+void CrostiniAppWindow::Minimize() {
+  widget_->Minimize();
+}
+
+void CrostiniAppWindow::Restore() {
+  NOTREACHED();
+}
+
+void CrostiniAppWindow::SetBounds(const gfx::Rect& bounds) {
+  NOTREACHED();
+}
+
+void CrostiniAppWindow::FlashFrame(bool flash) {
+  NOTREACHED();
+}
+
+bool CrostiniAppWindow::IsAlwaysOnTop() const {
+  NOTREACHED();
+  return false;
+}
+
+void CrostiniAppWindow::SetAlwaysOnTop(bool always_on_top) {
+  NOTREACHED();
+}
diff --git a/chrome/browser/ui/ash/launcher/crostini_app_window.h b/chrome/browser/ui/ash/launcher/crostini_app_window.h
new file mode 100644
index 0000000..a5468eb
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/crostini_app_window.h
@@ -0,0 +1,74 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_APP_WINDOW_H_
+#define CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_APP_WINDOW_H_
+
+#include <string>
+#include <vector>
+
+#include "ash/public/cpp/shelf_types.h"
+#include "base/macros.h"
+#include "ui/base/base_window.h"
+
+class CrostiniAppWindowShelfItemController;
+
+namespace views {
+class Widget;
+}
+
+// A ui::BaseWindow for a Chrome OS launcher to control Crostini applications.
+class CrostiniAppWindow final : public ui::BaseWindow {
+ public:
+  CrostiniAppWindow(const ash::ShelfID& shelf_id, views::Widget* widget);
+
+  void SetController(CrostiniAppWindowShelfItemController* controller);
+
+  const std::string& app_id() const { return shelf_id_.app_id; }
+
+  const ash::ShelfID& shelf_id() const { return shelf_id_; }
+
+  void set_shelf_id(const ash::ShelfID& shelf_id) { shelf_id_ = shelf_id; }
+
+  views::Widget* widget() const { return widget_; }
+
+  CrostiniAppWindowShelfItemController* controller() const {
+    return controller_;
+  }
+
+  // ui::BaseWindow:
+  bool IsActive() const override;
+  bool IsMaximized() const override;
+  bool IsMinimized() const override;
+  bool IsFullscreen() const override;
+  gfx::NativeWindow GetNativeWindow() const override;
+  gfx::Rect GetRestoredBounds() const override;
+  ui::WindowShowState GetRestoredState() const override;
+  gfx::Rect GetBounds() const override;
+  void Show() override;
+  void ShowInactive() override;
+  void Hide() override;
+  bool IsVisible() const override;
+  void Close() override;
+  void Activate() override;
+  void Deactivate() override;
+  void Maximize() override;
+  void Minimize() override;
+  void Restore() override;
+  void SetBounds(const gfx::Rect& bounds) override;
+  void FlashFrame(bool flash) override;
+  bool IsAlwaysOnTop() const override;
+  void SetAlwaysOnTop(bool always_on_top) override;
+
+ private:
+  // Keeps shelf id.
+  ash::ShelfID shelf_id_;
+  // Unowned pointers
+  views::Widget* const widget_;
+  CrostiniAppWindowShelfItemController* controller_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(CrostiniAppWindow);
+};
+
+#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_APP_WINDOW_H_
diff --git a/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc b/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc
new file mode 100644
index 0000000..df2ec31
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc
@@ -0,0 +1,193 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.h"
+
+#include <string>
+
+#include "ash/public/cpp/shelf_model.h"
+#include "ash/public/cpp/window_properties.h"
+#include "base/bind.h"
+#include "chrome/browser/ui/app_list/crostini/crostini_util.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
+#include "chrome/browser/ui/ash/launcher/crostini_app_window.h"
+#include "chrome/browser/ui/ash/launcher/crostini_app_window_shelf_item_controller.h"
+#include "components/exo/shell_surface.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/env.h"
+#include "ui/base/base_window.h"
+#include "ui/display/display.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/views/widget/widget.h"
+
+namespace {
+
+constexpr char kArcAppIdPrefix[] = "org.chromium.arc";
+
+// Please note the down cast from ShelfItemDelegate to
+// CrostiniAppWindowShelfItemController.
+CrostiniAppWindowShelfItemController* GetItemController(
+    const ash::ShelfID& shelf_id,
+    const ash::ShelfModel& model) {
+  DCHECK(IsCrostiniAppId(shelf_id.app_id));
+
+  ash::ShelfItemDelegate* item_delegate = model.GetShelfItemDelegate(shelf_id);
+  if (item_delegate == nullptr)
+    return nullptr;
+  return static_cast<CrostiniAppWindowShelfItemController*>(
+      item_delegate->AsAppWindowLauncherItemController());
+}
+
+}  // namespace
+
+CrostiniAppWindowShelfController::CrostiniAppWindowShelfController(
+    ChromeLauncherController* owner)
+    : AppWindowLauncherController(owner) {
+  aura::Env* env = aura::Env::GetInstanceDontCreate();
+  if (env)
+    env->AddObserver(this);
+}
+
+CrostiniAppWindowShelfController::~CrostiniAppWindowShelfController() {
+  for (auto* window : observed_windows_)
+    window->RemoveObserver(this);
+  aura::Env* env = aura::Env::GetInstanceDontCreate();
+  if (env)
+    env->RemoveObserver(this);
+}
+
+void CrostiniAppWindowShelfController::OnWindowInitialized(
+    aura::Window* window) {
+  // An Crostini window has type WINDOW_TYPE_NORMAL, a WindowDelegate and
+  // is a top level views widget.
+  if (window->type() != aura::client::WINDOW_TYPE_NORMAL || !window->delegate())
+    return;
+  views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
+  if (!widget || !widget->is_top_level())
+    return;
+  observed_windows_.push_back(window);
+
+  window->AddObserver(this);
+}
+
+void CrostiniAppWindowShelfController::OnWindowVisibilityChanged(
+    aura::Window* window,
+    bool visible) {
+  if (!visible)
+    return;
+
+  const std::string* window_app_id =
+      exo::ShellSurface::GetApplicationId(window);
+  if (window_app_id == nullptr)
+    return;
+
+  // Skip handling ARC++ windows.
+  if (strncmp(window_app_id->c_str(), kArcAppIdPrefix,
+              sizeof(kArcAppIdPrefix)) == 0)
+    return;
+
+  // Skip when this window has been handled. This can happen when the window
+  // becomes visible again.
+  auto app_window_it = aura_window_to_app_window_.find(window);
+  if (app_window_it != aura_window_to_app_window_.end())
+    return;
+
+  RegisterAppWindow(window, window_app_id);
+}
+
+void CrostiniAppWindowShelfController::RegisterAppWindow(
+    aura::Window* window,
+    const std::string* window_app_id) {
+  const std::string crostini_app_id = CreateCrostiniAppId(*window_app_id);
+  const ash::ShelfID shelf_id(crostini_app_id);
+  views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
+  aura_window_to_app_window_[window] =
+      std::make_unique<CrostiniAppWindow>(shelf_id, widget);
+  CrostiniAppWindow* app_window = aura_window_to_app_window_[window].get();
+
+  CrostiniAppWindowShelfItemController* item_controller =
+      GetItemController(shelf_id, *owner()->shelf_model());
+  if (item_controller == nullptr) {
+    std::unique_ptr<CrostiniAppWindowShelfItemController> controller =
+        std::make_unique<CrostiniAppWindowShelfItemController>(shelf_id);
+    item_controller = controller.get();
+    if (!owner()->GetItem(shelf_id)) {
+      owner()->CreateAppLauncherItem(std::move(controller),
+                                     ash::STATUS_RUNNING);
+    } else {
+      owner()->shelf_model()->SetShelfItemDelegate(shelf_id,
+                                                   std::move(controller));
+      owner()->SetItemStatus(shelf_id, ash::STATUS_RUNNING);
+    }
+    window->SetProperty(ash::kShelfIDKey,
+                        new std::string(shelf_id.Serialize()));
+  }
+
+  item_controller->AddWindow(app_window);
+  app_window->SetController(item_controller);
+}
+
+void CrostiniAppWindowShelfController::OnWindowDestroying(
+    aura::Window* window) {
+  auto it =
+      std::find(observed_windows_.begin(), observed_windows_.end(), window);
+  DCHECK(it != observed_windows_.end());
+  observed_windows_.erase(it);
+  window->RemoveObserver(this);
+
+  auto app_window_it = aura_window_to_app_window_.find(window);
+  if (app_window_it == aura_window_to_app_window_.end())
+    return;
+  UnregisterAppWindow(app_window_it->second.get());
+
+  // Check if we may close controller now, at this point we can safely remove
+  // controllers without window.
+  CrostiniAppWindowShelfItemController* item_controller = GetItemController(
+      app_window_it->second->shelf_id(), *owner()->shelf_model());
+  if (item_controller != nullptr && item_controller->window_count() == 0)
+    owner()->CloseLauncherItem(item_controller->shelf_id());
+
+  aura_window_to_app_window_.erase(app_window_it);
+}
+
+AppWindowLauncherItemController*
+CrostiniAppWindowShelfController::ControllerForWindow(aura::Window* window) {
+  if (!window)
+    return nullptr;
+
+  auto app_window_it = aura_window_to_app_window_.find(window);
+  if (app_window_it == aura_window_to_app_window_.end())
+    return nullptr;
+
+  CrostiniAppWindow* app_window = app_window_it->second.get();
+
+  if (app_window == nullptr)
+    return nullptr;
+
+  return app_window->controller();
+}
+
+void CrostiniAppWindowShelfController::UnregisterAppWindow(
+    CrostiniAppWindow* app_window) {
+  if (!app_window)
+    return;
+
+  CrostiniAppWindowShelfItemController* controller = app_window->controller();
+  if (controller)
+    controller->RemoveWindow(app_window);
+  app_window->SetController(nullptr);
+}
+
+void CrostiniAppWindowShelfController::OnItemDelegateDiscarded(
+    ash::ShelfItemDelegate* delegate) {
+  for (auto& it : aura_window_to_app_window_) {
+    CrostiniAppWindow* app_window = it.second.get();
+    if (!app_window || app_window->controller() != delegate)
+      continue;
+
+    VLOG(1) << "Item controller was released externally for the app "
+            << delegate->shelf_id().app_id << ".";
+
+    UnregisterAppWindow(it.second.get());
+  }
+}
diff --git a/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.h b/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.h
new file mode 100644
index 0000000..b00381fe
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.h
@@ -0,0 +1,64 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_APP_WINDOW_SHELF_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_APP_WINDOW_SHELF_CONTROLLER_H_
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "base/time/time.h"
+#include "chrome/browser/ui/ash/launcher/app_window_launcher_controller.h"
+#include "chrome/browser/ui/ash/launcher/crostini_app_window.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "ui/aura/env_observer.h"
+#include "ui/aura/window_observer.h"
+
+namespace aura {
+class Window;
+}
+
+class CrostiniAppWindow;
+class ChromeLauncherController;
+
+// A controller to manage Crostini app shelf items. It listens to window events
+// and events from the container bridge to put running Crostini apps on the
+// Chrome OS shelf.
+class CrostiniAppWindowShelfController : public AppWindowLauncherController,
+                                         public aura::EnvObserver,
+                                         public aura::WindowObserver {
+ public:
+  explicit CrostiniAppWindowShelfController(ChromeLauncherController* owner);
+  ~CrostiniAppWindowShelfController() override;
+
+  // aura::EnvObserver:
+  void OnWindowInitialized(aura::Window* window) override;
+
+  // aura::WindowObserver:
+  void OnWindowVisibilityChanged(aura::Window* window, bool visible) override;
+  void OnWindowDestroying(aura::Window* window) override;
+
+ private:
+  using AuraWindowToAppWindow =
+      std::map<aura::Window*, std::unique_ptr<CrostiniAppWindow>>;
+
+  void RegisterAppWindow(aura::Window* window,
+                         const std::string* window_app_id);
+  void UnregisterAppWindow(CrostiniAppWindow* app_window);
+
+  // AppWindowLauncherController:
+  AppWindowLauncherItemController* ControllerForWindow(
+      aura::Window* window) override;
+  void OnItemDelegateDiscarded(ash::ShelfItemDelegate* delegate) override;
+
+  AuraWindowToAppWindow aura_window_to_app_window_;
+  std::vector<aura::Window*> observed_windows_;
+
+  DISALLOW_COPY_AND_ASSIGN(CrostiniAppWindowShelfController);
+};
+
+#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_APP_WINDOW_SHELF_CONTROLLER_H_
diff --git a/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_item_controller.cc b/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_item_controller.cc
new file mode 100644
index 0000000..8f1d609d
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_item_controller.cc
@@ -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.
+
+#include "chrome/browser/ui/ash/launcher/crostini_app_window_shelf_item_controller.h"
+
+#include <utility>
+
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
+#include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h"
+#include "ui/aura/window.h"
+#include "ui/base/base_window.h"
+
+CrostiniAppWindowShelfItemController::CrostiniAppWindowShelfItemController(
+    const ash::ShelfID& shelf_id)
+    : AppWindowLauncherItemController(shelf_id) {}
+
+CrostiniAppWindowShelfItemController::~CrostiniAppWindowShelfItemController() {}
+
+void CrostiniAppWindowShelfItemController::ItemSelected(
+    std::unique_ptr<ui::Event> event,
+    int64_t display_id,
+    ash::ShelfLaunchSource source,
+    ItemSelectedCallback callback) {
+  if (window_count()) {
+    AppWindowLauncherItemController::ItemSelected(std::move(event), display_id,
+                                                  source, std::move(callback));
+    return;
+  }
+
+  NOTIMPLEMENTED()
+      << "TODO(crbug.com/824549): Make Crostini apps able to be pinned";
+}
diff --git a/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_item_controller.h b/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_item_controller.h
new file mode 100644
index 0000000..2fe5bed
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_item_controller.h
@@ -0,0 +1,34 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_APP_WINDOW_SHELF_ITEM_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_APP_WINDOW_SHELF_ITEM_CONTROLLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h"
+
+class CrostiniAppWindow;
+
+// Shelf item delegate for Crostini app windows.
+class CrostiniAppWindowShelfItemController
+    : public AppWindowLauncherItemController {
+ public:
+  explicit CrostiniAppWindowShelfItemController(const ash::ShelfID& shelf_id);
+
+  ~CrostiniAppWindowShelfItemController() override;
+
+  // AppWindowLauncherItemController overrides:
+  void ItemSelected(std::unique_ptr<ui::Event> event,
+                    int64_t display_id,
+                    ash::ShelfLaunchSource source,
+                    ItemSelectedCallback callback) override;
+
+ private:
+  // Update the shelf item's icon for the active window.
+  void UpdateIcon(CrostiniAppWindow* crostini_app_window);
+
+  DISALLOW_COPY_AND_ASSIGN(CrostiniAppWindowShelfItemController);
+};
+
+#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_APP_WINDOW_SHELF_ITEM_CONTROLLER_H_
diff --git a/chrome/browser/ui/ash/launcher/crostini_shelf_context_menu.cc b/chrome/browser/ui/ash/launcher/crostini_shelf_context_menu.cc
new file mode 100644
index 0000000..805ccae
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/crostini_shelf_context_menu.cc
@@ -0,0 +1,30 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/ash/launcher/crostini_shelf_context_menu.h"
+
+#include "ash/public/cpp/shelf_item.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/ui_base_features.h"
+
+CrostiniShelfContextMenu::CrostiniShelfContextMenu(
+    ChromeLauncherController* controller,
+    const ash::ShelfItem* item,
+    int64_t display_id)
+    : LauncherContextMenu(controller, item, display_id) {
+  Init();
+}
+
+CrostiniShelfContextMenu::~CrostiniShelfContextMenu() {}
+
+void CrostiniShelfContextMenu::Init() {
+  if (controller()->IsOpen(item().id))
+    AddItemWithStringId(MENU_CLOSE, IDS_LAUNCHER_CONTEXT_MENU_CLOSE);
+  else
+    AddItemWithStringId(MENU_OPEN_NEW, IDS_APP_CONTEXT_MENU_ACTIVATE_ARC);
+
+  if (!features::IsTouchableAppContextMenuEnabled())
+    AddSeparator(ui::NORMAL_SEPARATOR);
+}
diff --git a/chrome/browser/ui/ash/launcher/crostini_shelf_context_menu.h b/chrome/browser/ui/ash/launcher/crostini_shelf_context_menu.h
new file mode 100644
index 0000000..0ca6fc2
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/crostini_shelf_context_menu.h
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_SHELF_CONTEXT_MENU_H_
+#define CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_SHELF_CONTEXT_MENU_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
+
+// Class for context menu which is shown for Crostini app in the shelf.
+class CrostiniShelfContextMenu : public LauncherContextMenu {
+ public:
+  CrostiniShelfContextMenu(ChromeLauncherController* controller,
+                           const ash::ShelfItem* item,
+                           int64_t display_id);
+  ~CrostiniShelfContextMenu() override;
+
+ private:
+  void Init();
+
+  DISALLOW_COPY_AND_ASSIGN(CrostiniShelfContextMenu);
+};
+
+#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_SHELF_CONTEXT_MENU_H_
diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
index 4ae28dd..50063b6 100644
--- a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
@@ -11,9 +11,11 @@
 #include "base/metrics/user_metrics.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
+#include "chrome/browser/ui/app_list/crostini/crostini_util.h"
 #include "chrome/browser/ui/ash/launcher/arc_launcher_context_menu.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.h"
+#include "chrome/browser/ui/ash/launcher/crostini_shelf_context_menu.h"
 #include "chrome/browser/ui/ash/launcher/extension_launcher_context_menu.h"
 #include "chrome/browser/ui/ash/tablet_mode_client.h"
 #include "chrome/grit/generated_resources.h"
@@ -33,6 +35,12 @@
                                                     display_id);
   }
 
+  // Create an CrostiniShelfContextMenu if the item is Crostini app.
+  if (IsCrostiniAppId(item->id.app_id)) {
+    return std::make_unique<CrostiniShelfContextMenu>(controller, item,
+                                                      display_id);
+  }
+
   // Create an ExtensionLauncherContextMenu for other items.
   return std::make_unique<ExtensionLauncherContextMenu>(controller, item,
                                                         display_id);
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc
index eb3bac9..5ce30b85 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc
@@ -233,7 +233,7 @@
 
   // The BrowserListObserver would have been better to use then the old
   // notification system, but that observer fires before the window got created.
-  registrar_.Add(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+  registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
                  content::NotificationService::AllSources());
 
   // Add an app window observer & all already running apps.
@@ -484,7 +484,7 @@
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
-  DCHECK_EQ(chrome::NOTIFICATION_BROWSER_WINDOW_READY, type);
+  DCHECK_EQ(chrome::NOTIFICATION_BROWSER_OPENED, type);
   AddBrowserWindow(content::Source<Browser>(source).ptr());
 }
 
diff --git a/chrome/browser/ui/ash/system_tray_client_browsertest.cc b/chrome/browser/ui/ash/system_tray_client_browsertest.cc
index 38753ea7..a31f4b0 100644
--- a/chrome/browser/ui/ash/system_tray_client_browsertest.cc
+++ b/chrome/browser/ui/ash/system_tray_client_browsertest.cc
@@ -73,7 +73,10 @@
   EXPECT_FALSE(visible);
 
   // Simulate an upgrade. This sends a mojo message to ash.
-  UpgradeDetector::GetInstance()->NotifyUpgrade();
+  UpgradeDetector* detector = UpgradeDetector::GetInstance();
+  detector->set_upgrade_notification_stage(
+      UpgradeDetector::UPGRADE_ANNOYANCE_LOW);
+  detector->NotifyUpgrade();
   content::RunAllPendingInMessageLoop();
 
   // Tray icon is now visible.
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 74917041..1c360c0 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -467,12 +467,7 @@
   exclusive_access_manager_.reset(
       new ExclusiveAccessManager(window_->GetExclusiveAccessContext()));
 
-  // TODO(beng): Move BrowserList::AddBrowser() to the end of this function and
-  //             replace uses of this with BL's notifications.
   BrowserList::AddBrowser(this);
-  content::NotificationService::current()->Notify(
-      chrome::NOTIFICATION_BROWSER_WINDOW_READY, content::Source<Browser>(this),
-      content::NotificationService::NoDetails());
 }
 
 Browser::~Browser() {
diff --git a/chrome/browser/ui/browser_command_controller_browsertest.cc b/chrome/browser/ui/browser_command_controller_browsertest.cc
index 6ef9df5..0dcc972 100644
--- a/chrome/browser/ui/browser_command_controller_browsertest.cc
+++ b/chrome/browser/ui/browser_command_controller_browsertest.cc
@@ -91,7 +91,7 @@
   // does incomplete initialization that would lead to
   // SystemUrlRequestContextGetter being leaked.
   content::WindowedNotificationObserver browser_creation_observer(
-      chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+      chrome::NOTIFICATION_BROWSER_OPENED,
       content::NotificationService::AllSources());
   profiles::SwitchToGuestProfile(ProfileManager::CreateCallback());
 
diff --git a/chrome/browser/ui/cocoa/download/download_item_controller.h b/chrome/browser/ui/cocoa/download/download_item_controller.h
index b47b47ee..7b3f24f 100644
--- a/chrome/browser/ui/cocoa/download/download_item_controller.h
+++ b/chrome/browser/ui/cocoa/download/download_item_controller.h
@@ -10,6 +10,7 @@
 #include <memory>
 
 #include "base/time/time.h"
+#include "chrome/browser/download/download_commands.h"
 
 @class ChromeUILocalizer;
 @class DownloadItemCell;
@@ -157,6 +158,8 @@
 - (IBAction)saveDownload:(id)sender;
 - (IBAction)discardDownload:(id)sender;
 - (IBAction)showContextMenu:(id)sender;
+- (bool)submitDownloadToFeedbackService:(download::DownloadItem*)download
+                            withCommand:(DownloadCommands::Command)command;
 
 @end
 
diff --git a/chrome/browser/ui/cocoa/download/download_item_controller.mm b/chrome/browser/ui/cocoa/download/download_item_controller.mm
index 13dcea3..87894f5 100644
--- a/chrome/browser/ui/cocoa/download/download_item_controller.mm
+++ b/chrome/browser/ui/cocoa/download/download_item_controller.mm
@@ -10,9 +10,12 @@
 #include "base/strings/string_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/download/chrome_download_manager_delegate.h"
 #include "chrome/browser/download/download_item_model.h"
 #include "chrome/browser/download/download_shelf_context_menu.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/safe_browsing/download_protection/download_feedback_service.h"
 #import "chrome/browser/themes/theme_properties.h"
 #import "chrome/browser/themes/theme_service.h"
 #import "chrome/browser/ui/cocoa/download/download_item_button.h"
@@ -353,15 +356,23 @@
   // user did this to detect whether we're being clickjacked.
   UMA_HISTOGRAM_LONG_TIMES("clickjacking.save_download",
                            base::Time::Now() - creationTime_);
-  // This will change the state and notify us.
-  bridge_->download_model()->download()->ValidateDangerousDownload();
+
+  DownloadItem* download = bridge_->download_model()->download();
+  if (![self submitDownloadToFeedbackService:download
+                                 withCommand:DownloadCommands::Command::KEEP]) {
+    // This will change the state and notify us.
+    download->ValidateDangerousDownload();
+  }
 }
 
 - (IBAction)discardDownload:(id)sender {
   UMA_HISTOGRAM_LONG_TIMES("clickjacking.discard_download",
                            base::Time::Now() - creationTime_);
   DownloadItem* download = bridge_->download_model()->download();
-  download->Remove();
+  if (!
+      [self submitDownloadToFeedbackService:download
+                                withCommand:DownloadCommands::Command::DISCARD])
+    download->Remove();
   // WARNING: we are deleted at this point.  Don't access 'this'.
 }
 
@@ -371,4 +382,29 @@
   [static_cast<DownloadItemButton*>(progressView_) showContextMenu];
 }
 
+- (bool)submitDownloadToFeedbackService:(download::DownloadItem*)download
+                            withCommand:(DownloadCommands::Command)command {
+  safe_browsing::SafeBrowsingService* sb_service =
+      g_browser_process->safe_browsing_service();
+  if (!sb_service)
+    return false;
+
+  safe_browsing::DownloadProtectionService* download_protection_service =
+      sb_service->download_protection_service();
+  if (!download_protection_service)
+    return false;
+
+  DownloadItemModel* download_item_model = bridge_->download_model();
+  const Profile* profile = Profile::FromBrowserContext(
+      content::DownloadItemUtils::GetBrowserContext(download));
+  const PrefService* prefs = profile->GetPrefs();
+  if (!download_item_model->ShouldAllowDownloadFeedback() ||
+      profile->IsOffTheRecord() ||
+      !safe_browsing::IsExtendedReportingEnabled(*prefs))
+    return false;
+  download_protection_service->feedback_service()->BeginFeedbackForDownload(
+      download, command);
+  return true;
+}
+
 @end
diff --git a/chrome/browser/ui/cocoa/download/md_download_item_view.mm b/chrome/browser/ui/cocoa/download/md_download_item_view.mm
index 41db460..7c3a2add 100644
--- a/chrome/browser/ui/cocoa/download/md_download_item_view.mm
+++ b/chrome/browser/ui/cocoa/download/md_download_item_view.mm
@@ -172,7 +172,9 @@
     [self addSubview:iconView_];
 
     label_ = MakeLabel([NSFont systemFontOfSize:10], NSLineBreakByWordWrapping);
-    label_.frame = NSInsetRect(self.bounds, 0, kDangerousDownloadLabelYInset);
+    label_.frame =
+        [self cr_localizedRect:NSInsetRect(self.bounds, 0,
+                                           kDangerousDownloadLabelYInset)];
     label_.autoresizingMask =
         [NSView cr_localizedAutoresizingMask:NSViewMaxXMargin];
     [self addSubview:label_];
@@ -183,7 +185,7 @@
     discardButton_.title = l10n_util::GetNSString(IDS_DISCARD_DOWNLOAD);
     [discardButton_ sizeToFit];
     discardButton_.autoresizingMask =
-        [NSView cr_localizedAutoresizingMask:NSViewMinXMargin];
+        [NSView cr_localizedAutoresizingMask:NSViewMaxXMargin];
     [self addSubview:discardButton_];
 
     base::scoped_nsobject<HarmonyButton> saveButton(
@@ -191,7 +193,7 @@
     saveButton_ = saveButton;
     [saveButton_ sizeToFit];
     saveButton_.autoresizingMask =
-        [NSView cr_localizedAutoresizingMask:NSViewMinXMargin];
+        [NSView cr_localizedAutoresizingMask:NSViewMaxXMargin];
     saveButton_.hidden = YES;
     [self addSubview:saveButton_];
   }
@@ -215,12 +217,19 @@
   [GTMUILocalizerAndLayoutTweaker
       sizeToFitFixedHeightTextField:label_
                            minWidth:kDangerousDownloadLabelMinWidth];
-  NSRect labelRect = label_.frame;
+
+  // Flip the rect back to LTR if applicable.
+  NSRect labelRect = [self cr_localizedRect:label_.frame];
   labelRect.origin.x = kDangerousDownloadLabelX;
   labelRect.origin.y = NSMidY(self.bounds) - NSMidY(label_.bounds);
   label_.frame = [self cr_localizedRect:labelRect];
 
-  CGFloat maxX = NSMaxX(self.bounds);
+  NSRect discardButtonRect = [self cr_localizedRect:discardButton_.frame];
+  discardButtonRect.origin.x =
+      NSMaxX(labelRect) + kDangerousDownloadLabelButtonSpacing;
+  discardButtonRect.origin.y =
+      NSMidY(self.bounds) - NSMidY(discardButton_.bounds);
+  discardButton_.frame = [self cr_localizedRect:discardButtonRect];
 
   if (downloadModel->MightBeMalicious()) {
     saveButton_.hidden = YES;
@@ -229,18 +238,12 @@
     saveButton_.title =
         base::SysUTF16ToNSString(downloadModel->GetWarningConfirmButtonText());
     [saveButton_ sizeToFit];
-    NSRect saveButtonRect = saveButton_.frame;
-    saveButtonRect.origin.x = maxX - NSWidth(saveButtonRect);
+    NSRect saveButtonRect = [self cr_localizedRect:saveButton_.frame];
+    saveButtonRect.origin.x =
+        NSMaxX(discardButtonRect) + kDangerousDownloadLabelButtonSpacing;
     saveButtonRect.origin.y = NSMidY(self.bounds) - NSMidY(saveButton_.bounds);
     saveButton_.frame = [self cr_localizedRect:saveButtonRect];
-    maxX = NSMinX(saveButtonRect) - kDangerousDownloadLabelButtonSpacing;
   }
-
-  NSRect discardButtonRect = discardButton_.frame;
-  discardButtonRect.origin.x = maxX - NSWidth(discardButtonRect);
-  discardButtonRect.origin.y =
-      NSMidY(self.bounds) - NSMidY(discardButton_.bounds);
-  discardButton_.frame = [self cr_localizedRect:discardButtonRect];
 }
 
 // NSView overrides
@@ -521,8 +524,9 @@
       [self addSubview:dangerView_];
     }
     [dangerView_ setStateFromDownload:downloadModel];
-    [dangerView_ setFrameSize:NSMakeSize(dangerView_.preferredWidth,
-                                         NSHeight(dangerView_.frame))];
+    dangerView_.frame =
+        [self cr_localizedRect:NSMakeRect(0, 0, dangerView_.preferredWidth,
+                                          NSHeight(self.bounds))];
     return;
   } else if (dangerView_) {
     for (NSView* view in [self normalViews]) {
@@ -565,17 +569,11 @@
           setState:MDDownloadItemProgressIndicatorState::kComplete
           progress:1
           animations:^{
-            // Explicitly animate position.y so that x position isn't animated
-            // for a new download (which would happen with view.animator).
-            [filenameView_.layer
-                addAnimation:[CABasicAnimation
-                                 animationWithKeyPath:@"position.y"]
-                      forKey:nil];
-            [filenameView_
-                setFrameOrigin:NSMakePoint(NSMinX(filenameView_.frame),
-                                           statusString.length
-                                               ? kFilenameWithStatusY
-                                               : kFilenameY)];
+            NSRect filenameRect = [self cr_localizedRect:filenameView_.frame];
+            filenameRect.origin = NSMakePoint(
+                NSMinX(filenameView_.frame),
+                statusString.length ? kFilenameWithStatusY : kFilenameY);
+            filenameView_.animator.frame = [self cr_localizedRect:filenameRect];
             statusTextView_.animator.hidden = !statusString.length;
           }
           completion:^{
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model.cc b/chrome/browser/ui/content_settings/content_setting_image_model.cc
index f4f0dfc..55de5dc 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_image_model.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/blocked_content/framebust_block_tab_helper.h"
+#include "chrome/browser/ui/layout_constants.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -31,7 +32,9 @@
 #include "ui/base/material_design/material_design_controller.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/color_utils.h"
+#include "ui/gfx/favicon_size.h"
 #include "ui/gfx/paint_vector_icon.h"
+#include "ui/gfx/vector_icon_types.h"
 
 using content::WebContents;
 
@@ -329,7 +332,13 @@
   else if (content_settings->IsContentBlocked(type))
     badge_id = &kBlockedBadgeIcon;
 
-  set_icon(image_details->icon, *badge_id);
+  const gfx::VectorIcon* icon = &image_details->icon;
+  // Touch mode uses a different tab audio icon.
+  if (image_details->content_type == CONTENT_SETTINGS_TYPE_SOUND &&
+      ui::MaterialDesignController::IsTouchOptimizedUiEnabled()) {
+    icon = &kTabAudioRoundedIcon;
+  }
+  set_icon(*icon, *badge_id);
   set_explanatory_string_id(explanation_id);
   DCHECK(tooltip_id);
   set_tooltip(l10n_util::GetStringUTF16(tooltip_id));
@@ -677,8 +686,12 @@
 // Base class ------------------------------------------------------------------
 
 gfx::Image ContentSettingImageModel::GetIcon(SkColor icon_color) const {
-  return gfx::Image(
-      gfx::CreateVectorIconWithBadge(*icon_, 16, icon_color, *icon_badge_));
+  int icon_size = GetLayoutConstant(LOCATION_BAR_ICON_SIZE);
+#if defined(OS_MACOSX) && !BUILDFLAG(MAC_VIEWS_BROWSER)
+  icon_size = gfx::kFaviconSize;
+#endif
+  return gfx::Image(gfx::CreateVectorIconWithBadge(*icon_, icon_size,
+                                                   icon_color, *icon_badge_));
 }
 
 ContentSettingImageModel::ContentSettingImageModel(ImageType image_type)
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model.h b/chrome/browser/ui/content_settings/content_setting_image_model.h
index 98803b30..5ac0e90 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model.h
+++ b/chrome/browser/ui/content_settings/content_setting_image_model.h
@@ -92,6 +92,8 @@
       content::WebContents* web_contents);
 #endif
 
+  // Retrieve the icon that represents this content setting. Blocked content
+  // settings icons will have a blocked badge.
   gfx::Image GetIcon(SkColor icon_color) const;
 
   // Returns the resource ID of a string to show when the icon appears, or 0 if
diff --git a/chrome/browser/ui/global_error/global_error_browsertest.cc b/chrome/browser/ui/global_error/global_error_browsertest.cc
index 9827712..39a20e38 100644
--- a/chrome/browser/ui/global_error/global_error_browsertest.cc
+++ b/chrome/browser/ui/global_error/global_error_browsertest.cc
@@ -128,7 +128,7 @@
     // delay, but some tasks run on the IO thread, so post a task there to
     // ensure it was flushed.
     // The test also needs to invoke OnBlacklistUpdated() directly. Usually this
-    // happens via NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE but TestBlacklist
+    // happens via a callback from the SafeBrowsing DB, but TestBlacklist
     // replaced the SafeBrowsing DB with a fake one, so the notification source
     // is different.
     static_cast<extensions::Blacklist::Observer*>(extension_service)
diff --git a/chrome/browser/ui/layout_constants.cc b/chrome/browser/ui/layout_constants.cc
index 9ab1e4c..20c466da 100644
--- a/chrome/browser/ui/layout_constants.cc
+++ b/chrome/browser/ui/layout_constants.cc
@@ -43,17 +43,26 @@
         return 1;
       return hybrid ? 8 : 6;
     case LOCATION_BAR_ELEMENT_PADDING:
-      return hybrid ? 3 : 1;
-    case LOCATION_BAR_PADDING:
+      // Under touch, the padding gets moved from between the location bar
+      // elements to inside the location bar elements for larger touch targets
+      // (i.e. currently, LOCATION_BAR_ICON_INTERIOR_PADDING).
+      if (touch_optimized_material)
+        return 0;
       return hybrid ? 3 : 1;
     case LOCATION_BAR_HEIGHT: {
       constexpr int kHeights[] = {28, 32, 36, 28};
       return kHeights[mode];
     }
     case LOCATION_BAR_ICON_SIZE:
-      return 16;
+      return touch_optimized_material ? 20 : 16;
     case LOCATION_BAR_ICON_INTERIOR_PADDING:
-      return touch_optimized_material ? 8 : 4;
+      if (touch_optimized_material) {
+        // TODO(crbug.com/801583): This should actually be 8, but until
+        // LocationBarView is updated to remove its stroke, subtract the dips
+        // reserved for the stroke first.
+        return 7;
+      }
+      return 4;
     case TABSTRIP_NEW_TAB_BUTTON_SPACING: {
       // In non-touch optimized UI, we make the new tab button overlap with the
       // last tab in the tabstrip (i.e negative spacing). However, in
diff --git a/chrome/browser/ui/layout_constants.h b/chrome/browser/ui/layout_constants.h
index d7cae2c..feda2530 100644
--- a/chrome/browser/ui/layout_constants.h
+++ b/chrome/browser/ui/layout_constants.h
@@ -48,13 +48,10 @@
   // images inside.
   LOCATION_BAR_BUBBLE_ANCHOR_VERTICAL_INSET,
 
-  // The horizontal padding between location bar decorations.
+  // The horizontal padding between location bar decorations as well as the
+  // vertical and horizontal padding inside the border.
   LOCATION_BAR_ELEMENT_PADDING,
 
-  // The padding inside the location bar border (i.e. between the border and the
-  // location bar's children).
-  LOCATION_BAR_PADDING,
-
   // The height to be occupied by the LocationBar.
   LOCATION_BAR_HEIGHT,
 
diff --git a/chrome/browser/ui/startup/bad_flags_prompt.cc b/chrome/browser/ui/startup/bad_flags_prompt.cc
index a18b767..2fe29b7 100644
--- a/chrome/browser/ui/startup/bad_flags_prompt.cc
+++ b/chrome/browser/ui/startup/bad_flags_prompt.cc
@@ -119,25 +119,6 @@
 }  // namespace
 
 void ShowBadFlagsPrompt(content::WebContents* web_contents) {
-  // Flags only available in specific builds, for which to display a warning
-  // "the flag is not implemented in this build", if necessary.
-  struct {
-    const char* name;
-    bool is_invalid;
-  } conditional_flags[] = {
-      {switches::kEnableHeapProfiling,
-       base::trace_event::MemoryDumpManager::
-               GetHeapProfilingModeFromCommandLine() ==
-           base::trace_event::kHeapProfilingModeInvalid},
-  };
-  for (auto conditional_flag : conditional_flags) {
-    if (conditional_flag.is_invalid) {
-      ShowBadFlagsInfoBar(web_contents, IDS_UNIMPLEMENTED_FLAGS_WARNING_MESSAGE,
-                          conditional_flag.name);
-      return;
-    }
-  }
-
   for (const char* flag : kBadFlags) {
     if (base::CommandLine::ForCurrentProcess()->HasSwitch(flag)) {
       ShowBadFlagsInfoBar(web_contents, IDS_BAD_FLAGS_WARNING_MESSAGE, flag);
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index 077dd20..8a6f727 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -113,7 +113,7 @@
         activated_profile_(false) {
     registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
                    content::NotificationService::AllSources());
-    registrar_.Add(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+    registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
                    content::NotificationService::AllSources());
   }
   ~ProfileLaunchObserver() override {}
@@ -133,7 +133,7 @@
         MaybeActivateProfile();
         break;
       }
-      case chrome::NOTIFICATION_BROWSER_WINDOW_READY: {
+      case chrome::NOTIFICATION_BROWSER_OPENED: {
         Browser* browser = content::Source<Browser>(source).ptr();
         DCHECK(browser);
         opened_profiles_.insert(browser->profile());
@@ -153,7 +153,7 @@
     launched_profiles_.insert(profile);
     if (chrome::FindBrowserWithProfile(profile)) {
       // A browser may get opened before we get initialized (e.g., in tests),
-      // so we never see the NOTIFICATION_BROWSER_WINDOW_READY for it.
+      // so we never see the NOTIFICATION_BROWSER_OPENED for it.
       opened_profiles_.insert(profile);
     }
   }
@@ -189,7 +189,7 @@
         base::BindOnce(&ProfileLaunchObserver::ActivateProfile,
                        base::Unretained(this)));
     // Avoid posting more than once before ActivateProfile gets called.
-    registrar_.Remove(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+    registrar_.Remove(this, chrome::NOTIFICATION_BROWSER_OPENED,
                       content::NotificationService::AllSources());
     registrar_.Remove(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
                       content::NotificationService::AllSources());
diff --git a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
index 2a70941f..0edae5b68 100644
--- a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
@@ -113,6 +113,8 @@
   EXPECT_GT(itemCount, 10);
 
   UpgradeDetector* detector = UpgradeDetector::GetInstance();
+  detector->set_upgrade_notification_stage(
+      UpgradeDetector::UPGRADE_ANNOYANCE_LOW);
   detector->NotifyUpgrade();
   EXPECT_TRUE(detector->notify_upgrade());
   EXPECT_EQ(browser_defaults::kShowUpgradeMenuItem,
diff --git a/chrome/browser/ui/toolbar/chrome_toolbar_model_delegate.cc b/chrome/browser/ui/toolbar/chrome_toolbar_model_delegate.cc
index e7571f6..8ed49d98 100644
--- a/chrome/browser/ui/toolbar/chrome_toolbar_model_delegate.cc
+++ b/chrome/browser/ui/toolbar/chrome_toolbar_model_delegate.cc
@@ -27,6 +27,7 @@
 #if !defined(OS_ANDROID)
 #include "components/omnibox/browser/vector_icons.h" // nogncheck
 #include "components/toolbar/vector_icons.h"  // nogncheck
+#include "ui/base/material_design/material_design_controller.h"
 #endif  // !defined(OS_ANDROID)
 
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
@@ -131,11 +132,16 @@
   GURL url;
   GetURL(&url);
 
-  if (url.SchemeIs(content::kChromeUIScheme))
-    return &toolbar::kProductIcon;
+  const bool is_touch_ui =
+      ui::MaterialDesignController::IsTouchOptimizedUiEnabled();
+  if (url.SchemeIs(content::kChromeUIScheme)) {
+    return is_touch_ui ? &toolbar::kProduct20Icon : &toolbar::kProductIcon;
+  }
 
-  if (url.SchemeIs(extensions::kExtensionScheme))
-    return &omnibox::kExtensionAppIcon;
+  if (url.SchemeIs(extensions::kExtensionScheme)) {
+    return is_touch_ui ? &omnibox::kExtensionApp20Icon
+                       : &omnibox::kExtensionAppIcon;
+  }
 #endif
 
   return nullptr;
diff --git a/chrome/browser/ui/views/autofill/save_card_bubble_views.h b/chrome/browser/ui/views/autofill/save_card_bubble_views.h
index cc7d1be..c2b8bdce 100644
--- a/chrome/browser/ui/views/autofill/save_card_bubble_views.h
+++ b/chrome/browser/ui/views/autofill/save_card_bubble_views.h
@@ -85,6 +85,9 @@
   FRIEND_TEST_ALL_PREFIXES(
       SaveCardBubbleViewsFullFormBrowserTest,
       Upload_ClickingCloseClosesBubbleIfSecondaryUiMdExpOn);
+  FRIEND_TEST_ALL_PREFIXES(
+      SaveCardBubbleViewsFullFormBrowserTest,
+      Upload_DecliningUploadDoesNotLogUserAcceptedCardOriginUMA);
 
   // The current step of the save card flow.  Accounts for:
   //  1) Local save vs. Upload save
diff --git a/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest.cc b/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest.cc
index 0c2cdaa..a3a7765a 100644
--- a/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest.cc
@@ -907,4 +907,40 @@
   WaitForObservedEvent();
 }
 
+// Tests UMA logging for the upload save bubble. Ensures that if the user
+// declines upload, Autofill.UploadAcceptedCardOrigin is not logged.
+IN_PROC_BROWSER_TEST_F(
+    SaveCardBubbleViewsFullFormBrowserTest,
+    Upload_DecliningUploadDoesNotLogUserAcceptedCardOriginUMA) {
+  // Enable the SecondaryUiMd experiment (required for clicking the Close
+  // button).
+  scoped_feature_list_.InitAndEnableFeature(features::kSecondaryUiMd);
+
+  // Set up the Payments RPC.
+  SetUploadDetailsRpcPaymentsAccepts();
+
+  // Submitting the form should show the upload save bubble and legal footer.
+  // (Must wait for response from Payments before accessing the controller.)
+  base::HistogramTester histogram_tester;
+  ResetEventWaiterForSequence(
+      {DialogEvent::REQUESTED_UPLOAD_SAVE,
+       DialogEvent::RECEIVED_GET_UPLOAD_DETAILS_RESPONSE});
+  FillAndSubmitForm();
+  WaitForObservedEvent();
+  EXPECT_TRUE(
+      FindViewInBubbleById(DialogViewId::MAIN_CONTENT_VIEW_UPLOAD)->visible());
+  EXPECT_TRUE(FindViewInBubbleById(DialogViewId::FOOTNOTE_VIEW)->visible());
+
+  // Clicking the [X] close button should dismiss the bubble.
+  content::TestNavigationObserver nav_observer(GetActiveWebContents(), 1);
+  ClickOnDialogView(
+      GetSaveCardBubbleViews()->GetBubbleFrameView()->GetCloseButtonForTest());
+
+  // Ensure that UMA was logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.UploadOfferedCardOrigin",
+      AutofillMetrics::OFFERING_UPLOAD_OF_NEW_CARD, 1);
+  histogram_tester.ExpectTotalCount("Autofill.UploadAcceptedCardOrigin", 0);
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/save_card_icon_view.cc b/chrome/browser/ui/views/autofill/save_card_icon_view.cc
index 6476088..f559450 100644
--- a/chrome/browser/ui/views/autofill/save_card_icon_view.cc
+++ b/chrome/browser/ui/views/autofill/save_card_icon_view.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/views/location_bar/bubble_icon_view.h"
 #include "chrome/grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/material_design/material_design_controller.h"
 
 namespace autofill {
 
@@ -58,6 +59,8 @@
     BubbleIconView::ExecuteSource execute_source) {}
 
 const gfx::VectorIcon& SaveCardIconView::GetVectorIcon() const {
+  if (ui::MaterialDesignController::IsTouchOptimizedUiEnabled())
+    return kCreditCard20Icon;
   return kCreditCardIcon;
 }
 
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
index e34d404..6324be2 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -669,6 +669,15 @@
                          titlebar_rect.y(), frame_overlay_image.width() * scale,
                          frame_overlay_image.height() * scale, true);
   }
+
+  if (ShowCustomTitle()) {
+    const SkAlpha title_alpha =
+        ShouldPaintAsActive() ? SK_AlphaOPAQUE : kInactiveTitlebarFeatureAlpha;
+    const SkColor title_color = SkColorSetA(
+        color_utils::BlendTowardOppositeLuma(titlebar_color, SK_AlphaOPAQUE),
+        title_alpha);
+    window_title_->SetEnabledColor(title_color);
+  }
 }
 
 void GlassBrowserFrameView::PaintClientEdge(gfx::Canvas* canvas) const {
@@ -804,6 +813,7 @@
     const int max_text_width = std::max(0, MinimizeButtonX() - x);
     window_title_->SetBounds(x, window_icon_bounds.y(), max_text_width,
                              window_icon_bounds.height());
+    window_title_->SetAutoColorReadabilityEnabled(false);
   }
 }
 
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.h b/chrome/browser/ui/views/frame/glass_browser_frame_view.h
index a745037..8d4948f0 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.h
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.h
@@ -26,6 +26,10 @@
                               public TabIconViewModel,
                               public TabStripObserver {
  public:
+  // Alpha to use for features in the titlebar (the window title and caption
+  // buttons) when the window is inactive. They are opaque when active.
+  static constexpr SkAlpha kInactiveTitlebarFeatureAlpha = 0x65;
+
   // Constructs a non-client view for an BrowserFrame.
   GlassBrowserFrameView(BrowserFrame* frame, BrowserView* browser_view);
   ~GlassBrowserFrameView() override;
diff --git a/chrome/browser/ui/views/frame/hosted_app_button_container.cc b/chrome/browser/ui/views/frame/hosted_app_button_container.cc
index 7a977f3..7a6fe2f0 100644
--- a/chrome/browser/ui/views/frame/hosted_app_button_container.cc
+++ b/chrome/browser/ui/views/frame/hosted_app_button_container.cc
@@ -25,15 +25,13 @@
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/animation/ink_drop.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/widget/native_widget_aura.h"
 
 namespace {
 
-// Padding around content setting icons.
-constexpr int kContentSettingIconInteriorPadding = 4;
-
 constexpr int kMenuHighlightFadeDurationMs = 800;
 
 constexpr base::TimeDelta kContentSettingsFadeInDuration =
@@ -140,8 +138,10 @@
         std::move(model), this,
         views::NativeWidgetAura::GetWindowTitleFontList());
     image_view->SetIconColor(icon_color);
-    image_view->set_next_element_interior_padding(
-        kContentSettingIconInteriorPadding);
+    // Padding around content setting icons.
+    constexpr int kContentSettingIconInteriorPadding = 4;
+    image_view->SetBorder(views::CreateEmptyBorder(
+        gfx::Insets(kContentSettingIconInteriorPadding)));
     image_view->disable_animation();
     content_setting_views_.push_back(image_view.get());
     AddChildView(image_view.release());
diff --git a/chrome/browser/ui/views/frame/windows_10_caption_button.cc b/chrome/browser/ui/views/frame/windows_10_caption_button.cc
index b11f734e..4280842 100644
--- a/chrome/browser/ui/views/frame/windows_10_caption_button.cc
+++ b/chrome/browser/ui/views/frame/windows_10_caption_button.cc
@@ -131,7 +131,8 @@
   SkColor symbol_color = GetBaseColor();
   if (!frame_view_->ShouldPaintAsActive() && state() != STATE_HOVERED &&
       state() != STATE_PRESSED) {
-    symbol_color = SkColorSetA(symbol_color, 0x65);
+    symbol_color = SkColorSetA(
+        symbol_color, GlassBrowserFrameView::kInactiveTitlebarFeatureAlpha);
   } else if (button_type_ == VIEW_ID_CLOSE_BUTTON &&
              hover_animation().is_animating()) {
     symbol_color = gfx::Tween::ColorValueBetween(
diff --git a/chrome/browser/ui/views/location_bar/content_setting_image_view.cc b/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
index a6f71286..a85ed79a 100644
--- a/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
+++ b/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
@@ -133,6 +133,10 @@
          (slide_animator_.is_animating() || pause_animation_);
 }
 
+bool ContentSettingImageView::ShouldShowSeparator() const {
+  return false;
+}
+
 double ContentSettingImageView::WidthMultiplier() const {
   double state = pause_animation_ ? pause_animation_state_
                                   : slide_animator_.GetCurrentValue();
diff --git a/chrome/browser/ui/views/location_bar/content_setting_image_view.h b/chrome/browser/ui/views/location_bar/content_setting_image_view.h
index f2499adf..df88db22 100644
--- a/chrome/browser/ui/views/location_bar/content_setting_image_view.h
+++ b/chrome/browser/ui/views/location_bar/content_setting_image_view.h
@@ -75,6 +75,7 @@
   SkColor GetInkDropBaseColor() const override;
   SkColor GetTextColor() const override;
   bool ShouldShowLabel() const override;
+  bool ShouldShowSeparator() const override;
   double WidthMultiplier() const override;
   bool IsShrinking() const override;
   bool ShowBubble(const ui::Event& event) override;
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
index d399b0d..c06f103 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/ui/views/location_bar/background_with_1_px_border.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
 #include "ui/accessibility/ax_node_data.h"
+#include "ui/base/material_design/material_design_controller.h"
 #include "ui/compositor/layer_animator.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/gfx/canvas.h"
@@ -49,7 +50,8 @@
       ui::NativeTheme::kColorId_TextfieldDefaultColor);
   const SkColor separator_color = SkColorSetA(
       plain_text_color, color_utils::IsDark(plain_text_color) ? 0x59 : 0xCC);
-  float x = GetLocalBounds().right() - owner_->GetPostSeparatorPadding();
+  const float x = GetLocalBounds().right() - owner_->GetEndPadding() -
+                  1.0f / canvas->image_scale();
   canvas->Draw1pxLine(gfx::PointF(x, GetLocalBounds().y()),
                       gfx::PointF(x, GetLocalBounds().bottom()),
                       separator_color);
@@ -109,15 +111,12 @@
   // Disable separate hit testing for |image_|.  This prevents views treating
   // |image_| as a separate mouse hover region from |this|.
   image_->set_can_process_events_within_subtree(false);
-  image_->SetBorder(views::CreateEmptyBorder(
-      gfx::Insets(GetLayoutConstant(LOCATION_BAR_ICON_INTERIOR_PADDING))));
   AddChildView(image_);
 
   label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-
   AddChildView(label_);
 
-  separator_view_->SetVisible(ShouldShowLabel());
+  separator_view_->SetVisible(ShouldShowSeparator());
   AddChildView(separator_view_);
 
   AddChildView(ink_drop_container_);
@@ -128,7 +127,8 @@
   // the bubble should be smaller, so use an empty border to shrink down the
   // content bounds so the background gets painted correctly.
   SetBorder(views::CreateEmptyBorder(
-      gfx::Insets(GetLayoutConstant(LOCATION_BAR_BUBBLE_VERTICAL_PADDING), 0)));
+      gfx::Insets(GetLayoutConstant(LOCATION_BAR_BUBBLE_VERTICAL_PADDING),
+                  GetLayoutConstant(LOCATION_BAR_ICON_INTERIOR_PADDING))));
 
   set_notify_enter_exit_on_child(true);
 
@@ -148,7 +148,7 @@
 
 void IconLabelBubbleView::SetLabel(const base::string16& label) {
   label_->SetText(label);
-  separator_view_->SetVisible(ShouldShowLabel());
+  separator_view_->SetVisible(ShouldShowSeparator());
 }
 
 void IconLabelBubbleView::SetImage(const gfx::ImageSkia& image_skia) {
@@ -159,6 +159,10 @@
   return label_->visible() && !label_->text().empty();
 }
 
+bool IconLabelBubbleView::ShouldShowSeparator() const {
+  return ShouldShowLabel();
+}
+
 double IconLabelBubbleView::WidthMultiplier() const {
   return 1.0;
 }
@@ -185,7 +189,7 @@
   // padding. When the view is expanding (or showing-label steady state), the
   // image. When the view is contracting (or hidden-label steady state), whittle
   // away at the trailing padding instead.
-  int bubble_trailing_padding = GetPostSeparatorPadding();
+  int bubble_trailing_padding = GetEndPadding();
   int image_width = image_->GetPreferredSize().width();
   const int space_shortage = image_width + bubble_trailing_padding - width();
   if (space_shortage > 0) {
@@ -194,31 +198,29 @@
     else
       bubble_trailing_padding -= space_shortage;
   }
-  image_->SetBounds(0, 0, image_width, height());
+  image_->SetBounds(GetInsets().left(), 0, image_width, height());
 
-  // Compute the label bounds.  The label gets whatever size is left over after
-  // accounting for the preferred image width and padding amounts.  Note that if
+  // Compute the label bounds. The label gets whatever size is left over after
+  // accounting for the preferred image width and padding amounts. Note that if
   // the label has zero size it doesn't actually matter what we compute its X
   // value to be, since it won't be visible.
   const int label_x = image_->bounds().right() + GetInternalSpacing();
-  const int label_width =
-      std::max(0, width() - label_x - bubble_trailing_padding -
-                      kSpaceBesideSeparator - GetSeparatorLayoutWidth());
+  int label_width = std::max(0, width() - label_x - bubble_trailing_padding -
+                                    GetPrefixedSeparatorWidth());
   label_->SetBounds(label_x, 0, label_width, height());
 
-  const int kSeparatorHeight = 16;
+  // The separator should be the same height as the icons.
+  const int separator_height = GetLayoutConstant(LOCATION_BAR_ICON_SIZE);
   gfx::Rect separator_bounds(label_->bounds());
-  separator_bounds.Inset(0, (separator_bounds.height() - kSeparatorHeight) / 2);
+  separator_bounds.Inset(0, (separator_bounds.height() - separator_height) / 2);
 
-  float separator_width = kSpaceBesideSeparator + GetPostSeparatorPadding();
-  separator_view_->SetBounds(GetLocalBounds().right() - separator_width,
-                             separator_bounds.y(), separator_width,
-                             kSeparatorHeight);
+  float separator_width = GetPrefixedSeparatorWidth() + GetEndPadding();
+  separator_view_->SetBounds(label_->bounds().right(), separator_bounds.y(),
+                             separator_width, separator_height);
 
   gfx::Rect ink_drop_bounds = GetLocalBounds();
-  if (ShouldShowLabel()) {
-    ink_drop_bounds.set_width(ink_drop_bounds.width() -
-                              GetPostSeparatorPadding());
+  if (ShouldShowSeparator()) {
+    ink_drop_bounds.set_width(ink_drop_bounds.width() - GetEndPadding());
   }
 
   ink_drop_container_->SetBoundsRect(ink_drop_bounds);
@@ -343,25 +345,21 @@
 }
 
 gfx::Size IconLabelBubbleView::GetSizeForLabelWidth(int label_width) const {
-  gfx::Size size(image_->GetPreferredSize());
+  gfx::Size size(GetNonLabelSize());
   const bool shrinking = IsShrinking();
   // Animation continues for the last few pixels even after the label is not
   // visible in order to slide the icon into its final position. Therefore it
   // is necessary to animate |total_width| even when the background is hidden
   // as long as the animation is still shrinking.
   if (ShouldShowLabel() || shrinking) {
-    const int post_label_width =
-        (kSpaceBesideSeparator + GetSeparatorLayoutWidth() +
-         GetPostSeparatorPadding());
-
     // |multiplier| grows from zero to one, stays equal to one and then shrinks
     // to zero again. The view width should correspondingly grow from zero to
     // fully showing both label and icon, stay there, then shrink to just large
     // enough to show the icon. We don't want to shrink all the way back to
     // zero, since this would mean the view would completely disappear and then
     // pop back to an icon after the animation finishes.
-    const int max_width =
-        size.width() + GetInternalSpacing() + label_width + post_label_width;
+    const int max_width = size.width() + GetInternalSpacing() + label_width +
+                          GetPrefixedSeparatorWidth();
     const int current_width = WidthMultiplier() * max_width;
     size.set_width(shrinking ? std::max(current_width, size.width())
                              : current_width);
@@ -370,25 +368,24 @@
 }
 
 gfx::Size IconLabelBubbleView::GetMaxSizeForLabelWidth(int label_width) const {
-  gfx::Size size(image_->GetPreferredSize());
+  gfx::Size size(GetNonLabelSize());
   if (ShouldShowLabel() || IsShrinking()) {
-    // On scale factors < 2, we reserve 1 DIP for the 1 px separator.  For
-    // higher scale factors, we simply take the separator px out of the
-    // kSpaceBesideSeparator region before the separator, as that results in a
-    // width closer to the desired gap than if we added a whole DIP for the
-    // separator px.  (For scale 2, the two methods have equal error: 1 px.)
-    const int separator_width = (GetScaleFactor() >= 2) ? 0 : 1;
-    const int post_label_width =
-        (kSpaceBesideSeparator + separator_width + GetPostSeparatorPadding());
-    size.Enlarge(GetInternalSpacing() + label_width + post_label_width, 0);
+    size.Enlarge(
+        GetInternalSpacing() + label_width + GetPrefixedSeparatorWidth(), 0);
   }
   return size;
 }
 
 int IconLabelBubbleView::GetInternalSpacing() const {
-  return image_->GetPreferredSize().IsEmpty()
-             ? 0
-             : GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING);
+  if (image_->GetPreferredSize().IsEmpty())
+    return 0;
+
+  // In touch, the icon-to-label spacing is a custom value.
+  constexpr int kIconLabelSpacingTouch = 4;
+  return ui::MaterialDesignController::IsTouchOptimizedUiEnabled()
+             ? kIconLabelSpacingTouch
+             : GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING) +
+                   GetLayoutConstant(LOCATION_BAR_ICON_INTERIOR_PADDING);
 }
 
 int IconLabelBubbleView::GetSeparatorLayoutWidth() const {
@@ -396,15 +393,26 @@
   // higher scale factors, we simply take the separator px out of the
   // kSpaceBesideSeparator region before the separator, as that results in a
   // width closer to the desired gap than if we added a whole DIP for the
-  // separator px.  (For scale 2, the two methods have equal error: 1 px.)
+  // separator px. (For scale 2, the two methods have equal error: 1 px.)
   return (GetScaleFactor() >= 2) ? 0 : 1;
 }
 
-int IconLabelBubbleView::GetPostSeparatorPadding() const {
-  // The location bar will add LOCATION_BAR_ELEMENT_PADDING after us.
-  return kSpaceBesideSeparator -
-         GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING) -
-         next_element_interior_padding_;
+int IconLabelBubbleView::GetPrefixedSeparatorWidth() const {
+  return ShouldShowSeparator()
+             ? kSpaceBesideSeparator + GetSeparatorLayoutWidth()
+             : 0;
+}
+
+int IconLabelBubbleView::GetEndPadding() const {
+  if (ShouldShowSeparator())
+    return kSpaceBesideSeparator;
+  return GetInsets().right();
+}
+
+gfx::Size IconLabelBubbleView::GetNonLabelSize() const {
+  gfx::Size size(image_->GetPreferredSize());
+  size.Enlarge(GetInsets().left() + GetEndPadding(), GetInsets().height());
+  return size;
 }
 
 float IconLabelBubbleView::GetScaleFactor() const {
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
index e658c78..be40a87 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
@@ -102,6 +102,9 @@
   // Returns true when the label should be visible.
   virtual bool ShouldShowLabel() const;
 
+  // Returns true when the separator should be visible.
+  virtual bool ShouldShowSeparator() const;
+
   // Returns a multiplier used to calculate the actual width of the view based
   // on its desired width.  This ranges from 0 for a zero-width view to 1 for a
   // full-width view and can be used to animate the width of the view.
@@ -164,8 +167,15 @@
   // Returns the amount of space reserved for the separator in DIP.
   int GetSeparatorLayoutWidth() const;
 
+  // Retrieves the width taken the separator including padding before the
+  // separator stroke, taking into account whether it is shown or not.
+  int GetPrefixedSeparatorWidth() const;
+
   // Padding after the separator.
-  int GetPostSeparatorPadding() const;
+  int GetEndPadding() const;
+
+  // Gets the minimum size to use when the label is not shown.
+  gfx::Size GetNonLabelSize() const;
 
   float GetScaleFactor() const;
 
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc
index a3c1b741..4fc18882 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc
@@ -194,7 +194,8 @@
     minimum_size_reached_ = false;
     previous_width_ = 0;
     initial_image_x_ = GetImageBounds().x();
-    EXPECT_EQ(0, initial_image_x_);
+    EXPECT_EQ(GetLayoutConstant(LOCATION_BAR_ICON_INTERIOR_PADDING),
+              initial_image_x_);
   }
 
   void VerifyAnimationStep() {
diff --git a/chrome/browser/ui/views/location_bar/keyword_hint_view.cc b/chrome/browser/ui/views/location_bar/keyword_hint_view.cc
index 4d887f29..0984a6a 100644
--- a/chrome/browser/ui/views/location_bar/keyword_hint_view.cc
+++ b/chrome/browser/ui/views/location_bar/keyword_hint_view.cc
@@ -129,23 +129,41 @@
   NotifyAccessibilityEvent(ax::mojom::Event::kLiveRegionChanged, true);
 }
 
-gfx::Size KeywordHintView::CalculatePreferredSize() const {
-  // Height will be ignored by the LocationBarView.
-  return gfx::Size(leading_label_->GetPreferredSize().width() +
-                       chip_container_->GetPreferredSize().width() +
-                       trailing_label_->GetPreferredSize().width() +
-                       GetLayoutConstant(LOCATION_BAR_ICON_INTERIOR_PADDING),
-                   0);
+gfx::Insets KeywordHintView::GetInsets() const {
+  if (!BackgroundWith1PxBorder::IsRounded())
+    return gfx::Insets();
+
+  // The location bar and keyword hint view chip have rounded ends. Ensure the
+  // chip label's corner with the furthest extent from its midpoint is still at
+  // least kMinDistanceFromBorder DIPs away from the location bar rounded end.
+  constexpr float kMinDistanceFromBorder = 1;
+  const int radius = GetLayoutConstant(LOCATION_BAR_HEIGHT) / 2;
+  const int hypotenuse = radius - kMinDistanceFromBorder;
+  const float chip_midpoint = chip_container_->height() / 2.f;
+  const float extent = std::max(chip_midpoint - chip_label_->y(),
+                                chip_label_->bounds().bottom() - chip_midpoint);
+  DCHECK_GE(hypotenuse, extent)
+      << "LOCATION_BAR_HEIGHT must be tall enough to contain the chip.";
+  const float subsumed_width =
+      std::sqrt(hypotenuse * hypotenuse - extent * extent);
+  const int end_margin = gfx::ToCeiledInt(radius - subsumed_width);
+  return gfx::Insets(0, 0, 0, end_margin);
 }
 
 gfx::Size KeywordHintView::GetMinimumSize() const {
   // Height will be ignored by the LocationBarView.
-  return chip_container_->GetPreferredSize();
+  gfx::Size chip_size = chip_container_->GetPreferredSize();
+  chip_size.Enlarge(GetInsets().width(), GetInsets().height());
+  return chip_size;
+}
+
+const char* KeywordHintView::GetClassName() const {
+  return "KeywordHintView";
 }
 
 void KeywordHintView::Layout() {
   int chip_width = chip_container_->GetPreferredSize().width();
-  bool show_labels = (width() != chip_width);
+  bool show_labels = width() - GetInsets().width() > chip_width;
   gfx::Size leading_size(leading_label_->GetPreferredSize());
   leading_label_->SetBounds(0, 0, show_labels ? leading_size.width() : 0,
                             height());
@@ -156,8 +174,13 @@
                              show_labels ? trailing_size.width() : 0, height());
 }
 
-const char* KeywordHintView::GetClassName() const {
-  return "KeywordHintView";
+gfx::Size KeywordHintView::CalculatePreferredSize() const {
+  // Height will be ignored by the LocationBarView.
+  return gfx::Size(leading_label_->GetPreferredSize().width() +
+                       chip_container_->GetPreferredSize().width() +
+                       trailing_label_->GetPreferredSize().width() +
+                       GetLayoutConstant(LOCATION_BAR_ICON_INTERIOR_PADDING),
+                   0);
 }
 
 views::Label* KeywordHintView::CreateLabel(const gfx::FontList& font_list,
diff --git a/chrome/browser/ui/views/location_bar/keyword_hint_view.h b/chrome/browser/ui/views/location_bar/keyword_hint_view.h
index f1a95d07..1d162f3 100644
--- a/chrome/browser/ui/views/location_bar/keyword_hint_view.h
+++ b/chrome/browser/ui/views/location_bar/keyword_hint_view.h
@@ -43,14 +43,15 @@
 
   void SetKeyword(const base::string16& keyword);
 
- private:
   // views::View:
-  gfx::Size CalculatePreferredSize() const override;
+  gfx::Insets GetInsets() const override;
   // The minimum size is just big enough to show the tab.
   gfx::Size GetMinimumSize() const override;
-  void Layout() override;
   const char* GetClassName() const override;
+  void Layout() override;
+  gfx::Size CalculatePreferredSize() const override;
 
+ private:
   views::Label* CreateLabel(const gfx::FontList& font_list,
                             SkColor text_color,
                             SkColor background_color);
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index 8ff51521..a41b43846 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -88,11 +88,11 @@
 #include "ui/gfx/animation/slide_animation.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/image/image.h"
-#include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/gfx/text_utils.h"
+#include "ui/gfx/vector_icon_types.h"
 #include "ui/views/background.h"
 #include "ui/views/border.h"
 #include "ui/views/button_drag_utils.h"
@@ -110,7 +110,7 @@
 #endif
 
 #if defined(OS_WIN)
-#include "ui/base/win/osk_display_manager.h"
+#include "ui/base/ime/win/osk_display_manager.h"
 #endif
 
 using content::WebContents;
@@ -230,8 +230,6 @@
     ContentSettingImageView* image_view =
         new ContentSettingImageView(std::move(model), this, font_list);
     content_setting_views_.push_back(image_view);
-    image_view->set_next_element_interior_padding(
-        GetLayoutConstant(LOCATION_BAR_ICON_INTERIOR_PADDING));
     image_view->SetVisible(false);
     AddChildView(image_view);
   }
@@ -422,7 +420,7 @@
         location_icon_view_->GetMinimumSizeForLabelText(GetLocationIconText())
             .width();
   } else {
-    leading_width += GetLayoutConstant(LOCATION_BAR_PADDING) +
+    leading_width += GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING) +
                      location_icon_view_->GetMinimumSize().width();
   }
 
@@ -445,7 +443,7 @@
   }
 
   min_size.set_width(leading_width + omnibox_view_->GetMinimumSize().width() +
-                     2 * GetLayoutConstant(LOCATION_BAR_PADDING) -
+                     2 * GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING) -
                      omnibox_view_->GetInsets().width() + trailing_width);
   return min_size;
 }
@@ -547,17 +545,8 @@
 
   const int edge_thickness = GetHorizontalEdgeThickness();
 
-  // Add some padding to prevent text or Views displayed at the end of
-  // LocationBarView going too close to the ending border. This is also a
-  // compromise to avoid having to clip the corners of |omnibox_view_| when
-  // LocationBarView is a pill-shape.
-  constexpr int kRoundedPaddingHeightFactor = 3;
-  const int end_padding = BackgroundWith1PxBorder::IsRounded()
-                              ? location_height / kRoundedPaddingHeightFactor
-                              : 0;
-
   // Perform layout.
-  int full_width = width() - (2 * edge_thickness) - end_padding;
+  int full_width = width() - (2 * edge_thickness);
 
   int entry_width = full_width;
   leading_decorations.LayoutPass1(&entry_width);
@@ -840,6 +829,8 @@
       InTouchableMode() ? omnibox::kTouchableClearIcon : kTabCloseNormalIcon;
   SetImageFromVectorIcon(clear_all_button_, icon,
                          GetColor(OmniboxPart::LOCATION_BAR_CLEAR_ALL));
+  clear_all_button_->SetBorder(views::CreateEmptyBorder(
+      gfx::Insets(GetLayoutConstant(LOCATION_BAR_ICON_INTERIOR_PADDING))));
 }
 
 base::string16 LocationBarView::GetLocationIconText() const {
@@ -1120,5 +1111,5 @@
 // static
 int LocationBarView::GetTotalVerticalPadding() {
   return BackgroundWith1PxBorder::kLocationBarBorderThicknessDip +
-         GetLayoutConstant(LOCATION_BAR_PADDING);
+         GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING);
 }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
index be5d7bd..c16854c 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -375,7 +375,7 @@
 
 int OmniboxResultView::GetAnswerHeight() const {
   const int horizontal_padding =
-      GetLayoutConstant(LOCATION_BAR_PADDING) +
+      GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING) +
       GetLayoutConstant(LOCATION_BAR_ICON_INTERIOR_PADDING);
   const gfx::Image icon = GetIcon();
   int icon_width = icon.Width();
@@ -431,7 +431,7 @@
 void OmniboxResultView::Layout() {
   views::View::Layout();
   const int horizontal_padding =
-      GetLayoutConstant(LOCATION_BAR_PADDING) +
+      GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING) +
       GetLayoutConstant(LOCATION_BAR_ICON_INTERIOR_PADDING);
   const int start_x = GetIconAlignmentOffset() + horizontal_padding;
   int end_x = width();
@@ -498,8 +498,23 @@
 
   // NOTE: While animating the keyword match, both matches may be visible.
   int x = start_x;
-  x += icon.Width() + horizontal_padding;
   int y = GetVerticalMargin();
+
+  if (base::FeatureList::IsEnabled(omnibox::kOmniboxRichEntitySuggestions) &&
+      match_.answer) {
+    icon_view_->SetVisible(false);
+    int image_edge_length = text_height + description_view_->GetLineHeight();
+    image_view_->SetImageSize(gfx::Size(image_edge_length, image_edge_length));
+    image_view_->SetBounds(x, y, image_edge_length, image_edge_length);
+    x += image_edge_length + horizontal_padding;
+    content_view_->SetBounds(x, y, end_x - x, text_height);
+    y += text_height;
+    description_view_->SetBounds(x, y, end_x - x, text_height);
+    return;
+  }
+
+  icon_view_->SetVisible(true);
+  x += icon.Width() + horizontal_padding;
   if (match_.answer) {
     content_view_->SetBounds(x, y, end_x - x, text_height);
     y += text_height;
diff --git a/chrome/browser/ui/views/omnibox/omnibox_tab_switch_button.cc b/chrome/browser/ui/views/omnibox/omnibox_tab_switch_button.cc
index d3dbeee..c7b2408 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_tab_switch_button.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_tab_switch_button.cc
@@ -31,7 +31,7 @@
   gfx::Size size = MdTextButton::CalculatePreferredSize();
   size.set_height(text_height_ + kVerticalPadding);
   const int horizontal_padding =
-      GetLayoutConstant(LOCATION_BAR_PADDING) +
+      GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING) +
       GetLayoutConstant(LOCATION_BAR_ICON_INTERIOR_PADDING);
   size.set_width(size.width() + horizontal_padding);
   return size;
diff --git a/chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.cc b/chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.cc
index a4845ad1..162515f40 100644
--- a/chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.cc
+++ b/chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.cc
@@ -10,7 +10,6 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/location_bar/background_with_1_px_border.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
-#include "ui/base/material_design/material_design_controller.h"
 #include "ui/compositor/layer.h"
 #include "ui/views/painter.h"
 
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc
index 09a54e3..c35a1bac 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc
@@ -155,7 +155,7 @@
     }
     if (name == kGuest || name == kDiceGuest) {
       content::WindowedNotificationObserver browser_creation_observer(
-          chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+          chrome::NOTIFICATION_BROWSER_OPENED,
           content::NotificationService::AllSources());
       profiles::SwitchToGuestProfile(ProfileManager::CreateCallback());
       browser_creation_observer.Wait();
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.cc
index e246531..2ce07b5 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/time/default_tick_clock.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/ui/browser.h"
@@ -87,27 +88,30 @@
   return nullptr;
 }
 
-// Returns the deadline for a required relaunch (three minutes past either now
-// or when |uprade_detector| reaches the "high" annoyance level).
-base::TimeTicks GetDeadline(UpgradeDetector* upgrade_detector,
-                            base::TimeTicks now) {
-  // The amount of the final countdown given to the user before the browser is
-  // summarily relaunched.
-  static constexpr base::TimeDelta kRelaunchGracePeriod =
-      base::TimeDelta::FromMinutes(3);
-
-  return std::max(upgrade_detector->GetHighAnnoyanceDeadline(), now) +
-         kRelaunchGracePeriod;
-}
-
 }  // namespace
 
 RelaunchNotificationController::RelaunchNotificationController(
     UpgradeDetector* upgrade_detector)
+    : RelaunchNotificationController(upgrade_detector,
+                                     base::DefaultTickClock::GetInstance()) {}
+
+RelaunchNotificationController::~RelaunchNotificationController() {
+  if (last_notification_style_ != NotificationStyle::kNone)
+    StopObservingUpgrades();
+}
+
+// static
+constexpr base::TimeDelta RelaunchNotificationController::kRelaunchGracePeriod;
+
+RelaunchNotificationController::RelaunchNotificationController(
+    UpgradeDetector* upgrade_detector,
+    base::TickClock* tick_clock)
     : upgrade_detector_(upgrade_detector),
+      tick_clock_(tick_clock),
       last_notification_style_(NotificationStyle::kNone),
       last_level_(UpgradeDetector::UPGRADE_ANNOYANCE_NONE),
-      widget_(nullptr) {
+      widget_(nullptr),
+      timer_(tick_clock_) {
   PrefService* local_state = g_browser_process->local_state();
   if (local_state) {
     pref_change_registrar_.Init(local_state);
@@ -121,22 +125,20 @@
   }
 }
 
-RelaunchNotificationController::~RelaunchNotificationController() {
-  if (last_notification_style_ != NotificationStyle::kNone)
-    StopObservingUpgrades();
-}
-
 void RelaunchNotificationController::OnUpgradeRecommended() {
   DCHECK_NE(last_notification_style_, NotificationStyle::kNone);
   UpgradeDetector::UpgradeNotificationAnnoyanceLevel current_level =
       upgrade_detector_->upgrade_notification_stage();
+  const base::TimeTicks current_high_deadline =
+      upgrade_detector_->GetHighAnnoyanceDeadline();
 
-  // Nothing to do if there has been no change in the level. If appropriate, a
-  // notification for this level has already been shown.
-  if (current_level == last_level_)
+  // Nothing to do if there has been no change in the level and deadline. If
+  // appropriate, a notification for this level has already been shown.
+  if (current_level == last_level_ &&
+      current_high_deadline == last_high_deadline_) {
     return;
+  }
 
-  // Handle the new level.
   switch (current_level) {
     case UpgradeDetector::UPGRADE_ANNOYANCE_NONE:
       // While it's unexpected that the level could move back down to none, it's
@@ -146,7 +148,7 @@
     case UpgradeDetector::UPGRADE_ANNOYANCE_LOW:
     case UpgradeDetector::UPGRADE_ANNOYANCE_ELEVATED:
     case UpgradeDetector::UPGRADE_ANNOYANCE_HIGH:
-      ShowRelaunchNotification(current_level);
+      ShowRelaunchNotification(current_level, current_high_deadline);
       break;
     case UpgradeDetector::UPGRADE_ANNOYANCE_CRITICAL:
       // Critical notifications are handled by ToolbarView.
@@ -160,6 +162,7 @@
   }
 
   last_level_ = current_level;
+  last_high_deadline_ = current_high_deadline;
 }
 
 void RelaunchNotificationController::OnWidgetClosing(views::Widget* widget) {
@@ -173,7 +176,7 @@
   DCHECK_NE(last_notification_style_, NotificationStyle::kNone);
   return base_name.as_string().append(last_notification_style_ ==
                                               NotificationStyle::kRecommended
-                                          ? ".Recommneded"
+                                          ? ".Recommended"
                                           : ".Required");
 }
 
@@ -231,7 +234,8 @@
 }
 
 void RelaunchNotificationController::ShowRelaunchNotification(
-    UpgradeDetector::UpgradeNotificationAnnoyanceLevel level) {
+    UpgradeDetector::UpgradeNotificationAnnoyanceLevel level,
+    base::TimeTicks high_deadline) {
   DCHECK_NE(last_notification_style_, NotificationStyle::kNone);
 
   if (last_notification_style_ == NotificationStyle::kRecommended) {
@@ -245,15 +249,11 @@
       timer_.Stop();
     }
 
-    ShowRelaunchRecommendedBubble();
+    // Show the dialog if there has been a level change.
+    if (level != last_level_)
+      ShowRelaunchRecommendedBubble();
   } else {
-    // Start the timer to force a relaunch when the deadline is reached.
-    if (!timer_.IsRunning()) {
-      const base::TimeTicks now = base::TimeTicks::Now();
-      timer_.Start(FROM_HERE, GetDeadline(upgrade_detector_, now) - now, this,
-                   &RelaunchNotificationController::OnRelaunchDeadlineExpired);
-    }
-    ShowRelaunchRequiredDialog();
+    HandleRelaunchRequiredState(level, high_deadline);
   }
 }
 
@@ -273,13 +273,70 @@
   CloseWidget();
 }
 
+void RelaunchNotificationController::HandleRelaunchRequiredState(
+    UpgradeDetector::UpgradeNotificationAnnoyanceLevel level,
+    base::TimeTicks high_deadline) {
+  DCHECK_EQ(last_notification_style_, NotificationStyle::kRequired);
+
+  // Make no changes if the new deadline is not in the future and the browser is
+  // within the grace period of the previous deadline. The user has already been
+  // given the three-minute countdown so just let it go.
+  const base::TimeTicks now = tick_clock_->NowTicks();
+  if (timer_.IsRunning()) {
+    const base::TimeTicks& desired_run_time = timer_.desired_run_time();
+    DCHECK(!desired_run_time.is_null());
+    if (high_deadline <= now && desired_run_time - now <= kRelaunchGracePeriod)
+      return;
+  }
+
+  // Compute the new deadline (minimally three minutes into the future).
+  const base::TimeTicks deadline =
+      std::max(high_deadline, now) + kRelaunchGracePeriod;
+
+  // (re)Start the timer to perform the relaunch when the deadline is reached.
+  timer_.Start(FROM_HERE, deadline - now, this,
+               &RelaunchNotificationController::OnRelaunchDeadlineExpired);
+
+  if (widget_) {
+    // Tell the dialog to update its title if it is showing.
+    RelaunchRequiredDialogView::FromWidget(widget_)->SetDeadline(deadline);
+  } else {
+    // Otherwise, show the dialog if there has been a level change or if the
+    // deadline is in the past.
+    if (level != last_level_ || high_deadline <= now)
+      ShowRelaunchRequiredDialog();
+  }
+}
+
 void RelaunchNotificationController::StartReshowTimer() {
   DCHECK_EQ(last_notification_style_, NotificationStyle::kRecommended);
-  if (!timer_.IsRunning()) {
-    timer_.Start(
-        FROM_HERE, upgrade_detector_->GetHighAnnoyanceLevelDelta(), this,
-        &RelaunchNotificationController::OnReshowRelaunchRecommendedBubble);
+  base::TimeDelta delay = upgrade_detector_->GetHighAnnoyanceLevelDelta();
+  if (timer_.IsRunning()) {
+    // Leave well enough alone if the timer is already running with the proper
+    // frequency. This should not happen given the early-exit in
+    // OnUpgradeRecommended when there is no change in the annoyance level or
+    // high annoyance deadline.
+    if (timer_.GetCurrentDelay() == delay)
+      return;
+
+    // Compute the new delay to have the previously-scheduled reshow appear at
+    // the right time.
+    DCHECK(!timer_.desired_run_time().is_null());
+    const base::TimeTicks start_time =
+        timer_.desired_run_time() - timer_.GetCurrentDelay();
+    const base::TimeTicks new_run_time = start_time + delay;
+    const base::TimeTicks now = tick_clock_->NowTicks();
+    if (new_run_time <= now) {
+      // The new delay puts the next reshow in the past. Handle it now.
+      timer_.Stop();
+      OnReshowRelaunchRecommendedBubble();
+      return;
+    }
+    delay = new_run_time - now;
   }
+  timer_.Start(
+      FROM_HERE, delay, this,
+      &RelaunchNotificationController::OnReshowRelaunchRecommendedBubble);
 }
 
 void RelaunchNotificationController::OnReshowRelaunchRecommendedBubble() {
@@ -310,6 +367,9 @@
 }
 
 void RelaunchNotificationController::ShowRelaunchRequiredDialog() {
+  DCHECK(timer_.IsRunning());
+  DCHECK(!timer_.desired_run_time().is_null());
+
   // Nothing to do if the dialog is visible.
   if (widget_)
     return;
@@ -323,7 +383,7 @@
     return;
 
   widget_ = RelaunchRequiredDialogView::Show(
-      browser, GetDeadline(upgrade_detector_, base::TimeTicks::Now()),
+      browser, timer_.desired_run_time(),
       base::BindRepeating(&chrome::AttemptRestart));
 
   // Monitor the widget so that |widget_| can be cleared on close.
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.h b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.h
index 7b9a064..603052c 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.h
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.h
@@ -9,23 +9,37 @@
 
 #include "base/macros.h"
 #include "base/strings/string_piece.h"
+#include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/upgrade_detector.h"
 #include "chrome/browser/upgrade_observer.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "ui/views/widget/widget_observer.h"
 
+namespace base {
+class TickClock;
+}
 namespace views {
 class Widget;
 }
 
-// A class that observes changes to the browser.relaunch_notification preference
-// (which is backed by the RelaunchNotification policy setting) and annoyance
-// levels from the UpgradeDetector. An appropriate notification is shown to the
-// user based on the policy setting and current upgrade annoyance level.
-// Notifications are shown at low, elevated, and high annoyance levels (as
-// defined by the UpgradeDetector). In cases where an administrator recommends a
-// relaunch, a bubble for such is repeatedly shown until the user relaunches.
+// A class that observes changes to the browser.relaunch_notification
+// preference (which is backed by the RelaunchNotification policy
+// setting) and upgrade notifications from the UpgradeDetector. The two
+// values for the RelaunchNotification policy setting are handled as follows:
+//
+// - Recommended (1): The controller displays the relaunch recommended bubble on
+//   each change to the UpgradeDetector's upgrade_notification_stage (an
+//   "annoyance level" of low, elevated, or high). Once the high annoyance level
+//   is reached, the controller continually reshows a the bubble on a timer with
+//   a period equal to the time delta between the "elevated" and "high"
+//   showings.
+//
+// - Required (2): The controller displays the relaunch required dialog on each
+//   change to the UpgradeDetector's upgrade_notification_stage (described
+//   above). The browser is relaunched three minutes after the third and final
+//   showing of the dialog (which takes place when the UpgradeDetector reaches
+//   the high annoyance level).
 class RelaunchNotificationController : public UpgradeObserver,
                                        public views::WidgetObserver {
  public:
@@ -35,6 +49,14 @@
   ~RelaunchNotificationController() override;
 
  protected:
+  // The length of the final countdown given to the user before the browser is
+  // summarily relaunched.
+  static constexpr base::TimeDelta kRelaunchGracePeriod =
+      base::TimeDelta::FromMinutes(3);
+
+  RelaunchNotificationController(UpgradeDetector* upgrade_detector,
+                                 base::TickClock* tick_clock);
+
   // UpgradeObserver:
   void OnUpgradeRecommended() override;
 
@@ -60,29 +82,39 @@
   void StartObservingUpgrades();
   void StopObservingUpgrades();
 
-  // Shows the proper notification based on the preference setting. Invoked as a
-  // result of a detected change in the UpgradeDetector's annoyance level to
-  // |level|. In the case where a relaunch is recommended and |level| indicates
-  // that the highest annoyance level has been reached, starts a timer to
-  // repeatedly show the relaunch recommended notification.
+  // Shows the proper notification based on the preference setting and starts
+  // the timer to either reshow the bubble or relaunch the browser as
+  // appropriate. |level| is the current annoyance level reported by the
+  // UpgradeDetector, and |high_deadline| is the time at which the
+  // UpgradeDetector will reach the high annoyance level; see the class comment
+  // for further details.
   void ShowRelaunchNotification(
-      UpgradeDetector::UpgradeNotificationAnnoyanceLevel level);
+      UpgradeDetector::UpgradeNotificationAnnoyanceLevel level,
+      base::TimeTicks high_deadline);
 
   // Closes any previously-shown notifications. This is safe to call if no
   // notifications have been shown. Notifications may be closed by other means
   // (e.g., by the user), so there is no expectation that a previously-shown
-  // notification is still open when this is invoked. In the case where a
-  // relaunch is recommended, the timer to repeatedly show the relaunch
-  // recommended notification is also stopped.
+  // notification is still open when this is invoked. The timer to either
+  // repeatedly show the relaunch recommended notification or to force a
+  // relaunch once the deadline is reached is also stopped.
   void CloseRelaunchNotification();
 
-  // Starts a timer to periodically re-show the relaunch recommended bubble.
+  // Starts or reschedules a timer to periodically re-show the relaunch
+  // recommended bubble.
   void StartReshowTimer();
 
   // Run on a timer once high annoyance has been reached to re-show the relaunch
   // recommended bubble.
   void OnReshowRelaunchRecommendedBubble();
 
+  // Handles a new |level| and/or |high_deadline| by adjusting the runtime of
+  // the relaunch timer, updating the deadline displayed in the title of the
+  // relaunch required dialog (if shown), and showing the dialog if needed.
+  void HandleRelaunchRequiredState(
+      UpgradeDetector::UpgradeNotificationAnnoyanceLevel level,
+      base::TimeTicks high_deadline);
+
   // The following methods, which are invoked by the controller to show or close
   // notifications, are virtual for the sake of testing.
 
@@ -102,6 +134,10 @@
   // The process-wide upgrade detector.
   UpgradeDetector* const upgrade_detector_;
 
+  // A provider of TimeTicks to the controller and its timer for the sake of
+  // testability.
+  base::TickClock* const tick_clock_;
+
   // Observes changes to the browser.relaunch_notification Local State pref.
   PrefChangeRegistrar pref_change_registrar_;
 
@@ -118,6 +154,9 @@
   // when a notification has been shown.
   UpgradeDetector::UpgradeNotificationAnnoyanceLevel last_level_;
 
+  // The last observed high annoyance deadline.
+  base::TimeTicks last_high_deadline_;
+
   // The widget hosting the bubble or dialog, or null if neither is is currently
   // shown.
   views::Widget* widget_;
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_unittest.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_unittest.cc
index 9d4530c..6517152 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_unittest.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller_unittest.cc
@@ -7,7 +7,9 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/test/scoped_task_environment.h"
+#include "base/time/tick_clock.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "chrome/browser/upgrade_detector.h"
@@ -37,8 +39,12 @@
     : public RelaunchNotificationController {
  public:
   FakeRelaunchNotificationController(UpgradeDetector* upgrade_detector,
+                                     base::TickClock* tick_clock,
                                      ControllerDelegate* delegate)
-      : RelaunchNotificationController(upgrade_detector), delegate_(delegate) {}
+      : RelaunchNotificationController(upgrade_detector, tick_clock),
+        delegate_(delegate) {}
+
+  using RelaunchNotificationController::kRelaunchGracePeriod;
 
  private:
   void ShowRelaunchRecommendedBubble() override {
@@ -72,14 +78,18 @@
 // A fake UpgradeDetector.
 class FakeUpgradeDetector : public UpgradeDetector {
  public:
-  FakeUpgradeDetector() = default;
+  explicit FakeUpgradeDetector(base::TickClock* tick_clock)
+      : UpgradeDetector(tick_clock) {
+    set_upgrade_detected_time(this->tick_clock()->NowTicks());
+  }
 
+  // UpgradeDetector:
   base::TimeDelta GetHighAnnoyanceLevelDelta() override {
-    return base::TimeDelta::FromMinutes(1);
+    return high_threshold_ / 3;
   }
 
   base::TimeTicks GetHighAnnoyanceDeadline() override {
-    return base::TimeTicks::Now() - base::TimeDelta::FromDays(1);  // The past.
+    return upgrade_detected_time() + high_threshold_;
   }
 
   // Sets the annoyance level to |level| and broadcasts the change to all
@@ -89,7 +99,21 @@
     NotifyUpgrade();
   }
 
+  // Sets the high annoyance threshold to |high_threshold| and broadcasts the
+  // change to all observers.
+  void BroadcastHighThresholdChange(base::TimeDelta high_threshold) {
+    high_threshold_ = high_threshold;
+    NotifyUpgrade();
+  }
+
+  base::TimeDelta high_threshold() const { return high_threshold_; }
+
  private:
+  // UpgradeDetector:
+  void OnRelaunchNotificationPeriodPrefChanged() override {}
+
+  base::TimeDelta high_threshold_ = base::TimeDelta::FromDays(7);
+
   DISALLOW_COPY_AND_ASSIGN(FakeUpgradeDetector);
 };
 
@@ -103,7 +127,8 @@
       : scoped_task_environment_(
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
             base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED),
-        scoped_local_state_(TestingBrowserProcess::GetGlobal()) {}
+        scoped_local_state_(TestingBrowserProcess::GetGlobal()),
+        upgrade_detector_(scoped_task_environment_.GetMockTickClock()) {}
   UpgradeDetector* upgrade_detector() { return &upgrade_detector_; }
   FakeUpgradeDetector& fake_upgrade_detector() { return upgrade_detector_; }
 
@@ -114,14 +139,16 @@
         prefs::kRelaunchNotification, std::make_unique<base::Value>(value));
   }
 
+  // Returns the ScopedTaskEnvironment's MockTickClock.
+  base::TickClock* GetMockTickClock() {
+    return scoped_task_environment_.GetMockTickClock();
+  }
+
   // Fast-forwards virtual time by |delta|.
   void FastForwardBy(base::TimeDelta delta) {
     scoped_task_environment_.FastForwardBy(delta);
   }
 
-  // Runs tasks until the queues are empty.
-  void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
-
  private:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   ScopedTestingLocalState scoped_local_state_;
@@ -133,8 +160,8 @@
 TEST_F(RelaunchNotificationControllerTest, CreateDestroy) {
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
-  FakeRelaunchNotificationController controller(upgrade_detector(),
-                                                &mock_controller_delegate);
+  FakeRelaunchNotificationController controller(
+      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
 }
 
 // Without the browser.relaunch_notification preference set, the controller
@@ -143,8 +170,8 @@
 TEST_F(RelaunchNotificationControllerTest, PolicyUnset) {
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
-  FakeRelaunchNotificationController controller(upgrade_detector(),
-                                                &mock_controller_delegate);
+  FakeRelaunchNotificationController controller(
+      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
 
   fake_upgrade_detector().BroadcastLevelChange(
       UpgradeDetector::UPGRADE_ANNOYANCE_LOW);
@@ -163,8 +190,8 @@
   SetNotificationPref(1);
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
-  FakeRelaunchNotificationController controller(upgrade_detector(),
-                                                &mock_controller_delegate);
+  FakeRelaunchNotificationController controller(
+      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
 
   // Nothing shown if the level is broadcast at NONE.
   fake_upgrade_detector().BroadcastLevelChange(
@@ -193,14 +220,12 @@
       UpgradeDetector::UPGRADE_ANNOYANCE_HIGH);
   ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
 
-  // The timer should be running to reshow at the detector's delta (1m).
+  // The timer should be running to reshow at the detector's delta.
   EXPECT_CALL(mock_controller_delegate, ShowRelaunchRecommendedBubble());
-  FastForwardBy(base::TimeDelta::FromMinutes(1));
-  RunUntilIdle();
+  FastForwardBy(upgrade_detector()->GetHighAnnoyanceLevelDelta());
   ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
   EXPECT_CALL(mock_controller_delegate, ShowRelaunchRecommendedBubble());
-  FastForwardBy(base::TimeDelta::FromMinutes(1));
-  RunUntilIdle();
+  FastForwardBy(upgrade_detector()->GetHighAnnoyanceLevelDelta());
   ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
 
   // Drop back to elevated to stop the reshows and ensure there are none.
@@ -208,8 +233,7 @@
   fake_upgrade_detector().BroadcastLevelChange(
       UpgradeDetector::UPGRADE_ANNOYANCE_ELEVATED);
   ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
-  FastForwardBy(base::TimeDelta::FromMinutes(1));
-  RunUntilIdle();
+  FastForwardBy(upgrade_detector()->GetHighAnnoyanceLevelDelta());
   ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
 
   // And closed if the level drops back to none.
@@ -229,8 +253,8 @@
   SetNotificationPref(2);
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
-  FakeRelaunchNotificationController controller(upgrade_detector(),
-                                                &mock_controller_delegate);
+  FakeRelaunchNotificationController controller(
+      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
 
   // Nothing shown if the level is broadcast at NONE.
   fake_upgrade_detector().BroadcastLevelChange(
@@ -284,8 +308,8 @@
 TEST_F(RelaunchNotificationControllerTest, PolicyChangesNoUpgrade) {
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
-  FakeRelaunchNotificationController controller(upgrade_detector(),
-                                                &mock_controller_delegate);
+  FakeRelaunchNotificationController controller(
+      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
 
   SetNotificationPref(1);
   ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
@@ -304,8 +328,8 @@
 TEST_F(RelaunchNotificationControllerTest, PolicyChangesWithUpgrade) {
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
-  FakeRelaunchNotificationController controller(upgrade_detector(),
-                                                &mock_controller_delegate);
+  FakeRelaunchNotificationController controller(
+      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
 
   fake_upgrade_detector().BroadcastLevelChange(
       UpgradeDetector::UPGRADE_ANNOYANCE_LOW);
@@ -330,8 +354,8 @@
   SetNotificationPref(2);
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
-  FakeRelaunchNotificationController controller(upgrade_detector(),
-                                                &mock_controller_delegate);
+  FakeRelaunchNotificationController controller(
+      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
 
   // As in the RequiredByPolicy test, the dialog should be shown.
   EXPECT_CALL(mock_controller_delegate, ShowRelaunchRequiredDialog());
@@ -341,8 +365,8 @@
 
   // And the relaunch should be forced after the deadline passes.
   EXPECT_CALL(mock_controller_delegate, OnRelaunchDeadlineExpired());
-  FastForwardBy(base::TimeDelta::FromMinutes(5));
-  RunUntilIdle();
+  FastForwardBy(fake_upgrade_detector().high_threshold() +
+                FakeRelaunchNotificationController::kRelaunchGracePeriod);
   ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
 }
 
@@ -351,8 +375,8 @@
   SetNotificationPref(2);
   ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
 
-  FakeRelaunchNotificationController controller(upgrade_detector(),
-                                                &mock_controller_delegate);
+  FakeRelaunchNotificationController controller(
+      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
 
   // As in the RequiredByPolicy test, the dialog should be shown.
   EXPECT_CALL(mock_controller_delegate, ShowRelaunchRequiredDialog());
@@ -366,7 +390,153 @@
   ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
 
   // And no relaunch should take place.
-  FastForwardBy(base::TimeDelta::FromMinutes(5));
-  RunUntilIdle();
+  FastForwardBy(fake_upgrade_detector().high_threshold() +
+                FakeRelaunchNotificationController::kRelaunchGracePeriod);
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+}
+
+// NotificationPeriod changes should do nothing at any policy setting when the
+// annoyance level is at none.
+TEST_F(RelaunchNotificationControllerTest, NonePeriodChange) {
+  ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
+
+  FakeRelaunchNotificationController controller(
+      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
+
+  // Reduce the period.
+  fake_upgrade_detector().BroadcastHighThresholdChange(
+      base::TimeDelta::FromDays(1));
+  FastForwardBy(fake_upgrade_detector().high_threshold());
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  SetNotificationPref(1);
+  fake_upgrade_detector().BroadcastHighThresholdChange(
+      base::TimeDelta::FromHours(23));
+  FastForwardBy(fake_upgrade_detector().high_threshold());
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  SetNotificationPref(2);
+  fake_upgrade_detector().BroadcastHighThresholdChange(
+      base::TimeDelta::FromHours(22));
+  FastForwardBy(fake_upgrade_detector().high_threshold());
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+}
+
+// NotificationPeriod changes impact reshows of the relaunch recommended bubble.
+TEST_F(RelaunchNotificationControllerTest, PeriodChangeRecommended) {
+  SetNotificationPref(1);
+  ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
+
+  FakeRelaunchNotificationController controller(
+      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
+
+  // Get up to high annoyance so that the reshow timer is running.
+  EXPECT_CALL(mock_controller_delegate, ShowRelaunchRecommendedBubble());
+  fake_upgrade_detector().BroadcastLevelChange(
+      UpgradeDetector::UPGRADE_ANNOYANCE_HIGH);
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  // Advance time partway to the reshow, but not all the way there.
+  FastForwardBy(upgrade_detector()->GetHighAnnoyanceLevelDelta() * 0.9);
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  // Now shorten the period dramatically and expect an immediate reshow.
+  EXPECT_CALL(mock_controller_delegate, ShowRelaunchRecommendedBubble());
+  fake_upgrade_detector().BroadcastHighThresholdChange(
+      fake_upgrade_detector().high_threshold() / 10);
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  // And expect another reshow at the new delta.
+  base::TimeDelta short_reshow_delta =
+      upgrade_detector()->GetHighAnnoyanceLevelDelta();
+  EXPECT_CALL(mock_controller_delegate, ShowRelaunchRecommendedBubble());
+  FastForwardBy(short_reshow_delta);
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  // Now lengthen the period and expect no immediate reshow.
+  fake_upgrade_detector().BroadcastHighThresholdChange(
+      fake_upgrade_detector().high_threshold() * 10);
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  // Move forward by the short delta to be sure there's no reshow there.
+  FastForwardBy(short_reshow_delta);
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  // Move forward the rest of the way to the new delta and expect a reshow.
+  base::TimeDelta long_reshow_delta =
+      upgrade_detector()->GetHighAnnoyanceLevelDelta();
+  EXPECT_CALL(mock_controller_delegate, ShowRelaunchRecommendedBubble());
+  FastForwardBy(long_reshow_delta - short_reshow_delta);
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  // Similar to the above, move time forward a little bit.
+  FastForwardBy(long_reshow_delta * 0.1);
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  // Shorten the period a bit, but not enough to force a reshow.
+  fake_upgrade_detector().BroadcastHighThresholdChange(
+      fake_upgrade_detector().high_threshold() * 0.9);
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  // And ensure that moving forward the rest of the way to the new delta causes
+  // a reshow.
+  EXPECT_CALL(mock_controller_delegate, ShowRelaunchRecommendedBubble());
+  FastForwardBy(upgrade_detector()->GetHighAnnoyanceLevelDelta() -
+                long_reshow_delta * 0.1);
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+}
+
+// NotificationPeriod changes impact reshows of the relaunch required dialog.
+TEST_F(RelaunchNotificationControllerTest, PeriodChangeRequired) {
+  SetNotificationPref(2);
+  ::testing::StrictMock<MockControllerDelegate> mock_controller_delegate;
+
+  FakeRelaunchNotificationController controller(
+      upgrade_detector(), GetMockTickClock(), &mock_controller_delegate);
+
+  // Get up to low annoyance so that the relaunch timer is running.
+  EXPECT_CALL(mock_controller_delegate, ShowRelaunchRequiredDialog());
+  fake_upgrade_detector().BroadcastLevelChange(
+      UpgradeDetector::UPGRADE_ANNOYANCE_LOW);
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  // Move forward partway to the current deadline. Nothing should happen.
+  base::TimeTicks high_annoyance_deadline =
+      upgrade_detector()->GetHighAnnoyanceDeadline();
+  FastForwardBy((high_annoyance_deadline - GetMockTickClock()->NowTicks()) / 2);
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  // Lengthen the period, thereby pushing out the deadline.
+  fake_upgrade_detector().BroadcastHighThresholdChange(
+      fake_upgrade_detector().high_threshold() * 2);
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  // Ensure that nothing happens when the old deadline passes.
+  FastForwardBy(high_annoyance_deadline +
+                FakeRelaunchNotificationController::kRelaunchGracePeriod -
+                GetMockTickClock()->NowTicks());
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  // But now we enter elevated annoyance level and show the dialog.
+  EXPECT_CALL(mock_controller_delegate, ShowRelaunchRequiredDialog());
+  fake_upgrade_detector().BroadcastLevelChange(
+      UpgradeDetector::UPGRADE_ANNOYANCE_ELEVATED);
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  // Jumping to the new deadline relaunches the browser.
+  EXPECT_CALL(mock_controller_delegate, OnRelaunchDeadlineExpired());
+  FastForwardBy(upgrade_detector()->GetHighAnnoyanceDeadline() +
+                FakeRelaunchNotificationController::kRelaunchGracePeriod -
+                GetMockTickClock()->NowTicks());
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+
+  // Shorten the period, bringing in the deadline. Expect the dialog to show and
+  // a relaunch after the grace period passes.
+  EXPECT_CALL(mock_controller_delegate, ShowRelaunchRequiredDialog());
+  fake_upgrade_detector().BroadcastHighThresholdChange(
+      fake_upgrade_detector().high_threshold() / 2);
+  ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
+  EXPECT_CALL(mock_controller_delegate, OnRelaunchDeadlineExpired());
+  FastForwardBy(FakeRelaunchNotificationController::kRelaunchGracePeriod);
   ::testing::Mock::VerifyAndClear(&mock_controller_delegate);
 }
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.cc
index ab174db..bf0ff694 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.cc
@@ -45,6 +45,21 @@
 
 RelaunchRequiredDialogView::~RelaunchRequiredDialogView() = default;
 
+// static
+RelaunchRequiredDialogView* RelaunchRequiredDialogView::FromWidget(
+    views::Widget* widget) {
+  return static_cast<RelaunchRequiredDialogView*>(
+      widget->widget_delegate()->AsDialogDelegate());
+}
+
+void RelaunchRequiredDialogView::SetDeadline(base::TimeTicks deadline) {
+  if (deadline != relaunch_deadline_) {
+    relaunch_deadline_ = deadline;
+    // Refresh the title immediately.
+    OnTitleRefresh();
+  }
+}
+
 bool RelaunchRequiredDialogView::Accept() {
   base::RecordAction(base::UserMetricsAction("RelaunchRequired_Accept"));
 
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.h b/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.h
index 382dc25..319ade2a 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.h
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_required_dialog_view.h
@@ -31,6 +31,14 @@
 
   ~RelaunchRequiredDialogView() override;
 
+  // Returns the instance hosted by |widget|. |widget| must be an instance
+  // previously returned from Show().
+  static RelaunchRequiredDialogView* FromWidget(views::Widget* widget);
+
+  // Sets the relaunch deadline to |deadline| and refreshes the view's title
+  // accordingly.
+  void SetDeadline(base::TimeTicks deadline);
+
   // views::DialogDelegateView:
   bool Accept() override;
   bool Close() override;
@@ -71,7 +79,7 @@
   views::BubbleFrameView* GetBubbleFrameView();
 
   // The time at which Chrome will be forcefully relaunched.
-  const base::TimeTicks relaunch_deadline_;
+  base::TimeTicks relaunch_deadline_;
 
   // A callback to run if the user accepts the prompt to relaunch the browser.
   base::RepeatingClosure on_accept_;
diff --git a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
index 27850d2..8174543e 100644
--- a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
@@ -18,7 +18,7 @@
   num_tabs_++;
   tab_strip_->AddTabAt(index, TabRendererData(), is_active);
   if (is_active)
-    active_index_ = index;
+    SelectTab(index);
 }
 
 void FakeBaseTabStripController::AddPinnedTab(int index, bool is_active) {
@@ -33,8 +33,11 @@
 void FakeBaseTabStripController::RemoveTab(int index) {
   num_tabs_--;
   tab_strip_->RemoveTabAt(nullptr, index);
-  if (active_index_ == index)
-    active_index_ = -1;
+  if (active_index_ > index) {
+    --active_index_;
+  } else if (active_index_ == index) {
+    SetActiveIndex(std::min(active_index_, num_tabs_ - 1));
+  }
 }
 
 const ui::ListSelectionModel&
@@ -71,11 +74,8 @@
 void FakeBaseTabStripController::SelectTab(int index) {
   if (!IsValidIndex(index) || active_index_ == index)
     return;
-  ui::ListSelectionModel old_selection_model;
-  old_selection_model.SetSelectedIndex(active_index_);
-  active_index_ = index;
-  selection_model_.SetSelectedIndex(active_index_);
-  tab_strip_->SetSelection(old_selection_model, selection_model_);
+
+  SetActiveIndex(index);
 }
 
 void FakeBaseTabStripController::ExtendSelectionTo(int index) {
@@ -88,6 +88,8 @@
 }
 
 void FakeBaseTabStripController::CloseTab(int index, CloseTabSource source) {
+  tab_strip_->PrepareForCloseAt(index, source);
+  RemoveTab(index);
 }
 
 void FakeBaseTabStripController::ToggleTabAudioMute(int index) {
@@ -117,6 +119,7 @@
 }
 
 void FakeBaseTabStripController::CreateNewTab() {
+  AddTab(num_tabs_, true);
 }
 
 void FakeBaseTabStripController::CreateNewTabWithLocation(
@@ -152,3 +155,12 @@
 Profile* FakeBaseTabStripController::GetProfile() const {
   return nullptr;
 }
+
+void FakeBaseTabStripController::SetActiveIndex(int new_index) {
+  ui::ListSelectionModel old_selection_model;
+  old_selection_model.SetSelectedIndex(active_index_);
+  active_index_ = new_index;
+  selection_model_.SetSelectedIndex(active_index_);
+  if (IsValidIndex(active_index_))
+    tab_strip_->SetSelection(old_selection_model, selection_model_);
+}
diff --git a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
index 46eca0c..7bbeacb 100644
--- a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
+++ b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
@@ -56,6 +56,8 @@
   Profile* GetProfile() const override;
 
  private:
+  void SetActiveIndex(int new_index);
+
   TabStrip* tab_strip_ = nullptr;
 
   int num_tabs_ = 0;
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index e0923404..011dffa0 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -115,6 +115,8 @@
 const int kPinnedToNonPinnedOffset = 3;
 #endif
 
+TabSizeInfo* g_tab_size_info = nullptr;
+
 // Returns the width needed for the new tab button (and padding).
 int GetNewTabButtonWidth(bool is_incognito) {
   return GetLayoutSize(NEW_TAB_BUTTON, is_incognito).width() +
@@ -213,18 +215,17 @@
 }
 
 const TabSizeInfo& GetTabSizeInfo() {
-  static TabSizeInfo* tab_size_info = nullptr;
-  if (tab_size_info)
-    return *tab_size_info;
+  if (g_tab_size_info)
+    return *g_tab_size_info;
 
-  tab_size_info = new TabSizeInfo;
-  tab_size_info->pinned_tab_width = Tab::GetPinnedWidth();
-  tab_size_info->min_active_width = Tab::GetMinimumActiveSize().width();
-  tab_size_info->min_inactive_width = Tab::GetMinimumInactiveSize().width();
-  tab_size_info->max_size = Tab::GetStandardSize();
-  tab_size_info->tab_overlap = Tab::GetOverlap();
-  tab_size_info->pinned_to_normal_offset = kPinnedToNonPinnedOffset;
-  return *tab_size_info;
+  g_tab_size_info = new TabSizeInfo;
+  g_tab_size_info->pinned_tab_width = Tab::GetPinnedWidth();
+  g_tab_size_info->min_active_width = Tab::GetMinimumActiveSize().width();
+  g_tab_size_info->min_inactive_width = Tab::GetMinimumInactiveSize().width();
+  g_tab_size_info->max_size = Tab::GetStandardSize();
+  g_tab_size_info->tab_overlap = Tab::GetOverlap();
+  g_tab_size_info->pinned_to_normal_offset = kPinnedToNonPinnedOffset;
+  return *g_tab_size_info;
 }
 
 }  // namespace
@@ -654,13 +655,24 @@
       AnimateToIdealBounds();
     SchedulePaint();
   } else {
-    // We have "tiny tabs" if the tabs are so tiny that the unselected ones are
-    // a different size to the selected ones.
-    bool tiny_tabs = current_inactive_width_ != current_active_width_;
-    if (!IsAnimating() && (!in_tab_close_ || tiny_tabs)) {
-      DoLayout();
-    } else {
+    if (current_inactive_width_ == current_active_width_) {
+      // When tabs are wide enough, selecting a new tab cannot change the
+      // ideal bounds, so only a repaint is necessary.
       SchedulePaint();
+    } else if (IsAnimating()) {
+      // The selection change will have modified the ideal bounds of the tabs
+      // in |old_selection| and |new_selection|.  We need to recompute.
+      // Note: This is safe even if we're in the midst of mouse-based tab
+      // closure--we won't expand the tabstrip back to the full window
+      // width--because PrepareForCloseAt() will have set
+      // |available_width_for_tabs_| already.
+      GenerateIdealBounds();
+      AnimateToIdealBounds();
+    } else {
+      // As in the animating case above, the selection change will have
+      // affected the desired bounds of the tabs, but since we're not animating
+      // we can just snap to the new bounds.
+      DoLayout();
     }
   }
 
@@ -2170,41 +2182,16 @@
 }
 
 void TabStrip::StartMouseInitiatedRemoveTabAnimation(int model_index) {
-  // The user initiated the close. We want to persist the bounds of all the
-  // existing tabs, so we manually shift ideal_bounds then animate.
-  Tab* tab_closing = tab_at(model_index);
-  int delta = tab_closing->width() - Tab::GetOverlap();
-  // If the tab being closed is a pinned tab next to a non-pinned tab, be sure
-  // to add the extra padding.
-  DCHECK_LT(model_index, tab_count() - 1);
-  if (tab_closing->data().pinned && !tab_at(model_index + 1)->data().pinned)
-    delta += kPinnedToNonPinnedOffset;
-
-  // Set the ideal bounds of everything to be moved over to cover for the
-  // removed tab. This does not recompute the ideal bounds so every tab slides
-  // over without expansion until the user moves the mouse away.
-  for (int i = model_index + 1; i < tab_count(); ++i) {
-    gfx::Rect bounds = ideal_bounds(i);
-    bounds.set_x(bounds.x() - delta);
-    tabs_.set_ideal_bounds(i, bounds);
-  }
-
-  // Don't just subtract |delta| from the New Tab x-coordinate, as we might have
-  // overflow tabs that will be able to animate into the strip, in which case
-  // the new tab button should stay where it is.
-  new_tab_button_bounds_.set_x(
-      std::min(width() - new_tab_button_bounds_.width(),
-               ideal_bounds(tab_count() - 1).right() +
-                   GetLayoutConstant(TABSTRIP_NEW_TAB_BUTTON_SPACING)));
-
   PrepareForAnimation();
 
+  Tab* tab_closing = tab_at(model_index);
   tab_closing->set_closing(true);
 
   // We still need to paint the tab until we actually remove it. Put it in
   // tabs_closing_map_ so we can find it.
   RemoveTabFromViewModel(model_index);
 
+  GenerateIdealBounds();
   AnimateToIdealBounds();
 
   gfx::Rect tab_bounds = tab_closing->bounds();
@@ -2231,6 +2218,14 @@
          kPinnedToNonPinnedOffset;
 }
 
+// static
+void TabStrip::ResetTabSizeInfoForTesting() {
+  if (g_tab_size_info) {
+    delete g_tab_size_info;
+    g_tab_size_info = nullptr;
+  }
+}
+
 Tab* TabStrip::FindTabForEvent(const gfx::Point& point) {
   DCHECK(touch_layout_);
   int active_tab_index = touch_layout_->active_index();
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index 1011cf4..5447d0a 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -284,6 +284,7 @@
   FRIEND_TEST_ALL_PREFIXES(TabDragControllerTest, GestureEndShouldEndDragTest);
   FRIEND_TEST_ALL_PREFIXES(TabStripTest, TabForEventWhenStacked);
   FRIEND_TEST_ALL_PREFIXES(TabStripTest, TabCloseButtonVisibilityWhenStacked);
+  FRIEND_TEST_ALL_PREFIXES(TabStripTest, ActiveTabWidthWhenTabsAreTiny);
 
   // Used during a drop session of a url. Tracks the position of the drop as
   // well as a window used to highlight where the drop occurs.
@@ -519,6 +520,10 @@
   // hit-test region of the specified Tab.
   bool IsPointInTab(Tab* tab, const gfx::Point& point_in_tabstrip_coords);
 
+  // Reset cached tab size info. Because Tab size info can be different
+  // depending on touch ui optimization, we should be able to reset this.
+  static void ResetTabSizeInfoForTesting();
+
   // -- Touch Layout ----------------------------------------------------------
 
   // Returns the position normal tabs start at.
diff --git a/chrome/browser/ui/views/tabs/tab_strip_impl_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
similarity index 92%
rename from chrome/browser/ui/views/tabs/tab_strip_impl_unittest.cc
rename to chrome/browser/ui/views/tabs/tab_strip_unittest.cc
index 9f13422..8a546ff 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_impl_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/ui/views/tabs/tab.h"
 #include "chrome/browser/ui/views/tabs/tab_icon.h"
 #include "chrome/browser/ui/views/tabs/tab_renderer_data.h"
-#include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
 #include "chrome/browser/ui/views/tabs/tab_strip_observer.h"
 #include "chrome/test/base/testing_profile.h"
@@ -131,6 +130,7 @@
   }
 
   void TearDown() override {
+    TabStrip::ResetTabSizeInfoForTesting();
     widget_.reset();
     views::ViewsTestBase::TearDown();
   }
@@ -565,6 +565,41 @@
   EXPECT_FALSE(IsShowingAttentionIndicator(1));
 }
 
+// The active tab should always be at least as wide as its minimum width.
+// http://crbug.com/587688
+TEST_P(TabStripTest, ActiveTabWidthWhenTabsAreTiny) {
+  // The bug was caused when it's animating. Therefore we should make widget
+  // visible so that animation can be triggered.
+  tab_strip_->GetWidget()->Show();
+  tab_strip_->SetBounds(0, 0, 200, 20);
+
+  const int min_inactive_width = Tab::GetMinimumInactiveSize().width();
+  // Create a lot of tabs in order to make inactive tabs tiny.
+  while (tab_strip_->current_inactive_width_ != min_inactive_width)
+    controller_->CreateNewTab();
+
+  const int min_active_width = Tab::GetMinimumActiveSize().width();
+  int active_index = controller_->GetActiveIndex();
+  EXPECT_GT(tab_strip_->tab_count(), 1);
+  EXPECT_EQ(tab_strip_->tab_count() - 1, active_index);
+  EXPECT_LT(tab_strip_->ideal_bounds(0).width(),
+            tab_strip_->ideal_bounds(active_index).width());
+
+  // Even though other tabs are very tiny, the active tab should be at least
+  // as wide as it's minimum width.
+  EXPECT_GE(tab_strip_->ideal_bounds(active_index).width(), min_active_width);
+
+  // During mouse-based tab closure, the active tab should remain at least as
+  // wide as it's minium width.
+  controller_->SelectTab(0);
+  while (tab_strip_->tab_count()) {
+    const int active_index = controller_->GetActiveIndex();
+    EXPECT_GE(tab_strip_->ideal_bounds(active_index).width(), min_active_width);
+    tab_strip_->CloseTab(tab_strip_->tab_at(active_index),
+                         CLOSE_TAB_FROM_MOUSE);
+  }
+}
+
 // Defines an alias to be used for tests that are only relevant to the touch-
 // optimized UI mode.
 using TabStripTouchOptimizedUiOnlyTest = TabStripTest;
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc b/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
index 2f45344..9e380aa 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
@@ -675,8 +675,8 @@
   bool success = false;
   std::string status;
 
-  auto print_data =
-      base::MakeRefCounted<base::RefCountedBytes>(kPrintData, kPrintDataLength);
+  auto print_data = base::MakeRefCounted<base::RefCountedStaticMemory>(
+      kPrintData, kPrintDataLength);
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 6ff1d3b..17af7ce9 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -2331,6 +2331,11 @@
 }
 #endif
 
+void AddExtensionsStrings(content::WebUIDataSource* html_source) {
+  html_source->AddLocalizedString("extensionsPageTitle",
+                                  IDS_SETTINGS_EXTENSIONS_CHECKBOX_LABEL);
+}
+
 }  // namespace
 
 void AddLocalizedStrings(content::WebUIDataSource* html_source,
@@ -2379,6 +2384,7 @@
   AddImportDataStrings(html_source);
   AddSystemStrings(html_source);
 #endif
+  AddExtensionsStrings(html_source);
 
 #if defined(USE_NSS_CERTS)
   certificate_manager::AddLocalizedStrings(html_source);
diff --git a/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc b/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc
index 3f06185b..82cb431d 100644
--- a/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc
+++ b/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc
@@ -480,10 +480,10 @@
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
-  DCHECK_EQ(chrome::NOTIFICATION_BROWSER_WINDOW_READY, type);
+  DCHECK_EQ(chrome::NOTIFICATION_BROWSER_OPENED, type);
 
-  // Only respond to one Browser Window Ready event.
-  registrar_.Remove(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+  // Only respond to one Browser Opened event.
+  registrar_.Remove(this, chrome::NOTIFICATION_BROWSER_OPENED,
                     content::NotificationService::AllSources());
   UserManager::Hide();
 }
@@ -497,8 +497,7 @@
   if (browser && browser->window()) {
     UserManager::Hide();
   } else {
-    registrar_.Add(this,
-                   chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+    registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
                    content::NotificationService::AllSources());
   }
 }
diff --git a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
index a449210..13b02da2 100644
--- a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
+++ b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
@@ -857,7 +857,7 @@
   }
 }
 
-void UserManagerScreenHandler::OnBrowserWindowReady(Browser* browser) {
+void UserManagerScreenHandler::OnBrowserOpened(Browser* browser) {
   DCHECK(browser);
   DCHECK(browser->window());
 
@@ -891,12 +891,12 @@
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
-  DCHECK_EQ(chrome::NOTIFICATION_BROWSER_WINDOW_READY, type);
+  DCHECK_EQ(chrome::NOTIFICATION_BROWSER_OPENED, type);
 
-  // Only respond to one Browser Window Ready event.
-  registrar_.Remove(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+  // Only respond to one Browser Opened event.
+  registrar_.Remove(this, chrome::NOTIFICATION_BROWSER_OPENED,
                     content::NotificationService::AllSources());
-  OnBrowserWindowReady(content::Source<Browser>(source).ptr());
+  OnBrowserOpened(content::Source<Browser>(source).ptr());
 }
 
 // This callback is run after switching to a new profile has finished. This
@@ -908,10 +908,9 @@
     Profile* profile, Profile::CreateStatus profile_create_status) {
   Browser* browser = chrome::FindAnyBrowser(profile, false);
   if (browser && browser->window()) {
-    OnBrowserWindowReady(browser);
+    OnBrowserOpened(browser);
   } else {
-    registrar_.Add(this,
-                   chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+    registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
                    content::NotificationService::AllSources());
   }
 }
diff --git a/chrome/browser/ui/webui/signin/user_manager_screen_handler.h b/chrome/browser/ui/webui/signin/user_manager_screen_handler.h
index cf66e306..eac65e5 100644
--- a/chrome/browser/ui/webui/signin/user_manager_screen_handler.h
+++ b/chrome/browser/ui/webui/signin/user_manager_screen_handler.h
@@ -91,8 +91,8 @@
   void OnOAuthError() override;
   void OnNetworkError(int response_code) override;
 
-  // Handle when Notified of a NOTIFICATION_BROWSER_WINDOW_READY event.
-  void OnBrowserWindowReady(Browser* browser);
+  // Handle when Notified of a NOTIFICATION_BROWSER_OPENED event.
+  void OnBrowserOpened(Browser* browser);
 
   // Sends user list to account chooser.
   void SendUserList();
diff --git a/chrome/browser/upgrade_detector.cc b/chrome/browser/upgrade_detector.cc
index d1c4885..ea1fbd7 100644
--- a/chrome/browser/upgrade_detector.cc
+++ b/chrome/browser/upgrade_detector.cc
@@ -6,12 +6,15 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/time/tick_clock.h"
 #include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/ui/browser_otr_state.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/paint_vector_icon.h"
 
@@ -57,14 +60,27 @@
   return gfx::Image(gfx::CreateVectorIcon(kBrowserToolsUpdateIcon, color));
 }
 
-UpgradeDetector::UpgradeDetector()
-    : upgrade_available_(UPGRADE_AVAILABLE_NONE),
+UpgradeDetector::UpgradeDetector(base::TickClock* tick_clock)
+    : tick_clock_(tick_clock),
+      upgrade_available_(UPGRADE_AVAILABLE_NONE),
       best_effort_experiment_updates_available_(false),
       critical_experiment_updates_available_(false),
       critical_update_acknowledged_(false),
       is_factory_reset_required_(false),
+      idle_check_timer_(tick_clock_),
       upgrade_notification_stage_(UPGRADE_ANNOYANCE_NONE),
       notify_upgrade_(false) {
+  // Not all tests provide a PrefService for local_state().
+  PrefService* local_state = g_browser_process->local_state();
+  if (local_state) {
+    pref_change_registrar_.Init(local_state);
+    // base::Unretained is safe here because |this| outlives the registrar.
+    pref_change_registrar_.Add(
+        prefs::kRelaunchNotificationPeriod,
+        base::BindRepeating(
+            &UpgradeDetector::OnRelaunchNotificationPeriodPrefChanged,
+            base::Unretained(this)));
+  }
 }
 
 UpgradeDetector::~UpgradeDetector() {
@@ -80,8 +96,31 @@
     observer.OnOutdatedInstallNoAutoUpdate();
 }
 
+// static
+base::TimeDelta UpgradeDetector::GetRelaunchNotificationPeriod() {
+  // Not all tests provide a PrefService for local_state().
+  auto* local_state = g_browser_process->local_state();
+  if (!local_state)
+    return base::TimeDelta();
+  const auto* preference =
+      local_state->FindPreference(prefs::kRelaunchNotificationPeriod);
+  const int value = preference->GetValue()->GetInt();
+  // Enforce the preference's documented minimum value.
+  static constexpr base::TimeDelta kMinValue = base::TimeDelta::FromHours(1);
+  if (preference->IsDefaultValue() || value < kMinValue.InMilliseconds())
+    return base::TimeDelta();
+  return base::TimeDelta::FromMilliseconds(value);
+}
+
 void UpgradeDetector::NotifyUpgrade() {
-  notify_upgrade_ = true;
+  // An implementation will request that a notification be sent after dropping
+  // back to the "none" annoyance level if the RelaunchNotificationPeriod
+  // setting changes to a large enough value such that none of the revised
+  // thresholds have been hit. In this case, consumers should not perceive that
+  // an upgrade is available when checking notify_upgrade(). In practice, this
+  // is only the case on desktop Chrome and not Chrome OS, where the lowest
+  // threshold is hit the moment the upgrade is detected.
+  notify_upgrade_ = upgrade_notification_stage_ != UPGRADE_ANNOYANCE_NONE;
 
   NotifyUpgradeRecommended();
   if (upgrade_available_ == UPGRADE_NEEDED_OUTDATED_INSTALL) {
diff --git a/chrome/browser/upgrade_detector.h b/chrome/browser/upgrade_detector.h
index 42cc9e3..bb66f6b 100644
--- a/chrome/browser/upgrade_detector.h
+++ b/chrome/browser/upgrade_detector.h
@@ -11,11 +11,15 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/upgrade_observer.h"
+#include "components/prefs/pref_change_registrar.h"
 #include "ui/base/idle/idle.h"
 #include "ui/gfx/image/image.h"
 
 class PrefRegistrySimple;
 class UpgradeObserver;
+namespace base {
+class TickClock;
+}
 
 ///////////////////////////////////////////////////////////////////////////////
 // UpgradeDetector
@@ -133,7 +137,14 @@
     UPGRADE_NEEDED_OUTDATED_INSTALL_NO_AU,
   };
 
-  UpgradeDetector();
+  explicit UpgradeDetector(base::TickClock* tick_clock);
+
+  // Returns the notification period specified via the
+  // RelaunchNotificationPeriod policy setting, or a zero delta if unset or out
+  // of range.
+  static base::TimeDelta GetRelaunchNotificationPeriod();
+
+  base::TickClock* tick_clock() { return tick_clock_; }
 
   // Notifies that update is recommended and triggers different actions based
   // on the update availability.
@@ -196,6 +207,11 @@
   FRIEND_TEST_ALL_PREFIXES(SystemTrayClientTest, UpdateTrayIcon);
   friend class UpgradeMetricsProviderTest;
 
+  // Handles a change to the browser.relaunch_notification_period Local State
+  // preference. Subclasses should call NotifyUpgrade if observers are to be
+  // notified of the change (generally speaking, if an upgrade is available).
+  virtual void OnRelaunchNotificationPeriodPrefChanged() = 0;
+
   // Initiates an Idle check. See IdleCallback below.
   void CheckIdle();
 
@@ -203,6 +219,13 @@
   // input events since the specified time.
   void IdleCallback(ui::IdleState state);
 
+  // A provider of TimeTicks to the detector and its timers.
+  base::TickClock* const tick_clock_;
+
+  // Observes changes to the browser.relaunch_notification_period Local State
+  // preference.
+  PrefChangeRegistrar pref_change_registrar_;
+
   // Whether any software updates are available (experiment updates are tracked
   // separately via additional member variables below).
   UpgradeAvailable upgrade_available_;
diff --git a/chrome/browser/upgrade_detector_impl.cc b/chrome/browser/upgrade_detector_impl.cc
index 15f8820..699f7f44 100644
--- a/chrome/browser/upgrade_detector_impl.cc
+++ b/chrome/browser/upgrade_detector_impl.cc
@@ -7,11 +7,13 @@
 #include <stdint.h>
 
 #include <string>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/build_time.h"
 #include "base/command_line.h"
 #include "base/feature_list.h"
+#include "base/no_destructor.h"
 #include "base/process/launch.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/string_number_conversions.h"
@@ -22,6 +24,7 @@
 #include "base/task_scheduler/post_task.h"
 #include "base/task_scheduler/task_traits.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/default_tick_clock.h"
 #include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/google/google_brand.h"
@@ -51,17 +54,18 @@
 
 // How long (in milliseconds) to wait (each cycle) before checking whether
 // Chrome's been upgraded behind our back.
-const int kCheckForUpgradeMs = 2 * 60 * 60 * 1000;  // 2 hours.
+constexpr base::TimeDelta kCheckForUpgrade = base::TimeDelta::FromHours(2);
 
 // How long to wait (each cycle) before checking which severity level we should
 // be at. Once we reach the highest severity, the timer will stop.
-const int kNotifyCycleTimeMs = 20 * 60 * 1000;  // 20 minutes.
+constexpr base::TimeDelta kNotifyCycleTime = base::TimeDelta::FromMinutes(20);
 
 // Same as kNotifyCycleTimeMs but only used during testing.
-const int kNotifyCycleTimeForTestingMs = 500;  // Half a second.
+constexpr base::TimeDelta kNotifyCycleTimeForTesting =
+    base::TimeDelta::FromMilliseconds(500);
 
 // The number of days after which we identify a build/install as outdated.
-const uint64_t kOutdatedBuildAgeInDays = 12 * 7;
+constexpr base::TimeDelta kOutdatedBuildAge = base::TimeDelta::FromDays(12 * 7);
 
 // Return the string that was passed as a value for the
 // kCheckForUpdateIntervalSec switch.
@@ -88,14 +92,14 @@
 }
 
 // How often to check for an upgrade.
-int GetCheckForUpgradeEveryMs() {
+base::TimeDelta GetCheckForUpgradeDelay() {
   // Check for a value passed via the command line.
-  int interval_ms;
+  int seconds;
   std::string interval = CmdLineInterval();
-  if (!interval.empty() && base::StringToInt(interval, &interval_ms))
-    return interval_ms * 1000;  // Command line value is in seconds.
+  if (!interval.empty() && base::StringToInt(interval, &seconds))
+    return base::TimeDelta::FromSeconds(seconds);
 
-  return kCheckForUpgradeMs;
+  return kCheckForUpgrade;
 }
 
 #if !defined(OS_WIN) || defined(GOOGLE_CHROME_BUILD)
@@ -146,11 +150,14 @@
 
 }  // namespace
 
-UpgradeDetectorImpl::UpgradeDetectorImpl()
-    : blocking_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+UpgradeDetectorImpl::UpgradeDetectorImpl(base::TickClock* tick_clock)
+    : UpgradeDetector(tick_clock),
+      blocking_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
           {base::TaskPriority::BACKGROUND,
            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN,
            base::MayBlock()})),
+      detect_upgrade_timer_(this->tick_clock()),
+      upgrade_notification_timer_(this->tick_clock()),
       is_unstable_channel_(false),
       is_auto_update_enabled_(true),
       build_date_(base::GetBuildTime()),
@@ -296,9 +303,8 @@
 
 void UpgradeDetectorImpl::StartTimerForUpgradeCheck() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  detect_upgrade_timer_.Start(FROM_HERE,
-      base::TimeDelta::FromMilliseconds(GetCheckForUpgradeEveryMs()),
-      this, &UpgradeDetectorImpl::CheckForUpgrade);
+  detect_upgrade_timer_.Start(FROM_HERE, GetCheckForUpgradeDelay(), this,
+                              &UpgradeDetectorImpl::CheckForUpgrade);
 }
 
 void UpgradeDetectorImpl::StartUpgradeNotificationTimer() {
@@ -311,15 +317,14 @@
   // Ensure that the thresholds used by NotifyOnUpgrade have been initialized.
   InitializeThresholds();
 
-  set_upgrade_detected_time(base::TimeTicks::Now());
+  if (upgrade_detected_time().is_null())
+    set_upgrade_detected_time(tick_clock()->NowTicks());
 
   // Start the repeating timer for notifying the user after a certain period.
   // The called function will eventually figure out that enough time has passed
   // and stop the timer.
-  const int cycle_time_ms = IsTesting() ?
-      kNotifyCycleTimeForTestingMs : kNotifyCycleTimeMs;
-  upgrade_notification_timer_.Start(FROM_HERE,
-      base::TimeDelta::FromMilliseconds(cycle_time_ms),
+  upgrade_notification_timer_.Start(
+      FROM_HERE, IsTesting() ? kNotifyCycleTimeForTesting : kNotifyCycleTime,
       this, &UpgradeDetectorImpl::NotifyOnUpgrade);
 }
 
@@ -328,7 +333,17 @@
   if (!low_threshold_.is_zero())
     return;
 
-  // Start with the default values.
+  // Use a custom notification period for the "high" level, dividing it evenly
+  // to set the "low" and "elevated" levels. Such overrides trump all else.
+  const base::TimeDelta custom_high = GetRelaunchNotificationPeriod();
+  if (!custom_high.is_zero()) {
+    low_threshold_ = custom_high / 3;
+    elevated_threshold_ = custom_high - low_threshold_;
+    high_threshold_ = custom_high;
+    return;
+  }
+
+  // Use the default values when no override is set.
   low_threshold_ = kDefaultLowThreshold;
   elevated_threshold_ = kDefaultElevatedThreshold;
   high_threshold_ = kDefaultHighThreshold;
@@ -410,8 +425,7 @@
     return false;
   }
 
-  if (network_time - build_date_ >
-      base::TimeDelta::FromDays(kOutdatedBuildAgeInDays)) {
+  if (network_time - build_date_ > kOutdatedBuildAge) {
     UpgradeDetected(is_auto_update_enabled_ ?
         UPGRADE_NEEDED_OUTDATED_INSTALL :
         UPGRADE_NEEDED_OUTDATED_INSTALL_NO_AU);
@@ -422,13 +436,6 @@
   return simulate_outdated;
 }
 
-void UpgradeDetectorImpl::OnExperimentChangesDetected(Severity severity) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  set_best_effort_experiment_updates_available(severity == BEST_EFFORT);
-  set_critical_experiment_updates_available(severity == CRITICAL);
-  StartUpgradeNotificationTimer();
-}
-
 void UpgradeDetectorImpl::UpgradeDetected(UpgradeAvailable upgrade_available) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   set_upgrade_available(upgrade_available);
@@ -440,43 +447,78 @@
   StartUpgradeNotificationTimer();
 }
 
+void UpgradeDetectorImpl::OnExperimentChangesDetected(Severity severity) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  set_best_effort_experiment_updates_available(severity == BEST_EFFORT);
+  set_critical_experiment_updates_available(severity == CRITICAL);
+  StartUpgradeNotificationTimer();
+}
+
 void UpgradeDetectorImpl::NotifyOnUpgradeWithTimePassed(
     base::TimeDelta time_passed) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   const bool is_critical_or_outdated =
       upgrade_available() > UPGRADE_AVAILABLE_REGULAR ||
       critical_experiment_updates_available();
-  if (is_unstable_channel_) {
-    // There's only one threat level for dev and canary channels.
-    if (is_critical_or_outdated) {
-      set_upgrade_notification_stage(UPGRADE_ANNOYANCE_CRITICAL);
-    } else if (time_passed >= low_threshold_) {
-      set_upgrade_notification_stage(UPGRADE_ANNOYANCE_LOW);
+  // There's only one threat level for dev and canary channels.
+  const UpgradeNotificationAnnoyanceLevel max_stage =
+      is_unstable_channel_ ? UPGRADE_ANNOYANCE_LOW : UPGRADE_ANNOYANCE_HIGH;
+  bool notify = true;
 
-      // That's as high as it goes.
-      upgrade_notification_timer_.Stop();
-    } else {
-      return;  // Not ready to recommend upgrade.
-    }
-  } else {
-    // These if statements must be sorted (highest interval first).
-    if (time_passed >= high_threshold_ || is_critical_or_outdated) {
-      set_upgrade_notification_stage(is_critical_or_outdated
-                                         ? UPGRADE_ANNOYANCE_CRITICAL
-                                         : UPGRADE_ANNOYANCE_HIGH);
+  // These if statements must be sorted (highest interval first).
+  if (is_critical_or_outdated)
+    set_upgrade_notification_stage(UPGRADE_ANNOYANCE_CRITICAL);
+  else if (!is_unstable_channel_ && time_passed >= high_threshold_)
+    set_upgrade_notification_stage(UPGRADE_ANNOYANCE_HIGH);
+  else if (!is_unstable_channel_ && time_passed >= elevated_threshold_)
+    set_upgrade_notification_stage(UPGRADE_ANNOYANCE_ELEVATED);
+  else if (time_passed >= low_threshold_)
+    set_upgrade_notification_stage(UPGRADE_ANNOYANCE_LOW);
+  else if (upgrade_notification_stage() != UPGRADE_ANNOYANCE_NONE)
+    set_upgrade_notification_stage(UPGRADE_ANNOYANCE_NONE);
+  else
+    notify = false;  // Not ready to recommend upgrade.
 
-      // We can't get any higher, baby.
-      upgrade_notification_timer_.Stop();
-    } else if (time_passed >= elevated_threshold_) {
-      set_upgrade_notification_stage(UPGRADE_ANNOYANCE_ELEVATED);
-    } else if (time_passed >= low_threshold_) {
-      set_upgrade_notification_stage(UPGRADE_ANNOYANCE_LOW);
-    } else {
-      return;  // Not ready to recommend upgrade.
-    }
+  // Stop the timer if the highest threshold has been reached. Otherwise, make
+  // sure it is running. It is possible that a change in the notification period
+  // (via administrative policy) has moved the detector from high annoyance back
+  // down to a lower level, in which case the timer must be restarted.
+  if (upgrade_notification_stage() >= max_stage)
+    upgrade_notification_timer_.Stop();
+  else
+    StartUpgradeNotificationTimer();
+
+  if (notify)
+    NotifyUpgrade();
+}
+
+base::TimeDelta UpgradeDetectorImpl::GetThresholdForLevel(
+    UpgradeNotificationAnnoyanceLevel level) {
+  switch (level) {
+    case UPGRADE_ANNOYANCE_LOW:
+      return low_threshold_;
+    case UPGRADE_ANNOYANCE_ELEVATED:
+      return elevated_threshold_;
+    case UPGRADE_ANNOYANCE_HIGH:
+      break;
+    case UPGRADE_ANNOYANCE_NONE:
+    case UPGRADE_ANNOYANCE_CRITICAL:
+      NOTREACHED();
+      break;
   }
+  return high_threshold_;
+}
 
-  NotifyUpgrade();
+void UpgradeDetectorImpl::OnRelaunchNotificationPeriodPrefChanged() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Force a recomputation of the thresholds.
+  low_threshold_ = {};
+  InitializeThresholds();
+
+  // Broadcast the appropriate notification if an upgrade has been detected.
+  if (upgrade_available() != UPGRADE_AVAILABLE_NONE)
+    NotifyOnUpgrade();
 }
 
 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
@@ -490,15 +532,16 @@
 
 void UpgradeDetectorImpl::NotifyOnUpgrade() {
   const base::TimeDelta time_passed =
-      base::TimeTicks::Now() - upgrade_detected_time();
+      tick_clock()->NowTicks() - upgrade_detected_time();
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   NotifyOnUpgradeWithTimePassed(time_passed);
 }
 
 // static
 UpgradeDetectorImpl* UpgradeDetectorImpl::GetInstance() {
-  static auto* const instance = new UpgradeDetectorImpl();
-  return instance;
+  static base::NoDestructor<UpgradeDetectorImpl> instance(
+      base::DefaultTickClock::GetInstance());
+  return instance.get();
 }
 
 base::TimeDelta UpgradeDetectorImpl::GetHighAnnoyanceLevelDelta() {
diff --git a/chrome/browser/upgrade_detector_impl.h b/chrome/browser/upgrade_detector_impl.h
index d6ba0062..d1164273 100644
--- a/chrome/browser/upgrade_detector_impl.h
+++ b/chrome/browser/upgrade_detector_impl.h
@@ -17,8 +17,11 @@
 #include "components/variations/service/variations_service.h"
 
 namespace base {
+template <typename T>
+class NoDestructor;
 class SequencedTaskRunner;
 class TaskRunner;
+class TickClock;
 }
 
 // This class contains the non-CrOS desktop implementation of the detector.
@@ -40,7 +43,11 @@
   base::TimeTicks GetHighAnnoyanceDeadline() override;
 
  protected:
-  UpgradeDetectorImpl();
+  explicit UpgradeDetectorImpl(base::TickClock* tick_clock);
+
+  // Sends out a notification and starts a one shot timer to wait until
+  // notifying the user.
+  void UpgradeDetected(UpgradeAvailable upgrade_available);
 
   // variations::VariationsService::Observer:
   void OnExperimentChangesDetected(Severity severity) override;
@@ -49,10 +56,17 @@
   // interval. Exposed as protected for testing.
   void NotifyOnUpgradeWithTimePassed(base::TimeDelta time_passed);
 
+  base::TimeDelta GetThresholdForLevel(UpgradeNotificationAnnoyanceLevel level);
+
  private:
+  friend class base::NoDestructor<UpgradeDetectorImpl>;
+
   // A callback that receives the results of |DetectUpgradeTask|.
   using UpgradeDetectedCallback = base::OnceCallback<void(UpgradeAvailable)>;
 
+  // UpgradeDetector:
+  void OnRelaunchNotificationPeriodPrefChanged() override;
+
 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
   // Receives the results of AreAutoupdatesEnabled and starts the upgrade check
   // timer.
@@ -73,10 +87,6 @@
   // Lazy-initialization for the various threshold deltas (idempotent).
   void InitializeThresholds();
 
-  // Sends out a notification and starts a one shot timer to wait until
-  // notifying the user.
-  void UpgradeDetected(UpgradeAvailable upgrade_available);
-
   // Returns true after calling UpgradeDetected if current install is outdated.
   bool DetectOutdatedInstall();
 
diff --git a/chrome/browser/upgrade_detector_impl_unittest.cc b/chrome/browser/upgrade_detector_impl_unittest.cc
index 20cde04..14735ab 100644
--- a/chrome/browser/upgrade_detector_impl_unittest.cc
+++ b/chrome/browser/upgrade_detector_impl_unittest.cc
@@ -7,18 +7,26 @@
 #include "base/macros.h"
 #include "base/test/scoped_task_environment.h"
 #include "chrome/browser/upgrade_observer.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 
 class TestUpgradeDetectorImpl : public UpgradeDetectorImpl {
  public:
-  TestUpgradeDetectorImpl() = default;
+  explicit TestUpgradeDetectorImpl(base::TickClock* tick_clock)
+      : UpgradeDetectorImpl(tick_clock) {}
   ~TestUpgradeDetectorImpl() override = default;
 
-  // Methods exposed for testing.
+  // Exposed for testing.
+  using UpgradeDetectorImpl::UPGRADE_AVAILABLE_REGULAR;
+  using UpgradeDetectorImpl::UpgradeDetected;
   using UpgradeDetectorImpl::OnExperimentChangesDetected;
   using UpgradeDetectorImpl::NotifyOnUpgradeWithTimePassed;
+  using UpgradeDetectorImpl::GetThresholdForLevel;
 
   // UpgradeDetector:
   void TriggerCriticalUpdate() override {
@@ -62,12 +70,68 @@
   DISALLOW_COPY_AND_ASSIGN(TestUpgradeNotificationListener);
 };
 
+class MockUpgradeObserver : public UpgradeObserver {
+ public:
+  explicit MockUpgradeObserver(UpgradeDetector* upgrade_detector)
+      : upgrade_detector_(upgrade_detector) {
+    upgrade_detector_->AddObserver(this);
+  }
+  ~MockUpgradeObserver() override { upgrade_detector_->RemoveObserver(this); }
+  MOCK_METHOD0(OnUpdateOverCellularAvailable, void());
+  MOCK_METHOD0(OnUpdateOverCellularOneTimePermissionGranted, void());
+  MOCK_METHOD0(OnUpgradeRecommended, void());
+  MOCK_METHOD0(OnCriticalUpgradeInstalled, void());
+  MOCK_METHOD0(OnOutdatedInstall, void());
+  MOCK_METHOD0(OnOutdatedInstallNoAutoUpdate, void());
+
+ private:
+  UpgradeDetector* const upgrade_detector_;
+  DISALLOW_COPY_AND_ASSIGN(MockUpgradeObserver);
+};
+
 }  // namespace
 
-TEST(UpgradeDetectorImplTest, VariationsChanges) {
-  base::test::ScopedTaskEnvironment task_environment;
+class UpgradeDetectorImplTest : public ::testing::Test {
+ protected:
+  UpgradeDetectorImplTest()
+      : scoped_task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME),
+        scoped_local_state_(TestingBrowserProcess::GetGlobal()) {}
 
-  TestUpgradeDetectorImpl detector;
+  base::TickClock* GetMockTickClock() {
+    return scoped_task_environment_.GetMockTickClock();
+  }
+
+  // Sets the browser.relaunch_notification_period preference in Local State to
+  // |value|.
+  void SetNotificationPeriodPref(base::TimeDelta value) {
+    if (value.is_zero()) {
+      scoped_local_state_.Get()->RemoveManagedPref(
+          prefs::kRelaunchNotificationPeriod);
+    } else {
+      scoped_local_state_.Get()->SetManagedPref(
+          prefs::kRelaunchNotificationPeriod,
+          std::make_unique<base::Value>(
+              base::saturated_cast<int>(value.InMilliseconds())));
+    }
+  }
+
+  void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
+
+  // Fast-forwards virtual time by |delta|.
+  void FastForwardBy(base::TimeDelta delta) {
+    scoped_task_environment_.FastForwardBy(delta);
+  }
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  ScopedTestingLocalState scoped_local_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpgradeDetectorImplTest);
+};
+
+TEST_F(UpgradeDetectorImplTest, VariationsChanges) {
+  TestUpgradeDetectorImpl detector(GetMockTickClock());
   TestUpgradeNotificationListener notifications_listener(&detector);
   EXPECT_FALSE(detector.notify_upgrade());
   EXPECT_EQ(0, notifications_listener.notification_count());
@@ -84,13 +148,11 @@
 
   // Execute tasks posted by |detector| referencing it while it's still in
   // scope.
-  task_environment.RunUntilIdle();
+  RunUntilIdle();
 }
 
-TEST(UpgradeDetectorImplTest, VariationsCriticalChanges) {
-  base::test::ScopedTaskEnvironment task_environment;
-
-  TestUpgradeDetectorImpl detector;
+TEST_F(UpgradeDetectorImplTest, VariationsCriticalChanges) {
+  TestUpgradeDetectorImpl detector(GetMockTickClock());
   TestUpgradeNotificationListener notifications_listener(&detector);
   EXPECT_FALSE(detector.notify_upgrade());
   EXPECT_EQ(0, notifications_listener.notification_count());
@@ -107,5 +169,111 @@
 
   // Execute tasks posted by |detector| referencing it while it's still in
   // scope.
-  task_environment.RunUntilIdle();
+  RunUntilIdle();
+}
+
+TEST_F(UpgradeDetectorImplTest, TestPeriodChanges) {
+  // Fast forward a little bit to get away from zero ticks, which has special
+  // meaning in the detector.
+  FastForwardBy(base::TimeDelta::FromHours(1));
+
+  TestUpgradeDetectorImpl upgrade_detector(GetMockTickClock());
+  ::testing::StrictMock<MockUpgradeObserver> mock_observer(&upgrade_detector);
+
+  // Changing the period when no upgrade has been detected updates the
+  // thresholds and nothing else.
+  SetNotificationPeriodPref(base::TimeDelta::FromHours(3));
+
+  EXPECT_EQ(upgrade_detector.GetThresholdForLevel(
+                UpgradeDetector::UPGRADE_ANNOYANCE_HIGH),
+            base::TimeDelta::FromHours(3));
+  ::testing::Mock::VerifyAndClear(&mock_observer);
+
+  // Back to default.
+  SetNotificationPeriodPref(base::TimeDelta());
+  EXPECT_EQ(upgrade_detector.GetThresholdForLevel(
+                UpgradeDetector::UPGRADE_ANNOYANCE_HIGH),
+            base::TimeDelta::FromDays(7));
+  ::testing::Mock::VerifyAndClear(&mock_observer);
+
+  // Pretend that an upgrade was just detected now.
+  upgrade_detector.UpgradeDetected(
+      TestUpgradeDetectorImpl::UPGRADE_AVAILABLE_REGULAR);
+  ::testing::Mock::VerifyAndClear(&mock_observer);
+
+  // Fast forward an amount that is still in the "don't annoy me" period at the
+  // default period.
+  FastForwardBy(base::TimeDelta::FromHours(1));
+  ::testing::Mock::VerifyAndClear(&mock_observer);
+
+  // Drop the period so that the current time is in the "low" annoyance level.
+  EXPECT_CALL(mock_observer, OnUpgradeRecommended());
+  SetNotificationPeriodPref(base::TimeDelta::FromHours(3));
+  ::testing::Mock::VerifyAndClear(&mock_observer);
+  EXPECT_EQ(upgrade_detector.upgrade_notification_stage(),
+            UpgradeDetector::UPGRADE_ANNOYANCE_LOW);
+
+  // Bring it back up.
+  EXPECT_CALL(mock_observer, OnUpgradeRecommended());
+  SetNotificationPeriodPref(base::TimeDelta());
+  ::testing::Mock::VerifyAndClear(&mock_observer);
+  EXPECT_EQ(upgrade_detector.upgrade_notification_stage(),
+            UpgradeDetector::UPGRADE_ANNOYANCE_NONE);
+
+  // Fast forward an amount that is still in the "don't annoy me" period at the
+  // default period.
+  FastForwardBy(base::TimeDelta::FromHours(1));
+  ::testing::Mock::VerifyAndClear(&mock_observer);
+
+  // Drop the period so that the current time is in the "elevated" level.
+  EXPECT_CALL(mock_observer, OnUpgradeRecommended());
+  SetNotificationPeriodPref(base::TimeDelta::FromHours(3));
+  ::testing::Mock::VerifyAndClear(&mock_observer);
+  EXPECT_EQ(upgrade_detector.upgrade_notification_stage(),
+            UpgradeDetector::UPGRADE_ANNOYANCE_ELEVATED);
+
+  // Bring it back up.
+  EXPECT_CALL(mock_observer, OnUpgradeRecommended());
+  SetNotificationPeriodPref(base::TimeDelta());
+  ::testing::Mock::VerifyAndClear(&mock_observer);
+  EXPECT_EQ(upgrade_detector.upgrade_notification_stage(),
+            UpgradeDetector::UPGRADE_ANNOYANCE_NONE);
+
+  // Fast forward an amount that is still in the "don't annoy me" period at the
+  // default period.
+  FastForwardBy(base::TimeDelta::FromHours(1));
+  ::testing::Mock::VerifyAndClear(&mock_observer);
+
+  // Drop the period so that the current time is in the "high" level.
+  EXPECT_CALL(mock_observer, OnUpgradeRecommended());
+  SetNotificationPeriodPref(base::TimeDelta::FromHours(3));
+  ::testing::Mock::VerifyAndClear(&mock_observer);
+  EXPECT_EQ(upgrade_detector.upgrade_notification_stage(),
+            UpgradeDetector::UPGRADE_ANNOYANCE_HIGH);
+
+  // Bring it back up.
+  EXPECT_CALL(mock_observer, OnUpgradeRecommended());
+  SetNotificationPeriodPref(base::TimeDelta());
+  ::testing::Mock::VerifyAndClear(&mock_observer);
+  EXPECT_EQ(upgrade_detector.upgrade_notification_stage(),
+            UpgradeDetector::UPGRADE_ANNOYANCE_NONE);
+
+  // Fast forward an amount that is still in the "don't annoy me" period at the
+  // default period.
+  FastForwardBy(base::TimeDelta::FromHours(1));
+  ::testing::Mock::VerifyAndClear(&mock_observer);
+
+  // Drop the period so that the current time is deep in the "high" level.
+  EXPECT_CALL(mock_observer, OnUpgradeRecommended());
+  SetNotificationPeriodPref(base::TimeDelta::FromHours(3));
+  ::testing::Mock::VerifyAndClear(&mock_observer);
+  EXPECT_EQ(upgrade_detector.upgrade_notification_stage(),
+            UpgradeDetector::UPGRADE_ANNOYANCE_HIGH);
+
+  // Bring it back up.
+  EXPECT_CALL(mock_observer, OnUpgradeRecommended());
+  SetNotificationPeriodPref(base::TimeDelta());
+  ::testing::Mock::VerifyAndClear(&mock_observer);
+  EXPECT_EQ(upgrade_detector.upgrade_notification_stage(),
+            UpgradeDetector::UPGRADE_ANNOYANCE_NONE);
 }
diff --git a/chrome/browser/upgrade_observer.h b/chrome/browser/upgrade_observer.h
index 4fe5838..02cb6f1 100644
--- a/chrome/browser/upgrade_observer.h
+++ b/chrome/browser/upgrade_observer.h
@@ -18,9 +18,9 @@
   virtual void OnUpdateOverCellularOneTimePermissionGranted() {}
 
   // Triggered when Chrome believes an update has been installed and available
-  // for long enough with the user shutting down to let it take effect. See
-  // upgrade_detector.cc for details on how long it waits. No details are
-  // expected.
+  // for long enough with the user shutting down to let it take effect, or
+  // following a change to the thresholds that move the UpgradeDetector through
+  // the low, elevated, and high annoyance levels. No details are expected.
   virtual void OnUpgradeRecommended() {}
 
   // Triggered when a critical update has been installed. No details are
diff --git a/chrome/browser/vr/databinding/binding.h b/chrome/browser/vr/databinding/binding.h
index ab747da8..3bded41d 100644
--- a/chrome/browser/vr/databinding/binding.h
+++ b/chrome/browser/vr/databinding/binding.h
@@ -32,6 +32,11 @@
       : getter_(getter), setter_(setter) {}
 
   Binding(const base::RepeatingCallback<T()>& getter,
+          const base::RepeatingCallback<void(const base::Optional<T>&,
+                                             const T&)>& setter)
+      : getter_(getter), historic_setter_(setter) {}
+
+  Binding(const base::RepeatingCallback<T()>& getter,
           const std::string& getter_text,
           const base::RepeatingCallback<void(const T&)>& setter,
           const std::string& setter_text)
@@ -39,6 +44,16 @@
         setter_(setter),
         getter_text_(getter_text),
         setter_text_(setter_text) {}
+
+  Binding(const base::RepeatingCallback<T()>& getter,
+          const std::string& getter_text,
+          const base::RepeatingCallback<void(const base::Optional<T>&,
+                                             const T&)>& setter,
+          const std::string& setter_text)
+      : getter_(getter),
+        historic_setter_(setter),
+        getter_text_(getter_text),
+        setter_text_(setter_text) {}
   ~Binding() override = default;
 
   // This function will check if the getter is producing a different value than
@@ -48,8 +63,11 @@
     T current_value = getter_.Run();
     if (last_value_ && current_value == last_value_.value())
       return false;
+    if (setter_)
+      setter_.Run(current_value);
+    if (historic_setter_)
+      historic_setter_.Run(last_value_, current_value);
     last_value_ = current_value;
-    setter_.Run(current_value);
     return true;
   }
 
@@ -64,6 +82,8 @@
  private:
   base::RepeatingCallback<T()> getter_;
   base::RepeatingCallback<void(const T&)> setter_;
+  base::RepeatingCallback<void(const base::Optional<T>&, const T&)>
+      historic_setter_;
   base::Optional<T> last_value_;
 
   std::string getter_text_;
diff --git a/chrome/browser/vr/databinding/binding_unittest.cc b/chrome/browser/vr/databinding/binding_unittest.cc
index 6b18f41..d072449 100644
--- a/chrome/browser/vr/databinding/binding_unittest.cc
+++ b/chrome/browser/vr/databinding/binding_unittest.cc
@@ -55,4 +55,37 @@
   EXPECT_EQ(true, b.value);
 }
 
+TEST(Binding, HistoricBinding) {
+  TestModel a;
+  a.value = true;
+
+  TestView b;
+  b.value = false;
+
+  b.binding = std::make_unique<Binding<bool>>(
+      VR_BIND_LAMBDA([](TestModel* m) { return m->value; },
+                     base::Unretained(&a)),
+      VR_BIND_LAMBDA(
+          [](TestView* v, const base::Optional<bool>& last_value,
+             const bool& value) {
+            if (last_value)
+              v->value = value;
+          },
+          base::Unretained(&b)));
+
+  EXPECT_NE(a.value, b.value);
+  b.binding->Update();
+
+  EXPECT_EQ(true, a.value);
+  EXPECT_EQ(false, b.value);
+
+  a.value = false;
+  b.binding->Update();
+  EXPECT_EQ(false, b.value);
+
+  a.value = true;
+  b.binding->Update();
+  EXPECT_EQ(true, b.value);
+}
+
 }  // namespace vr
diff --git a/chrome/browser/vr/elements/button.cc b/chrome/browser/vr/elements/button.cc
index 4cd7c9b..0a867861 100644
--- a/chrome/browser/vr/elements/button.cc
+++ b/chrome/browser/vr/elements/button.cc
@@ -30,7 +30,9 @@
   background->SetType(kTypeButtonBackground);
   background->set_bubble_events(true);
   background->set_contributes_to_parent_bounds(false);
-  background->SetTransitionedProperties({TRANSFORM});
+  background->SetColor(colors_.background);
+  background->SetTransitionedProperties(
+      {TRANSFORM, BACKGROUND_COLOR, FOREGROUND_COLOR});
   background_ = background.get();
   AddChild(std::move(background));
 
diff --git a/chrome/browser/vr/elements/omnibox_formatting.cc b/chrome/browser/vr/elements/omnibox_formatting.cc
index 5ddc59bd..f5716c269 100644
--- a/chrome/browser/vr/elements/omnibox_formatting.cc
+++ b/chrome/browser/vr/elements/omnibox_formatting.cc
@@ -16,7 +16,7 @@
     size_t text_length,
     const ColorScheme& color_scheme) {
   TextFormatting formatting;
-  formatting.push_back(TextFormattingAttribute(color_scheme.suggestion_text,
+  formatting.push_back(TextFormattingAttribute(color_scheme.url_bar_text,
                                                gfx::Range(0, text_length)));
 
   for (size_t i = 0; i < classifications.size(); ++i) {
@@ -41,11 +41,8 @@
     }
 
     if (classifications[i].style & ACMatchClassification::URL) {
-      formatting.push_back(TextFormattingAttribute(
-          color_scheme.suggestion_url_text, current_range));
-    } else if (classifications[i].style & ACMatchClassification::DIM) {
-      formatting.push_back(TextFormattingAttribute(
-          color_scheme.suggestion_dim_text, current_range));
+      formatting.push_back(
+          TextFormattingAttribute(color_scheme.hyperlink, current_range));
     } else if (classifications[i].style & ACMatchClassification::INVISIBLE) {
       formatting.push_back(
           TextFormattingAttribute(SK_ColorTRANSPARENT, current_range));
diff --git a/chrome/browser/vr/elements/omnibox_formatting_unittest.cc b/chrome/browser/vr/elements/omnibox_formatting_unittest.cc
index 891009b2..219cd499 100644
--- a/chrome/browser/vr/elements/omnibox_formatting_unittest.cc
+++ b/chrome/browser/vr/elements/omnibox_formatting_unittest.cc
@@ -16,7 +16,6 @@
 namespace {
 
 constexpr SkColor kDefaultColor = 0xFF000001;
-constexpr SkColor kDimColor = 0xFF000002;
 constexpr SkColor kUrlColor = 0xFF000003;
 
 constexpr bool kNoOffset = false;
@@ -29,21 +28,19 @@
       ACMatchClassification(0, ACMatchClassification::NONE),
       ACMatchClassification(1, ACMatchClassification::URL),
       ACMatchClassification(2, ACMatchClassification::MATCH),
-      ACMatchClassification(3, ACMatchClassification::DIM),
-      ACMatchClassification(4, ACMatchClassification::INVISIBLE),
+      ACMatchClassification(3, ACMatchClassification::INVISIBLE),
   };
   size_t string_length = classifications.size();
 
   ColorScheme color_scheme;
   memset(&color_scheme, 0, sizeof(color_scheme));
-  color_scheme.suggestion_text = kDefaultColor;
-  color_scheme.suggestion_dim_text = kDimColor;
-  color_scheme.suggestion_url_text = kUrlColor;
+  color_scheme.url_bar_text = kDefaultColor;
+  color_scheme.hyperlink = kUrlColor;
 
   TextFormatting formatting =
       ConvertClassification(classifications, string_length, color_scheme);
 
-  ASSERT_EQ(formatting.size(), 6u);
+  ASSERT_EQ(formatting.size(), 5u);
 
   EXPECT_EQ(formatting[0].type(), TextFormattingAttribute::COLOR);
   EXPECT_EQ(formatting[0].color(), kDefaultColor);
@@ -62,12 +59,8 @@
   EXPECT_EQ(formatting[3].range(), gfx::Range(2, 3));
 
   EXPECT_EQ(formatting[4].type(), TextFormattingAttribute::COLOR);
-  EXPECT_EQ(formatting[4].color(), kDimColor);
+  EXPECT_EQ(formatting[4].color(), SK_ColorTRANSPARENT);
   EXPECT_EQ(formatting[4].range(), gfx::Range(3, 4));
-
-  EXPECT_EQ(formatting[5].type(), TextFormattingAttribute::COLOR);
-  EXPECT_EQ(formatting[5].color(), SK_ColorTRANSPARENT);
-  EXPECT_EQ(formatting[5].range(), gfx::Range(4, 5));
 }
 
 struct ElisionTestcase {
diff --git a/chrome/browser/vr/elements/ui_element_name.cc b/chrome/browser/vr/elements/ui_element_name.cc
index 288500ba..a936514 100644
--- a/chrome/browser/vr/elements/ui_element_name.cc
+++ b/chrome/browser/vr/elements/ui_element_name.cc
@@ -68,7 +68,7 @@
     "kOmniboxVisibilityControlForAudioPermissionPrompt",
     "kOmniboxDmmRoot",
     "kOmniboxRoot",
-    "kOmniboxContainer",
+    "kOmniboxBackground",
     "kOmniboxTextField",
     "kOmniboxTextFieldLayout",
     "kOmniboxVoiceSearchButton",
@@ -76,7 +76,6 @@
     "kOmniboxSuggestions",
     "kOmniboxSuggestionsOuterLayout",
     "kOmniboxOuterLayout",
-    "kOmniboxOuterLayoutSpacer",
     "kOmniboxShadow",
     "k2dBrowsingVisibiltyControlForVoice",
     "k2dBrowsingVisibilityControlForPrompt",
diff --git a/chrome/browser/vr/elements/ui_element_name.h b/chrome/browser/vr/elements/ui_element_name.h
index e6dc723..5d25186 100644
--- a/chrome/browser/vr/elements/ui_element_name.h
+++ b/chrome/browser/vr/elements/ui_element_name.h
@@ -67,7 +67,7 @@
   kOmniboxVisibilityControlForAudioPermissionPrompt,
   kOmniboxDmmRoot,
   kOmniboxRoot,
-  kOmniboxContainer,
+  kOmniboxBackground,
   kOmniboxTextField,
   kOmniboxTextFieldLayout,
   kOmniboxVoiceSearchButton,
@@ -75,7 +75,6 @@
   kOmniboxSuggestions,
   kOmniboxSuggestionsOuterLayout,
   kOmniboxOuterLayout,
-  kOmniboxOuterLayoutSpacer,
   kOmniboxShadow,
   k2dBrowsingVisibiltyControlForVoice,
   k2dBrowsingVisibilityControlForPrompt,
diff --git a/chrome/browser/vr/elements/url_bar.cc b/chrome/browser/vr/elements/url_bar.cc
index 4c54977b..f503896 100644
--- a/chrome/browser/vr/elements/url_bar.cc
+++ b/chrome/browser/vr/elements/url_bar.cc
@@ -25,7 +25,7 @@
   texture_->SetToolbarState(state);
 }
 
-void UrlBar::SetColors(const UrlBarColors& colors) {
+void UrlBar::SetColors(const UrlTextColors& colors) {
   texture_->SetColors(colors);
 }
 
diff --git a/chrome/browser/vr/elements/url_bar.h b/chrome/browser/vr/elements/url_bar.h
index a69ff48..64b5317 100644
--- a/chrome/browser/vr/elements/url_bar.h
+++ b/chrome/browser/vr/elements/url_bar.h
@@ -19,7 +19,7 @@
 
 class UrlBarTexture;
 struct ToolbarState;
-struct UrlBarColors;
+struct UrlTextColors;
 
 class UrlBar : public TexturedElement {
  public:
@@ -29,7 +29,7 @@
   ~UrlBar() override;
 
   void SetToolbarState(const ToolbarState& state);
-  void SetColors(const UrlBarColors& colors);
+  void SetColors(const UrlTextColors& colors);
 
  private:
   UiTexture* GetTexture() const override;
diff --git a/chrome/browser/vr/elements/url_bar_texture.cc b/chrome/browser/vr/elements/url_bar_texture.cc
index 1da6cac9..0aa9c05 100644
--- a/chrome/browser/vr/elements/url_bar_texture.cc
+++ b/chrome/browser/vr/elements/url_bar_texture.cc
@@ -35,7 +35,7 @@
 void SetEmphasis(RenderTextWrapper* render_text,
                  bool emphasis,
                  const gfx::Range& range,
-                 const UrlBarColors& colors) {
+                 const UrlTextColors& colors) {
   SkColor color = emphasis ? colors.emphasized : colors.deemphasized;
   if (range.IsValid()) {
     render_text->ApplyColor(color, range);
@@ -99,7 +99,7 @@
   return pixels * kWidth / size_.width();
 }
 
-void UrlBarTexture::SetColors(const UrlBarColors& colors) {
+void UrlBarTexture::SetColors(const UrlTextColors& colors) {
   SetAndDirty(&colors_, colors);
 }
 
@@ -148,11 +148,10 @@
 
 // static
 // This method replicates behavior in OmniboxView::UpdateTextStyle().
-void UrlBarTexture::ApplyUrlStyling(
-    const base::string16& formatted_url,
-    const url::Parsed& parsed,
-    RenderTextWrapper* render_text,
-    const UrlBarColors& colors) {
+void UrlBarTexture::ApplyUrlStyling(const base::string16& formatted_url,
+                                    const url::Parsed& parsed,
+                                    RenderTextWrapper* render_text,
+                                    const UrlTextColors& colors) {
   const url::Component& scheme = parsed.scheme;
   const url::Component& host = parsed.host;
 
diff --git a/chrome/browser/vr/elements/url_bar_texture.h b/chrome/browser/vr/elements/url_bar_texture.h
index a301171..b97c40da 100644
--- a/chrome/browser/vr/elements/url_bar_texture.h
+++ b/chrome/browser/vr/elements/url_bar_texture.h
@@ -31,14 +31,14 @@
   gfx::Size GetPreferredTextureSize(int width) const override;
   gfx::SizeF GetDrawnSize() const override;
 
-  void SetColors(const UrlBarColors& colors);
+  void SetColors(const UrlTextColors& colors);
   void SetToolbarState(const ToolbarState& state);
 
  protected:
   static void ApplyUrlStyling(const base::string16& formatted_url,
                               const url::Parsed& parsed,
                               RenderTextWrapper* render_text,
-                              const UrlBarColors& colors);
+                              const UrlTextColors& colors);
 
  private:
   void Draw(SkCanvas* canvas, const gfx::Size& texture_size) override;
@@ -47,7 +47,7 @@
 
   gfx::SizeF size_;
   ToolbarState state_;
-  UrlBarColors colors_;
+  UrlTextColors colors_;
 
   base::Callback<void(UiUnsupportedMode)> failure_callback_;
 
diff --git a/chrome/browser/vr/elements/url_bar_texture_unittest.cc b/chrome/browser/vr/elements/url_bar_texture_unittest.cc
index 6350e90..776459f 100644
--- a/chrome/browser/vr/elements/url_bar_texture_unittest.cc
+++ b/chrome/browser/vr/elements/url_bar_texture_unittest.cc
@@ -27,15 +27,10 @@
 
 namespace vr {
 
-// TODO(cjgrant): Use ColorScheme instead of hardcoded values
-// where it makes sense.
-static const SkColor kEmphasizedColor = SK_ColorBLACK;
-static const SkColor kDeemphasizedColor = 0xFF5A5A5A;
+constexpr SkColor kEmphasizedColor = 0x01010101;
+constexpr SkColor kDeemphasizedColor = 0x02020202;
 
-static const SkColor kIncognitoDeemphasizedColor = 0xCCFFFFFF;
-static const SkColor kIncognitoEmphasizedColor = 0xFFFFFFFF;
-
-static constexpr int kUrlWidthPixels = 1024;
+constexpr int kUrlWidthPixels = 1024;
 
 class TestUrlBarTexture : public UrlBarTexture {
  public:
@@ -62,8 +57,10 @@
   static void TestUrlStyling(const base::string16& formatted_url,
                              const url::Parsed& parsed,
                              security_state::SecurityLevel security_level,
-                             vr::RenderTextWrapper* render_text,
-                             const UrlBarColors& colors) {
+                             vr::RenderTextWrapper* render_text) {
+    UrlTextColors colors;
+    colors.deemphasized = kDeemphasizedColor;
+    colors.emphasized = kEmphasizedColor;
     ApplyUrlStyling(formatted_url, parsed, render_text, colors);
   }
 
@@ -102,7 +99,11 @@
           base::BindRepeating(&TestUrlBarTexture::OnUnsupportedFeature,
                               base::Unretained(this))) {
   gfx::FontList::SetDefaultFontDescription("Arial, Times New Roman, 15px");
-  SetColors(ColorScheme::GetColorScheme(ColorScheme::kModeNormal).url_bar);
+
+  UrlTextColors colors;
+  colors.deemphasized = kDeemphasizedColor;
+  colors.emphasized = kEmphasizedColor;
+  SetColors(colors);
   SetBackgroundColor(SK_ColorBLACK);
   SetForegroundColor(SK_ColorWHITE);
 }
@@ -118,12 +119,7 @@
         url, GetVrFormatUrlTypes(), net::UnescapeRule::NORMAL, &parsed, nullptr,
         nullptr);
     EXPECT_EQ(formatted_url, base::UTF8ToUTF16(expected_string));
-    TestUrlBarTexture::TestUrlStyling(
-        formatted_url, parsed, level, &mock_,
-        ColorScheme::GetColorScheme(ColorScheme::kModeNormal).url_bar);
-    TestUrlBarTexture::TestUrlStyling(
-        formatted_url, parsed, level, &mock_,
-        ColorScheme::GetColorScheme(ColorScheme::kModeIncognito).url_bar);
+    TestUrlBarTexture::TestUrlStyling(formatted_url, parsed, level, &mock_);
   }
 
   testing::InSequence in_sequence_;
@@ -143,16 +139,12 @@
 TEST_F(UrlEmphasisTest, SecureHttpsHost) {
   EXPECT_CALL(mock_, SetColor(kDeemphasizedColor));
   EXPECT_CALL(mock_, ApplyColor(kEmphasizedColor, gfx::Range(0, 8)));
-  EXPECT_CALL(mock_, SetColor(kIncognitoDeemphasizedColor));
-  EXPECT_CALL(mock_, ApplyColor(kIncognitoEmphasizedColor, gfx::Range(0, 8)));
   Verify("https://host.com/page", SecurityLevel::SECURE, "host.com/page");
 }
 
 TEST_F(UrlEmphasisTest, NotSecureHttpsHost) {
   EXPECT_CALL(mock_, SetColor(kDeemphasizedColor));
   EXPECT_CALL(mock_, ApplyColor(kEmphasizedColor, gfx::Range(0, 8)));
-  EXPECT_CALL(mock_, SetColor(kIncognitoDeemphasizedColor));
-  EXPECT_CALL(mock_, ApplyColor(kIncognitoEmphasizedColor, gfx::Range(0, 8)));
   Verify("https://host.com/page", SecurityLevel::HTTP_SHOW_WARNING,
          "host.com/page");
 }
@@ -160,8 +152,6 @@
 TEST_F(UrlEmphasisTest, NotSecureHttpHost) {
   EXPECT_CALL(mock_, SetColor(kDeemphasizedColor));
   EXPECT_CALL(mock_, ApplyColor(kEmphasizedColor, gfx::Range(0, 8)));
-  EXPECT_CALL(mock_, SetColor(kIncognitoDeemphasizedColor));
-  EXPECT_CALL(mock_, ApplyColor(kIncognitoEmphasizedColor, gfx::Range(0, 8)));
   Verify("http://host.com/page", SecurityLevel::HTTP_SHOW_WARNING,
          "host.com/page");
 }
@@ -169,8 +159,6 @@
 TEST_F(UrlEmphasisTest, Data) {
   EXPECT_CALL(mock_, SetColor(kDeemphasizedColor));
   EXPECT_CALL(mock_, ApplyColor(kEmphasizedColor, gfx::Range(0, 4)));
-  EXPECT_CALL(mock_, SetColor(kIncognitoDeemphasizedColor));
-  EXPECT_CALL(mock_, ApplyColor(kIncognitoEmphasizedColor, gfx::Range(0, 4)));
   Verify("data:text/html,lots of data", SecurityLevel::NONE,
          "data:text/html,lots of data");
 }
diff --git a/chrome/browser/vr/model/color_scheme.cc b/chrome/browser/vr/model/color_scheme.cc
index b298ad0..2218b04 100644
--- a/chrome/browser/vr/model/color_scheme.cc
+++ b/chrome/browser/vr/model/color_scheme.cc
@@ -13,6 +13,25 @@
 
 namespace {
 
+SkColor BuildColor(uint32_t color, int percentage) {
+  DCHECK_GE(percentage, 0);
+  DCHECK_LE(percentage, 100);
+  return (static_cast<uint8_t>((2.55f * percentage + 0.5)) << 24) | color;
+}
+
+SkColor MakeColor(uint32_t color, int percentage) {
+  DCHECK(!(color & 0xFF000000));
+  return BuildColor(color, percentage);
+}
+
+SkColor MakeBlack(int percentage) {
+  return BuildColor(0, percentage);
+}
+
+SkColor MakeWhite(int percentage) {
+  return BuildColor(0xFFFFFF, percentage);
+}
+
 base::LazyInstance<ColorScheme>::Leaky g_normal_scheme =
     LAZY_INSTANCE_INITIALIZER;
 base::LazyInstance<ColorScheme>::Leaky g_fullscreen_scheme =
@@ -31,17 +50,11 @@
   normal_scheme.ceiling = normal_scheme.floor;
   normal_scheme.floor_grid = 0x26FFFFFF;
   normal_scheme.web_vr_background = SK_ColorBLACK;
-  normal_scheme.element_foreground = 0xFF333333;
-  normal_scheme.element_background = 0xCCB3B3B3;
-  normal_scheme.element_background_hover = 0xCCE3E3E3;
-  normal_scheme.element_background_down = 0xCCF3F3F3;
-  normal_scheme.button_colors.foreground = 0x87000000;
-  normal_scheme.button_colors.background = normal_scheme.element_background;
-  normal_scheme.button_colors.background_hover =
-      normal_scheme.element_background_hover;
-  normal_scheme.button_colors.background_down =
-      normal_scheme.element_background_down;
-  normal_scheme.button_colors.foreground_disabled = 0x33333333;
+  normal_scheme.disc_button_colors.foreground = 0x87000000;
+  normal_scheme.disc_button_colors.foreground_disabled = 0x33333333;
+  normal_scheme.disc_button_colors.background = 0xCCB3B3B3;
+  normal_scheme.disc_button_colors.background_hover = 0xCCE3E3E3;
+  normal_scheme.disc_button_colors.background_down = 0xCCF3F3F3;
   normal_scheme.loading_indicator_foreground = 0xFF2979FF;
   normal_scheme.loading_indicator_background = 0xFF454545;
   normal_scheme.exit_warning_foreground = SK_ColorWHITE;
@@ -50,14 +63,12 @@
   normal_scheme.web_vr_transient_toast_background = SK_ColorBLACK;
   normal_scheme.exclusive_screen_toast_foreground = 0xCCFFFFFF;
   normal_scheme.exclusive_screen_toast_background = 0xCC2F2F2F;
-  normal_scheme.system_indicator_foreground = SK_ColorWHITE;
-  normal_scheme.system_indicator_background = 0x99212121;
   normal_scheme.modal_prompt_icon_foreground = 0xFF4285F4;
   normal_scheme.modal_prompt_background = 0xFFF5F5F5;
   normal_scheme.modal_prompt_foreground = 0xFF333333;
   normal_scheme.modal_prompt_secondary_button_colors.foreground = 0xFF4285F4;
   normal_scheme.modal_prompt_secondary_button_colors.foreground_disabled =
-      normal_scheme.button_colors.foreground_disabled;
+      normal_scheme.disc_button_colors.foreground_disabled;
   normal_scheme.modal_prompt_secondary_button_colors.background =
       normal_scheme.modal_prompt_background;
   normal_scheme.modal_prompt_secondary_button_colors.background_hover =
@@ -67,24 +78,11 @@
   normal_scheme.modal_prompt_primary_button_colors.foreground =
       normal_scheme.modal_prompt_background;
   normal_scheme.modal_prompt_secondary_button_colors.foreground_disabled =
-      normal_scheme.button_colors.foreground_disabled;
+      normal_scheme.disc_button_colors.foreground_disabled;
   normal_scheme.modal_prompt_primary_button_colors.background = 0xFF4285F4;
   normal_scheme.modal_prompt_primary_button_colors.background_hover =
       0xFF3E7DE6;
   normal_scheme.modal_prompt_primary_button_colors.background_down = 0xFF3E7DE6;
-  normal_scheme.url_bar_button.background = SK_ColorTRANSPARENT;
-  normal_scheme.url_bar_button.background_down =
-      normal_scheme.element_background_down;
-  normal_scheme.url_bar_button.background_hover =
-      normal_scheme.element_background_hover;
-  normal_scheme.url_bar_button.foreground = normal_scheme.element_foreground;
-  normal_scheme.url_bar_button.foreground_disabled = 0x33333333;
-  normal_scheme.url_bar_separator = 0xFF9E9E9E;
-  normal_scheme.url_bar_hint_text = 0xFF5A5A5A;
-  normal_scheme.url_bar_default_icon = 0xFF535353;
-  normal_scheme.url_bar_dangerous_icon = gfx::kGoogleRed700;
-  normal_scheme.url_bar.deemphasized = 0xFF5A5A5A;
-  normal_scheme.url_bar.emphasized = SK_ColorBLACK;
   normal_scheme.prompt_foreground = 0xCC000000;
   normal_scheme.prompt_primary_button_colors.foreground = 0xA6000000;
   normal_scheme.prompt_primary_button_colors.foreground_disabled = 0xA6000000;
@@ -96,6 +94,25 @@
   normal_scheme.prompt_secondary_button_colors.background = 0x66FFFFFF;
   normal_scheme.prompt_secondary_button_colors.background_hover = 0xFFFFFFFF;
   normal_scheme.prompt_secondary_button_colors.background_down = 0xE6FFFFFF;
+
+  normal_scheme.url_bar_background = 0xCCB3B3B3;
+  normal_scheme.url_bar_separator = MakeBlack(12);
+  normal_scheme.url_bar_text = MakeBlack(65);
+  normal_scheme.url_bar_hint_text = MakeBlack(50);
+  normal_scheme.url_bar_dangerous_icon = gfx::kGoogleRed700;
+  normal_scheme.url_bar_button.background = SK_ColorTRANSPARENT;
+  normal_scheme.url_bar_button.background_hover = MakeBlack(8);
+  normal_scheme.url_bar_button.background_down = MakeBlack(8);
+  normal_scheme.url_bar_button.foreground = MakeBlack(65);
+  normal_scheme.url_bar_button.foreground_disabled = MakeBlack(24);
+  normal_scheme.url_text.deemphasized = MakeBlack(30);
+  normal_scheme.url_text.emphasized = MakeBlack(80);
+  normal_scheme.omnibox_background = 0xFFEEEEEE;
+  normal_scheme.omnibox_text_selection.cursor = 0xFF5595FE;      // TODO
+  normal_scheme.omnibox_text_selection.background = 0xFFC6DAFC;  // TODO
+  normal_scheme.omnibox_text_selection.foreground = normal_scheme.url_bar_text;
+  normal_scheme.hyperlink = MakeColor(0x4285F4, 100);
+
   normal_scheme.dimmer_inner = 0xCC0D0D0D;
   normal_scheme.dimmer_outer = 0xE6000000;
   normal_scheme.splash_screen_background = SK_ColorBLACK;
@@ -105,30 +122,6 @@
   normal_scheme.web_vr_timeout_message_foreground =
       normal_scheme.web_vr_timeout_spinner;
   normal_scheme.speech_recognition_circle_background = 0xFF4285F4;
-  normal_scheme.omnibox_background = 0xFFEEEEEE;
-  normal_scheme.omnibox_icon = 0xA6000000;
-  normal_scheme.omnibox_text = 0xFF595959;
-  normal_scheme.omnibox_hint = 0xFF999999;
-  normal_scheme.omnibox_text_selection.cursor = 0xFF5595FE;
-  normal_scheme.omnibox_text_selection.background = 0xFFC6DAFC;
-  normal_scheme.omnibox_text_selection.foreground = normal_scheme.omnibox_text;
-  normal_scheme.suggestion_text = 0xFF595959;
-  normal_scheme.suggestion_dim_text = 0xFF999999;
-  normal_scheme.suggestion_url_text = 0xFF5595FE;
-  normal_scheme.suggestion_button_colors.foreground =
-      normal_scheme.suggestion_text;
-  normal_scheme.suggestion_button_colors.background =
-      normal_scheme.omnibox_background;
-  normal_scheme.suggestion_button_colors.background_hover = 0xFFE0E0E0;
-  normal_scheme.suggestion_button_colors.background_down = 0xFFE0E0E0;
-  normal_scheme.omnibox_voice_search_button_colors.foreground =
-      normal_scheme.suggestion_text;
-  normal_scheme.omnibox_voice_search_button_colors.background =
-      normal_scheme.omnibox_background;
-  normal_scheme.omnibox_voice_search_button_colors.background_hover =
-      0x14000000;
-  normal_scheme.omnibox_voice_search_button_colors.background_down = 0x14000000;
-
   normal_scheme.snackbar_foreground = 0xFFEEEEEE;
   normal_scheme.snackbar_background = 0xDD212121;
   normal_scheme.snackbar_button_colors.background =
@@ -169,26 +162,12 @@
   fullscreen_scheme.floor = 0xFF070F1C;
   fullscreen_scheme.ceiling = 0xFF04080F;
   fullscreen_scheme.floor_grid = 0x40A3E0FF;
-  fullscreen_scheme.element_foreground = 0x80FFFFFF;
-  fullscreen_scheme.element_background = 0xCC2B3E48;
-  fullscreen_scheme.element_background_hover = 0xCC536B77;
-  fullscreen_scheme.element_background_down = 0xCC96AFBB;
 
-  fullscreen_scheme.button_colors.foreground =
-      fullscreen_scheme.element_foreground;
-  fullscreen_scheme.button_colors.foreground_disabled =
-      fullscreen_scheme.element_foreground;
-  fullscreen_scheme.button_colors.background =
-      fullscreen_scheme.element_background;
-  fullscreen_scheme.button_colors.background_hover =
-      fullscreen_scheme.element_background_hover;
-  fullscreen_scheme.button_colors.background_down =
-      fullscreen_scheme.element_background_down;
-
-  fullscreen_scheme.system_indicator_foreground =
-      fullscreen_scheme.element_foreground;
-  fullscreen_scheme.system_indicator_background =
-      fullscreen_scheme.element_background;
+  fullscreen_scheme.disc_button_colors.foreground = 0x80FFFFFF;
+  fullscreen_scheme.disc_button_colors.foreground_disabled = 0x80FFFFFF;
+  fullscreen_scheme.disc_button_colors.background = 0xCC2B3E48;
+  fullscreen_scheme.disc_button_colors.background_hover = 0xCC536B77;
+  fullscreen_scheme.disc_button_colors.background_down = 0xCC96AFBB;
 
   fullscreen_scheme.normal_factor = 0.0f;
   fullscreen_scheme.incognito_factor = 0.0f;
@@ -200,41 +179,13 @@
   incognito_scheme.floor = 0xFF282828;
   incognito_scheme.ceiling = 0xFF2F2F2F;
   incognito_scheme.floor_grid = 0xCC595959;
-  incognito_scheme.element_foreground = 0xFFFFFFFF;
-  incognito_scheme.element_background = 0xFF454545;
-  incognito_scheme.element_background_hover = 0xCC505050;
-  incognito_scheme.element_background_down = 0xCC888888;
 
-  incognito_scheme.button_colors.foreground =
-      fullscreen_scheme.element_foreground;
-  incognito_scheme.button_colors.foreground_disabled = 0x33E6E6E6;
-  incognito_scheme.button_colors.background =
-      fullscreen_scheme.element_background;
-  incognito_scheme.button_colors.background_hover =
-      fullscreen_scheme.element_background_hover;
-  incognito_scheme.button_colors.background_down =
-      fullscreen_scheme.element_background_down;
+  incognito_scheme.disc_button_colors.foreground = 0x80FFFFFF;
+  incognito_scheme.disc_button_colors.foreground_disabled = 0x33E6E6E6;
+  incognito_scheme.disc_button_colors.background = 0xCC2B3E48;
+  incognito_scheme.disc_button_colors.background_hover = 0xCC505050;
+  incognito_scheme.disc_button_colors.background_down = 0xCC888888;
 
-  incognito_scheme.system_indicator_foreground =
-      incognito_scheme.element_foreground;
-  incognito_scheme.system_indicator_background =
-      incognito_scheme.element_background;
-  incognito_scheme.url_bar_button.background =
-      incognito_scheme.element_background;
-  incognito_scheme.url_bar_button.background_down =
-      incognito_scheme.element_background_down;
-  incognito_scheme.url_bar_button.background_hover =
-      incognito_scheme.element_background_hover;
-  incognito_scheme.url_bar_button.foreground =
-      incognito_scheme.element_foreground;
-  incognito_scheme.url_bar_button.foreground_disabled = 0x33E6E6E6;
-  incognito_scheme.url_bar_separator = 0x1FFFFFFF;
-  incognito_scheme.url_bar_hint_text = 0xCCFFFFFF;
-  incognito_scheme.url_bar_default_icon = SK_ColorWHITE;
-  incognito_scheme.url_bar_dangerous_icon = SK_ColorWHITE;
-  incognito_scheme.url_bar_separator = incognito_scheme.url_bar_separator;
-  incognito_scheme.url_bar.deemphasized = 0xCCFFFFFF;
-  incognito_scheme.url_bar.emphasized = SK_ColorWHITE;
   incognito_scheme.prompt_foreground = 0xCCFFFFFF;
   incognito_scheme.prompt_primary_button_colors.foreground = 0xD9000000;
   incognito_scheme.prompt_primary_button_colors.foreground_disabled =
@@ -248,23 +199,22 @@
   incognito_scheme.prompt_secondary_button_colors.background = 0x80FFFFFF;
   incognito_scheme.prompt_secondary_button_colors.background_hover = 0xFF8C8C8C;
   incognito_scheme.prompt_secondary_button_colors.background_down = 0xE6FFFFFF;
-  incognito_scheme.omnibox_background = 0xFF454545;
-  incognito_scheme.omnibox_icon = 0xCCFFFFFF;
-  incognito_scheme.omnibox_text = 0xCCFFFFFF;
-  incognito_scheme.omnibox_hint = 0x80FFFFFF;
+
+  incognito_scheme.url_bar_background = 0xFF454545;
+  incognito_scheme.url_bar_separator = MakeWhite(12);
+  incognito_scheme.url_bar_text = MakeWhite(65);
+  incognito_scheme.url_bar_hint_text = MakeWhite(50);
+  incognito_scheme.url_bar_dangerous_icon = SK_ColorWHITE;
+  incognito_scheme.url_bar_button.background_hover = MakeWhite(8);
+  incognito_scheme.url_bar_button.background_down = MakeWhite(8);
+  incognito_scheme.url_bar_button.foreground = MakeWhite(65);
+  incognito_scheme.url_bar_button.foreground_disabled = MakeWhite(24);
+  incognito_scheme.url_text.deemphasized = MakeWhite(30);
+  incognito_scheme.url_text.emphasized = MakeWhite(80);
+  incognito_scheme.omnibox_background = incognito_scheme.url_bar_background;
   incognito_scheme.omnibox_text_selection.foreground =
-      incognito_scheme.omnibox_text;
-  incognito_scheme.omnibox_text_selection.background =
-      incognito_scheme.omnibox_text_selection.cursor;
-  incognito_scheme.suggestion_text = 0xCCFFFFFF;
-  incognito_scheme.suggestion_dim_text = 0x88FFFFFF;
-  incognito_scheme.suggestion_url_text = 0xFF5595FE;
-  incognito_scheme.suggestion_button_colors.foreground =
-      incognito_scheme.suggestion_text;
-  incognito_scheme.suggestion_button_colors.background =
-      incognito_scheme.omnibox_background;
-  incognito_scheme.suggestion_button_colors.background_hover = 0xFF656565;
-  incognito_scheme.suggestion_button_colors.background_down = 0xFF656565;
+      incognito_scheme.url_bar_text;
+  incognito_scheme.omnibox_text_selection.background = MakeWhite(8);
 
   incognito_scheme.normal_factor = 0.0f;
   incognito_scheme.incognito_factor = 1.0f;
@@ -274,7 +224,7 @@
 }
 
 static constexpr size_t kButtonColorsSize = 20;
-static constexpr size_t kUrlBarColorsSize = 8;
+static constexpr size_t kUrlTextColorsSize = 8;
 
 }  // namespace
 
@@ -311,15 +261,16 @@
   return disabled ? foreground_disabled : foreground;
 }
 
-static_assert(kUrlBarColorsSize == sizeof(UrlBarColors),
-              "If the new colors are added to UrlBarColors, we must explicitly "
-              "bump this size and update operator== below");
+static_assert(
+    kUrlTextColorsSize == sizeof(UrlTextColors),
+    "If the new colors are added to UrlTextColors, we must explicitly "
+    "bump this size and update operator== below");
 
-bool UrlBarColors::operator==(const UrlBarColors& other) const {
+bool UrlTextColors::operator==(const UrlTextColors& other) const {
   return deemphasized == other.deemphasized && emphasized == other.emphasized;
 }
 
-bool UrlBarColors::operator!=(const UrlBarColors& other) const {
+bool UrlTextColors::operator!=(const UrlTextColors& other) const {
   return !(*this == other);
 }
 
@@ -344,73 +295,23 @@
 void ColorScheme::UpdateForComponent(const base::Version& component_version) {
   if (component_version >= AssetsLoader::MinVersionWithGradients()) {
     ColorScheme& normal_scheme = g_normal_scheme.Get();
-    normal_scheme.element_background = 0xFFEEEEEE;
-    normal_scheme.element_background_hover = 0xFFFFFFFF;
-    normal_scheme.button_colors.foreground = 0xA6000000;
-    normal_scheme.button_colors.background = 0xCCEEEEEE;
-    normal_scheme.button_colors.foreground_disabled = 0x33000000;
-    normal_scheme.url_bar_separator = 0xFFD0D0D0;
-    normal_scheme.url_bar_hint_text = 0x61333333;
-    normal_scheme.url_bar_button.background_down =
-        normal_scheme.element_background_down;
-    normal_scheme.url_bar_button.background_hover =
-        normal_scheme.element_background_hover;
-    normal_scheme.url_bar_button.foreground = normal_scheme.element_foreground;
-    normal_scheme.url_bar_button.foreground_disabled = 0x33333333;
-    normal_scheme.url_bar.deemphasized = 0x61333333;
-    normal_scheme.url_bar.emphasized = 0xFF333333;
-    normal_scheme.button_colors.background = normal_scheme.element_background;
-    normal_scheme.button_colors.background_hover =
-        normal_scheme.element_background_hover;
-    normal_scheme.button_colors.background_down =
-        normal_scheme.element_background_down;
-    normal_scheme.system_indicator_foreground =
-        normal_scheme.element_foreground;
-    normal_scheme.system_indicator_background =
-        normal_scheme.element_background;
-
+    normal_scheme.disc_button_colors.foreground = 0xA6000000;
+    normal_scheme.disc_button_colors.foreground_disabled = 0x33000000;
+    normal_scheme.disc_button_colors.background = 0xFFEEEEEE;
+    normal_scheme.disc_button_colors.background_hover = SK_ColorWHITE;
     normal_scheme.modal_prompt_secondary_button_colors.foreground_disabled =
-        normal_scheme.button_colors.foreground_disabled;
+        normal_scheme.disc_button_colors.foreground_disabled;
     normal_scheme.modal_prompt_secondary_button_colors.background =
         normal_scheme.modal_prompt_background;
+    normal_scheme.url_bar_background = MakeColor(0xEEEEEE, 87);
+    normal_scheme.omnibox_background = MakeColor(0xEEEEEE, 100);
 
     ColorScheme& incognito_scheme = g_incognito_scheme.Get();
-    incognito_scheme.element_foreground = 0xA6FFFFFF;
-    incognito_scheme.element_background = 0xFF263238;
-    incognito_scheme.element_background_hover = 0xCC404A50;
-    incognito_scheme.element_background_down = 0xCC212B31;
-    incognito_scheme.url_bar_separator = 0xFF445056;
-    incognito_scheme.url_bar_hint_text = 0x80FFFFFF;
-    incognito_scheme.url_bar_default_icon = 0xA7FFFFFF;
-    incognito_scheme.url_bar_dangerous_icon = 0xA7FFFFFF;
-    incognito_scheme.url_bar.deemphasized = 0x80FFFFFF;
-    incognito_scheme.url_bar.emphasized = 0xCCFFFFFF;
-
-    incognito_scheme.omnibox_background = incognito_scheme.element_background;
-    incognito_scheme.omnibox_icon = 0xA7FFFFFF;
-
-    incognito_scheme.button_colors.background =
-        incognito_scheme.element_background;
-    incognito_scheme.button_colors.background_hover =
-        incognito_scheme.element_background_hover;
-    incognito_scheme.button_colors.background_down =
-        incognito_scheme.element_background_down;
-
-    incognito_scheme.system_indicator_foreground =
-        incognito_scheme.element_foreground;
-    incognito_scheme.system_indicator_background =
-        incognito_scheme.element_background;
-    incognito_scheme.url_bar_button.background_down =
-        incognito_scheme.element_background_down;
-    incognito_scheme.url_bar_button.background_hover = 0xFF445056;
-    incognito_scheme.url_bar_button.foreground =
-        incognito_scheme.element_foreground;
-    incognito_scheme.suggestion_button_colors.foreground =
-        incognito_scheme.suggestion_text;
-    incognito_scheme.suggestion_button_colors.background =
-        incognito_scheme.omnibox_background;
-    incognito_scheme.suggestion_button_colors.background_hover = 0xFF3A464C;
-    incognito_scheme.suggestion_button_colors.background_down = 0xFF3A464C;
+    incognito_scheme.disc_button_colors.background = MakeColor(0x263238, 100);
+    incognito_scheme.disc_button_colors.background_hover = 0xCC404A50;
+    incognito_scheme.disc_button_colors.background_down = 0xCC212B31;
+    incognito_scheme.url_bar_background = MakeColor(0x263238, 87);
+    incognito_scheme.omnibox_background = MakeColor(0x263238, 100);
   }
 }
 
diff --git a/chrome/browser/vr/model/color_scheme.h b/chrome/browser/vr/model/color_scheme.h
index b66fcc7..9b33db4 100644
--- a/chrome/browser/vr/model/color_scheme.h
+++ b/chrome/browser/vr/model/color_scheme.h
@@ -17,16 +17,16 @@
   SkColor GetBackgroundColor(bool hovered, bool pressed) const;
   SkColor GetForegroundColor(bool disabled) const;
 
-  SkColor background = SK_ColorBLACK;
-  SkColor background_hover = SK_ColorBLACK;
-  SkColor background_down = SK_ColorBLACK;
+  SkColor background = SK_ColorTRANSPARENT;
+  SkColor background_hover = SK_ColorTRANSPARENT;
+  SkColor background_down = SK_ColorTRANSPARENT;
   SkColor foreground = SK_ColorBLACK;
   SkColor foreground_disabled = SK_ColorBLACK;
 };
 
-struct UrlBarColors {
-  bool operator==(const UrlBarColors& other) const;
-  bool operator!=(const UrlBarColors& other) const;
+struct UrlTextColors {
+  bool operator==(const UrlTextColors& other) const;
+  bool operator!=(const UrlTextColors& other) const;
   SkColor deemphasized = SK_ColorBLACK;
   SkColor emphasized = SK_ColorBLACK;
 };
@@ -61,16 +61,9 @@
   SkColor floor_grid;
   SkColor web_vr_background;
 
-  // The foreground color is used for text and sometimes for icons.
-  SkColor element_foreground;
-  // The background color is used behind text or icons in the foreground color.
-  // The related hover and down colors are to be used for buttons.
-  SkColor element_background;
-  SkColor element_background_hover;
-  SkColor element_background_down;
+  ButtonColors disc_button_colors;
 
   // Specific element background and foregrounds
-  ButtonColors button_colors;
   SkColor loading_indicator_foreground;
   SkColor loading_indicator_background;
   SkColor exit_warning_foreground;
@@ -79,8 +72,6 @@
   SkColor web_vr_transient_toast_background;
   SkColor exclusive_screen_toast_foreground;
   SkColor exclusive_screen_toast_background;
-  SkColor system_indicator_foreground;
-  SkColor system_indicator_background;
   SkColor modal_prompt_icon_foreground;
   SkColor modal_prompt_background;
   SkColor modal_prompt_foreground;
@@ -92,12 +83,16 @@
   ButtonColors prompt_secondary_button_colors;
   ButtonColors prompt_primary_button_colors;
 
-  ButtonColors url_bar_button;
+  SkColor url_bar_background;
   SkColor url_bar_separator;
+  SkColor url_bar_text;
   SkColor url_bar_hint_text;
-  SkColor url_bar_default_icon;
   SkColor url_bar_dangerous_icon;
-  UrlBarColors url_bar;
+  ButtonColors url_bar_button;
+  UrlTextColors url_text;
+  SkColor omnibox_background;
+  TextSelectionColors omnibox_text_selection;
+  SkColor hyperlink;
 
   ButtonColors indicator;
 
@@ -113,17 +108,6 @@
 
   SkColor speech_recognition_circle_background;
 
-  SkColor omnibox_background;
-  SkColor omnibox_icon;
-  SkColor omnibox_text;
-  SkColor omnibox_hint;
-  TextSelectionColors omnibox_text_selection;
-  SkColor suggestion_text;
-  SkColor suggestion_dim_text;
-  SkColor suggestion_url_text;
-  ButtonColors omnibox_voice_search_button_colors;
-  ButtonColors suggestion_button_colors;
-
   SkColor snackbar_background;
   SkColor snackbar_foreground;
   ButtonColors snackbar_button_colors;
diff --git a/chrome/browser/vr/model/controller_model.h b/chrome/browser/vr/model/controller_model.h
index 3a791ce..211ccf21 100644
--- a/chrome/browser/vr/model/controller_model.h
+++ b/chrome/browser/vr/model/controller_model.h
@@ -33,6 +33,7 @@
   bool quiescent = false;
   bool resting_in_viewport = false;
   bool recentered = false;
+  bool app_button_long_pressed = false;
   PlatformController::Handedness handedness = PlatformController::kRightHanded;
 };
 
diff --git a/chrome/browser/vr/testapp/vr_test_context.cc b/chrome/browser/vr/testapp/vr_test_context.cc
index 8e0fc9d..b4e7c1bb 100644
--- a/chrome/browser/vr/testapp/vr_test_context.cc
+++ b/chrome/browser/vr/testapp/vr_test_context.cc
@@ -84,9 +84,6 @@
 
   base::i18n::InitializeICU();
 
-  // TODO(cjgrant): Remove this when the keyboard is enabled by default.
-  base::FeatureList::InitializeInstance("VrBrowserKeyboard", "");
-
   text_input_delegate_ = std::make_unique<TextInputDelegate>();
   keyboard_delegate_ = std::make_unique<TestKeyboardDelegate>();
 
@@ -527,6 +524,11 @@
 void VrTestContext::StartAutocomplete(const AutocompleteRequest& request) {
   auto result = std::make_unique<OmniboxSuggestions>();
 
+  if (request.text.empty()) {
+    ui_->SetOmniboxSuggestions(std::move(result));
+    return;
+  }
+
   // Supply an in-line match if the input matches a canned URL.
   base::string16 full_string = base::UTF8ToUTF16("wikipedia.org");
   if (!request.prevent_inline_autocomplete && request.text.size() >= 2 &&
diff --git a/chrome/browser/vr/ui.cc b/chrome/browser/vr/ui.cc
index 88d384a..3381609 100644
--- a/chrome/browser/vr/ui.cc
+++ b/chrome/browser/vr/ui.cc
@@ -313,9 +313,8 @@
   }
 }
 
-void Ui::OnAppButtonGesturePerformed(
-    PlatformController::SwipeDirection direction) {
-}
+void Ui::OnAppButtonSwipePerformed(
+    PlatformController::SwipeDirection direction) {}
 
 void Ui::OnControllerUpdated(const ControllerModel& controller_model,
                              const ReticleModel& reticle_model) {
@@ -362,6 +361,10 @@
   return controller_group && controller_group->GetTargetOpacity() > 0.0f;
 }
 
+bool Ui::IsAppButtonLongPressed() const {
+  return model_->controller.app_button_long_pressed;
+}
+
 bool Ui::SkipsRedrawWhenNotDirty() const {
   return model_->skips_redraw_when_not_dirty;
 }
diff --git a/chrome/browser/vr/ui.h b/chrome/browser/vr/ui.h
index 1a56d37..0cd14f7b 100644
--- a/chrome/browser/vr/ui.h
+++ b/chrome/browser/vr/ui.h
@@ -127,8 +127,7 @@
       bool use_ganesh);
 
   void OnAppButtonClicked();
-  void OnAppButtonGesturePerformed(
-      PlatformController::SwipeDirection direction);
+  void OnAppButtonSwipePerformed(PlatformController::SwipeDirection direction);
   void OnControllerUpdated(const ControllerModel& controller_model,
                            const ReticleModel& reticle_model);
   void OnProjMatrixChanged(const gfx::Transform& proj_matrix);
@@ -136,6 +135,7 @@
   void OnWebVrTimedOut();
   void OnWebVrTimeoutImminent();
   bool IsControllerVisible() const;
+  bool IsAppButtonLongPressed() const;
   bool SkipsRedrawWhenNotDirty() const;
   void OnSwapContents(int new_content_id);
   void OnContentBoundsChanged(int width, int height);
diff --git a/chrome/browser/vr/ui_scene_constants.h b/chrome/browser/vr/ui_scene_constants.h
index a43c9c7..6eb8af2a3 100644
--- a/chrome/browser/vr/ui_scene_constants.h
+++ b/chrome/browser/vr/ui_scene_constants.h
@@ -199,7 +199,7 @@
 static constexpr int kOmniboxTransitionMs = 300;
 
 static constexpr float kOmniboxTextFieldIconButtonSizeDMM = 0.064f;
-static constexpr float kOmniboxTextFieldIconButtonHoverOffsetDMM = 0.012f;
+static constexpr float kUrlBarButtonHoverOffsetDMM = 0.012f;
 static constexpr float kOmniboxTextFieldRightMargin =
     ((kOmniboxHeightDMM - kOmniboxTextFieldIconButtonSizeDMM) / 2);
 
diff --git a/chrome/browser/vr/ui_scene_creator.cc b/chrome/browser/vr/ui_scene_creator.cc
index db07e20..fb1784f 100644
--- a/chrome/browser/vr/ui_scene_creator.cc
+++ b/chrome/browser/vr/ui_scene_creator.cc
@@ -146,8 +146,9 @@
   icon->SetDrawPhase(kPhaseForeground);
   icon->SetType(kTypeOmniboxSuggestionIcon);
   icon->SetSize(kSuggestionIconSizeDMM, kSuggestionIconSizeDMM);
-  VR_BIND_COLOR(model, icon.get(), &ColorScheme::omnibox_icon,
-                &VectorIcon::SetColor);
+  icon->AddBinding(VR_BIND_FUNC(SkColor, Model, model,
+                                model->color_scheme().url_bar_button.foreground,
+                                VectorIcon, icon.get(), SetColor));
   VectorIcon* p_icon = icon.get();
 
   auto icon_box = std::make_unique<UiElement>();
@@ -208,8 +209,7 @@
   background->set_bubble_events(true);
   background->set_bounds_contain_children(true);
   background->set_hover_offset(0.0);
-  VR_BIND_BUTTON_COLORS(model, background.get(),
-                        &ColorScheme::suggestion_button_colors,
+  VR_BIND_BUTTON_COLORS(model, background.get(), &ColorScheme::url_bar_button,
                         &Button::SetButtonColors);
   background->AddChild(std::move(suggestion_layout));
 
@@ -285,26 +285,6 @@
   return element;
 }
 
-std::unique_ptr<Rect> CreateOmniboxSpacer(Model* model) {
-  auto spacer = Create<Rect>(kNone, kPhaseForeground);
-  spacer->SetType(kTypeSpacer);
-  spacer->SetSize(kOmniboxWidthDMM, kSuggestionVerticalPaddingDMM);
-  spacer->set_focusable(false);
-  spacer->set_hit_testable(true);
-  spacer->AddBinding(std::make_unique<Binding<bool>>(
-      VR_BIND_LAMBDA([](Model* m) { return !m->omnibox_suggestions.empty(); },
-                     base::Unretained(model)),
-      VR_BIND_LAMBDA(
-          [](UiElement* e, const bool& v) {
-            e->SetVisible(v);
-            e->set_requires_layout(v);
-          },
-          base::Unretained(spacer.get()))));
-  VR_BIND_COLOR(model, spacer.get(), &ColorScheme::omnibox_background,
-                &Rect::SetColor);
-  return spacer;
-}
-
 // Util to bind the visibility of the given control element to the given
 // property in the model and the visibility of the voice search UI root.
 #define BIND_VISIBILITY_CONTROL_FOR_VOICE(control_element, model, property) \
@@ -323,6 +303,7 @@
 
 std::unique_ptr<UiElement> CreateSpacer(float width, float height) {
   auto spacer = Create<UiElement>(kNone, kPhaseNone);
+  spacer->SetType(kTypeSpacer);
   spacer->SetSize(width, height);
   return spacer;
 }
@@ -1337,7 +1318,7 @@
   button->SetSize(kWebVrTimeoutMessageButtonDiameterDMM,
                   kWebVrTimeoutMessageButtonDiameterDMM);
   VR_BIND_VISIBILITY(button, model->web_vr.state == kWebVrTimedOut);
-  VR_BIND_BUTTON_COLORS(model_, button.get(), &ColorScheme::button_colors,
+  VR_BIND_BUTTON_COLORS(model_, button.get(), &ColorScheme::disc_button_colors,
                         &DiscButton::SetButtonColors);
 
   auto timeout_button_text =
@@ -1611,7 +1592,8 @@
   close_button->SetTranslate(0, -kVoiceSearchCloseButtonYOffset, 0);
   close_button->SetRotate(
       1, 0, 0, atan(-kVoiceSearchCloseButtonYOffset / kContentDistance));
-  VR_BIND_BUTTON_COLORS(model_, close_button.get(), &ColorScheme::button_colors,
+  VR_BIND_BUTTON_COLORS(model_, close_button.get(),
+                        &ColorScheme::disc_button_colors,
                         &DiscButton::SetButtonColors);
   scene_->AddUiElement(kSpeechRecognitionListening, std::move(close_button));
 
@@ -1848,7 +1830,7 @@
   url_bar->set_bounds_contain_children(true);
   url_bar->set_corner_radius(kUrlBarHeightDMM / 2);
   VR_BIND_VISIBILITY(url_bar, !model->fullscreen_enabled());
-  VR_BIND_COLOR(model_, url_bar.get(), &ColorScheme::element_background,
+  VR_BIND_COLOR(model_, url_bar.get(), &ColorScheme::url_bar_background,
                 &Rect::SetColor);
   scene_->AddUiElement(kUrlBarDmmRoot, std::move(url_bar));
 
@@ -1962,7 +1944,7 @@
   security_button->SetIconScaleFactor(kUrlBarButtonIconScaleFactor);
   security_button->SetSize(kUrlBarButtonSizeDMM, kUrlBarButtonSizeDMM);
   security_button->set_corner_radius(kUrlBarItemCornerRadiusDMM);
-  security_button->set_hover_offset(kOmniboxTextFieldIconButtonHoverOffsetDMM);
+  security_button->set_hover_offset(kUrlBarButtonHoverOffsetDMM);
   VR_BIND_VISIBILITY(security_button, model->toolbar_state.should_display_url);
   VR_BIND_BUTTON_COLORS(model_, security_button.get(),
                         &ColorScheme::url_bar_button, &Button::SetButtonColors);
@@ -1983,8 +1965,6 @@
             if (m->toolbar_state.security_level ==
                 security_state::SecurityLevel::DANGEROUS) {
               colors.foreground = m->color_scheme().url_bar_dangerous_icon;
-            } else {
-              colors.foreground = m->color_scheme().url_bar_default_icon;
             }
             return colors;
           },
@@ -2005,11 +1985,9 @@
   url_text->AddBinding(VR_BIND_FUNC(ToolbarState, Model, model_,
                                     model->toolbar_state, UrlBar,
                                     url_text.get(), SetToolbarState));
-  url_text->AddBinding(VR_BIND_FUNC(UrlBarColors, Model, model_,
-                                    model->color_scheme().url_bar, UrlBar,
+  url_text->AddBinding(VR_BIND_FUNC(UrlTextColors, Model, model_,
+                                    model->color_scheme().url_text, UrlBar,
                                     url_text.get(), SetColors));
-  VR_BIND_COLOR(model_, url_text.get(), &ColorScheme::element_background,
-                &TexturedElement::SetBackgroundColor);
   scene_->AddUiElement(kUrlBarOriginLayout, std::move(url_text));
 
   auto right_margin = Create<Rect>(kNone, kPhaseNone);
@@ -2076,7 +2054,7 @@
   overflow_menu->set_bounds_contain_children(true);
   overflow_menu->SetTranslate(0, kOverflowMenuOffset, 0);
   overflow_menu->set_corner_radius(kUrlBarItemCornerRadiusDMM);
-  VR_BIND_COLOR(model_, overflow_menu.get(), &ColorScheme::element_background,
+  VR_BIND_COLOR(model_, overflow_menu.get(), &ColorScheme::omnibox_background,
                 &Rect::SetColor);
 
   auto overflow_layout =
@@ -2101,7 +2079,7 @@
     button->SetDrawPhase(kPhaseForeground);
     button->SetSize(kUrlBarButtonSizeDMM, kUrlBarButtonSizeDMM);
     button->SetIconScaleFactor(kUrlBarButtonIconScaleFactor);
-    button->set_hover_offset(kOmniboxTextFieldIconButtonHoverOffsetDMM);
+    button->set_hover_offset(kUrlBarButtonHoverOffsetDMM);
     button->set_corner_radius(kUrlBarItemCornerRadiusDMM);
     button->set_requires_layout(false);
     button->set_contributes_to_parent_bounds(false);
@@ -2168,8 +2146,9 @@
     text->SetText(l10n_util::GetStringUTF16(std::get<1>(item)));
     text->SetLayoutMode(TextLayoutMode::kSingleLineFixedHeight);
     text->SetAlignment(UiTexture::kTextAlignmentLeft);
-    VR_BIND_COLOR(model_, text.get(), &ColorScheme::element_foreground,
-                  &Text::SetColor);
+    text->AddBinding(VR_BIND_FUNC(
+        SkColor, Model, model_, model->color_scheme().url_bar_button.foreground,
+        Text, text.get(), SetColor));
     layout->AddChild(std::move(text));
 
     auto spacer = Create<Rect>(kNone, kPhaseNone);
@@ -2184,7 +2163,8 @@
     background->set_bounds_contain_children(true);
     background->set_hover_offset(0);
     background->set_padding(kOverflowMenuItemXPadding, 0);
-    VR_BIND_BUTTON_COLORS(model_, background.get(), &ColorScheme::button_colors,
+    VR_BIND_BUTTON_COLORS(model_, background.get(),
+                          &ColorScheme::url_bar_button,
                           &Button::SetButtonColors);
     background->AddChild(std::move(layout));
 
@@ -2242,8 +2222,8 @@
       Create<UiElement>(kOmniboxVisibiltyControlForVoice, kPhaseNone);
   visibility_control_root->set_contributes_to_parent_bounds(false);
 
-  auto scaler = std::make_unique<ScaledDepthAdjuster>(kUrlBarDistance);
-  scaler->SetName(kOmniboxDmmRoot);
+  auto scaler =
+      Create<ScaledDepthAdjuster>(kOmniboxDmmRoot, kPhaseNone, kUrlBarDistance);
 
   auto visibility_toggle_for_audio_permission = Create<UiElement>(
       kOmniboxVisibilityControlForAudioPermissionPrompt, kPhaseNone);
@@ -2256,27 +2236,22 @@
           kModalPromptTypeExitVRForVoiceSearchRecordAudioOsPermission,
       UiElement, visibility_toggle_for_audio_permission.get(), SetVisible));
 
-  auto omnibox_root = std::make_unique<UiElement>();
-  omnibox_root->SetName(kOmniboxRoot);
-  omnibox_root->SetDrawPhase(kPhaseNone);
-  omnibox_root->SetVisible(false);
+  auto omnibox_root = Create<UiElement>(kOmniboxRoot, kPhaseNone);
   omnibox_root->SetTransitionedProperties({OPACITY});
+  omnibox_root->SetTransitionDuration(
+      base::TimeDelta::FromMilliseconds(kOmniboxTransitionMs));
   VR_BIND_VISIBILITY(omnibox_root, model->omnibox_editing_enabled());
 
-  auto shadow = std::make_unique<Shadow>();
-  shadow->SetName(kOmniboxShadow);
-  shadow->SetDrawPhase(kPhaseForeground);
+  // The shadow also controls omnibox Y offset.
+  auto shadow = Create<Shadow>(kOmniboxShadow, kPhaseForeground);
   shadow->set_intensity(kOmniboxShadowIntensity);
   shadow->set_y_anchoring(TOP);
   shadow->set_y_centering(BOTTOM);
   shadow->set_corner_radius(kOmniboxCornerRadiusDMM);
-
-  auto omnibox_outer_layout = std::make_unique<LinearLayout>(LinearLayout::kUp);
-  omnibox_outer_layout->SetName(kOmniboxOuterLayout);
-  omnibox_outer_layout->SetTranslate(
-      0, kUrlBarVerticalOffsetDMM - 0.5f * kOmniboxHeightDMM,
-      kOmniboxShadowOffset);
-  omnibox_outer_layout->AddBinding(std::make_unique<Binding<bool>>(
+  shadow->SetTransitionedProperties({TRANSFORM});
+  shadow->SetTransitionDuration(
+      base::TimeDelta::FromMilliseconds(kOmniboxTransitionMs));
+  shadow->AddBinding(std::make_unique<Binding<bool>>(
       VR_BIND_LAMBDA([](Model* m) { return m->omnibox_editing_enabled(); },
                      base::Unretained(model_)),
       VR_BIND_LAMBDA(
@@ -2286,44 +2261,16 @@
             y_offset -= 0.5 * kOmniboxHeightDMM;
             e->SetTranslate(0, y_offset, kOmniboxShadowOffset);
           },
-          omnibox_outer_layout.get())));
+          shadow.get())));
 
-  auto omnibox_outer_layout_spacer =
-      Create<Rect>(kOmniboxOuterLayoutSpacer, kPhaseForeground);
-  omnibox_outer_layout_spacer->set_hit_testable(true);
-  omnibox_outer_layout_spacer->SetSize(kOmniboxWidthDMM, kSuggestionGapDMM);
-  omnibox_outer_layout_spacer->set_hit_testable(true);
-  VR_BIND_COLOR(model_, omnibox_outer_layout_spacer.get(),
+  auto omnibox_outer_layout =
+      Create<LinearLayout>(kOmniboxOuterLayout, kPhaseNone, LinearLayout::kUp);
+
+  auto omnibox_suggestion_divider = Create<Rect>(kNone, kPhaseForeground);
+  omnibox_suggestion_divider->SetType(kTypeSpacer);
+  omnibox_suggestion_divider->SetSize(kOmniboxWidthDMM, kSuggestionGapDMM);
+  VR_BIND_COLOR(model_, omnibox_suggestion_divider.get(),
                 &ColorScheme::url_bar_separator, &Rect::SetColor);
-  VR_BIND_VISIBILITY(omnibox_outer_layout_spacer,
-                     !model->omnibox_suggestions.empty());
-
-  auto omnibox_container = std::make_unique<Rect>();
-  omnibox_container->SetName(kOmniboxContainer);
-  omnibox_container->SetDrawPhase(kPhaseForeground);
-  omnibox_container->SetSize(kOmniboxWidthDMM, kOmniboxHeightDMM);
-  omnibox_container->SetTransitionedProperties({TRANSFORM, OPACITY});
-  omnibox_container->SetTransitionDuration(
-      base::TimeDelta::FromMilliseconds(kOmniboxTransitionMs));
-  omnibox_container->set_focusable(false);
-  omnibox_container->set_hit_testable(true);
-  omnibox_container->AddBinding(std::make_unique<Binding<bool>>(
-      VR_BIND_LAMBDA([](Model* m) { return m->omnibox_suggestions.empty(); },
-                     base::Unretained(model_)),
-      VR_BIND_LAMBDA(
-          [](Rect* r, const bool& v) {
-            if (v) {
-              r->SetCornerRadii(
-                  {kOmniboxCornerRadiusDMM, kOmniboxCornerRadiusDMM,
-                   kOmniboxCornerRadiusDMM, kOmniboxCornerRadiusDMM});
-            } else {
-              r->SetCornerRadii(
-                  {0, 0, kOmniboxCornerRadiusDMM, kOmniboxCornerRadiusDMM});
-            }
-          },
-          omnibox_container.get())));
-  VR_BIND_COLOR(model_, omnibox_container.get(),
-                &ColorScheme::omnibox_background, &Rect::SetColor);
 
   auto omnibox_text_field = Create<OmniboxTextField>(
       kOmniboxTextField, kPhaseNone, kOmniboxTextHeightDMM,
@@ -2340,7 +2287,6 @@
       base::BindRepeating(
           [](UiBrowserInterface* browser) { browser->StopAutocomplete(); },
           base::Unretained(browser_)));
-
   omnibox_text_field->SetTextInputDelegate(text_input_delegate_);
   omnibox_text_field->set_hit_testable(false);
   omnibox_text_field->SetHintText(
@@ -2428,10 +2374,10 @@
       bool, Model, model_, model->supports_selection, OmniboxTextField,
       omnibox_text_field.get(), set_allow_inline_autocomplete));
 
-  VR_BIND_COLOR(model_, omnibox_text_field.get(), &ColorScheme::omnibox_text,
+  VR_BIND_COLOR(model_, omnibox_text_field.get(), &ColorScheme::url_bar_text,
                 &TextInput::SetTextColor);
-  VR_BIND_COLOR(model_, omnibox_text_field.get(), &ColorScheme::omnibox_hint,
-                &TextInput::SetHintColor);
+  VR_BIND_COLOR(model_, omnibox_text_field.get(),
+                &ColorScheme::url_bar_hint_text, &TextInput::SetHintColor);
   omnibox_text_field->AddBinding(std::make_unique<Binding<TextSelectionColors>>(
       VR_BIND_LAMBDA(
           [](Model* m) { return m->color_scheme().omnibox_text_selection; },
@@ -2448,38 +2394,27 @@
           [](UiBrowserInterface* b, Ui* ui) { b->SetVoiceSearchActive(true); },
           base::Unretained(browser_), base::Unretained(ui_)),
       vector_icons::kMicIcon, audio_delegate_);
-  mic_button->SetIconScaleFactor(kVoiceSearchIconScaleFactor);
-  mic_button->SetSize(kOmniboxTextFieldIconButtonSizeDMM,
-                      kOmniboxTextFieldIconButtonSizeDMM);
-  mic_button->set_hover_offset(kOmniboxTextFieldIconButtonHoverOffsetDMM);
+  mic_button->SetSize(kUrlBarButtonSizeDMM, kUrlBarButtonSizeDMM);
+  mic_button->SetIconScaleFactor(kUrlBarButtonIconScaleFactor);
+  mic_button->set_hover_offset(kUrlBarButtonHoverOffsetDMM);
   mic_button->set_corner_radius(kUrlBarItemCornerRadiusDMM);
-
   VR_BIND_VISIBILITY(mic_button,
                      model->speech.has_or_can_request_audio_permission &&
                          !model->incognito &&
                          !model->capturing_state.audio_capture_enabled);
-
-  VR_BIND_BUTTON_COLORS(model_, mic_button.get(),
-                        &ColorScheme::omnibox_voice_search_button_colors,
+  VR_BIND_BUTTON_COLORS(model_, mic_button.get(), &ColorScheme::url_bar_button,
                         &Button::SetButtonColors);
 
-  auto left_spacer = Create<Rect>(kNone, kPhaseNone);
-  left_spacer->SetSize(kOmniboxTextMarginDMM, kOmniboxTextHeightDMM);
-  left_spacer->SetType(kTypeSpacer);
-  auto middle_spacer = Create<Rect>(kNone, kPhaseNone);
-  middle_spacer->SetType(kTypeSpacer);
-  middle_spacer->SetSize(kOmniboxTextMarginDMM, kOmniboxTextHeightDMM);
-  auto right_spacer = Create<Rect>(kNone, kPhaseNone);
-  right_spacer->SetSize(kOmniboxTextFieldRightMargin, kOmniboxTextHeightDMM);
-  right_spacer->SetType(kTypeSpacer);
-
   auto text_field_layout = Create<LinearLayout>(
       kOmniboxTextFieldLayout, kPhaseNone, LinearLayout::kRight);
-  text_field_layout->AddChild(std::move(left_spacer));
+  text_field_layout->AddChild(
+      CreateSpacer(kOmniboxTextMarginDMM, kOmniboxHeightDMM));
   text_field_layout->AddChild(std::move(omnibox_text_field));
-  text_field_layout->AddChild(std::move(middle_spacer));
+  text_field_layout->AddChild(
+      CreateSpacer(kOmniboxTextMarginDMM, kOmniboxHeightDMM));
   text_field_layout->AddChild(std::move(mic_button));
-  text_field_layout->AddChild(std::move(right_spacer));
+  text_field_layout->AddChild(
+      CreateSpacer(kOmniboxTextFieldRightMargin, kOmniboxHeightDMM));
 
   // Set up the vector binding to manage suggestions dynamically.
   SuggestionSetBinding::ModelAddedCallback added_callback = base::BindRepeating(
@@ -2489,18 +2424,13 @@
   SuggestionSetBinding::ModelRemovedCallback removed_callback =
       base::BindRepeating(&OnSuggestionModelRemoved, base::Unretained(scene_));
 
-  auto suggestions_outer_layout =
-      std::make_unique<LinearLayout>(LinearLayout::kDown);
-  suggestions_outer_layout->SetName(kOmniboxSuggestionsOuterLayout);
-
-  auto suggestions_layout = std::make_unique<LinearLayout>(LinearLayout::kUp);
-  suggestions_layout->SetName(kOmniboxSuggestions);
-  suggestions_layout->SetDrawPhase(kPhaseNone);
+  auto suggestions_layout =
+      Create<LinearLayout>(kOmniboxSuggestions, kPhaseNone, LinearLayout::kUp);
   suggestions_layout->AddBinding(std::make_unique<SuggestionSetBinding>(
       &model_->omnibox_suggestions, added_callback, removed_callback));
 
-  auto button_scaler =
-      std::make_unique<ScaledDepthAdjuster>(kOmniboxCloseButtonDepthOffset);
+  auto button_scaler = Create<ScaledDepthAdjuster>(
+      kNone, kPhaseNone, kOmniboxCloseButtonDepthOffset);
 
   auto close_button = Create<DiscButton>(
       kOmniboxCloseButton, kPhaseForeground,
@@ -2513,23 +2443,38 @@
   close_button->SetTranslate(0, kOmniboxCloseButtonVerticalOffsetDMM, 0);
   close_button->SetRotate(1, 0, 0, atan(kOmniboxCloseButtonVerticalOffsetDMM));
   close_button->set_hover_offset(kButtonZOffsetHoverDMM);
-  VR_BIND_BUTTON_COLORS(model_, close_button.get(), &ColorScheme::button_colors,
+  VR_BIND_BUTTON_COLORS(model_, close_button.get(),
+                        &ColorScheme::disc_button_colors,
                         &DiscButton::SetButtonColors);
 
-  auto spacer = CreateOmniboxSpacer(model_);
-  spacer->SetCornerRadii(
-      {kOmniboxCornerRadiusDMM, kOmniboxCornerRadiusDMM, 0, 0});
-  suggestions_outer_layout->AddChild(std::move(spacer));
+  auto suggestions_outer_layout = Create<LinearLayout>(
+      kOmniboxSuggestionsOuterLayout, kPhaseNone, LinearLayout::kUp);
+  VR_BIND_VISIBILITY(suggestions_outer_layout,
+                     !model->omnibox_suggestions.empty());
+  suggestions_outer_layout->AddBinding(VR_BIND_FUNC(
+      bool, Model, model_, !model->omnibox_suggestions.empty(), LinearLayout,
+      suggestions_outer_layout.get(), set_requires_layout));
+  suggestions_outer_layout->AddChild(std::move(omnibox_suggestion_divider));
+  suggestions_outer_layout->AddChild(
+      CreateSpacer(kOmniboxWidthDMM, kSuggestionVerticalPaddingDMM));
   suggestions_outer_layout->AddChild(std::move(suggestions_layout));
-  suggestions_outer_layout->AddChild(CreateOmniboxSpacer(model_));
+  suggestions_outer_layout->AddChild(
+      CreateSpacer(kOmniboxWidthDMM, kSuggestionVerticalPaddingDMM));
 
-  omnibox_container->AddChild(std::move(text_field_layout));
-
-  omnibox_outer_layout->AddChild(std::move(omnibox_container));
-  omnibox_outer_layout->AddChild(std::move(omnibox_outer_layout_spacer));
+  omnibox_outer_layout->AddChild(std::move(text_field_layout));
   omnibox_outer_layout->AddChild(std::move(suggestions_outer_layout));
 
-  shadow->AddChild(std::move(omnibox_outer_layout));
+  // Rounded-corner background of all omnibox and suggestion elements.
+  auto omnibox_background = Create<Rect>(kOmniboxBackground, kPhaseForeground);
+  omnibox_background->set_bounds_contain_children(true);
+  omnibox_background->set_hit_testable(true);
+  omnibox_background->set_focusable(false);
+  omnibox_background->set_corner_radius(kOmniboxCornerRadiusDMM);
+  VR_BIND_COLOR(model_, omnibox_background.get(),
+                &ColorScheme::omnibox_background, &Rect::SetColor);
+  omnibox_background->AddChild(std::move(omnibox_outer_layout));
+
+  shadow->AddChild(std::move(omnibox_background));
 
   button_scaler->AddChild(std::move(close_button));
 
@@ -2564,7 +2509,7 @@
   element->set_hover_offset(kButtonZOffsetHoverDMM * kCloseButtonDistance);
   element->set_y_anchoring(BOTTOM);
   element->SetTranslate(0, kCloseButtonRelativeOffset, -kCloseButtonDistance);
-  VR_BIND_BUTTON_COLORS(model_, element.get(), &ColorScheme::button_colors,
+  VR_BIND_BUTTON_COLORS(model_, element.get(), &ColorScheme::disc_button_colors,
                         &DiscButton::SetButtonColors);
 
   // Close button is a special control element that needs to be hidden when
@@ -2745,7 +2690,6 @@
                                    nullptr,
                                    nullptr};
   auto app_button_to_exit = CreateWebVrIndicator(model_, app_button_spec);
-  VR_BIND_VISIBILITY(app_button_to_exit, model->web_vr.show_exit_toast);
   indicators->AddChild(std::move(app_button_to_exit));
 
   // TODO(crbug.com/824472): add an indicator for the transient URL toast.
@@ -2756,19 +2700,40 @@
   }
 
   parent = CreateTransientParent(kNone, kToastTimeoutSeconds, true);
-  parent->AddBinding(std::make_unique<Binding<bool>>(
+  parent->AddBinding(std::make_unique<Binding<std::pair<bool, bool>>>(
       VR_BIND_LAMBDA(
           [](Model* model, UiElement* splash_screen) {
-            return model->web_vr.has_produced_frames() &&
-                   model->web_vr.has_received_permissions &&
-                   splash_screen->GetTargetOpacity() == 0.f;
+            return std::make_pair(model->web_vr_enabled() &&
+                                      model->web_vr.has_produced_frames() &&
+                                      model->web_vr.has_received_permissions &&
+                                      splash_screen->GetTargetOpacity() == 0.f,
+                                  model->controller.app_button_long_pressed);
           },
           base::Unretained(model_),
           base::Unretained(
               scene_->GetUiElementByName(kSplashScreenTransientParent))),
       VR_BIND_LAMBDA(
-          [](UiElement* e, Model* model, UiScene* scene, const bool& value) {
-            SetVisibleInLayout(e, value);
+          [](TransientElement* e, Model* model, UiScene* scene,
+             const base::Optional<std::pair<bool, bool>>& last_value,
+             const std::pair<bool, bool>& value) {
+            if (!value.first) {
+              e->SetVisibleImmediately(false);
+              return;
+            }
+
+            // The reason we need the previous state is to disguish the
+            // situation where the app button has been released after a long
+            // press, and the situation when we want to initially show the
+            // indicators.
+            if (last_value && last_value.value().second && !value.second)
+              return;
+
+            e->SetVisible(true);
+            e->RefreshVisible();
+            SetVisibleInLayout(
+                scene->GetUiElementByName(kExclusiveScreenToastViewportAware),
+                model->web_vr.show_exit_toast && !value.second);
+
             auto specs = GetIndicatorSpecs();
             for (const auto& spec : specs) {
               SetVisibleInLayout(
@@ -2778,6 +2743,11 @@
             }
 
             e->RemoveKeyframeModels(TRANSFORM);
+            if (value.second) {
+              e->SetTranslate(0, 0, 0);
+              return;
+            }
+
             e->SetTranslate(0, kWebVrPermissionOffsetStart, 0);
 
             // Build up a keyframe model for the initial transition.
diff --git a/chrome/browser/vr/ui_unittest.cc b/chrome/browser/vr/ui_unittest.cc
index 429d0185..13dc1ef 100644
--- a/chrome/browser/vr/ui_unittest.cc
+++ b/chrome/browser/vr/ui_unittest.cc
@@ -97,8 +97,9 @@
     kSpeechRecognitionResult, kSpeechRecognitionCircle,
     kSpeechRecognitionMicrophoneIcon, kSpeechRecognitionResultBackplane};
 
-static constexpr float kTolerance = 1e-5f;
-static constexpr float kSmallDelaySeconds = 0.1f;
+constexpr float kTolerance = 1e-5f;
+constexpr float kSmallDelaySeconds = 0.1f;
+constexpr float kAnimationTimeMs = 300;
 
 MATCHER_P2(SizeFsAreApproximatelyEqual, other, tolerance, "") {
   return base::IsApproximatelyEqual(arg.width(), other.width(), tolerance) &&
@@ -1017,7 +1018,7 @@
   model_->push_mode(kModeEditingOmnibox);
   OnBeginFrame();
   EXPECT_EQ(container->children().size(), 0u);
-  EXPECT_EQ(NumVisibleInTree(kOmniboxSuggestions), 1);
+  EXPECT_EQ(NumVisibleInTree(kOmniboxSuggestions), 0);
 
   model_->omnibox_suggestions.emplace_back(OmniboxSuggestion(
       base::string16(), base::string16(), ACMatchClassifications(),
@@ -1030,7 +1031,7 @@
   model_->omnibox_suggestions.clear();
   OnBeginFrame();
   EXPECT_EQ(container->children().size(), 0u);
-  EXPECT_EQ(NumVisibleInTree(kOmniboxSuggestions), 1);
+  EXPECT_EQ(NumVisibleInTree(kOmniboxSuggestions), 0);
 }
 
 TEST_F(UiTest, OmniboxSuggestionNavigates) {
@@ -1094,25 +1095,25 @@
       ui_->SetFullscreen(true);
     }
     ColorScheme scheme = ColorScheme::GetColorScheme(mode);
-    EXPECT_TRUE(OnBeginFrame());
-    VerifyButtonColor(button, scheme.button_colors.foreground,
-                      scheme.button_colors.background, "normal");
+    RunFor(MsToDelta(kAnimationTimeMs));
+    VerifyButtonColor(button, scheme.disc_button_colors.foreground,
+                      scheme.disc_button_colors.background, "normal");
     button->hit_plane()->OnHoverEnter(gfx::PointF(0.5f, 0.5f));
-    EXPECT_TRUE(OnBeginFrame());
-    VerifyButtonColor(button, scheme.button_colors.foreground,
-                      scheme.button_colors.background_hover, "hover");
+    RunFor(MsToDelta(kAnimationTimeMs));
+    VerifyButtonColor(button, scheme.disc_button_colors.foreground,
+                      scheme.disc_button_colors.background_hover, "hover");
     button->hit_plane()->OnButtonDown(gfx::PointF(0.5f, 0.5f));
-    EXPECT_TRUE(OnBeginFrame());
-    VerifyButtonColor(button, scheme.button_colors.foreground,
-                      scheme.button_colors.background_down, "down");
+    RunFor(MsToDelta(kAnimationTimeMs));
+    VerifyButtonColor(button, scheme.disc_button_colors.foreground,
+                      scheme.disc_button_colors.background_down, "down");
     button->hit_plane()->OnMove(gfx::PointF());
-    EXPECT_TRUE(OnBeginFrame());
-    VerifyButtonColor(button, scheme.button_colors.foreground,
-                      scheme.button_colors.background, "move");
+    RunFor(MsToDelta(kAnimationTimeMs));
+    VerifyButtonColor(button, scheme.disc_button_colors.foreground,
+                      scheme.disc_button_colors.background, "move");
     button->hit_plane()->OnButtonUp(gfx::PointF());
-    EXPECT_TRUE(OnBeginFrame());
-    VerifyButtonColor(button, scheme.button_colors.foreground,
-                      scheme.button_colors.background, "up");
+    RunFor(MsToDelta(kAnimationTimeMs));
+    VerifyButtonColor(button, scheme.disc_button_colors.foreground,
+                      scheme.disc_button_colors.background, "up");
   }
 }
 
@@ -1348,4 +1349,30 @@
   EXPECT_EQ(original, hosted_ui->world_space_transform());
 }
 
+// Ensures that permissions appear on long press, and that when the app button
+// is released that we do not show the exclusive screen toast. Distinguishing
+// these cases requires knowledge of the previous state.
+TEST_F(UiTest, LongPressAppButtonInWebVrMode) {
+  CreateScene(kNotInCct, kInWebVr);
+  ui_->SetWebVrMode(true, true);
+  EXPECT_FALSE(IsVisible(kExclusiveScreenToastViewportAware));
+  ui_->OnWebVrFrameAvailable();
+  ui_->SetCapturingState(CapturingStateModel());
+  OnBeginFrame();
+  EXPECT_TRUE(IsVisible(kExclusiveScreenToastViewportAware));
+  RunFor(MsToDelta(8000));
+  EXPECT_FALSE(IsVisible(kExclusiveScreenToastViewportAware));
+  model_->capturing_state.audio_capture_enabled = true;
+  model_->controller.app_button_long_pressed = true;
+  OnBeginFrame();
+  EXPECT_FALSE(IsVisible(kExclusiveScreenToastViewportAware));
+  EXPECT_TRUE(IsVisible(kWebVrAudioCaptureIndicator));
+  RunFor(MsToDelta(8000));
+  EXPECT_FALSE(IsVisible(kWebVrAudioCaptureIndicator));
+  model_->controller.app_button_long_pressed = true;
+  OnBeginFrame();
+  EXPECT_FALSE(IsVisible(kWebVrAudioCaptureIndicator));
+  EXPECT_FALSE(IsVisible(kExclusiveScreenToastViewportAware));
+}
+
 }  // namespace vr
diff --git a/chrome/common/extensions/api/file_manager_private.idl b/chrome/common/extensions/api/file_manager_private.idl
index 676fb187..fbe58f9 100644
--- a/chrome/common/extensions/api/file_manager_private.idl
+++ b/chrome/common/extensions/api/file_manager_private.idl
@@ -926,10 +926,9 @@
   // |destName| Name of the destination zip file. The zip file will be created
   //     under the directory specified by |parentEntry|.
   // |callback|
-  // TODO(mtomasz): Swap order of |entries| and |parentEntry|.
   [nocompile]
-  static void zipSelection([instanceof=DirectoryEntry] object parentEntry,
-                           [instanceof=Entry] object[] entries,
+  static void zipSelection([instanceof=Entry] object[] entries,
+                           [instanceof=DirectoryEntry] object parentEntry,
                            DOMString destName,
                            ZipSelectionCallback callback);
 
diff --git a/chrome/common/extensions/api/language_settings_private.idl b/chrome/common/extensions/api/language_settings_private.idl
index bc10d84..ab78fa9 100644
--- a/chrome/common/extensions/api/language_settings_private.idl
+++ b/chrome/common/extensions/api/language_settings_private.idl
@@ -126,6 +126,9 @@
     // Removes the input method from the current user's list of enabled input
     // methods, disabling the input method for the current user. Chrome OS only.
     static void removeInputMethod(DOMString inputMethodId);
+
+    // Tries to download the dictionary after a failed download.
+    static void retryDownloadDictionary(DOMString languageCode);
   };
 
   interface Events {
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 9301f60..19d6e88 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -1107,31 +1107,6 @@
       is_extension_force_installed ||
       is_invoked_by_webstore_installed_extension;
   bool is_nacl_allowed = is_nacl_allowed_by_location || is_nacl_unrestricted;
-  if (is_nacl_allowed) {
-    // Make sure that PPAPI 'dev' interfaces are only available for unpacked
-    // and component extensions.  Also allow dev interfaces when --enable-nacl
-    // is set, but do not allow --enable-nacl to provide dev interfaces to
-    // webstore installed and other normally allowed URLs.
-    std::string dev_attribute("@dev");
-    if (is_extension_unrestricted ||
-        (is_nacl_unrestricted && !is_nacl_allowed_by_location)) {
-      // Add the special '@dev' attribute.
-      std::vector<WebPluginMimeType::Param> mime_params;
-      mime_params.emplace_back(base::ASCIIToUTF16(dev_attribute),
-                               base::string16());
-      AppendParams(mime_params, &params->attribute_names,
-                   &params->attribute_values);
-    } else {
-      // If the params somehow contain '@dev', remove it.
-      size_t attribute_count = params->attribute_names.size();
-      for (size_t i = 0; i < attribute_count; ++i) {
-        if (params->attribute_names[i].Equals(dev_attribute.data(),
-                                              dev_attribute.length())) {
-          params->attribute_names[i] = WebString();
-        }
-      }
-    }
-  }
   return is_nacl_allowed;
 }
 #endif  // BUILDFLAG(ENABLE_NACL)
diff --git a/chrome/renderer/chrome_content_renderer_client_unittest.cc b/chrome/renderer/chrome_content_renderer_client_unittest.cc
index 0f9ee20..11149830 100644
--- a/chrome/renderer/chrome_content_renderer_client_unittest.cc
+++ b/chrome/renderer/chrome_content_renderer_client_unittest.cc
@@ -69,25 +69,6 @@
 
 const char kChatAppURL[] = "https://talkgadget.google.com/hangouts/foo";
 
-#if BUILDFLAG(ENABLE_NACL)
-bool AllowsDevInterfaces(const WebPluginParams& params) {
-  for (size_t i = 0; i < params.attribute_names.size(); ++i) {
-    if (params.attribute_names[i] == "@dev")
-      return true;
-  }
-  return false;
-}
-
-void AddFakeDevAttribute(WebPluginParams* params) {
-  WebVector<WebString> names(static_cast<size_t>(1));
-  WebVector<WebString> values(static_cast<size_t>(1));
-  names[0] = WebString::FromUTF8("@dev");
-  values[0] = WebString();
-  params->attribute_names.Swap(names);
-  params->attribute_values.Swap(values);
-}
-#endif
-
 void AddContentTypeHandler(content::WebPluginInfo* info,
                            const char* mime_type,
                            const char* manifest_url) {
@@ -165,7 +146,7 @@
                   "application/x-foo", info));
   }
 #if BUILDFLAG(ENABLE_NACL)
-  // --enable-nacl allows all NaCl apps, with 'dev' interfaces.
+  // --enable-nacl allows all NaCl apps.
   {
     WebPluginParams params;
     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
@@ -174,10 +155,8 @@
         kNaClUnrestricted,
         CreateExtension(kExtensionNotFromWebStore).get(),
         &params));
-    EXPECT_TRUE(AllowsDevInterfaces(params));
   }
-  // Unpacked extensions are allowed without --enable-nacl, with
-  // 'dev' interfaces.
+  // Unpacked extensions are allowed without --enable-nacl.
   {
     WebPluginParams params;
     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
@@ -187,10 +166,8 @@
         CreateExtensionWithLocation(extensions::Manifest::UNPACKED,
                                     kExtensionNotFromWebStore).get(),
         &params));
-    EXPECT_TRUE(AllowsDevInterfaces(params));
   }
-  // Component extensions are allowed without --enable-nacl, with
-  // 'dev' interfaces.
+  // Component extensions are allowed without --enable-nacl.
   {
     WebPluginParams params;
     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
@@ -200,7 +177,6 @@
         CreateExtensionWithLocation(extensions::Manifest::COMPONENT,
                                     kExtensionNotFromWebStore).get(),
         &params));
-    EXPECT_TRUE(AllowsDevInterfaces(params));
   }
   {
     WebPluginParams params;
@@ -211,10 +187,9 @@
         CreateExtensionWithLocation(extensions::Manifest::EXTERNAL_COMPONENT,
                                     kExtensionNotFromWebStore).get(),
         &params));
-    EXPECT_TRUE(AllowsDevInterfaces(params));
   }
   // Extensions that are force installed by policy are allowed without
-  // --enable-nacl, without 'dev' interfaces.
+  // --enable-nacl.
   {
     WebPluginParams params;
     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
@@ -224,7 +199,6 @@
         CreateExtensionWithLocation(extensions::Manifest::EXTERNAL_POLICY,
                                     kExtensionNotFromWebStore).get(),
         &params));
-    EXPECT_FALSE(AllowsDevInterfaces(params));
     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
         GURL(),
         GURL(kExtensionUrl),
@@ -233,10 +207,9 @@
             extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD,
             kExtensionNotFromWebStore).get(),
         &params));
-    EXPECT_FALSE(AllowsDevInterfaces(params));
   }
-  // CWS extensions are allowed without --enable-nacl, without 'dev'
-  // interfaces if called from an extension url.
+  // CWS extensions are allowed without --enable-nacl if called from an
+  // extension url.
   {
     WebPluginParams params;
     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
@@ -245,35 +218,10 @@
         kNaClRestricted,
         CreateExtension(kExtensionFromWebStore).get(),
         &params));
-    EXPECT_FALSE(AllowsDevInterfaces(params));
-  }
-  // CWS extensions can't get 'dev' interfaces with --enable-nacl.
-  {
-    WebPluginParams params;
-    EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(),
-        GURL(kExtensionUrl),
-        kNaClUnrestricted,
-        CreateExtension(kExtensionFromWebStore).get(),
-        &params));
-    EXPECT_FALSE(AllowsDevInterfaces(params));
-  }
-  // CWS extensions can't get 'dev' interfaces by injecting a fake
-  // '@dev' attribute.
-  {
-    WebPluginParams params;
-    AddFakeDevAttribute(&params);
-    EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(),
-        GURL(kExtensionUrl),
-        kNaClRestricted,
-        CreateExtension(kExtensionFromWebStore).get(),
-        &params));
-    EXPECT_FALSE(AllowsDevInterfaces(params));
   }
 
-  // Whitelisted URLs are allowed without --enable-nacl, without 'dev'
-  // interfaces. There is a whitelist for the app URL and the manifest URL.
+  // Whitelisted URLs are allowed without --enable-nacl. There is a whitelist
+  // for the app URL and the manifest URL.
   {
     WebPluginParams params;
     // Whitelisted Chat app is allowed.
@@ -308,24 +256,6 @@
         GURL("https://ssl.gstatic.com/wrong/s2/oz/nacl/foo"),  // bad path
         GURL(kChatAppURL), kNaClRestricted, nullptr, &params));
   }
-  // Whitelisted URLs can't get 'dev' interfaces with --enable-nacl.
-  {
-    WebPluginParams params;
-    EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(kChatManifestFS), GURL(kChatAppURL), kNaClUnrestricted, nullptr,
-        &params));
-    EXPECT_FALSE(AllowsDevInterfaces(params));
-  }
-  // Whitelisted URLs can't get 'dev' interfaces by injecting a fake
-  // '@dev' attribute.
-  {
-    WebPluginParams params;
-    AddFakeDevAttribute(&params);
-    EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
-        GURL(kChatManifestFS), GURL(kChatAppURL), kNaClRestricted, nullptr,
-        &params));
-    EXPECT_FALSE(AllowsDevInterfaces(params));
-  }
   // Non-whitelisted URLs are blocked without --enable-nacl.
   {
     WebPluginParams params;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 10bf8df3..fc9e318 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3309,6 +3309,8 @@
       "../browser/extensions/api/image_writer_private/test_utils.h",
       "../browser/extensions/api/image_writer_private/write_from_file_operation_unittest.cc",
       "../browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc",
+      "../browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc",
+      "../browser/extensions/api/language_settings_private/language_settings_private_delegate_unittest.cc",
       "../browser/extensions/api/management/management_api_unittest.cc",
       "../browser/extensions/api/notifications/extension_notification_handler_unittest.cc",
       "../browser/extensions/api/omnibox/omnibox_unittest.cc",
@@ -4250,8 +4252,8 @@
         "../browser/ui/views/tabs/fake_base_tab_strip_controller.cc",
         "../browser/ui/views/tabs/fake_base_tab_strip_controller.h",
         "../browser/ui/views/tabs/stacked_tab_strip_layout_unittest.cc",
-        "../browser/ui/views/tabs/tab_strip_impl_unittest.cc",
         "../browser/ui/views/tabs/tab_strip_layout_unittest.cc",
+        "../browser/ui/views/tabs/tab_strip_unittest.cc",
         "../browser/ui/views/tabs/tab_unittest.cc",
         "../browser/ui/views/toolbar/reload_button_unittest.cc",
         "../browser/ui/views/toolbar/toolbar_action_view_unittest.cc",
diff --git a/chrome/test/data/pdf/viewport_test.js b/chrome/test/data/pdf/viewport_test.js
index 387bb10..876cce14 100644
--- a/chrome/test/data/pdf/viewport_test.js
+++ b/chrome/test/data/pdf/viewport_test.js
@@ -605,6 +605,110 @@
     chrome.test.succeed();
   },
 
+  function testScrollTo() {
+    var mockWindow = new MockWindow(100, 100);
+    var mockSizer = new MockSizer();
+    var mockCallback = new MockViewportChangedCallback();
+    var viewport = new Viewport(mockWindow, mockSizer, mockCallback.callback,
+                                function() {}, function() {}, function() {},
+                                0, 1, 0);
+    var documentDimensions = new MockDocumentDimensions();
+
+    documentDimensions.addPage(200, 200);
+    viewport.setDocumentDimensions(documentDimensions);
+    viewport.setZoom(1);
+
+    chrome.test.assertEq(0, viewport.position.x);
+    chrome.test.assertEq(0, viewport.position.y);
+
+    mockCallback.reset();
+    viewport.scrollTo({x: 0, y: 0});
+    chrome.test.assertFalse(mockCallback.wasCalled);
+    chrome.test.assertEq(0, viewport.position.x);
+    chrome.test.assertEq(0, viewport.position.y);
+
+    mockCallback.reset();
+    viewport.scrollTo({x: 10, y: 20});
+    chrome.test.assertTrue(mockCallback.wasCalled);
+    chrome.test.assertEq(10, viewport.position.x);
+    chrome.test.assertEq(20, viewport.position.y);
+
+    mockCallback.reset();
+    viewport.scrollTo({y: 30});
+    chrome.test.assertTrue(mockCallback.wasCalled);
+    chrome.test.assertEq(10, viewport.position.x);
+    chrome.test.assertEq(30, viewport.position.y);
+
+    mockCallback.reset();
+    viewport.scrollTo({y: 30});
+    chrome.test.assertFalse(mockCallback.wasCalled);
+    chrome.test.assertEq(10, viewport.position.x);
+    chrome.test.assertEq(30, viewport.position.y);
+
+    mockCallback.reset();
+    viewport.scrollTo({x: 40});
+    chrome.test.assertTrue(mockCallback.wasCalled);
+    chrome.test.assertEq(40, viewport.position.x);
+    chrome.test.assertEq(30, viewport.position.y);
+
+    mockCallback.reset();
+    viewport.scrollTo({});
+    chrome.test.assertFalse(mockCallback.wasCalled);
+    chrome.test.assertEq(40, viewport.position.x);
+    chrome.test.assertEq(30, viewport.position.y);
+
+    chrome.test.succeed();
+  },
+
+  function testScrollBy() {
+    var mockWindow = new MockWindow(100, 100);
+    var mockSizer = new MockSizer();
+    var mockCallback = new MockViewportChangedCallback();
+    var viewport = new Viewport(mockWindow, mockSizer, mockCallback.callback,
+                                function() {}, function() {}, function() {},
+                                0, 1, 0);
+    var documentDimensions = new MockDocumentDimensions();
+
+    documentDimensions.addPage(200, 200);
+    viewport.setDocumentDimensions(documentDimensions);
+    viewport.setZoom(1);
+
+    chrome.test.assertEq(0, viewport.position.x);
+    chrome.test.assertEq(0, viewport.position.y);
+
+    mockCallback.reset();
+    viewport.scrollBy({x: 10, y: 20});
+    chrome.test.assertTrue(mockCallback.wasCalled);
+    chrome.test.assertEq(10, viewport.position.x);
+    chrome.test.assertEq(20, viewport.position.y);
+
+    mockCallback.reset();
+    viewport.scrollBy({x: 10, y: 20});
+    chrome.test.assertTrue(mockCallback.wasCalled);
+    chrome.test.assertEq(20, viewport.position.x);
+    chrome.test.assertEq(40, viewport.position.y);
+
+    mockCallback.reset();
+    viewport.scrollBy({x: -5, y: 0});
+    chrome.test.assertTrue(mockCallback.wasCalled);
+    chrome.test.assertEq(15, viewport.position.x);
+    chrome.test.assertEq(40, viewport.position.y);
+
+    mockCallback.reset();
+    viewport.scrollBy({x: 0, y: 60});
+    chrome.test.assertTrue(mockCallback.wasCalled);
+    chrome.test.assertEq(15, viewport.position.x);
+    chrome.test.assertEq(100, viewport.position.y);
+
+    mockCallback.reset();
+    viewport.scrollBy({x: 0, y: 0});
+    chrome.test.assertFalse(mockCallback.wasCalled);
+    chrome.test.assertEq(15, viewport.position.x);
+    chrome.test.assertEq(100, viewport.position.y);
+
+    chrome.test.succeed();
+  },
+
   function testGetPageScreenRect() {
     var mockWindow = new MockWindow(100, 100);
     var mockSizer = new MockSizer();
diff --git a/chrome/test/data/webui/cr_elements/cr_lazy_render_tests.js b/chrome/test/data/webui/cr_elements/cr_lazy_render_tests.js
index 08895cb..4d0a69d 100644
--- a/chrome/test/data/webui/cr_elements/cr_lazy_render_tests.js
+++ b/chrome/test/data/webui/cr_elements/cr_lazy_render_tests.js
@@ -13,15 +13,17 @@
 
   setup(function() {
     PolymerTest.clearBody();
-    const template =
-        '<template is="dom-bind" id="bind">' +
-        '  <template is="cr-lazy-render" id="lazy">' +
-        '    <h1>' +
-        '      <paper-checkbox checked="{{checked}}"></paper-checkbox>' +
-        '      {{name}}' +
-        '    </h1>' +
-        '  </template>' +
-        '</template>';
+    const template = `
+        <template is="dom-bind" id="bind">
+          <cr-lazy-render id="lazy">
+            <template>
+              <h1>
+                <paper-checkbox checked="{{checked}}"></paper-checkbox>
+                {{name}}
+              </h1>
+            </template>
+          </cr-lazy-render>
+        </template>`;
     document.body.innerHTML = template;
     lazy = document.getElementById('lazy');
     bind = document.getElementById('bind');
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 91a2deb..11aa8235 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -1276,6 +1276,7 @@
 
   /** @override */
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
+    'test_util.js',
     'settings_menu_test.js',
   ]),
 };
diff --git a/chrome/test/data/webui/settings/settings_menu_test.js b/chrome/test/data/webui/settings/settings_menu_test.js
index 7629908..2527a5f 100644
--- a/chrome/test/data/webui/settings/settings_menu_test.js
+++ b/chrome/test/data/webui/settings/settings_menu_test.js
@@ -75,6 +75,13 @@
       MockInteractions.tap(settingsMenu.$.people);
       assertEquals('', settings.getQueryParameters().toString());
     });
+
+    test('extensionsButtonClicked', function() {
+      const eventPromise =
+          test_util.eventToPromise('external-link-click', settingsMenu);
+      settingsMenu.$.extensionsButton.click();
+      return eventPromise;
+    });
   });
 
   suite('SettingsMenuReset', function() {
diff --git a/chrome/test/media_router/telemetry/benchmarks/media_router_benchmark.py b/chrome/test/media_router/telemetry/benchmarks/media_router_benchmark.py
index 750db18..5516d5d 100644
--- a/chrome/test/media_router/telemetry/benchmarks/media_router_benchmark.py
+++ b/chrome/test/media_router/telemetry/benchmarks/media_router_benchmark.py
@@ -107,7 +107,7 @@
     options.AppendExtraBrowserArgs([
         '--load-extension=' +
              os.path.join(path_util.GetChromiumSrcDir(), 'out',
-             'Release', 'media_router', 'test_extension'),
+             'Release', 'media_router', 'telemetry_extension'),
         '--media-router=0',
         '--enable-stats-collection-bindings'
     ])
diff --git a/components/arc/arc_features.cc b/components/arc/arc_features.cc
index daff6597..02e5403 100644
--- a/components/arc/arc_features.cc
+++ b/components/arc/arc_features.cc
@@ -6,6 +6,10 @@
 
 namespace arc {
 
+// Controls whether ARC is available for CHILD accounts.
+const base::Feature kAvailableForChildAccountFeature{
+    "ArcAvailableForChildAccount", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls ACTION_BOOT_COMPLETED broadcast for third party applications on ARC.
 // When disabled, third party apps will not receive this broadcast.
 const base::Feature kBootCompletedBroadcastFeature {
diff --git a/components/arc/arc_features.h b/components/arc/arc_features.h
index dcf16b4..2489c694 100644
--- a/components/arc/arc_features.h
+++ b/components/arc/arc_features.h
@@ -12,6 +12,7 @@
 namespace arc {
 
 // Please keep alphabetized.
+extern const base::Feature kAvailableForChildAccountFeature;
 extern const base::Feature kBootCompletedBroadcastFeature;
 extern const base::Feature kNativeBridgeExperimentFeature;
 extern const base::Feature kUsbHostFeature;
diff --git a/components/arc/arc_util.cc b/components/arc/arc_util.cc
index 35cb0f6f..b1925365 100644
--- a/components/arc/arc_util.cc
+++ b/components/arc/arc_util.cc
@@ -12,6 +12,7 @@
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/session_manager_client.h"
+#include "components/arc/arc_features.h"
 #include "components/user_manager/user_manager.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
@@ -165,7 +166,8 @@
     return false;
   }
 
-  if (user->GetType() == user_manager::USER_TYPE_CHILD) {
+  if (user->GetType() == user_manager::USER_TYPE_CHILD &&
+      !base::FeatureList::IsEnabled(arc::kAvailableForChildAccountFeature)) {
     VLOG(1) << "ARC usage by Child users is prohibited";
     return false;
   }
diff --git a/components/arc/arc_util_unittest.cc b/components/arc/arc_util_unittest.cc
index 65c952f..f83a51d 100644
--- a/components/arc/arc_util_unittest.cc
+++ b/components/arc/arc_util_unittest.cc
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "ash/public/cpp/app_types.h"
+#include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
@@ -222,6 +223,18 @@
   EXPECT_TRUE(IsArcAllowedForUser(ephemeral_user));
 }
 
+TEST_F(ArcUtilTest, IsArcAllowedForChildUserWithExperiment) {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv(
+      {"", "--enable-features=ArcAvailableForChildAccount"});
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitFromCommandLine(
+      command_line->GetSwitchValueASCII(switches::kEnableFeatures),
+      command_line->GetSwitchValueASCII(switches::kDisableFeatures));
+  const FakeUser user(user_manager::USER_TYPE_CHILD);
+  EXPECT_TRUE(IsArcAllowedForUser(&user));
+}
+
 TEST_F(ArcUtilTest, ArcStartModeDefault) {
   auto* command_line = base::CommandLine::ForCurrentProcess();
   command_line->InitFromArgv({"", "--arc-availability=installed"});
diff --git a/components/arc/video_accelerator/protected_buffer_manager.cc b/components/arc/video_accelerator/protected_buffer_manager.cc
index 993342af..d3c5a1f 100644
--- a/components/arc/video_accelerator/protected_buffer_manager.cc
+++ b/components/arc/video_accelerator/protected_buffer_manager.cc
@@ -29,6 +29,11 @@
 // Currently we have no way to know the resources of ProtectedBufferAllocator.
 // Arbitrarily chosen a reasonable constant as the limit.
 constexpr size_t kMaxConcurrentProtectedBufferAllocators = 32;
+
+// Maximum number of concurrent allocated protected buffers in a single
+// ProtectedBufferAllocator. This limitation, 48, is chosen expectedly, 16 for
+// protected input buffers and 32 for protected output buffers.
+constexpr size_t kMaxBuffersPerAllocator = 48;
 }  // namespace
 
 class ProtectedBufferManager::ProtectedBuffer {
@@ -291,11 +296,8 @@
     return false;
 
   base::AutoLock lock(buffer_map_lock_);
-
-  if (buffer_map_.find(id) != buffer_map_.end()) {
-    VLOGF(1) << "A protected buffer for this handle already exists";
+  if (!CanAllocateFor(allocator_id, id))
     return false;
-  }
 
   // Allocate a protected buffer and associate it with the dummy pixmap.
   // The pixmap needs to be stored to ensure the id remains the same for
@@ -306,17 +308,14 @@
     return false;
   }
 
-  VLOGF(2) << "New protected shared memory buffer, handle id: " << id;
-
-  // This will always succeed as we find() first above.
-  buffer_map_.emplace(id, std::move(protected_shmem));
-
-  DCHECK_EQ(allocator_to_buffers_map_.count(allocator_id), 1u);
   if (!allocator_to_buffers_map_[allocator_id].insert(id).second) {
     VLOGF(1) << "Failed inserting id: " << id
              << " to allocator_to_buffers_map_, allocator_id: " << allocator_id;
     return false;
   }
+  // This will always succeed as we find() first in CanAllocateFor().
+  buffer_map_.emplace(id, std::move(protected_shmem));
+  VLOGF(2) << "New protected shared memory buffer, handle id: " << id;
   return true;
 }
 
@@ -337,11 +336,8 @@
     return false;
 
   base::AutoLock lock(buffer_map_lock_);
-
-  if (buffer_map_.find(id) != buffer_map_.end()) {
-    VLOGF(1) << "A protected buffer for this handle already exists";
+  if (!CanAllocateFor(allocator_id, id))
     return false;
-  }
 
   // Allocate a protected buffer and associate it with the dummy pixmap.
   // The pixmap needs to be stored to ensure the id remains the same for
@@ -352,16 +348,14 @@
     return false;
   }
 
-  VLOGF(2) << "New protected native pixmap, handle id: " << id;
-  // This will always succeed as we find() first above.
-  buffer_map_.emplace(id, std::move(protected_pixmap));
-
-  DCHECK_EQ(allocator_to_buffers_map_.count(allocator_id), 1u);
   if (!allocator_to_buffers_map_[allocator_id].insert(id).second) {
     VLOGF(1) << "Failed inserting id: " << id
              << " to allocator_to_buffers_map_, allocator_id: " << allocator_id;
     return false;
   }
+  // This will always succeed as we find() first in CanAllocateFor().
+  buffer_map_.emplace(id, std::move(protected_pixmap));
+  VLOGF(2) << "New protected native pixmap, handle id: " << id;
   return true;
 }
 
@@ -384,10 +378,8 @@
     VLOGF(1) << "No allocated buffer by allocator id " << allocator_id;
     return;
   }
-  if (it->second.erase(id) != 1) {
+  if (it->second.erase(id) != 1)
     VLOGF(1) << "No buffer id " << id << " to destroy";
-    return;
-  }
 }
 
 void ProtectedBufferManager::ReleaseAllProtectedBuffers(uint64_t allocator_id) {
@@ -495,4 +487,29 @@
   if (num_erased != 1)
     VLOGF(1) << "No buffer id " << id << " to destroy";
 }
+
+bool ProtectedBufferManager::CanAllocateFor(uint64_t allocator_id,
+                                            uint32_t id) {
+  buffer_map_lock_.AssertAcquired();
+  if (buffer_map_.find(id) != buffer_map_.end()) {
+    VLOGF(1) << "A protected buffer for this handle already exists";
+    return false;
+  }
+
+  auto it = allocator_to_buffers_map_.find(allocator_id);
+  if (it == allocator_to_buffers_map_.end()) {
+    VLOGF(1) << "allocator_to_buffers_map_ has no entry, allocator_id="
+             << allocator_id;
+    return false;
+  }
+  auto& allocated_protected_buffer_ids = it->second;
+  // Check if the number of allocated protected buffers for |allocator_id| is
+  // less than kMaxBuffersPerAllocator.
+  if (allocated_protected_buffer_ids.size() >= kMaxBuffersPerAllocator) {
+    VLOGF(1) << "Too many allocated protected buffers: "
+             << kMaxBuffersPerAllocator;
+    return false;
+  }
+  return true;
+}
 }  // namespace arc
diff --git a/components/arc/video_accelerator/protected_buffer_manager.h b/components/arc/video_accelerator/protected_buffer_manager.h
index 95feeba8..e6eb108 100644
--- a/components/arc/video_accelerator/protected_buffer_manager.h
+++ b/components/arc/video_accelerator/protected_buffer_manager.h
@@ -113,6 +113,10 @@
   // Removes an entry for given |id| from buffer_map_.
   void RemoveEntry(uint32_t id);
 
+  // Returns whether a protected buffer whose unique id is |id| can be
+  // allocated by PBA whose allocator id is |allocator_id|.
+  bool CanAllocateFor(uint64_t allocator_id, uint32_t id);
+
   // A map of unique ids to the ProtectedBuffers associated with them.
   using ProtectedBufferMap =
       std::map<uint32_t, std::unique_ptr<ProtectedBuffer>>;
diff --git a/components/autofill/core/browser/autofill_metrics.cc b/components/autofill/core/browser/autofill_metrics.cc
index c9b25a0..3e6f0b99 100644
--- a/components/autofill/core/browser/autofill_metrics.cc
+++ b/components/autofill/core/browser/autofill_metrics.cc
@@ -629,6 +629,14 @@
 }
 
 // static
+void AutofillMetrics::LogSubmittedCardStateMetric(
+    SubmittedCardStateMetric metric) {
+  DCHECK_LT(metric, NUM_SUBMITTED_CARD_STATE_METRICS);
+  UMA_HISTOGRAM_ENUMERATION("Autofill.SubmittedCardState", metric,
+                            NUM_SUBMITTED_CARD_STATE_METRICS);
+}
+
+// static
 void AutofillMetrics::LogSubmittedServerCardExpirationStatusMetric(
     SubmittedServerCardExpirationStatusMetric metric) {
   DCHECK_LT(metric, NUM_SUBMITTED_SERVER_CARD_EXPIRATION_STATUS_METRICS);
@@ -638,11 +646,19 @@
 }
 
 // static
-void AutofillMetrics::LogSubmittedCardStateMetric(
-    SubmittedCardStateMetric metric) {
-  DCHECK_LT(metric, NUM_SUBMITTED_CARD_STATE_METRICS);
-  UMA_HISTOGRAM_ENUMERATION("Autofill.SubmittedCardState", metric,
-                            NUM_SUBMITTED_CARD_STATE_METRICS);
+void AutofillMetrics::LogUploadOfferedCardOriginMetric(
+    UploadOfferedCardOriginMetric metric) {
+  DCHECK_LT(metric, NUM_UPLOAD_OFFERED_CARD_ORIGIN_METRICS);
+  UMA_HISTOGRAM_ENUMERATION("Autofill.UploadOfferedCardOrigin", metric,
+                            NUM_UPLOAD_OFFERED_CARD_ORIGIN_METRICS);
+}
+
+// static
+void AutofillMetrics::LogUploadAcceptedCardOriginMetric(
+    UploadAcceptedCardOriginMetric metric) {
+  DCHECK_LT(metric, NUM_UPLOAD_ACCEPTED_CARD_ORIGIN_METRICS);
+  UMA_HISTOGRAM_ENUMERATION("Autofill.UploadAcceptedCardOrigin", metric,
+                            NUM_UPLOAD_ACCEPTED_CARD_ORIGIN_METRICS);
 }
 
 // static
diff --git a/components/autofill/core/browser/autofill_metrics.h b/components/autofill/core/browser/autofill_metrics.h
index be86575..207d4f5 100644
--- a/components/autofill/core/browser/autofill_metrics.h
+++ b/components/autofill/core/browser/autofill_metrics.h
@@ -151,6 +151,26 @@
     NUM_SUBMITTED_SERVER_CARD_EXPIRATION_STATUS_METRICS,
   };
 
+  // Metric to measure if a card for which upload was offered is already stored
+  // as a local card on the device or if it has not yet been seen.
+  enum UploadOfferedCardOriginMetric {
+    // Credit card upload was offered for a local card already on the device.
+    OFFERING_UPLOAD_OF_LOCAL_CARD,
+    // Credit card upload was offered for a newly-seen credit card.
+    OFFERING_UPLOAD_OF_NEW_CARD,
+    NUM_UPLOAD_OFFERED_CARD_ORIGIN_METRICS,
+  };
+
+  // Metric to measure if a card for which upload was accepted is already stored
+  // as a local card on the device or if it has not yet been seen.
+  enum UploadAcceptedCardOriginMetric {
+    // The user accepted upload of a local card already on the device.
+    USER_ACCEPTED_UPLOAD_OF_LOCAL_CARD,
+    // The user accepted upload of a newly-seen credit card.
+    USER_ACCEPTED_UPLOAD_OF_NEW_CARD,
+    NUM_UPLOAD_ACCEPTED_CARD_ORIGIN_METRICS,
+  };
+
   // Metrics to measure user interaction with the save credit card prompt.
   //
   // SAVE_CARD_PROMPT_DISMISS_FOCUS is not stored explicitly, but can be
@@ -701,13 +721,23 @@
                               ukm::SourceId source_id,
                               const GURL& url);
 
+  static void LogSubmittedCardStateMetric(SubmittedCardStateMetric metric);
+
   // If a credit card that matches a server card (unmasked or not) was submitted
   // on a form, logs whether the submitted card's expiration date matched the
   // server card's known expiration date.
   static void LogSubmittedServerCardExpirationStatusMetric(
       SubmittedServerCardExpirationStatusMetric metric);
 
-  static void LogSubmittedCardStateMetric(SubmittedCardStateMetric metric);
+  // When credit card upload is offered, logs whether the card being offered is
+  // already a local card on the device or not.
+  static void LogUploadOfferedCardOriginMetric(
+      UploadOfferedCardOriginMetric metric);
+
+  // When credit card upload is accepted, logs whether the card being accepted
+  // is already a local card on the device or not.
+  static void LogUploadAcceptedCardOriginMetric(
+      UploadAcceptedCardOriginMetric metric);
 
   // |upload_decision_metrics| is a bitmask of |CardUploadDecisionMetric|.
   static void LogCardUploadDecisionMetrics(int upload_decision_metrics);
diff --git a/components/autofill/core/browser/credit_card_save_manager.cc b/components/autofill/core/browser/credit_card_save_manager.cc
index e3935b3c4..161d9419 100644
--- a/components/autofill/core/browser/credit_card_save_manager.cc
+++ b/components/autofill/core/browser/credit_card_save_manager.cc
@@ -97,9 +97,11 @@
 
 void CreditCardSaveManager::AttemptToOfferCardUploadSave(
     const FormStructure& submitted_form,
-    const CreditCard& card) {
+    const CreditCard& card,
+    const bool uploading_local_card) {
   upload_request_ = payments::PaymentsClient::UploadRequestDetails();
   upload_request_.card = card;
+  uploading_local_card_ = uploading_local_card;
 
   // In order to prompt the user to upload their card, we must have both:
   //  1) Card with CVC
@@ -249,6 +251,9 @@
         base::Bind(&CreditCardSaveManager::OnDidGetUploadRiskData,
                    weak_ptr_factory_.GetWeakPtr()));
     upload_decision_metrics_ |= AutofillMetrics::UPLOAD_OFFERED;
+    AutofillMetrics::LogUploadOfferedCardOriginMetric(
+        uploading_local_card_ ? AutofillMetrics::OFFERING_UPLOAD_OF_LOCAL_CARD
+                              : AutofillMetrics::OFFERING_UPLOAD_OF_NEW_CARD);
   } else {
     // If the upload details request failed, fall back to a local save. The
     // reasoning here is as follows:
@@ -526,6 +531,10 @@
         static_cast<int64_t>(payments_client_->GetPrefService()->GetDouble(
             prefs::kAutofillBillingCustomerNumber));
   }
+  AutofillMetrics::LogUploadAcceptedCardOriginMetric(
+      uploading_local_card_
+          ? AutofillMetrics::USER_ACCEPTED_UPLOAD_OF_LOCAL_CARD
+          : AutofillMetrics::USER_ACCEPTED_UPLOAD_OF_NEW_CARD);
   payments_client_->UploadCard(upload_request_);
 }
 
diff --git a/components/autofill/core/browser/credit_card_save_manager.h b/components/autofill/core/browser/credit_card_save_manager.h
index fc0897b..c200797 100644
--- a/components/autofill/core/browser/credit_card_save_manager.h
+++ b/components/autofill/core/browser/credit_card_save_manager.h
@@ -80,7 +80,8 @@
   // Begins the process to offer upload credit card save to the user if the
   // imported card passes all requirements and Google Payments approves.
   void AttemptToOfferCardUploadSave(const FormStructure& submitted_form,
-                                    const CreditCard& card);
+                                    const CreditCard& card,
+                                    const bool uploading_local_card);
 
   // Returns true if all the conditions for enabling the upload of credit card
   // are satisfied.
@@ -169,6 +170,10 @@
   // offered.
   int upload_decision_metrics_ = 0;
 
+  // |true| if the card being offered for upload is already a local card on the
+  // device; |false| otherwise.
+  bool uploading_local_card_ = false;
+
   // |true| if the user has opted to upload save their credit card to Google.
   bool user_did_accept_upload_prompt_ = false;
 
diff --git a/components/autofill/core/browser/credit_card_save_manager_unittest.cc b/components/autofill/core/browser/credit_card_save_manager_unittest.cc
index 300d861..592abaf 100644
--- a/components/autofill/core/browser/credit_card_save_manager_unittest.cc
+++ b/components/autofill/core/browser/credit_card_save_manager_unittest.cc
@@ -3804,4 +3804,101 @@
                                    kAutofillUpstreamSendPanFirstSix.name));
 }
 
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfLocalCard) {
+  personal_data_.ClearCreditCards();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+
+  // Add a local credit card whose |TypeAndLastFourDigits| matches what we will
+  // enter below.
+  CreditCard local_card;
+  test::SetCreditCardInfo(&local_card, "Flo Master", "4111111111111111", "11",
+                          NextYear().c_str(), "1");
+  local_card.set_record_type(CreditCard::LOCAL_CARD);
+  personal_data_.AddCreditCard(local_card);
+
+  // Create, fill and submit an address form in order to establish a recent
+  // profile which can be selected for the upload request.
+  FormData address_form;
+  test::CreateTestAddressFormData(&address_form);
+  FormsSeen(std::vector<FormData>(1, address_form));
+  ExpectUniqueFillableFormParsedUkm();
+
+  ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
+  FormSubmitted(address_form);
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+  ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */);
+
+  // Edit the data, and submit.
+  credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
+  credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
+  credit_card_form.fields[2].value = ASCIIToUTF16("11");
+  credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+  credit_card_form.fields[4].value = ASCIIToUTF16("123");
+
+  base::HistogramTester histogram_tester;
+
+  EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+  FormSubmitted(credit_card_form);
+  EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
+
+  // Verify that metrics noted it was an existing local card for which credit
+  // card upload was offered and accepted.
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.UploadOfferedCardOrigin",
+      AutofillMetrics::OFFERING_UPLOAD_OF_LOCAL_CARD, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.UploadAcceptedCardOrigin",
+      AutofillMetrics::USER_ACCEPTED_UPLOAD_OF_LOCAL_CARD, 1);
+}
+
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_UploadOfNewCard) {
+  // No cards already on the device.
+  personal_data_.ClearCreditCards();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+
+  // Create, fill and submit an address form in order to establish a recent
+  // profile which can be selected for the upload request.
+  FormData address_form;
+  test::CreateTestAddressFormData(&address_form);
+  FormsSeen(std::vector<FormData>(1, address_form));
+  ExpectUniqueFillableFormParsedUkm();
+
+  ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
+  FormSubmitted(address_form);
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+  ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */);
+
+  // Edit the data, and submit.
+  credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
+  credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
+  credit_card_form.fields[2].value = ASCIIToUTF16("11");
+  credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+  credit_card_form.fields[4].value = ASCIIToUTF16("123");
+
+  base::HistogramTester histogram_tester;
+
+  EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+  FormSubmitted(credit_card_form);
+  EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
+
+  // Verify that metrics noted it was a brand new card for which credit card
+  // upload was offered and accepted.
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.UploadOfferedCardOrigin",
+      AutofillMetrics::OFFERING_UPLOAD_OF_NEW_CARD, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.UploadAcceptedCardOrigin",
+      AutofillMetrics::USER_ACCEPTED_UPLOAD_OF_NEW_CARD, 1);
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/form_data_importer.cc b/components/autofill/core/browser/form_data_importer.cc
index 5195eb2..4809b10 100644
--- a/components/autofill/core/browser/form_data_importer.cc
+++ b/components/autofill/core/browser/form_data_importer.cc
@@ -129,7 +129,8 @@
     // note that unless the "send detected values" experiment is enabled, they
     // must pass address/name/CVC validation requirements first.
     credit_card_save_manager_->AttemptToOfferCardUploadSave(
-        submitted_form, *imported_credit_card);
+        submitted_form, *imported_credit_card,
+        offering_upload_of_local_credit_card_);
   }
 }
 
@@ -343,6 +344,9 @@
       // without setting |imported_credit_card|.
       if (!should_return_local_card)
         return true;
+      // Mark that the credit card potentially being offered to upload is
+      // already a local card.
+      offering_upload_of_local_credit_card_ = true;
 
       break;
     }
diff --git a/components/autofill/core/browser/form_data_importer.h b/components/autofill/core/browser/form_data_importer.h
index 861b2fb5..f60dc1b 100644
--- a/components/autofill/core/browser/form_data_importer.h
+++ b/components/autofill/core/browser/form_data_importer.h
@@ -104,6 +104,10 @@
   // May be NULL.  NULL indicates OTR.
   PersonalDataManager* personal_data_manager_;
 
+  // For metrics, to be passed to |credit_card_save_manager_|. Notes if the
+  // credit card being offered for upload is already a locally-saved card.
+  bool offering_upload_of_local_credit_card_ = false;
+
   std::string app_locale_;
 
   friend class AutofillMergeTest;
@@ -116,6 +120,10 @@
   FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest, DontDuplicateFullServerCard);
   FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest, DontDuplicateMaskedServerCard);
   FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest,
+                           ImportCreditCard_TrackOfferingUploadOfLocalCard);
+  FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest,
+                           ImportCreditCard_TrackOfferingUploadOfNewCard);
+  FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest,
                            ImportFormData_OneAddressCreditCardDisabled);
   FRIEND_TEST_ALL_PREFIXES(FormDataImporterTest,
                            ImportFormData_OneAddressOneCreditCard);
diff --git a/components/autofill/core/browser/form_data_importer_unittest.cc b/components/autofill/core/browser/form_data_importer_unittest.cc
index f6483214..e648282 100644
--- a/components/autofill/core/browser/form_data_importer_unittest.cc
+++ b/components/autofill/core/browser/form_data_importer_unittest.cc
@@ -1992,6 +1992,53 @@
   EXPECT_EQ(0, credit_card.Compare(*results[0]));
 }
 
+// Ensures that |offering_upload_of_local_credit_card_| is set correctly.
+TEST_F(FormDataImporterTest, ImportCreditCard_TrackOfferingUploadOfLocalCard) {
+  // Start with a single valid credit card stored via the preferences.
+  CreditCard saved_credit_card(base::GenerateGUID(), "https://www.example.com");
+  test::SetCreditCardInfo(&saved_credit_card, "Biggie Smalls",
+                          "4111 1111 1111 1111" /* Visa */, "01", "2999", "");
+  personal_data_manager_->AddCreditCard(saved_credit_card);
+
+  WaitForOnPersonalDataChanged();
+
+  const std::vector<CreditCard*>& results =
+      personal_data_manager_->GetCreditCards();
+  ASSERT_EQ(1U, results.size());
+  EXPECT_EQ(0, saved_credit_card.Compare(*results[0]));
+
+  // Simulate a form submission with the same card.
+  FormData form;
+  AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01",
+                        "2999");
+
+  FormStructure form_structure(form);
+  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+  std::unique_ptr<CreditCard> imported_credit_card;
+  EXPECT_TRUE(ImportCreditCard(form_structure, true, &imported_credit_card));
+  ASSERT_TRUE(imported_credit_card);
+  // |offering_upload_of_local_credit_card_| should be true because upload was
+  // offered and the card is a local card already on the device.
+  ASSERT_TRUE(form_data_importer_->offering_upload_of_local_credit_card_);
+}
+
+// Ensures that |offering_upload_of_local_credit_card_| is set correctly.
+TEST_F(FormDataImporterTest, ImportCreditCard_TrackOfferingUploadOfNewCard) {
+  // Simulate a form submission with a new credit card.
+  FormData form;
+  AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01",
+                        "2999");
+
+  FormStructure form_structure(form);
+  form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */);
+  std::unique_ptr<CreditCard> imported_credit_card;
+  EXPECT_TRUE(ImportCreditCard(form_structure, true, &imported_credit_card));
+  ASSERT_TRUE(imported_credit_card);
+  // |offering_upload_of_local_credit_card_| should be false because upload was
+  // offered but the card is NOT a local card already on the device.
+  ASSERT_FALSE(form_data_importer_->offering_upload_of_local_credit_card_);
+}
+
 // ImportFormData tests (both addresses and credit cards).
 
 // Test that a form with both address and credit card sections imports the
diff --git a/components/cronet/BUILD.gn b/components/cronet/BUILD.gn
index 31d4ebf..a70f148 100644
--- a/components/cronet/BUILD.gn
+++ b/components/cronet/BUILD.gn
@@ -69,8 +69,7 @@
 # For platforms on which the native Cronet library is used, build the library,
 # a cronet_tests binary that exercises it, and a unit-tests binary.
 # Android and iOS have their own platform-specific rules to build Cronet.
-# TODO(https://crbug.com/812268): Fix Windows build, and enable it.
-if (!is_ios && !is_android && !is_win) {
+if (!is_ios && !is_android) {
   shared_library("cronet") {
     deps = [
       "//base",
diff --git a/components/cronet/cronet_prefs_manager.cc b/components/cronet/cronet_prefs_manager.cc
index e06f21ae..07ed170d 100644
--- a/components/cronet/cronet_prefs_manager.cc
+++ b/components/cronet/cronet_prefs_manager.cc
@@ -30,9 +30,11 @@
 // Name of the pref used for HTTP server properties persistence.
 const char kHttpServerPropertiesPref[] = "net.http_server_properties";
 // Name of preference directory.
-const char kPrefsDirectoryName[] = "prefs";
+const base::FilePath::CharType kPrefsDirectoryName[] =
+    FILE_PATH_LITERAL("prefs");
 // Name of preference file.
-const char kPrefsFileName[] = "local_prefs.json";
+const base::FilePath::CharType kPrefsFileName[] =
+    FILE_PATH_LITERAL("local_prefs.json");
 // Current version of disk storage.
 const int32_t kStorageVersion = 1;
 // Version number used when the version of disk storage is unknown.
@@ -60,7 +62,7 @@
 // TODO(xunjieli): Handle failures.
 void InitializeStorageDirectory(const base::FilePath& dir) {
   // Checks version file and clear old storage.
-  base::FilePath version_filepath = dir.Append("version");
+  base::FilePath version_filepath(dir.AppendASCII("version"));
   if (IsCurrentVersion(version_filepath)) {
     // The version is up to date, so there is nothing to do.
     return;
@@ -88,7 +90,7 @@
     DLOG(WARNING) << "Cannot write to version file.";
     return;
   }
-  base::FilePath prefs_dir = dir.Append(FILE_PATH_LITERAL(kPrefsDirectoryName));
+  base::FilePath prefs_dir = dir.Append(kPrefsDirectoryName);
   if (!base::CreateDirectory(prefs_dir)) {
     DLOG(WARNING) << "Cannot create prefs directory";
     return;
@@ -214,7 +216,12 @@
   DCHECK(network_task_runner->BelongsToCurrentThread());
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
+#if defined(OS_WIN)
+  base::FilePath storage_file_path(
+      base::FilePath::FromUTF8Unsafe(storage_path));
+#else
   base::FilePath storage_file_path(storage_path);
+#endif
 
   // Make sure storage directory has correct version.
   {
@@ -223,8 +230,7 @@
   }
 
   base::FilePath filepath =
-      storage_file_path.Append(FILE_PATH_LITERAL(kPrefsDirectoryName))
-          .Append(FILE_PATH_LITERAL(kPrefsFileName));
+      storage_file_path.Append(kPrefsDirectoryName).Append(kPrefsFileName);
 
   json_pref_store_ = new JsonPrefStore(filepath, file_task_runner,
                                        std::unique_ptr<PrefFilter>());
diff --git a/components/cronet/cronet_url_request_context.cc b/components/cronet/cronet_url_request_context.cc
index 42e9684..746116b6 100644
--- a/components/cronet/cronet_url_request_context.cc
+++ b/components/cronet/cronet_url_request_context.cc
@@ -350,7 +350,12 @@
 
   // Set up pref file if storage path is specified.
   if (!config->storage_path.empty()) {
+#if defined(OS_WIN)
+    base::FilePath storage_path(
+        base::FilePath::FromUTF8Unsafe(config->storage_path));
+#else
     base::FilePath storage_path(config->storage_path);
+#endif
     // Set up the HttpServerPropertiesManager.
     cronet_prefs_manager_ = std::make_unique<CronetPrefsManager>(
         config->storage_path, network_task_runner_, file_task_runner,
@@ -505,7 +510,11 @@
 
 bool CronetURLRequestContext::StartNetLogToFile(const std::string& file_name,
                                                 bool log_all) {
+#if defined(OS_WIN)
+  base::FilePath file_path(base::FilePath::FromUTF8Unsafe(file_name));
+#else
   base::FilePath file_path(file_name);
+#endif
   base::ScopedFILE file(base::OpenFile(file_path, "w"));
   if (!file) {
     LOG(ERROR) << "Failed to open NetLog file for writing.";
@@ -640,8 +649,13 @@
 
   // TODO(eroman): The cronet API passes a directory here. But it should now
   // just pass a file path.
-  base::FilePath file_path =
-      base::FilePath(dir_path).AppendASCII("netlog.json");
+#if defined(OS_WIN)
+  base::FilePath file_path(base::FilePath::FromUTF8Unsafe(dir_path));
+#else
+  base::FilePath file_path(dir_path);
+#endif
+  file_path = file_path.AppendASCII("netlog.json");
+
   {
     base::ScopedAllowBlocking allow_blocking;
     if (!base::PathIsWritable(file_path)) {
diff --git a/components/cronet/host_cache_persistence_manager_unittest.cc b/components/cronet/host_cache_persistence_manager_unittest.cc
index c93da37f6..e87ce34 100644
--- a/components/cronet/host_cache_persistence_manager_unittest.cc
+++ b/components/cronet/host_cache_persistence_manager_unittest.cc
@@ -45,7 +45,7 @@
   // not the full contents, since the tests in this file are only intended
   // to test that writes happen when they're supposed to, not serialization
   // correctness.
-  void CheckPref(uint size) {
+  void CheckPref(size_t size) {
     const base::Value* value = pref_service_->GetUserPref(kPrefName);
     base::ListValue list;
     if (value)
diff --git a/components/cronet/native/buffer.cc b/components/cronet/native/buffer.cc
index 11d4b4c..60d8cdb 100644
--- a/components/cronet/native/buffer.cc
+++ b/components/cronet/native/buffer.cc
@@ -7,6 +7,7 @@
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
 
 namespace {
 
@@ -62,6 +63,8 @@
 }
 
 void Cronet_BufferImpl::InitWithAlloc(uint64_t size) {
+  if (!base::IsValueInRangeForNumericType<size_t, uint64_t>(size))
+    return;
   data_ = malloc(size);
   if (!data_)
     return;
diff --git a/components/cronet/native/engine.cc b/components/cronet/native/engine.cc
index 6dab5bf..d63b07a 100644
--- a/components/cronet/native/engine.cc
+++ b/components/cronet/native/engine.cc
@@ -114,9 +114,15 @@
     case Cronet_EngineParams_HTTP_CACHE_MODE_IN_MEMORY:
       context_config_builder.http_cache = URLRequestContextConfig::MEMORY;
       break;
-    case Cronet_EngineParams_HTTP_CACHE_MODE_DISK:
+    case Cronet_EngineParams_HTTP_CACHE_MODE_DISK: {
       context_config_builder.http_cache = URLRequestContextConfig::DISK;
-      if (!base::DirectoryExists(base::FilePath(params->storage_path))) {
+#if defined(OS_WIN)
+      const base::FilePath storage_path(
+          base::FilePath::FromUTF8Unsafe(params->storage_path));
+#else
+      const base::FilePath storage_path(params->storage_path);
+#endif
+      if (!base::DirectoryExists(storage_path)) {
         return CheckResult(
             Cronet_RESULT_ILLEGAL_ARGUMENT_STORAGE_PATH_MUST_EXIST);
       }
@@ -128,6 +134,7 @@
       }
       in_use_storage_path_ = params->storage_path;
       break;
+    }
     default:
       context_config_builder.http_cache = URLRequestContextConfig::DISABLED;
   }
diff --git a/components/cronet/native/include/cronet_export.h b/components/cronet/native/include/cronet_export.h
index 0a29976..68379ae 100644
--- a/components/cronet/native/include/cronet_export.h
+++ b/components/cronet/native/include/cronet_export.h
@@ -6,7 +6,7 @@
 #define COMPONENTS_CRONET_NATIVE_INCLUDE_CRONET_EXPORT_H_
 
 #if defined(WIN32)
-#define CRONET_EXPORT
+#define CRONET_EXPORT __declspec(dllexport)
 #else
 #define CRONET_EXPORT __attribute__((visibility("default")))
 #endif
diff --git a/components/cronet/native/test/buffer_test.cc b/components/cronet/native/test/buffer_test.cc
index 08af6a4..85ce107 100644
--- a/components/cronet/native/test/buffer_test.cc
+++ b/components/cronet/native/test/buffer_test.cc
@@ -4,6 +4,8 @@
 
 #include "cronet_c.h"
 
+#include <limits>
+
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
@@ -64,30 +66,36 @@
   // Create Cronet buffer and allocate buffer data.
   Cronet_BufferPtr buffer = Cronet_Buffer_Create();
   Cronet_Buffer_InitWithAlloc(buffer, kTestBufferSize);
-  ASSERT_TRUE(Cronet_Buffer_GetData(buffer));
-  ASSERT_EQ(Cronet_Buffer_GetSize(buffer), kTestBufferSize);
+  EXPECT_TRUE(Cronet_Buffer_GetData(buffer));
+  EXPECT_EQ(Cronet_Buffer_GetSize(buffer), kTestBufferSize);
   Cronet_Buffer_Destroy(buffer);
   ASSERT_FALSE(on_destroy_called());
 }
 
-#if defined(ADDRESS_SANITIZER)
+#if defined(ADDRESS_SANITIZER) || defined(OS_FUCHSIA)
 // ASAN malloc by default triggers crash instead of returning null on failure.
+// Fuchsia malloc() also crashes on allocation failure in some kernel builds.
 #define MAYBE_TestInitWithHugeAllocFails DISABLED_TestInitWithHugeAllocFails
 #else
 #define MAYBE_TestInitWithHugeAllocFails TestInitWithHugeAllocFails
 #endif
-// Example of allocating buffer with hugereasonable size.
+// Verify behaviour when an unsatisfiably huge buffer allocation is requested.
+// On 32-bit platforms, we want to ensure that a 64-bit range allocation size
+// is rejected, rather than resulting in a 32-bit truncated allocation.
+// Some platforms over-commit allocations, so we request an allocation of the
+// whole 64-bit address-space, which cannot possibly be satisfied in a 32- or
+// 64-bit process.
 TEST_F(BufferTest, MAYBE_TestInitWithHugeAllocFails) {
-  // Create Cronet buffer and allocate buffer data.
   Cronet_BufferPtr buffer = Cronet_Buffer_Create();
-  Cronet_Buffer_InitWithAlloc(buffer, 1000 * 1000 * 1000 * 1000);
-  ASSERT_FALSE(Cronet_Buffer_GetData(buffer));
-  ASSERT_EQ(Cronet_Buffer_GetSize(buffer), 0ull);
+  const uint64_t kHugeTestBufferSize = std::numeric_limits<uint64_t>::max();
+  Cronet_Buffer_InitWithAlloc(buffer, kHugeTestBufferSize);
+  EXPECT_FALSE(Cronet_Buffer_GetData(buffer));
+  EXPECT_EQ(Cronet_Buffer_GetSize(buffer), 0ull);
   Cronet_Buffer_Destroy(buffer);
   ASSERT_FALSE(on_destroy_called());
 }
 
-// Example of intializing buffer with app-allocated data.
+// Example of initializing buffer with app-allocated data.
 TEST_F(BufferTest, TestInitWithDataAndCallback) {
   Cronet_BufferCallbackPtr buffer_callback =
       Cronet_BufferCallback_CreateWith(BufferCallback_OnDestroy);
@@ -96,8 +104,8 @@
   Cronet_BufferPtr buffer = Cronet_Buffer_Create();
   Cronet_Buffer_InitWithDataAndCallback(buffer, malloc(kTestBufferSize),
                                         kTestBufferSize, buffer_callback);
-  ASSERT_TRUE(Cronet_Buffer_GetData(buffer));
-  ASSERT_EQ(Cronet_Buffer_GetSize(buffer), kTestBufferSize);
+  EXPECT_TRUE(Cronet_Buffer_GetData(buffer));
+  EXPECT_EQ(Cronet_Buffer_GetSize(buffer), kTestBufferSize);
   Cronet_Buffer_Destroy(buffer);
   ASSERT_TRUE(on_destroy_called());
 }
diff --git a/components/cronet/native/test/engine_test.cc b/components/cronet/native/test/engine_test.cc
index 4fc6378..57b3474 100644
--- a/components/cronet/native/test/engine_test.cc
+++ b/components/cronet/native/test/engine_test.cc
@@ -75,7 +75,7 @@
   EXPECT_TRUE(temp_dir.CreateUniqueTempDir());
   base::FilePath temp_path = base::MakeAbsoluteFilePath(temp_dir.GetPath());
   Cronet_EngineParams_storage_path_set(engine_params,
-                                       temp_path.value().c_str());
+                                       temp_path.AsUTF8Unsafe().c_str());
   // Now the engine should start successfully.
   EXPECT_EQ(Cronet_RESULT_SUCCESS,
             Cronet_Engine_StartWithParams(engine, engine_params));
@@ -170,7 +170,7 @@
       "{ \"QUIC\" : {\"max_server_configs_stored_in_properties\" : 8} }");
   // Test that net log cannot start/stop before engine start.
   EXPECT_FALSE(Cronet_Engine_StartNetLogToFile(
-      engine, net_log_file.value().c_str(), true));
+      engine, net_log_file.AsUTF8Unsafe().c_str(), true));
   Cronet_Engine_StopNetLog(engine);
 
   // Start the engine.
@@ -179,15 +179,15 @@
 
   // Test that normal start/stop net log works.
   EXPECT_TRUE(Cronet_Engine_StartNetLogToFile(
-      engine, net_log_file.value().c_str(), true));
+      engine, net_log_file.AsUTF8Unsafe().c_str(), true));
   Cronet_Engine_StopNetLog(engine);
 
   // Test that double start/stop net log works.
   EXPECT_TRUE(Cronet_Engine_StartNetLogToFile(
-      engine, net_log_file.value().c_str(), true));
+      engine, net_log_file.AsUTF8Unsafe().c_str(), true));
   // Test that second start fails.
   EXPECT_FALSE(Cronet_Engine_StartNetLogToFile(
-      engine, net_log_file.value().c_str(), true));
+      engine, net_log_file.AsUTF8Unsafe().c_str(), true));
   // Test that multiple stops work.
   Cronet_Engine_StopNetLog(engine);
   Cronet_Engine_StopNetLog(engine);
@@ -207,7 +207,7 @@
   Cronet_Engine_Shutdown(engine);
   // Test that net log cannot start/stop after engine shutdown.
   EXPECT_FALSE(Cronet_Engine_StartNetLogToFile(
-      engine, net_log_file.value().c_str(), true));
+      engine, net_log_file.AsUTF8Unsafe().c_str(), true));
   Cronet_Engine_StopNetLog(engine);
   Cronet_Engine_Destroy(engine);
 }
diff --git a/components/cronet/stale_host_resolver.h b/components/cronet/stale_host_resolver.h
index 5deffad..2a9115b1 100644
--- a/components/cronet/stale_host_resolver.h
+++ b/components/cronet/stale_host_resolver.h
@@ -15,9 +15,9 @@
 // A HostResolver that wraps a HostResolverImpl and uses it to make requests,
 // but "impatiently" returns stale data (if available and usable) after a delay,
 // to reduce DNS latency at the expense of accuracy.
-class NET_EXPORT StaleHostResolver : public net::HostResolver {
+class StaleHostResolver : public net::HostResolver {
  public:
-  struct NET_EXPORT StaleOptions {
+  struct StaleOptions {
     StaleOptions();
 
     // How long to wait before returning stale data, if available.
diff --git a/components/cronet/url_request_context_config.cc b/components/cronet/url_request_context_config.cc
index 1c53208f..72130a5 100644
--- a/components/cronet/url_request_context_config.cc
+++ b/components/cronet/url_request_context_config.cc
@@ -42,7 +42,8 @@
 namespace {
 
 // Name of disk cache directory.
-const char kDiskCacheDirectoryName[] = "disk_cache";
+const base::FilePath::CharType kDiskCacheDirectoryName[] =
+    FILE_PATH_LITERAL("disk_cache");
 // TODO(xunjieli): Refactor constants in io_thread.cc.
 const char kQuicFieldTrialName[] = "QUIC";
 const char kQuicConnectionOptions[] = "connection_options";
@@ -461,7 +462,8 @@
     } else if (it.key() == kSSLKeyLogFile) {
       std::string ssl_key_log_file_string;
       if (it.value().GetAsString(&ssl_key_log_file_string)) {
-        base::FilePath ssl_key_log_file(ssl_key_log_file_string);
+        base::FilePath ssl_key_log_file(
+            base::FilePath::FromUTF8Unsafe(ssl_key_log_file_string));
         if (!ssl_key_log_file.empty()) {
           // SetSSLKeyLogFile is only safe to call before any SSLClientSockets
           // are created. This should not be used if there are multiple
@@ -551,9 +553,8 @@
     net::URLRequestContextBuilder::HttpCacheParams cache_params;
     if (http_cache == DISK && !storage_path.empty()) {
       cache_params.type = net::URLRequestContextBuilder::HttpCacheParams::DISK;
-      cache_params.path =
-          base::FilePath(storage_path)
-              .Append(FILE_PATH_LITERAL(kDiskCacheDirectoryName));
+      cache_params.path = base::FilePath::FromUTF8Unsafe(storage_path)
+                              .Append(kDiskCacheDirectoryName);
     } else {
       cache_params.type =
           net::URLRequestContextBuilder::HttpCacheParams::IN_MEMORY;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
index f3a2beb..8cfdd54 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
@@ -194,7 +194,7 @@
 }
 
 base::TimeTicks
-TestDataReductionProxyConfigServiceClient::TestTickClock::NowTicks() {
+TestDataReductionProxyConfigServiceClient::TestTickClock::NowTicks() const {
   return base::TimeTicks::UnixEpoch() + (time_ - base::Time::UnixEpoch());
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
index 5a63e583..984bb811 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
@@ -152,7 +152,7 @@
     TestTickClock(const base::Time& initial_time);
 
     // base::TickClock implementation.
-    base::TimeTicks NowTicks() override;
+    base::TimeTicks NowTicks() const override;
 
     // base::Clock implementation.
     base::Time Now() override;
diff --git a/components/domain_reliability/test_util.cc b/components/domain_reliability/test_util.cc
index 7793784..e34d1bd 100644
--- a/components/domain_reliability/test_util.cc
+++ b/components/domain_reliability/test_util.cc
@@ -122,7 +122,9 @@
 MockTime::~MockTime() {}
 
 base::Time MockTime::Now() { return now_; }
-base::TimeTicks MockTime::NowTicks() { return now_ticks_; }
+base::TimeTicks MockTime::NowTicks() const {
+  return now_ticks_;
+}
 
 std::unique_ptr<MockableTime::Timer> MockTime::CreateTimer() {
   return std::unique_ptr<MockableTime::Timer>(new MockTimer(this));
diff --git a/components/domain_reliability/test_util.h b/components/domain_reliability/test_util.h
index 0211ae0..30d32d0 100644
--- a/components/domain_reliability/test_util.h
+++ b/components/domain_reliability/test_util.h
@@ -76,7 +76,7 @@
 
   // MockableTime implementation:
   base::Time Now() override;
-  base::TimeTicks NowTicks() override;
+  base::TimeTicks NowTicks() const override;
   std::unique_ptr<MockableTime::Timer> CreateTimer() override;
 
   // Pretends that |delta| has passed, and runs tasks that would've happened
diff --git a/components/domain_reliability/util.cc b/components/domain_reliability/util.cc
index 4fb7894..5895c76 100644
--- a/components/domain_reliability/util.cc
+++ b/components/domain_reliability/util.cc
@@ -242,7 +242,9 @@
 ActualTime::~ActualTime() {}
 
 base::Time ActualTime::Now() { return base::Time::Now(); }
-base::TimeTicks ActualTime::NowTicks() { return base::TimeTicks::Now(); }
+base::TimeTicks ActualTime::NowTicks() const {
+  return base::TimeTicks::Now();
+}
 
 std::unique_ptr<MockableTime::Timer> ActualTime::CreateTimer() {
   return std::unique_ptr<MockableTime::Timer>(new ActualTimer());
diff --git a/components/domain_reliability/util.h b/components/domain_reliability/util.h
index b6daa1d..0b95d23 100644
--- a/components/domain_reliability/util.h
+++ b/components/domain_reliability/util.h
@@ -84,7 +84,7 @@
   // Clock impl; returns base::Time::Now() or a mocked version thereof.
   base::Time Now() override = 0;
   // TickClock impl; returns base::TimeTicks::Now() or a mocked version thereof.
-  base::TimeTicks NowTicks() override = 0;
+  base::TimeTicks NowTicks() const override = 0;
 
   // Returns a new Timer, or a mocked version thereof.
   virtual std::unique_ptr<MockableTime::Timer> CreateTimer() = 0;
@@ -106,7 +106,7 @@
 
   // MockableTime implementation:
   base::Time Now() override;
-  base::TimeTicks NowTicks() override;
+  base::TimeTicks NowTicks() const override;
   std::unique_ptr<MockableTime::Timer> CreateTimer() override;
 };
 
diff --git a/components/download/internal/background_service/BUILD.gn b/components/download/internal/background_service/BUILD.gn
index f695f87f..9b20067d 100644
--- a/components/download/internal/background_service/BUILD.gn
+++ b/components/download/internal/background_service/BUILD.gn
@@ -27,6 +27,8 @@
     "controller_impl.h",
     "debugging_client.cc",
     "debugging_client.h",
+    "download_blockage_status.cc",
+    "download_blockage_status.h",
     "download_driver.h",
     "download_service_impl.cc",
     "download_service_impl.h",
diff --git a/components/download/internal/background_service/config.h b/components/download/internal/background_service/config.h
index 4e276f3..26af2e0 100644
--- a/components/download/internal/background_service/config.h
+++ b/components/download/internal/background_service/config.h
@@ -69,8 +69,8 @@
 constexpr char kNavigationTimeoutDelaySecondsConfig[] =
     "navigation_timeout_delay_seconds";
 
-// Configuration name for the timeout value after which an upload will be killed
-// if the client still hasn't responded with the upload data. Measured in
+// Configuration name for the minimum timeout value after which an upload can be
+// killed if the client still hasn't responded with the upload data. Measured in
 // seconds.
 constexpr char kPendingUploadTimeoutDelaySecondsConfig[] =
     "pending_upload_timeout_delay_seconds";
@@ -148,8 +148,8 @@
   // The timeout to wait for after a navigation starts.
   base::TimeDelta navigation_timeout_delay;
 
-  // The timeout after which upload entries waiting on data from their clients
-  // will be aborted.
+  // The minimum timeout after which upload entries waiting on data from their
+  // clients might be killed.
   base::TimeDelta pending_upload_timeout_delay;
 
   // The delay to retry a download when the download is failed.
diff --git a/components/download/internal/background_service/controller.h b/components/download/internal/background_service/controller.h
index 6ea581c..e4725da 100644
--- a/components/download/internal/background_service/controller.h
+++ b/components/download/internal/background_service/controller.h
@@ -36,8 +36,10 @@
   OUT_OF_RETRIES = 6,
   // The download expended it's number of 'free' retries.
   OUT_OF_RESUMPTIONS = 7,
+  // The upload was timed out due to unresponsive client.
+  UPLOAD_TIMEOUT = 8,
   // The count of entries for the enum.
-  COUNT = 8,
+  COUNT = 9,
 };
 
 // The core Controller responsible for gluing various DownloadService components
diff --git a/components/download/internal/background_service/controller_impl.cc b/components/download/internal/background_service/controller_impl.cc
index 77e5ada..91c8aec 100644
--- a/components/download/internal/background_service/controller_impl.cc
+++ b/components/download/internal/background_service/controller_impl.cc
@@ -46,6 +46,14 @@
   model->Update(*entry);
 }
 
+// Helper function to post the callback once again before starting a download.
+void RunOnDownloadReadyToStart(
+    GetUploadDataCallback callback,
+    scoped_refptr<network::ResourceRequestBody> post_body) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), post_body));
+}
+
 // Helper function to move from a CompletionType to a Client::FailureReason.
 Client::FailureReason FailureReasonFromCompletionType(CompletionType type) {
   // SUCCEED does not map to a FailureReason.
@@ -60,6 +68,8 @@
       return Client::FailureReason::ABORTED;
     case CompletionType::TIMEOUT:
       return Client::FailureReason::TIMEDOUT;
+    case CompletionType::UPLOAD_TIMEOUT:
+      return Client::FailureReason::UPLOAD_TIMEDOUT;
     case CompletionType::UNKNOWN:
       return Client::FailureReason::UNKNOWN;
     case CompletionType::CANCEL:
@@ -442,7 +452,8 @@
     return;
   }
 
-  if (!download.done && failure_type == FailureType::RECOVERABLE) {
+  if (!download.done && failure_type == FailureType::RECOVERABLE &&
+      !entry->has_upload_data) {
     // Because the network offline signal comes later than actual download
     // failure, retry the download after a delay to avoid the retry to fail
     // immediately again.
@@ -795,7 +806,8 @@
           case DriverEntry::State::INTERRUPTED:
             // The DriverEntry isn't done, so we need to set the Entry to the
             // 'active' state.
-            new_state = active;
+            new_state =
+                entry->has_upload_data ? Entry::State::COMPLETE : active;
             break;
           case DriverEntry::State::COMPLETE:  // Intentional fallthrough.
           // TODO(dtrainor, xingliu) Revisit this CANCELLED state to make sure
@@ -848,6 +860,7 @@
                driver_entry->state == DriverEntry::State::INTERRUPTED)
                   ? CompletionType::UNKNOWN
                   : CompletionType::SUCCEED;
+          // TODO(shaktisahu) : May be set a completion type for upload.
           HandleCompleteDownload(completion_type, entry->guid);
         } else {
           // We're staying in COMPLETE.  Make sure there is no DriverEntry here.
@@ -878,8 +891,9 @@
 void ControllerImpl::UpdateDriverState(Entry* entry) {
   DCHECK_EQ(controller_state_, State::READY);
 
-  if (entry->state != Entry::State::ACTIVE &&
-      entry->state != Entry::State::PAUSED) {
+  if ((entry->state != Entry::State::ACTIVE &&
+       entry->state != Entry::State::PAUSED) ||
+      pending_uploads_.find(entry->guid) != pending_uploads_.end()) {
     return;
   }
 
@@ -900,27 +914,14 @@
 
   bool active = driver_entry.has_value() &&
                 driver_entry->state == DriverEntry::State::IN_PROGRESS;
-  bool want_active = entry->state == Entry::State::ACTIVE;
 
-  bool blocked_by_criteria =
-      !device_status_listener_->CurrentDeviceStatus()
-           .MeetsCondition(entry->scheduling_params,
-                           config_->download_battery_percentage)
-           .MeetsRequirements();
-  bool blocked_by_downloads =
-      !externally_active_downloads_.empty() &&
-      entry->scheduling_params.priority <= SchedulingParams::Priority::NORMAL;
+  auto blockage_status = IsDownloadBlocked(entry);
 
-  bool blocked_by_navigation = ShouldBlockDownloadOnNavigation(entry);
-  bool is_blocked =
-      blocked_by_criteria || blocked_by_downloads || blocked_by_navigation;
-
-  if (!want_active || is_blocked) {
+  if (blockage_status.IsBlocked()) {
     if (active) {
       stats::LogEntryEvent(stats::DownloadEvent::SUSPEND);
-      stats::LogDownloadPauseReason(blocked_by_criteria, !want_active,
-                                    blocked_by_navigation,
-                                    blocked_by_downloads);
+      stats::LogDownloadPauseReason(blockage_status,
+                                    false /*on_upload_data_received*/);
     }
 
     if (driver_entry.has_value())
@@ -959,18 +960,96 @@
     }
 
     if (driver_entry.has_value()) {
+      // For uploads, we should never call resume unless it is already in
+      // progress, since we have to re-supply the upload data from client.
+      DCHECK(!entry->has_upload_data ||
+             driver_entry->state == DriverEntry::State::IN_PROGRESS);
+
       driver_->Resume(entry->guid);
     } else {
       stats::LogEntryEvent(stats::DownloadEvent::START);
-      driver_->Start(
-          entry->request_params, entry->guid, entry->target_file_path, nullptr,
-          net::NetworkTrafficAnnotationTag(entry->traffic_annotation));
+      PrepareToStartDownload(entry);
     }
   }
 
   log_sink_->OnServiceDownloadChanged(entry->guid);
 }
 
+void ControllerImpl::PrepareToStartDownload(Entry* entry) {
+  pending_uploads_.insert(entry->guid);
+
+  auto* client = clients_->GetClient(entry->client);
+  DCHECK(client);
+
+  auto callback = base::BindOnce(&ControllerImpl::OnDownloadReadyToStart,
+                                 weak_ptr_factory_.GetWeakPtr(), entry->guid);
+
+  // To ensure no re-entrancy, we post the response again after receiving from
+  // the client
+  client->GetUploadData(entry->guid, base::BindOnce(&RunOnDownloadReadyToStart,
+                                                    std::move(callback)));
+
+  // Reset the timeout timer in case client doesn't respond.
+  cancel_uploads_callback_.Reset(base::BindRepeating(
+      &ControllerImpl::KillTimedOutUploads, weak_ptr_factory_.GetWeakPtr()));
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, cancel_uploads_callback_.callback(),
+      config_->pending_upload_timeout_delay);
+}
+
+void ControllerImpl::OnDownloadReadyToStart(
+    const std::string& guid,
+    scoped_refptr<network::ResourceRequestBody> post_body) {
+  DCHECK(pending_uploads_.find(guid) != pending_uploads_.end());
+  pending_uploads_.erase(guid);
+
+  auto* entry = model_->Get(guid);
+  if (!entry) {
+    stats::LogEntryRemovedWhileWaitingForUploadResponse();
+    return;
+  }
+
+  if (post_body) {
+    entry->has_upload_data = true;
+    model_->Update(*entry);
+  }
+
+  stats::LogHasUploadData(entry->client, entry->has_upload_data);
+
+  auto blockage_status = IsDownloadBlocked(entry);
+  if (blockage_status.IsBlocked()) {
+    stats::LogDownloadPauseReason(blockage_status,
+                                  true /*on_upload_data_received*/);
+    return;
+  }
+
+  DCHECK(!driver_->Find(guid).has_value());
+  driver_->Start(entry->request_params, entry->guid, entry->target_file_path,
+                 post_body,
+                 net::NetworkTrafficAnnotationTag(entry->traffic_annotation));
+}
+
+DownloadBlockageStatus ControllerImpl::IsDownloadBlocked(Entry* entry) {
+  DownloadBlockageStatus status;
+  status.blocked_by_criteria =
+      !device_status_listener_->CurrentDeviceStatus()
+           .MeetsCondition(entry->scheduling_params,
+                           config_->download_battery_percentage)
+           .MeetsRequirements();
+  status.blocked_by_downloads =
+      !externally_active_downloads_.empty() &&
+      entry->scheduling_params.priority <= SchedulingParams::Priority::NORMAL;
+
+  status.blocked_by_navigation = ShouldBlockDownloadOnNavigation(entry);
+  status.entry_not_active = entry->state != Entry::State::ACTIVE;
+  return status;
+}
+
+void ControllerImpl::KillTimedOutUploads() {
+  for (const std::string& guid : std::move(pending_uploads_))
+    HandleCompleteDownload(CompletionType::UPLOAD_TIMEOUT, guid);
+}
+
 void ControllerImpl::NotifyClientsOfStartup(bool state_lost) {
   auto categorized = util::MapEntriesToMetadataForClients(
       clients_->GetRegisteredClients(), model_->PeekEntries());
diff --git a/components/download/internal/background_service/controller_impl.h b/components/download/internal/background_service/controller_impl.h
index 52365f31..8c3b3754 100644
--- a/components/download/internal/background_service/controller_impl.h
+++ b/components/download/internal/background_service/controller_impl.h
@@ -15,6 +15,7 @@
 #include "base/optional.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "components/download/internal/background_service/controller.h"
+#include "components/download/internal/background_service/download_blockage_status.h"
 #include "components/download/internal/background_service/download_driver.h"
 #include "components/download/internal/background_service/entry.h"
 #include "components/download/internal/background_service/log_source.h"
@@ -165,6 +166,10 @@
   // or resume a download accordingly.
   void UpdateDriverState(Entry* entry);
 
+  // Returns a struct representing various reasons whether the download cannot
+  // start or continue at this time.
+  DownloadBlockageStatus IsDownloadBlocked(Entry* entry);
+
   // Notifies all Client in |clients_| that this controller is initialized and
   // lets them know which download requests we are aware of for their
   // DownloadClient.
@@ -202,6 +207,10 @@
   void RemoveCleanupEligibleDownloads();
 
   void HandleExternalDownload(const std::string& guid, bool active);
+  void PrepareToStartDownload(Entry* entry);
+  void OnDownloadReadyToStart(
+      const std::string& guid,
+      scoped_refptr<network::ResourceRequestBody> post_body);
 
   // Postable methods meant to just be pass throughs to Client APIs.  This is
   // meant to help prevent reentrancy.
@@ -230,6 +239,10 @@
   // Kills the downloads which have surpassed their cancel_after time.
   void KillTimedOutDownloads();
 
+  // A periodical task that will kill all the pending uploads that haven't
+  // received upload data from their respective clients.
+  void KillTimedOutUploads();
+
   Configuration* config_;
   LogSink* log_sink_;
 
@@ -251,9 +264,11 @@
   State controller_state_;
   StartupStatus startup_status_;
   std::set<std::string> externally_active_downloads_;
+  std::set<std::string> pending_uploads_;
   std::map<std::string, DownloadParams::StartCallback> start_callbacks_;
   std::map<DownloadTaskType, TaskFinishedCallback> task_finished_callbacks_;
   base::CancelableClosure cancel_downloads_callback_;
+  base::CancelableClosure cancel_uploads_callback_;
 
   // Only used to post tasks on the same thread.
   base::WeakPtrFactory<ControllerImpl> weak_ptr_factory_;
diff --git a/components/download/internal/background_service/controller_impl_unittest.cc b/components/download/internal/background_service/controller_impl_unittest.cc
index aaa2dda..66d8ff52 100644
--- a/components/download/internal/background_service/controller_impl_unittest.cc
+++ b/components/download/internal/background_service/controller_impl_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/strings/string_util.h"
 #include "base/test/histogram_tester.h"
+#include "base/test/test_mock_time_task_runner.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/download/internal/background_service/client_set.h"
@@ -65,6 +66,37 @@
 
 void NotifyTaskFinished(bool success) {}
 
+class UploadClient : public test::MockClient {
+ public:
+  UploadClient() = default;
+  ~UploadClient() override = default;
+
+  void GetUploadData(const std::string& guid,
+                     GetUploadDataCallback callback) override;
+  void SetUploadResponseDelayForGuid(const std::string& guid,
+                                     unsigned int delay);
+
+ private:
+  std::map<std::string, unsigned int> upload_response_delay_;
+
+  DISALLOW_COPY_AND_ASSIGN(UploadClient);
+};
+
+void UploadClient::GetUploadData(const std::string& guid,
+                                 GetUploadDataCallback callback) {
+  scoped_refptr<network::ResourceRequestBody> post_body =
+      new network::ResourceRequestBody();
+  unsigned int delay = upload_response_delay_[guid];
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, base::BindOnce(std::move(callback), post_body),
+      base::TimeDelta::FromSeconds(delay));
+}
+
+void UploadClient::SetUploadResponseDelayForGuid(const std::string& guid,
+                                                 unsigned int delay) {
+  upload_response_delay_[guid] = delay;
+}
+
 class MockTaskScheduler : public TaskScheduler {
  public:
   MockTaskScheduler() = default;
@@ -126,7 +158,7 @@
 class DownloadServiceControllerImplTest : public testing::Test {
  public:
   DownloadServiceControllerImplTest()
-      : task_runner_(new base::TestSimpleTaskRunner),
+      : task_runner_(new base::TestMockTimeTaskRunner),
         handle_(task_runner_),
         controller_(nullptr),
         client_(nullptr),
@@ -146,6 +178,7 @@
 
   void SetUp() override {
     auto client = std::make_unique<NiceMock<test::MockClient>>();
+    auto client3 = std::make_unique<NiceMock<UploadClient>>();
     auto driver = std::make_unique<test::TestDownloadDriver>();
     auto store = std::make_unique<test::TestStore>();
     config_ = std::make_unique<Configuration>();
@@ -159,6 +192,7 @@
     log_sink_ = std::make_unique<test::BlackHoleLogSink>();
 
     client_ = client.get();
+    client3_ = client3.get();
     driver_ = driver.get();
     store_ = store.get();
 
@@ -166,6 +200,7 @@
     clients->insert(std::make_pair(DownloadClient::TEST, std::move(client)));
     clients->insert(std::make_pair(DownloadClient::TEST_2,
                                    std::make_unique<test::EmptyClient>()));
+    clients->insert(std::make_pair(DownloadClient::TEST_3, std::move(client3)));
     auto client_set = std::make_unique<ClientSet>(std::move(clients));
     auto model = std::make_unique<ModelImpl>(std::move(store));
     auto device_status_listener =
@@ -213,7 +248,7 @@
   MOCK_METHOD2(StartCallback,
                void(const std::string&, DownloadParams::StartResult));
 
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
   base::ThreadTaskRunnerHandle handle_;
 
   std::unique_ptr<ControllerImpl> controller_;
@@ -221,6 +256,7 @@
   std::unique_ptr<LogSink> log_sink_;
   NavigationMonitorImpl navigation_monitor;
   test::MockClient* client_;
+  UploadClient* client3_;
   test::TestDownloadDriver* driver_;
   test::TestStore* store_;
   ModelImpl* model_;
@@ -940,7 +976,7 @@
   // Failed entry should exist because we retry after a delay.
   EXPECT_NE(nullptr, model_->Get(entry2.guid));
 
-  task_runner_->RunUntilIdle();
+  task_runner_->FastForwardUntilNoTasksRemain();
   // Retry is done, and failed entry should be removed.
   EXPECT_EQ(nullptr, model_->Get(entry2.guid));
 
@@ -955,7 +991,7 @@
   // Failed entry should exist because we retry after a delay.
   EXPECT_NE(nullptr, model_->Get(entry3.guid));
 
-  task_runner_->RunUntilIdle();
+  task_runner_->FastForwardUntilNoTasksRemain();
   // Retry is done, and failed entry should be removed.
   EXPECT_EQ(nullptr, model_->Get(entry2.guid));
 }
@@ -1142,6 +1178,138 @@
   task_runner_->RunUntilIdle();
 }
 
+TEST_F(DownloadServiceControllerImplTest,
+       UploadTestForSuccessPauseCancelFailureTimeout) {
+  auto create_entry = [this](unsigned int delay) {
+    Entry entry = test::BuildBasicEntry(Entry::State::ACTIVE);
+    entry.client = DownloadClient::TEST_3;
+    client3_->SetUploadResponseDelayForGuid(entry.guid, delay);
+    return entry;
+  };
+
+  auto verify_entry =
+      [this](const std::string& guid,
+             base::Optional<Entry::State> expected_state,
+             base::Optional<DriverEntry::State> expected_driver_state,
+             bool has_upload_data) {
+        auto* entry = model_->Get(guid);
+        auto driver_entry = driver_->Find(guid);
+        EXPECT_EQ(expected_state.has_value(), entry != nullptr);
+        if (expected_state.has_value()) {
+          EXPECT_EQ(expected_state.value(), entry->state);
+          EXPECT_EQ(has_upload_data, entry->has_upload_data);
+        }
+
+        EXPECT_EQ(expected_driver_state.has_value(), driver_entry.has_value());
+        if (expected_driver_state.has_value()) {
+          EXPECT_EQ(expected_driver_state.value(), driver_entry.value().state);
+        }
+      };
+
+  // entry1 - successful flow, entry2 - cancel before client response,
+  // entry3 - client response timeout, entry4 - network failure.
+  // entry5 - pause before client response.
+  Entry entry1 = create_entry(15);
+  Entry entry2 = create_entry(25);
+  Entry entry3 = create_entry(50);
+  Entry entry4 = create_entry(10);
+  Entry entry5 = create_entry(25);
+  config_->pending_upload_timeout_delay = base::TimeDelta::FromSeconds(30);
+  config_->max_concurrent_downloads = 8u;
+  config_->max_running_downloads = 8u;
+  config_->max_retry_count = 4u;
+  std::vector<Entry> entries = {entry1, entry2, entry3, entry4, entry5};
+
+  EXPECT_CALL(*client3_, OnServiceInitialized(false, _)).Times(1);
+
+  // Set up the Controller.
+  device_status_listener_->SetDeviceStatus(
+      DeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+
+  InitializeController();
+  store_->TriggerInit(true, std::make_unique<std::vector<Entry>>(entries));
+  file_monitor_->TriggerInit(true);
+  driver_->MakeReady();
+  task_runner_->RunUntilIdle();
+
+  // No driver entry yet as entries are waiting for client response.
+  verify_entry(entry1.guid, Entry::State::ACTIVE, base::nullopt, false);
+  verify_entry(entry2.guid, Entry::State::ACTIVE, base::nullopt, false);
+  verify_entry(entry3.guid, Entry::State::ACTIVE, base::nullopt, false);
+  verify_entry(entry4.guid, Entry::State::ACTIVE, base::nullopt, false);
+  verify_entry(entry5.guid, Entry::State::ACTIVE, base::nullopt, false);
+
+  // At 20 seconds.
+  task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(20));
+
+  // Test that entry1 is marked as upload and is in progress.
+  verify_entry(entry1.guid, Entry::State::ACTIVE,
+               DriverEntry::State::IN_PROGRESS, true);
+
+  // Successfully complete the upload for entry1.
+  EXPECT_CALL(*client3_, OnDownloadSucceeded(entry1.guid, _)).Times(1);
+  auto dentry1 = driver_->Find(entry1.guid);
+  dentry1.value().state = DriverEntry::State::COMPLETE;
+  driver_->NotifyDownloadSucceeded(dentry1.value());
+  task_runner_->RunUntilIdle();
+  verify_entry(entry1.guid, Entry::State::COMPLETE,
+               DriverEntry::State::COMPLETE, true);
+
+  // Call PauseDownload before client response for entry5.
+  controller_->PauseDownload(entry5.guid);
+  task_runner_->RunUntilIdle();
+  verify_entry(entry5.guid, Entry::State::PAUSED, base::nullopt, false);
+
+  // Test CancelDownload before client response for entry2.
+  EXPECT_CALL(*client3_,
+              OnDownloadFailed(entry2.guid, Client::FailureReason::CANCELLED))
+      .Times(1);
+  controller_->CancelDownload(entry2.guid);
+  task_runner_->RunUntilIdle();
+  verify_entry(entry2.guid, base::nullopt, base::nullopt, false);
+
+  // At 25 seconds.
+  task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(5));
+
+  // Entry2, entry5 receive client response.
+  verify_entry(entry2.guid, base::nullopt, base::nullopt, false);
+  verify_entry(entry5.guid, Entry::State::PAUSED, base::nullopt, true);
+
+  // Entry3 timeouts before client response.
+  EXPECT_CALL(
+      *client3_,
+      OnDownloadFailed(entry3.guid, Client::FailureReason::UPLOAD_TIMEDOUT))
+      .Times(1);
+
+  // At 40 seconds.
+  task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(15));
+  verify_entry(entry3.guid, base::nullopt, base::nullopt, false);
+
+  // Test network failure for entry4. First check the entry is in progress.
+  verify_entry(entry4.guid, Entry::State::ACTIVE,
+               DriverEntry::State::IN_PROGRESS, true);
+  EXPECT_CALL(*client3_,
+              OnDownloadFailed(entry4.guid, Client::FailureReason::NETWORK))
+      .Times(1);
+  DriverEntry dentry4 =
+      BuildDriverEntry(entry4, DriverEntry::State::INTERRUPTED);
+  driver_->NotifyDownloadFailed(dentry4, FailureType::NOT_RECOVERABLE);
+  task_runner_->RunUntilIdle();
+  verify_entry(entry4.guid, base::nullopt, base::nullopt, false);
+
+  // Entry5 is still paused, call ResumeDownload. It should make another fresh
+  // request for data.
+  verify_entry(entry5.guid, Entry::State::PAUSED, base::nullopt, true);
+  controller_->ResumeDownload(entry5.guid);
+  task_runner_->RunUntilIdle();
+  verify_entry(entry5.guid, Entry::State::ACTIVE, base::nullopt, true);
+
+  // At 65 seconds. Entry5 receives data for the second time and continues.
+  task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(25));
+  verify_entry(entry5.guid, Entry::State::ACTIVE,
+               DriverEntry::State::IN_PROGRESS, true);
+}
+
 TEST_F(DownloadServiceControllerImplTest, StartupRecovery) {
   EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
 
@@ -1304,6 +1472,92 @@
   EXPECT_EQ(base::nullopt, driver_->Find(entries[24].guid));
 }
 
+TEST_F(DownloadServiceControllerImplTest, StartupRecoveryForUploadEntries) {
+  EXPECT_CALL(*client_, OnServiceInitialized(false, _)).Times(1);
+
+  std::vector<Entry> entries;
+  std::vector<DriverEntry> driver_entries;
+
+  auto add_entry = [&entries, &driver_entries](
+                       Entry::State state,
+                       base::Optional<DriverEntry::State> driver_state) {
+    Entry entry = test::BuildBasicEntry(state);
+    entry.has_upload_data = true;
+    if (state == Entry::State::COMPLETE)
+      entry.completion_time = base::Time::Now();
+
+    entries.push_back(entry);
+    if (driver_state.has_value())
+      driver_entries.push_back(BuildDriverEntry(entry, driver_state.value()));
+  };
+
+  add_entry(Entry::State::ACTIVE, DriverEntry::State::IN_PROGRESS);
+  add_entry(Entry::State::ACTIVE, DriverEntry::State::COMPLETE);
+  add_entry(Entry::State::ACTIVE, DriverEntry::State::CANCELLED);
+  add_entry(Entry::State::ACTIVE, DriverEntry::State::INTERRUPTED);
+  add_entry(Entry::State::ACTIVE, base::nullopt);
+
+  add_entry(Entry::State::PAUSED, DriverEntry::State::IN_PROGRESS);
+  add_entry(Entry::State::PAUSED, DriverEntry::State::COMPLETE);
+  add_entry(Entry::State::PAUSED, DriverEntry::State::CANCELLED);
+  add_entry(Entry::State::PAUSED, DriverEntry::State::INTERRUPTED);
+  add_entry(Entry::State::PAUSED, base::nullopt);
+
+  add_entry(Entry::State::COMPLETE, DriverEntry::State::IN_PROGRESS);
+  add_entry(Entry::State::COMPLETE, DriverEntry::State::COMPLETE);
+  add_entry(Entry::State::COMPLETE, DriverEntry::State::CANCELLED);
+  add_entry(Entry::State::COMPLETE, DriverEntry::State::INTERRUPTED);
+  add_entry(Entry::State::COMPLETE, base::nullopt);
+
+  // Set up the Controller.
+  device_status_listener_->SetDeviceStatus(
+      DeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+
+  InitializeController();
+  driver_->AddTestData(driver_entries);
+  driver_->MakeReady();
+  store_->AutomaticallyTriggerAllFutureCallbacks(true);
+  store_->TriggerInit(true, std::make_unique<std::vector<Entry>>(entries));
+  file_monitor_->TriggerInit(true);
+
+  // Allow the initialization routines and persistent layers to do their thing.
+  task_runner_->RunUntilIdle();
+
+  auto verify_entry = [this](const std::string& guid, Entry::State state,
+                             base::Optional<DriverEntry::State> driver_state) {
+    EXPECT_EQ(state, model_->Get(guid)->state);
+    auto driver_entry = driver_->Find(guid);
+    EXPECT_EQ(driver_state.has_value(), driver_entry.has_value());
+    if (driver_entry.has_value())
+      EXPECT_EQ(driver_state, driver_entry->state);
+  };
+
+  // Validate Model and DownloadDriver states. Any IN_PROGRESS or INTERRUPTED
+  // download should be moved to complete state for ACTIVE/PAUSED entries.
+
+  // Entry::State::ACTIVE.
+  verify_entry(entries[0].guid, Entry::State::COMPLETE, base::nullopt);
+  verify_entry(entries[1].guid, Entry::State::COMPLETE, base::nullopt);
+  verify_entry(entries[2].guid, Entry::State::COMPLETE, base::nullopt);
+  verify_entry(entries[3].guid, Entry::State::COMPLETE, base::nullopt);
+  verify_entry(entries[4].guid, Entry::State::ACTIVE,
+               DriverEntry::State::IN_PROGRESS);
+
+  // Entry::State::PAUSED.
+  verify_entry(entries[5].guid, Entry::State::COMPLETE, base::nullopt);
+  verify_entry(entries[6].guid, Entry::State::COMPLETE, base::nullopt);
+  verify_entry(entries[7].guid, Entry::State::COMPLETE, base::nullopt);
+  verify_entry(entries[8].guid, Entry::State::COMPLETE, base::nullopt);
+  verify_entry(entries[9].guid, Entry::State::PAUSED, base::nullopt);
+
+  // Entry::State::COMPLETE.
+  verify_entry(entries[10].guid, Entry::State::COMPLETE, base::nullopt);
+  verify_entry(entries[11].guid, Entry::State::COMPLETE, base::nullopt);
+  verify_entry(entries[12].guid, Entry::State::COMPLETE, base::nullopt);
+  verify_entry(entries[13].guid, Entry::State::COMPLETE, base::nullopt);
+  verify_entry(entries[14].guid, Entry::State::COMPLETE, base::nullopt);
+}
+
 TEST_F(DownloadServiceControllerImplTest, ExistingExternalDownload) {
   Entry entry1 = test::BuildBasicEntry(Entry::State::ACTIVE);
   Entry entry2 = test::BuildBasicEntry(Entry::State::ACTIVE);
diff --git a/components/download/internal/background_service/download_blockage_status.cc b/components/download/internal/background_service/download_blockage_status.cc
new file mode 100644
index 0000000..49fc103
--- /dev/null
+++ b/components/download/internal/background_service/download_blockage_status.cc
@@ -0,0 +1,22 @@
+// 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.
+
+#include "components/download/internal/background_service/download_blockage_status.h"
+
+namespace download {
+
+DownloadBlockageStatus::DownloadBlockageStatus()
+    : blocked_by_criteria(false),
+      blocked_by_navigation(false),
+      blocked_by_downloads(false),
+      entry_not_active(false) {}
+
+DownloadBlockageStatus::~DownloadBlockageStatus() = default;
+
+bool DownloadBlockageStatus::IsBlocked() {
+  return blocked_by_criteria || blocked_by_navigation || blocked_by_downloads ||
+         entry_not_active;
+}
+
+}  // namespace download
diff --git a/components/download/internal/background_service/download_blockage_status.h b/components/download/internal/background_service/download_blockage_status.h
new file mode 100644
index 0000000..e6ef9ea
--- /dev/null
+++ b/components/download/internal/background_service/download_blockage_status.h
@@ -0,0 +1,30 @@
+// 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_DOWNLOAD_INTERNAL_BACKGROUND_SERVICE_DOWNLOAD_BLOCKAGE_STATUS_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_BACKGROUND_SERVICE_DOWNLOAD_BLOCKAGE_STATUS_H_
+
+#include "base/macros.h"
+#include "base/optional.h"
+
+namespace download {
+
+// A helper class representing various conditions where a download can be
+// blocked.
+struct DownloadBlockageStatus {
+  bool blocked_by_criteria;
+  bool blocked_by_navigation;
+  bool blocked_by_downloads;
+  bool entry_not_active;
+
+  DownloadBlockageStatus();
+  ~DownloadBlockageStatus();
+
+  // Whether the download is blocked currently.
+  bool IsBlocked();
+};
+
+}  // namespace download
+
+#endif  // COMPONENTS_DOWNLOAD_INTERNAL_BACKGROUND_SERVICE_DOWNLOAD_BLOCKAGE_STATUS_H_
diff --git a/components/download/internal/background_service/entry.h b/components/download/internal/background_service/entry.h
index 85a24e3..90bf8507 100644
--- a/components/download/internal/background_service/entry.h
+++ b/components/download/internal/background_service/entry.h
@@ -96,7 +96,9 @@
   // Stores the number of times the service tried to delete the download file.
   uint32_t cleanup_attempt_count;
 
-  // Stores whether this request also has some data to be uploaded.
+  // Stores whether this request has some data to be uploaded. This is set to
+  // true only when the client has provided with the upload data and is not
+  // cleared afterwards. Retry and resumption logic are impacted by this.
   bool has_upload_data;
 
   // Traffic annotation for the network request.
diff --git a/components/download/internal/background_service/stats.cc b/components/download/internal/background_service/stats.cc
index 9c3687d..524ecffe 100644
--- a/components/download/internal/background_service/stats.cc
+++ b/components/download/internal/background_service/stats.cc
@@ -121,6 +121,8 @@
       return "OutOfRetries";
     case CompletionType::OUT_OF_RESUMPTIONS:
       return "OutOfResumptions";
+    case CompletionType::UPLOAD_TIMEOUT:
+      return "UploadTimeout";
     case CompletionType::COUNT:
       NOTREACHED();
   }
@@ -167,9 +169,12 @@
 }
 
 // Helper method to log the pause reason for a particular download.
-void LogDownloadPauseReason(PauseReason reason) {
-  UMA_HISTOGRAM_ENUMERATION("Download.Service.PauseReason", reason,
-                            PauseReason::COUNT);
+void LogDownloadPauseReason(PauseReason reason, bool on_upload_data_received) {
+  std::string name(on_upload_data_received
+                       ? "Download.Service.OnUploadDataReceived.PauseReason"
+                       : "Download.Service.PauseReason");
+
+  base::UmaHistogramEnumeration(name, reason, PauseReason::COUNT);
 }
 
 }  // namespace
@@ -261,23 +266,24 @@
   base::UmaHistogramCustomCounts(name, file_size_kb, 1, kMaxFileSizeKB, 50);
 }
 
-void LogDownloadPauseReason(bool unmet_device_criteria,
-                            bool pause_by_client,
-                            bool external_navigation,
-                            bool external_download) {
-  LogDownloadPauseReason(PauseReason::ANY);
+void LogDownloadPauseReason(const DownloadBlockageStatus& blockage_status,
+                            bool currently_in_progress) {
+  LogDownloadPauseReason(PauseReason::ANY, currently_in_progress);
 
-  if (unmet_device_criteria)
-    LogDownloadPauseReason(PauseReason::UNMET_DEVICE_CRITERIA);
+  if (blockage_status.blocked_by_criteria)
+    LogDownloadPauseReason(PauseReason::UNMET_DEVICE_CRITERIA,
+                           currently_in_progress);
 
-  if (pause_by_client)
-    LogDownloadPauseReason(PauseReason::PAUSE_BY_CLIENT);
+  if (blockage_status.entry_not_active)
+    LogDownloadPauseReason(PauseReason::PAUSE_BY_CLIENT, currently_in_progress);
 
-  if (external_navigation)
-    LogDownloadPauseReason(PauseReason::EXTERNAL_NAVIGATION);
+  if (blockage_status.blocked_by_navigation)
+    LogDownloadPauseReason(PauseReason::EXTERNAL_NAVIGATION,
+                           currently_in_progress);
 
-  if (external_download)
-    LogDownloadPauseReason(PauseReason::EXTERNAL_DOWNLOAD);
+  if (blockage_status.blocked_by_downloads)
+    LogDownloadPauseReason(PauseReason::EXTERNAL_DOWNLOAD,
+                           currently_in_progress);
 }
 
 void LogModelOperationResult(ModelAction action, bool success) {
@@ -377,5 +383,15 @@
   UMA_HISTOGRAM_COUNTS_100("Download.Service.Entry.RetryCount", retry_count);
 }
 
+void LogEntryRemovedWhileWaitingForUploadResponse() {
+  UMA_HISTOGRAM_BOOLEAN("Download.Service.Upload.EntryNotFound", true);
+}
+
+void LogHasUploadData(DownloadClient client, bool has_upload_data) {
+  std::string name("Download.Service.Upload.HasUploadData");
+  name.append(".").append(ClientToHistogramSuffix(client));
+  base::UmaHistogramBoolean(name, has_upload_data);
+}
+
 }  // namespace stats
 }  // namespace download
diff --git a/components/download/internal/background_service/stats.h b/components/download/internal/background_service/stats.h
index ab9d2b5..7a0e40f 100644
--- a/components/download/internal/background_service/stats.h
+++ b/components/download/internal/background_service/stats.h
@@ -8,6 +8,7 @@
 #include "base/files/file.h"
 #include "base/optional.h"
 #include "components/download/internal/background_service/controller.h"
+#include "components/download/internal/background_service/download_blockage_status.h"
 #include "components/download/internal/background_service/driver_entry.h"
 #include "components/download/internal/background_service/entry.h"
 #include "components/download/public/background_service/clients.h"
@@ -174,10 +175,9 @@
 
 // Logs various pause reasons for download. The reasons are not mutually
 // exclusive.
-void LogDownloadPauseReason(bool unmet_device_criteria,
-                            bool pause_by_client,
-                            bool external_navigation,
-                            bool external_download);
+void LogDownloadPauseReason(const DownloadBlockageStatus& blockage_status,
+                            bool on_upload_data_received);
+void LogEntryRemovedWhileWaitingForUploadResponse();
 
 // Logs statistics about the result of a model operation.  Used to track failure
 // cases.
@@ -224,6 +224,9 @@
 // At the time of a retry, logs which retry attempt count this is.
 void LogEntryRetryCount(uint32_t retry_count);
 
+// Records whether the entry was an upload.
+void LogHasUploadData(DownloadClient client, bool has_upload_data);
+
 }  // namespace stats
 }  // namespace download
 
diff --git a/components/download/public/background_service/client.h b/components/download/public/background_service/client.h
index d42380e6..b6ba61e 100644
--- a/components/download/public/background_service/client.h
+++ b/components/download/public/background_service/client.h
@@ -55,6 +55,10 @@
     // DownloadParams::cancel_after timeout.
     TIMEDOUT,
 
+    // Used when the upload data was not received from the client before
+    // timeout.
+    UPLOAD_TIMEDOUT,
+
     // Used when the download was cancelled by the Client.
     CANCELLED,
 
diff --git a/components/favicon/content/content_favicon_driver.cc b/components/favicon/content/content_favicon_driver.cc
index eac49d43..a4d2fbc 100644
--- a/components/favicon/content/content_favicon_driver.cc
+++ b/components/favicon/content/content_favicon_driver.cc
@@ -26,7 +26,7 @@
 namespace {
 
 void ExtractManifestIcons(
-    const ContentFaviconDriver::ManifestDownloadCallback& callback,
+    ContentFaviconDriver::ManifestDownloadCallback callback,
     const GURL& manifest_url,
     const content::Manifest& manifest) {
   std::vector<FaviconURL> candidates;
@@ -34,7 +34,7 @@
     candidates.emplace_back(icon.src, favicon_base::IconType::kWebManifestIcon,
                             icon.sizes);
   }
-  callback.Run(candidates);
+  std::move(callback).Run(candidates);
 }
 
 }  // namespace
@@ -129,12 +129,13 @@
   bypass_cache_page_url_ = GURL();
 
   return web_contents()->DownloadImage(url, true, max_image_size, bypass_cache,
-                                       callback);
+                                       std::move(callback));
 }
 
 void ContentFaviconDriver::DownloadManifest(const GURL& url,
                                             ManifestDownloadCallback callback) {
-  web_contents()->GetManifest(base::Bind(&ExtractManifestIcons, callback));
+  web_contents()->GetManifest(
+      base::BindOnce(&ExtractManifestIcons, std::move(callback)));
 }
 
 bool ContentFaviconDriver::IsOffTheRecord() {
diff --git a/components/favicon/core/favicon_handler.h b/components/favicon/core/favicon_handler.h
index 7f63f9f..0ee94a11 100644
--- a/components/favicon/core/favicon_handler.h
+++ b/components/favicon/core/favicon_handler.h
@@ -79,17 +79,15 @@
   class Delegate {
    public:
     // Mimics WebContents::ImageDownloadCallback.
-    typedef base::Callback<void(
+    using ImageDownloadCallback = base::OnceCallback<void(
         int id,
         int status_code,
         const GURL& image_url,
         const std::vector<SkBitmap>& bitmaps,
-        const std::vector<gfx::Size>& original_bitmap_sizes)>
-        ImageDownloadCallback;
+        const std::vector<gfx::Size>& original_bitmap_sizes)>;
 
-    typedef base::Callback<void(
-        const std::vector<favicon::FaviconURL>& favicons)>
-        ManifestDownloadCallback;
+    using ManifestDownloadCallback = base::OnceCallback<void(
+        const std::vector<favicon::FaviconURL>& favicons)>;
 
     // Starts the download for the given favicon. When finished, the callback
     // is called with the results. Returns the unique id of the download
diff --git a/components/favicon/core/favicon_handler_unittest.cc b/components/favicon/core/favicon_handler_unittest.cc
index d88b7ab..318675d 100644
--- a/components/favicon/core/favicon_handler_unittest.cc
+++ b/components/favicon/core/favicon_handler_unittest.cc
@@ -151,13 +151,14 @@
     }
 
     int download_id = next_download_id_++;
-    base::Closure bound_callback =
-        base::Bind(callback, download_id, response.http_status_code, url,
-                   response.bitmaps, response.original_bitmap_sizes);
+    base::OnceClosure bound_callback = base::BindOnce(
+        std::move(callback), download_id, response.http_status_code, url,
+        response.bitmaps, response.original_bitmap_sizes);
     if (url == manual_callback_url_)
-      manual_callbacks_.push_back(bound_callback);
+      manual_callbacks_.push_back(std::move(bound_callback));
     else
-      base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, bound_callback);
+      base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                    std::move(bound_callback));
     return download_id;
   }
 
@@ -195,8 +196,8 @@
   bool RunCallbackManually() {
     if (!HasPendingManualCallback())
       return false;
-    for (base::Closure& callback : std::move(manual_callbacks_))
-      callback.Run();
+    for (auto& callback : std::move(manual_callbacks_))
+      std::move(callback).Run();
     return true;
   }
 
@@ -208,7 +209,7 @@
   GURL manual_callback_url_;
 
   // Callback for DownloadImage() request for |manual_callback_url_|.
-  std::vector<base::Closure> manual_callbacks_;
+  std::vector<base::OnceClosure> manual_callbacks_;
 
   // Registered responses.
   std::map<GURL, Response> responses_;
@@ -236,11 +237,13 @@
     downloads_->push_back(url);
 
     const Response& response = responses_[url];
-    base::Closure bound_callback = base::Bind(callback, response.favicon_urls);
+    base::OnceClosure bound_callback =
+        base::BindOnce(std::move(callback), response.favicon_urls);
     if (url == manual_callback_url_)
-      manual_callbacks_.push_back(bound_callback);
+      manual_callbacks_.push_back(std::move(bound_callback));
     else
-      base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, bound_callback);
+      base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                    std::move(bound_callback));
   }
 
   void Add(const GURL& manifest_url,
@@ -270,8 +273,8 @@
   bool RunCallbackManually() {
     if (!HasPendingManualCallback())
       return false;
-    for (base::Closure& callback : std::move(manual_callbacks_))
-      callback.Run();
+    for (auto& callback : std::move(manual_callbacks_))
+      std::move(callback).Run();
     return true;
   }
 
@@ -282,7 +285,7 @@
   GURL manual_callback_url_;
 
   // Callback for DownloadManifest() request for |manual_callback_url_|.
-  std::vector<base::Closure> manual_callbacks_;
+  std::vector<base::OnceClosure> manual_callbacks_;
 
   // Registered responses.
   std::map<GURL, Response> responses_;
@@ -295,22 +298,20 @@
   MockDelegate()
       : fake_image_downloader_(&downloads_),
         fake_manifest_downloader_(&downloads_) {
-    // Delegate image downloading to FakeImageDownloader.
-    ON_CALL(*this, DownloadImage(_, _, _))
-        .WillByDefault(Invoke(&fake_image_downloader_,
-                              &FakeImageDownloader::DownloadImage));
-    // Delegate manifest downloading to FakeManifestDownloader.
-    ON_CALL(*this, DownloadManifest(_, _))
-        .WillByDefault(Invoke(&fake_manifest_downloader_,
-                              &FakeManifestDownloader::DownloadManifest));
   }
 
-  MOCK_METHOD3(DownloadImage,
-               int(const GURL& url,
-                   int max_image_size,
-                   ImageDownloadCallback callback));
-  MOCK_METHOD2(DownloadManifest,
-               void(const GURL& url, ManifestDownloadCallback callback));
+  int DownloadImage(const GURL& url,
+                    int max_image_size,
+                    ImageDownloadCallback callback) override {
+    return fake_image_downloader_.DownloadImage(url, max_image_size,
+                                                std::move(callback));
+  }
+
+  void DownloadManifest(const GURL& url,
+                        ManifestDownloadCallback callback) override {
+    fake_manifest_downloader_.DownloadManifest(url, std::move(callback));
+  }
+
   MOCK_METHOD0(IsOffTheRecord, bool());
   MOCK_METHOD1(IsBookmarked, bool(const GURL& url));
   MOCK_METHOD5(OnFaviconUpdated,
diff --git a/components/favicon/ios/web_favicon_driver.mm b/components/favicon/ios/web_favicon_driver.mm
index dc155d3..e46e39c 100644
--- a/components/favicon/ios/web_favicon_driver.mm
+++ b/components/favicon/ios/web_favicon_driver.mm
@@ -73,8 +73,9 @@
   int local_download_id = ++downloaded_image_count;
 
   GURL local_url(url);
+  __block ImageDownloadCallback local_callback = std::move(callback);
 
-  image_fetcher::IOSImageDataFetcherCallback local_callback =
+  image_fetcher::IOSImageDataFetcherCallback ios_callback =
       ^(NSData* data, const image_fetcher::RequestMetadata& metadata) {
         if (metadata.http_response_code ==
             image_fetcher::RequestMetadata::RESPONSE_CODE_INVALID)
@@ -88,10 +89,11 @@
             sizes.push_back(gfx::Size(frame.width(), frame.height()));
           }
         }
-        callback.Run(local_download_id, metadata.http_response_code, local_url,
-                     frames, sizes);
+        std::move(local_callback)
+            .Run(local_download_id, metadata.http_response_code, local_url,
+                 frames, sizes);
       };
-  image_fetcher_.FetchImageDataWebpDecoded(url, local_callback);
+  image_fetcher_.FetchImageDataWebpDecoded(url, ios_callback);
 
   return downloaded_image_count;
 }
diff --git a/components/image_fetcher/DEPS b/components/image_fetcher/DEPS
index 9e23443..aec4d91f 100644
--- a/components/image_fetcher/DEPS
+++ b/components/image_fetcher/DEPS
@@ -2,5 +2,6 @@
   "+components/data_use_measurement/core",
   "+net",
   "+ui/gfx/geometry",
+  "+ui/gfx/image",
   "+url",
 ]
diff --git a/components/image_fetcher/core/BUILD.gn b/components/image_fetcher/core/BUILD.gn
index ad44616..9ad99a2 100644
--- a/components/image_fetcher/core/BUILD.gn
+++ b/components/image_fetcher/core/BUILD.gn
@@ -18,6 +18,7 @@
     "//base",
     "//components/data_use_measurement/core",
     "//net",
+    "//ui/gfx",
     "//ui/gfx/geometry",
     "//url",
   ]
@@ -40,6 +41,7 @@
   testonly = true
   sources = [
     "image_data_fetcher_unittest.cc",
+    "image_fetcher_impl_unittest.cc",
     "request_metadata_unittest.cc",
   ]
   deps = [
@@ -49,5 +51,6 @@
     "//net:test_support",
     "//testing/gmock",
     "//testing/gtest",
+    "//ui/gfx:test_support",
   ]
 }
diff --git a/components/image_fetcher/core/image_fetcher_impl.cc b/components/image_fetcher/core/image_fetcher_impl.cc
index f4cf904..5b98424 100644
--- a/components/image_fetcher/core/image_fetcher_impl.cc
+++ b/components/image_fetcher/core/image_fetcher_impl.cc
@@ -10,6 +10,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "net/base/load_flags.h"
 #include "net/url_request/url_request_context_getter.h"
+#include "ui/gfx/image/image.h"
 
 namespace image_fetcher {
 
@@ -98,21 +99,24 @@
   DCHECK(request->image_data.empty());
   DCHECK_EQ(RequestMetadata::RESPONSE_CODE_INVALID,
             request->request_metadata.http_response_code);
-  request->image_data = image_data;
-  request->request_metadata = metadata;
   for (auto& callback : request->image_data_callbacks) {
-    std::move(callback).Run(request->image_data, request->request_metadata);
+    std::move(callback).Run(image_data, metadata);
   }
   request->image_data_callbacks.clear();
 
-  if (!request->image_callbacks.empty()) {
-    image_decoder_->DecodeImage(
-        image_data, desired_image_frame_size_,
-        base::BindRepeating(&ImageFetcherImpl::OnImageDecoded,
-                            base::Unretained(this), image_url, metadata));
-  } else {
+  if (image_data.empty() || request->image_callbacks.empty()) {
+    for (auto& callback : request->image_callbacks) {
+      std::move(callback).Run(request->id, gfx::Image(), metadata);
+    }
     pending_net_requests_.erase(it);
+    return;
   }
+  request->image_data = image_data;
+  request->request_metadata = metadata;
+  image_decoder_->DecodeImage(
+      image_data, desired_image_frame_size_,
+      base::BindRepeating(&ImageFetcherImpl::OnImageDecoded,
+                          base::Unretained(this), image_url, metadata));
 }
 
 void ImageFetcherImpl::OnImageDecoded(const GURL& image_url,
diff --git a/components/image_fetcher/core/image_fetcher_impl_unittest.cc b/components/image_fetcher/core/image_fetcher_impl_unittest.cc
new file mode 100644
index 0000000..e6280d29
--- /dev/null
+++ b/components/image_fetcher/core/image_fetcher_impl_unittest.cc
@@ -0,0 +1,255 @@
+// 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/image_fetcher/core/image_fetcher_impl.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/mock_callback.h"
+#include "base/test/scoped_task_environment.h"
+#include "components/image_fetcher/core/image_decoder.h"
+#include "components/image_fetcher/core/image_fetcher.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_unittest_util.h"
+
+using testing::_;
+using testing::Eq;
+using testing::Property;
+
+namespace image_fetcher {
+
+namespace {
+
+const char kFetchID[] = "fetch-1";
+const char kFetchID2[] = "fetch-2";
+const char kImageData[] = "data";
+const char kImageURL[] = "http://image.test/test.png";
+
+// Always decodes a valid image for all non-empty input.
+class FakeImageDecoder : public image_fetcher::ImageDecoder {
+ public:
+  void DecodeImage(
+      const std::string& image_data,
+      const gfx::Size& desired_image_frame_size,
+      const image_fetcher::ImageDecodedCallback& callback) override {
+    ASSERT_TRUE(enabled_);
+    gfx::Image image;
+    if (!image_data.empty()) {
+      ASSERT_EQ(kImageData, image_data);
+      image = gfx::test::CreateImage(2, 3);
+    }
+    if (before_image_decoded_) {
+      base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                       before_image_decoded_);
+    }
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(callback, image));
+  }
+  void SetBeforeImageDecoded(const base::RepeatingClosure& callback) {
+    before_image_decoded_ = callback;
+  }
+  void SetEnabled(bool enabled) { enabled_ = enabled; }
+
+ private:
+  bool enabled_ = true;
+  base::RepeatingClosure before_image_decoded_;
+};
+
+class ImageFetcherImplTest : public testing::Test {
+ public:
+  ImageFetcherImplTest() : fake_url_fetcher_factory_(nullptr) {
+    request_context_getter_ = scoped_refptr<net::TestURLRequestContextGetter>(
+        new net::TestURLRequestContextGetter(
+            scoped_task_environment_.GetMainThreadTaskRunner()));
+
+    auto decoder = std::make_unique<FakeImageDecoder>();
+    fake_image_decoder_ = decoder.get();
+    image_fetcher_ = std::make_unique<image_fetcher::ImageFetcherImpl>(
+        std::move(decoder), request_context_getter_.get());
+  }
+
+  void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
+
+  FakeImageDecoder* image_decoder() { return fake_image_decoder_; }
+  net::FakeURLFetcherFactory* fake_url_fetcher_factory() {
+    return &fake_url_fetcher_factory_;
+  }
+  ImageFetcherImpl* image_fetcher() { return image_fetcher_.get(); }
+
+ private:
+  std::unique_ptr<ImageFetcherImpl> image_fetcher_;
+  FakeImageDecoder* fake_image_decoder_;
+  net::FakeURLFetcherFactory fake_url_fetcher_factory_;
+  scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  DISALLOW_COPY_AND_ASSIGN(ImageFetcherImplTest);
+};
+MATCHER(ValidImage, "") {
+  return arg.Width() == 2 && arg.Height() == 3;
+}
+MATCHER(EmptyImage, "") {
+  return arg.Width() == 0;
+}
+
+TEST_F(ImageFetcherImplTest, FetchImageAndDataSuccess) {
+  fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kImageData,
+                                              net::HTTP_OK,
+                                              net::URLRequestStatus::SUCCESS);
+  base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback;
+  base::MockCallback<ImageFetcher::ImageFetcherCallback> image_callback;
+  EXPECT_CALL(data_callback, Run(kImageData, _));
+  EXPECT_CALL(image_callback, Run(kFetchID, ValidImage(), _));
+
+  image_fetcher()->FetchImageAndData(kFetchID, GURL(kImageURL),
+                                     data_callback.Get(), image_callback.Get(),
+                                     TRAFFIC_ANNOTATION_FOR_TESTS);
+  RunUntilIdle();
+}
+
+TEST_F(ImageFetcherImplTest, FetchImageAndData3xSuccess) {
+  // Fetch an image three times.
+  fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kImageData,
+                                              net::HTTP_OK,
+                                              net::URLRequestStatus::SUCCESS);
+  base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback1;
+  base::MockCallback<ImageFetcher::ImageFetcherCallback> image_callback1;
+  EXPECT_CALL(data_callback1, Run(kImageData, _));
+  EXPECT_CALL(image_callback1, Run(kFetchID, ValidImage(), _));
+
+  image_fetcher()->FetchImageAndData(
+      kFetchID, GURL(kImageURL), data_callback1.Get(), image_callback1.Get(),
+      TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback2;
+  base::MockCallback<ImageFetcher::ImageFetcherCallback> image_callback2;
+  EXPECT_CALL(data_callback2, Run(kImageData, _));
+  EXPECT_CALL(image_callback2, Run(kFetchID, ValidImage(), _));
+
+  // This call happens before the network request completes.
+  image_fetcher()->FetchImageAndData(
+      kFetchID, GURL(kImageURL), data_callback2.Get(), image_callback2.Get(),
+      TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback3;
+  base::MockCallback<ImageFetcher::ImageFetcherCallback> image_callback3;
+  EXPECT_CALL(data_callback3, Run(kImageData, _));
+  EXPECT_CALL(image_callback3, Run(kFetchID, ValidImage(), _));
+
+  image_decoder()->SetBeforeImageDecoded(base::BindLambdaForTesting([&]() {
+    // This happens after the network request completes.
+    // Shouldn't need to fetch.
+    fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), "",
+                                                net::HTTP_NOT_FOUND,
+                                                net::URLRequestStatus::FAILED);
+    image_fetcher()->FetchImageAndData(
+        kFetchID2, GURL(kImageURL), data_callback3.Get(), image_callback3.Get(),
+        TRAFFIC_ANNOTATION_FOR_TESTS);
+  }));
+
+  RunUntilIdle();
+}
+
+TEST_F(ImageFetcherImplTest, FetchImageAndData2xFail) {
+  // Fetch an image two times. The fetch fails.
+  image_decoder()->SetEnabled(false);
+  fake_url_fetcher_factory()->SetFakeResponse(
+      GURL(kImageURL), "", net::HTTP_NOT_FOUND, net::URLRequestStatus::FAILED);
+  base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback1;
+  base::MockCallback<ImageFetcher::ImageFetcherCallback> image_callback1;
+  EXPECT_CALL(data_callback1, Run("", _));
+  EXPECT_CALL(image_callback1, Run(kFetchID, EmptyImage(), _));
+
+  image_fetcher()->FetchImageAndData(
+      kFetchID, GURL(kImageURL), data_callback1.Get(), image_callback1.Get(),
+      TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback2;
+  base::MockCallback<ImageFetcher::ImageFetcherCallback> image_callback2;
+  EXPECT_CALL(data_callback2, Run("", _));
+  EXPECT_CALL(image_callback2, Run(kFetchID, EmptyImage(), _));
+
+  image_fetcher()->FetchImageAndData(
+      kFetchID2, GURL(kImageURL), data_callback2.Get(), image_callback2.Get(),
+      TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  RunUntilIdle();
+}
+
+TEST_F(ImageFetcherImplTest, FetchOnlyData) {
+  image_decoder()->SetEnabled(false);
+  fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kImageData,
+                                              net::HTTP_OK,
+                                              net::URLRequestStatus::SUCCESS);
+  base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback;
+  EXPECT_CALL(data_callback, Run(kImageData, _));
+
+  image_fetcher()->FetchImageAndData(
+      kFetchID, GURL(kImageURL), data_callback.Get(),
+      ImageFetcher::ImageFetcherCallback(), TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  RunUntilIdle();
+}
+
+TEST_F(ImageFetcherImplTest, FetchDataThenImage) {
+  fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kImageData,
+                                              net::HTTP_OK,
+                                              net::URLRequestStatus::SUCCESS);
+  base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback;
+  EXPECT_CALL(data_callback, Run(kImageData, _));
+
+  image_fetcher()->FetchImageAndData(
+      kFetchID, GURL(kImageURL), data_callback.Get(),
+      ImageFetcher::ImageFetcherCallback(), TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  base::MockCallback<ImageFetcher::ImageFetcherCallback> image_callback;
+  EXPECT_CALL(image_callback, Run(kFetchID, ValidImage(), _));
+
+  image_fetcher()->FetchImageAndData(
+      kFetchID2, GURL(kImageURL), ImageFetcher::ImageDataFetcherCallback(),
+      image_callback.Get(), TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  RunUntilIdle();
+}
+
+TEST_F(ImageFetcherImplTest, FetchImageThenData) {
+  fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kImageData,
+                                              net::HTTP_OK,
+                                              net::URLRequestStatus::SUCCESS);
+
+  base::MockCallback<ImageFetcher::ImageFetcherCallback> image_callback;
+  EXPECT_CALL(image_callback, Run(kFetchID, ValidImage(), _));
+
+  image_fetcher()->FetchImageAndData(
+      kFetchID, GURL(kImageURL), ImageFetcher::ImageDataFetcherCallback(),
+      image_callback.Get(), TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  base::MockCallback<ImageFetcher::ImageDataFetcherCallback> data_callback;
+  EXPECT_CALL(data_callback, Run(kImageData, _));
+
+  image_decoder()->SetBeforeImageDecoded(base::BindLambdaForTesting([&]() {
+    // This happens after the network request completes.
+    // Shouldn't need to fetch.
+    fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), "",
+                                                net::HTTP_NOT_FOUND,
+                                                net::URLRequestStatus::FAILED);
+    image_fetcher()->FetchImageAndData(
+        kFetchID2, GURL(kImageURL), data_callback.Get(),
+        ImageFetcher::ImageFetcherCallback(), TRAFFIC_ANNOTATION_FOR_TESTS);
+  }));
+
+  RunUntilIdle();
+}
+
+}  // namespace
+}  // namespace image_fetcher
diff --git a/components/nacl/browser/nacl_host_message_filter.cc b/components/nacl/browser/nacl_host_message_filter.cc
index 43fa889..fbf1c0c 100644
--- a/components/nacl/browser/nacl_host_message_filter.cc
+++ b/components/nacl/browser/nacl_host_message_filter.cc
@@ -41,14 +41,14 @@
     uint32_t permission_bits,
     content::BrowserContext* browser_context,
     const GURL& document_url) {
-  // Only allow NaCl plugins to request certain permissions. We don't want
-  // a compromised renderer to be able to start a nacl plugin with e.g. Flash
+  // Don't grant any special permissions to NaCl plugins. We don't want
+  // a compromised renderer to be able to start a NaCl plugin with Dev or Flash
   // permissions which may expand the surface area of the sandbox.
-  uint32_t masked_bits = permission_bits & ppapi::PERMISSION_DEV;
+  uint32_t nacl_permissions = ppapi::PERMISSION_NONE;
   if (content::PluginService::GetInstance()->PpapiDevChannelSupported(
           browser_context, document_url))
-    masked_bits |= ppapi::PERMISSION_DEV_CHANNEL;
-  return ppapi::PpapiPermissions::GetForCommandLine(masked_bits);
+    nacl_permissions |= ppapi::PERMISSION_DEV_CHANNEL;
+  return ppapi::PpapiPermissions::GetForCommandLine(nacl_permissions);
 }
 
 ppapi::PpapiPermissions GetPpapiPermissions(uint32_t permission_bits,
diff --git a/components/nacl/browser/pnacl_host.cc b/components/nacl/browser/pnacl_host.cc
index 163f594..687ef23 100644
--- a/components/nacl/browser/pnacl_host.cc
+++ b/components/nacl/browser/pnacl_host.cc
@@ -566,9 +566,11 @@
     PendingTranslationMap::iterator to_erase(it++);
     if (to_erase->first.first == render_process_id) {
       // Clean up the open files.
-      std::unique_ptr<base::File> file(to_erase->second.nexe_fd);
-      to_erase->second.nexe_fd = NULL;
-      CloseBaseFile(std::move(*file.get()));
+      if (to_erase->second.nexe_fd) {
+        std::unique_ptr<base::File> file(to_erase->second.nexe_fd);
+        to_erase->second.nexe_fd = NULL;
+        CloseBaseFile(std::move(*file.get()));
+      }
       std::string key(to_erase->second.cache_key);
       bool may_be_cached = TranslationMayBeCached(to_erase);
       pending_translations_.erase(to_erase);
diff --git a/components/nacl/loader/nacl_loader_manifest.json b/components/nacl/loader/nacl_loader_manifest.json
index 7c9e0364..480d742 100644
--- a/components/nacl/loader/nacl_loader_manifest.json
+++ b/components/nacl/loader/nacl_loader_manifest.json
@@ -6,10 +6,10 @@
       "provides": {
         "browser": [
           "IPC::mojom::ChannelBootstrap",
-          "chrome::mojom::ResourceUsageReporter",
           "content::mojom::Child",
           "content::mojom::ChildControl",
-          "content::mojom::ChildHistogramFetcherFactory"
+          "content::mojom::ChildHistogramFetcherFactory",
+          "content::mojom::ResourceUsageReporter"
         ]
       }
     }
diff --git a/components/nacl/renderer/nexe_load_manager.cc b/components/nacl/renderer/nexe_load_manager.cc
index 31ccad0..648a114 100644
--- a/components/nacl/renderer/nexe_load_manager.cc
+++ b/components/nacl/renderer/nexe_load_manager.cc
@@ -54,9 +54,6 @@
 // MIME type because the "src" attribute is used to supply us with the resource
 // of that MIME type that we're supposed to display.
 const char* const kNaClManifestAttribute = "nacl";
-// Define an argument name to enable 'dev' interfaces. To make sure it doesn't
-// collide with any user-defined HTML attribute, make the first character '@'.
-const char* const kDevAttribute = "@dev";
 
 const char* const kNaClMIMEType = "application/x-nacl";
 const char* const kPNaClMIMEType = "application/x-pnacl";
@@ -418,12 +415,6 @@
   return mime_type_ == kPNaClMIMEType;
 }
 
-bool NexeLoadManager::DevInterfacesEnabled() const {
-  // Look for the developer attribute; if it's present, enable 'dev'
-  // interfaces.
-  return args_.find(kDevAttribute) != args_.end();
-}
-
 void NexeLoadManager::ReportDeadNexe() {
   if (nacl_ready_state_ == PP_NACL_READY_STATE_DONE &&  // After loadEnd
       !nexe_error_reported_) {
diff --git a/components/nacl/renderer/nexe_load_manager.h b/components/nacl/renderer/nexe_load_manager.h
index 37dfb15..ae68b2f 100644
--- a/components/nacl/renderer/nexe_load_manager.h
+++ b/components/nacl/renderer/nexe_load_manager.h
@@ -107,9 +107,6 @@
   // false otherwise.
   bool IsPNaCl() const;
 
-  // Returns true if dev interfaces are enabled for this plugin.
-  bool DevInterfacesEnabled() const;
-
   // Returns the time that the work for PNaCl translation began.
   base::Time pnacl_start_time() const { return pnacl_start_time_; }
   void set_pnacl_start_time(base::Time time) {
diff --git a/components/nacl/renderer/ppb_nacl_private_impl.cc b/components/nacl/renderer/ppb_nacl_private_impl.cc
index b322e03..5d4b951 100644
--- a/components/nacl/renderer/ppb_nacl_private_impl.cc
+++ b/components/nacl/renderer/ppb_nacl_private_impl.cc
@@ -439,11 +439,6 @@
   instance_info.url = GURL(alleged_url);
 
   uint32_t perm_bits = ppapi::PERMISSION_NONE;
-  // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
-  // it's clearer to developers when they are using 'Dev' inappropriately. We
-  // must also check on the trusted side of the proxy.
-  if (load_manager->DevInterfacesEnabled())
-    perm_bits |= ppapi::PERMISSION_DEV;
   instance_info.permissions =
       ppapi::PpapiPermissions::GetForCommandLine(perm_bits);
 
diff --git a/components/network_session_configurator/browser/network_session_configurator.cc b/components/network_session_configurator/browser/network_session_configurator.cc
index 50e8063..6857609 100644
--- a/components/network_session_configurator/browser/network_session_configurator.cc
+++ b/components/network_session_configurator/browser/network_session_configurator.cc
@@ -155,10 +155,10 @@
 
 bool ShouldRetryWithoutAltSvcOnQuicErrors(
     const VariationParameters& quic_trial_params) {
-  return base::LowerCaseEqualsASCII(
+  return !base::LowerCaseEqualsASCII(
       GetVariationParam(quic_trial_params,
                         "retry_without_alt_svc_on_quic_errors"),
-      "true");
+      "false");
 }
 
 bool ShouldSupportIetfFormatQuicAltSvc(
diff --git a/components/network_session_configurator/browser/network_session_configurator_unittest.cc b/components/network_session_configurator/browser/network_session_configurator_unittest.cc
index 8b10309c..9ba7e76 100644
--- a/components/network_session_configurator/browser/network_session_configurator_unittest.cc
+++ b/components/network_session_configurator/browser/network_session_configurator_unittest.cc
@@ -86,7 +86,7 @@
 
   EXPECT_TRUE(params_.enable_quic);
   EXPECT_FALSE(params_.mark_quic_broken_when_network_blackholes);
-  EXPECT_FALSE(params_.retry_without_alt_svc_on_quic_errors);
+  EXPECT_TRUE(params_.retry_without_alt_svc_on_quic_errors);
   EXPECT_FALSE(params_.support_ietf_format_quic_altsvc);
   EXPECT_EQ(1350u, params_.quic_max_packet_length);
   EXPECT_EQ(net::QuicTagVector(), params_.quic_connection_options);
@@ -148,15 +148,15 @@
   EXPECT_TRUE(params_.mark_quic_broken_when_network_blackholes);
 }
 
-TEST_F(NetworkSessionConfiguratorTest, RetryWithoutAltSvcOnQuicErrors) {
+TEST_F(NetworkSessionConfiguratorTest, DisableRetryWithoutAltSvcOnQuicErrors) {
   std::map<std::string, std::string> field_trial_params;
-  field_trial_params["retry_without_alt_svc_on_quic_errors"] = "true";
+  field_trial_params["retry_without_alt_svc_on_quic_errors"] = "false";
   variations::AssociateVariationParams("QUIC", "Enabled", field_trial_params);
   base::FieldTrialList::CreateFieldTrial("QUIC", "Enabled");
 
   ParseFieldTrials();
 
-  EXPECT_TRUE(params_.retry_without_alt_svc_on_quic_errors);
+  EXPECT_FALSE(params_.retry_without_alt_svc_on_quic_errors);
 }
 
 TEST_F(NetworkSessionConfiguratorTest, SupportIetfFormatQuicAltSvc) {
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index 7dec54fc..953d9712 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -23,6 +23,7 @@
     "blank.icon",
     "calculator.icon",
     "extension_app.icon",
+    "extension_app_20.icon",
     "http.icon",
     "keyword_search.icon",
     "star.icon",
diff --git a/components/omnibox/browser/autocomplete_match.cc b/components/omnibox/browser/autocomplete_match.cc
index 86fd83d..9397694 100644
--- a/components/omnibox/browser/autocomplete_match.cc
+++ b/components/omnibox/browser/autocomplete_match.cc
@@ -229,7 +229,8 @@
                          : vector_icons::kSearchIcon;
 
     case Type::EXTENSION_APP:
-      return omnibox::kExtensionAppIcon;
+      return is_touch_ui ? omnibox::kExtensionApp20Icon
+                         : omnibox::kExtensionAppIcon;
 
     case Type::CALCULATOR:
       return omnibox::kCalculatorIcon;
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index 02ee67f..9be78c7a 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -46,7 +46,7 @@
 #include "url/url_util.h"
 
 #if defined(OS_WIN)
-#include "ui/base/win/osk_display_manager.h"
+#include "ui/base/ime/win/osk_display_manager.h"
 #endif
 
 using bookmarks::BookmarkModel;
diff --git a/components/omnibox/browser/vector_icons/extension_app_20.icon b/components/omnibox/browser/vector_icons/extension_app_20.icon
new file mode 100644
index 0000000..3fb49fc
--- /dev/null
+++ b/components/omnibox/browser/vector_icons/extension_app_20.icon
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(crbug.com/647286): This is a duplicate of the existing
+// kExtensionAppIcon, just sized to 20x20dips for use in Chrome's touch mode.
+// Delete this when vector icons can support multiple icon sizes.
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 15, 10,
+V_LINE_TO, 6.5f,
+R_CUBIC_TO, 0, -1, -0.5f, -1.5f, -1.5f, -1.5f,
+R_H_LINE_TO, -3,
+V_LINE_TO, 3.59f,
+CUBIC_TO, 10.5f, 2.64f, 9.68f, 2, 8.73f, 2,
+CUBIC_TO, 7.77f, 2, 7, 2.64f, 7, 3.59f,
+V_LINE_TO, 5,
+H_LINE_TO, 3.5f,
+CUBIC_TO, 2.5f, 5, 2, 5.5f, 2, 6.5f,
+V_LINE_TO, 10,
+R_H_LINE_TO, 1.46f,
+CUBIC_TO, 4.41f, 10, 5, 10.55f, 5, 11.5f,
+CUBIC_TO_SHORTHAND, 4.41f, 13, 3.46f, 13,
+H_LINE_TO, 2,
+R_V_LINE_TO, 3.5f,
+R_CUBIC_TO, 0, 1, 0.5f, 1.5f, 1.5f, 1.5f,
+H_LINE_TO, 7,
+R_V_LINE_TO, -1.45f,
+CUBIC_TO, 7, 15.6f, 7.77f, 15, 8.73f, 15,
+R_CUBIC_TO, 0.96f, 0, 1.77f, 0.6f, 1.77f, 1.55f,
+V_LINE_TO, 18,
+R_H_LINE_TO, 3,
+R_CUBIC_TO, 1, 0, 1.5f, -0.5f, 1.5f, -1.5f,
+V_LINE_TO, 13,
+R_H_LINE_TO, 1.5f,
+R_CUBIC_TO, 0.96f, 0, 1.5f, -0.55f, 1.5f, -1.5f,
+R_CUBIC_TO, 0, -0.95f, -0.55f, -1.5f, -1.5f, -1.5f,
+H_LINE_TO, 15,
+CLOSE
diff --git a/components/safe_browsing/db/database_manager.cc b/components/safe_browsing/db/database_manager.cc
index 71d942a..8d69c85 100644
--- a/components/safe_browsing/db/database_manager.cc
+++ b/components/safe_browsing/db/database_manager.cc
@@ -140,6 +140,12 @@
   v4_get_hash_protocol_manager_.reset();
 }
 
+std::unique_ptr<base::CallbackList<void()>::Subscription>
+SafeBrowsingDatabaseManager::RegisterDatabaseUpdatedCallback(
+    const OnDatabaseUpdated& cb) {
+  return update_complete_callback_list_.Add(cb);
+}
+
 SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::SafeBrowsingApiCheck(
     const GURL& url,
     Client* client)
diff --git a/components/safe_browsing/db/database_manager.h b/components/safe_browsing/db/database_manager.h
index 3c3bac0..d7361d7 100644
--- a/components/safe_browsing/db/database_manager.h
+++ b/components/safe_browsing/db/database_manager.h
@@ -14,6 +14,7 @@
 #include <unordered_set>
 #include <vector>
 
+#include "base/callback_list.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted_delete_on_sequence.h"
@@ -221,6 +222,17 @@
       net::URLRequestContextGetter* request_context_getter,
       const V4ProtocolConfig& config);
 
+  //
+  // Method to manage getting database updates of the DatabaseManager.
+  //
+
+  // Subscribe to receive callbacks when the database is updated, both initially
+  // when it's loaded from disk at startup, and then periodically. These
+  // callbacks will be on the UI thread.
+  using OnDatabaseUpdated = base::RepeatingClosure;
+  std::unique_ptr<base::CallbackList<void()>::Subscription>
+  RegisterDatabaseUpdatedCallback(const OnDatabaseUpdated& cb);
+
   // Called to stop or shutdown operations on the io_thread. All subclasses
   // should override this method, set enabled_ to false and call the base class
   // method at the bottom of it.
@@ -289,6 +301,9 @@
   // Created and destroyed via StartOnIOThread/StopOnIOThread.
   std::unique_ptr<V4GetHashProtocolManager> v4_get_hash_protocol_manager_;
 
+  // A list of parties to be notified about database updates.
+  base::CallbackList<void()> update_complete_callback_list_;
+
  private:
   // Returns an iterator to the pending API check with the given |client|.
   ApiCheckSet::iterator FindClientApiCheck(Client* client);
diff --git a/components/safe_browsing/db/notification_types.h b/components/safe_browsing/db/notification_types.h
deleted file mode 100644
index 57280dd..0000000
--- a/components/safe_browsing/db/notification_types.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 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_SAFE_BROWSING_DB_NOTIFICATION_TYPES_H_
-#define COMPONENTS_SAFE_BROWSING_DB_NOTIFICATION_TYPES_H_
-
-// **
-// ** NOTICE
-// **
-// ** The notification system is deprecated, obsolete, and is slowly being
-// ** removed. See https://crbug.com/268984.
-// **
-// ** Please don't add any new notification types, and please help migrate
-// ** existing uses of the notification types below to use the Observer and
-// ** Callback patterns.
-// **
-
-// Lists of Safe Browsing related notifications, used with NotificationService.
-namespace safe_browsing {
-
-enum NotificationType {
-  NOTIFICATION_SAFE_BROWSING_START = 0,
-
-  // General -----------------------------------------------------------------
-
-  // Special signal value to represent an interest in all notifications.
-  // Not valid when posting a notification.
-  NOTIFICATION_ALL = NOTIFICATION_SAFE_BROWSING_START,
-
-  // A safe browsing database update completed.  Source is the
-  // SafeBrowsingDatabaseManager and the details are none. It is posted on the
-  // UI thread.
-  NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE,
-
-  NOTIFICATION_SAFE_BROWSING_END
-};
-
-}  // namespace safe_browsing
-
-// **
-// ** NOTICE
-// **
-// ** The notification system is deprecated, obsolete, and is slowly being
-// ** removed. See https://crbug.com/268984.
-// **
-// ** Please don't add any new notification types, and please help migrate
-// ** existing uses of the notification types below to use the Observer and
-// ** Callback patterns.
-// **
-
-#endif  // COMPONENTS_SAFE_BROWSING_DB_NOTIFICATION_TYPES_H_
diff --git a/components/safe_browsing/db/v4_local_database_manager.cc b/components/safe_browsing/db/v4_local_database_manager.cc
index 4789492..9242873 100644
--- a/components/safe_browsing/db/v4_local_database_manager.cc
+++ b/components/safe_browsing/db/v4_local_database_manager.cc
@@ -19,11 +19,9 @@
 #include "base/strings/string_util.h"
 #include "base/task_scheduler/post_task.h"
 #include "base/timer/elapsed_timer.h"
-#include "components/safe_browsing/db/notification_types.h"
 #include "components/safe_browsing/db/v4_feature_list.h"
 #include "components/safe_browsing/db/v4_protocol_manager_util.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_service.h"
 #include "crypto/sha2.h"
 
 using content::BrowserThread;
@@ -561,21 +559,14 @@
 
     BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
-        base::Bind(&V4LocalDatabaseManager::PostUpdateNotificationOnUIThread,
-                   content::Source<SafeBrowsingDatabaseManager>(this)));
+        base::BindOnce(
+            &V4LocalDatabaseManager::PostUpdateNotificationOnUIThread, this));
   }
 }
 
-// static
-void V4LocalDatabaseManager::PostUpdateNotificationOnUIThread(
-    const content::NotificationSource& source) {
+void V4LocalDatabaseManager::PostUpdateNotificationOnUIThread() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  // The notification needs to be posted on the UI thread because the extension
-  // checker is observing UI thread's notification service.
-  content::NotificationService::current()->Notify(
-      NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE, source,
-      content::NotificationService::NoDetails());
+  update_complete_callback_list_.Notify();
 }
 
 void V4LocalDatabaseManager::DeletePVer3StoreFiles() {
diff --git a/components/safe_browsing/db/v4_local_database_manager.h b/components/safe_browsing/db/v4_local_database_manager.h
index f3d61306..dc28165 100644
--- a/components/safe_browsing/db/v4_local_database_manager.h
+++ b/components/safe_browsing/db/v4_local_database_manager.h
@@ -19,7 +19,6 @@
 #include "components/safe_browsing/db/v4_protocol_manager_util.h"
 #include "components/safe_browsing/db/v4_update_protocol_manager.h"
 #include "components/safe_browsing/proto/webui.pb.h"
-#include "content/public/browser/notification_service.h"
 #include "url/gurl.h"
 
 namespace safe_browsing {
@@ -278,11 +277,10 @@
                                     const FullHashToStoreAndHashPrefixesMap&
                                         full_hash_to_store_and_hash_prefixes);
 
-  // Post a notification about the completion of database update process.
-  // This is currently used by the extension blacklist checker to disable any
-  // installed extensions that have been blacklisted since.
-  static void PostUpdateNotificationOnUIThread(
-      const content::NotificationSource& source);
+  // Make callbacks about the completion of database update process. This is
+  // currently used by the extension blacklist checker to disable any installed
+  // extensions that have been blacklisted since.
+  void PostUpdateNotificationOnUIThread();
 
   // When the database is ready to use, process the checks that were queued
   // while the database was loading from disk.
diff --git a/components/safe_browsing/db/v4_local_database_manager_unittest.cc b/components/safe_browsing/db/v4_local_database_manager_unittest.cc
index 7231c925..505d0d2 100644
--- a/components/safe_browsing/db/v4_local_database_manager_unittest.cc
+++ b/components/safe_browsing/db/v4_local_database_manager_unittest.cc
@@ -13,7 +13,6 @@
 #include "base/run_loop.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "components/safe_browsing/db/notification_types.h"
 #include "components/safe_browsing/db/v4_database.h"
 #include "components/safe_browsing/db/v4_protocol_manager_util.h"
 #include "components/safe_browsing/db/v4_test_util.h"
@@ -1114,10 +1113,10 @@
 }
 
 TEST_F(V4LocalDatabaseManagerTest, NotificationOnUpdate) {
-  content::WindowedNotificationObserver observer(
-      NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE,
-      content::Source<SafeBrowsingDatabaseManager>(
-          v4_local_database_manager_.get()));
+  base::RunLoop run_loop;
+  auto callback_subscription =
+      v4_local_database_manager_->RegisterDatabaseUpdatedCallback(
+          run_loop.QuitClosure());
 
   // Creates and associates a V4Database instance.
   StoreAndHashPrefixes store_and_hash_prefixes;
@@ -1125,8 +1124,7 @@
 
   v4_local_database_manager_->DatabaseUpdated();
 
-  // This observer waits until it receives the notification.
-  observer.Wait();
+  run_loop.Run();
 }
 
 }  // namespace safe_browsing
diff --git a/components/toolbar/BUILD.gn b/components/toolbar/BUILD.gn
index 97fcbb13..3e6affdd 100644
--- a/components/toolbar/BUILD.gn
+++ b/components/toolbar/BUILD.gn
@@ -20,10 +20,14 @@
     "http.icon",
     "https_invalid.icon",
     "https_valid.icon",
+    "http_20.icon",
+    "https_invalid_20.icon",
+    "https_valid_20.icon",
     "https_valid_in_chip.icon",
     "offline_pin.icon",
     "open_in_new.icon",
     "product.icon",
+    "product_20.icon",
     "star_active.icon",
     "star.icon",
   ]
diff --git a/components/toolbar/toolbar_model_impl.cc b/components/toolbar/toolbar_model_impl.cc
index 0d6bbaf..cdb80da4 100644
--- a/components/toolbar/toolbar_model_impl.cc
+++ b/components/toolbar/toolbar_model_impl.cc
@@ -22,6 +22,7 @@
 #include "net/cert/x509_certificate.h"
 #include "net/ssl/ssl_connection_status_flags.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/material_design/material_design_controller.h"
 #include "ui/gfx/text_elider.h"
 #include "ui/gfx/vector_icon_types.h"
 
@@ -88,6 +89,9 @@
 
 const gfx::VectorIcon& ToolbarModelImpl::GetVectorIcon() const {
 #if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
+  const bool is_touch_ui =
+      ui::MaterialDesignController::IsTouchOptimizedUiEnabled();
+
   auto* const icon_override = delegate_->GetVectorIconOverride();
   if (icon_override)
     return *icon_override;
@@ -98,14 +102,16 @@
   switch (GetSecurityLevel(false)) {
     case security_state::NONE:
     case security_state::HTTP_SHOW_WARNING:
-      return toolbar::kHttpIcon;
+      return is_touch_ui ? toolbar::kHttp20Icon : toolbar::kHttpIcon;
     case security_state::EV_SECURE:
     case security_state::SECURE:
-      return toolbar::kHttpsValidIcon;
+      return is_touch_ui ? toolbar::kHttpsValid20Icon
+                         : toolbar::kHttpsValidIcon;
     case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
       return vector_icons::kBusinessIcon;
     case security_state::DANGEROUS:
-      return toolbar::kHttpsInvalidIcon;
+      return is_touch_ui ? toolbar::kHttpsInvalid20Icon
+                         : toolbar::kHttpsInvalidIcon;
     case security_state::SECURITY_LEVEL_COUNT:
       NOTREACHED();
       return toolbar::kHttpIcon;
diff --git a/components/toolbar/vector_icons/http_20.icon b/components/toolbar/vector_icons/http_20.icon
new file mode 100644
index 0000000..20abc44
--- /dev/null
+++ b/components/toolbar/vector_icons/http_20.icon
@@ -0,0 +1,32 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(crbug.com/647286): This is a duplicate of the existing kHttpIcon, just
+// sized to 20x20dips for use in Chrome's touch mode.
+// Delete this when vector icons can support multiple icon sizes.
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 10, 2,
+R_CUBIC_TO, -4.42f, 0, -8, 3.58f, -8, 8,
+R_CUBIC_TO, 0, 4.42f, 3.58f, 8, 8, 8,
+R_CUBIC_TO, 4.42f, 0, 8, -3.58f, 8, -8,
+R_CUBIC_TO, 0, -4.42f, -3.58f, -8, -8, -8,
+CLOSE,
+R_MOVE_TO, -6, 8,
+R_CUBIC_TO, 0, -3.31f, 2.69f, -6, 6, -6,
+R_CUBIC_TO, 3.31f, 0, 6, 2.69f, 6, 6,
+R_CUBIC_TO, 0, 3.31f, -2.69f, 6, -6, 6,
+R_CUBIC_TO, -3.31f, 0, -6, -2.69f, -6, -6,
+CLOSE,
+R_MOVE_TO, 7, 4,
+V_LINE_TO, 9,
+H_LINE_TO, 9,
+R_V_LINE_TO, 5,
+R_H_LINE_TO, 2,
+CLOSE,
+MOVE_TO, 9, 8,
+R_H_LINE_TO, 2,
+V_LINE_TO, 6,
+H_LINE_TO, 9,
+R_V_LINE_TO, 2,
+CLOSE
diff --git a/components/toolbar/vector_icons/https_invalid_20.icon b/components/toolbar/vector_icons/https_invalid_20.icon
new file mode 100644
index 0000000..3e96bb9
--- /dev/null
+++ b/components/toolbar/vector_icons/https_invalid_20.icon
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(crbug.com/647286): This is a duplicate of the existing
+// kHttpsInvalidIcon, just sized to 20x20dips for use in Chrome's touch mode.
+// Delete this when vector icons can support multiple icon sizes.
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 1, 18,
+R_H_LINE_TO, 18,
+LINE_TO, 10, 2,
+LINE_TO, 1, 18,
+CLOSE,
+R_MOVE_TO, 10, -2,
+H_LINE_TO, 9,
+R_V_LINE_TO, -2,
+R_H_LINE_TO, 2,
+R_V_LINE_TO, 2,
+CLOSE,
+R_MOVE_TO, -2, -3,
+V_LINE_TO, 8,
+R_H_LINE_TO, 2,
+R_V_LINE_TO, 5,
+H_LINE_TO, 9,
+CLOSE
diff --git a/components/toolbar/vector_icons/https_valid_20.icon b/components/toolbar/vector_icons/https_valid_20.icon
new file mode 100644
index 0000000..61c61bca
--- /dev/null
+++ b/components/toolbar/vector_icons/https_valid_20.icon
@@ -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.
+
+// TODO(crbug.com/647286): This is a duplicate of the existing kHttpsValidIcon,
+// just sized to 20x20dips for use in Chrome's touch mode. Delete this when
+// vector icons can support multiple icon sizes.
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 13, 7,
+V_LINE_TO, 6,
+R_CUBIC_TO, 0, -1.66f, -1.34f, -3, -3, -3,
+CUBIC_TO_SHORTHAND, 7, 4.34f, 7, 6,
+R_V_LINE_TO, 1,
+R_H_LINE_TO, -0.5f,
+CUBIC_TO, 5.33f, 7, 5, 7.79f, 5, 8.5f,
+R_V_LINE_TO, 6,
+R_CUBIC_TO, 0, 0.83f, 0.67f, 1.5f, 1.5f, 1.5f,
+R_H_LINE_TO, 7,
+R_CUBIC_TO, 0.83f, 0, 1.5f, -0.67f, 1.5f, -1.5f,
+R_V_LINE_TO, -6,
+R_CUBIC_TO, 0, -0.83f, -0.67f, -1.5f, -1.5f, -1.5f,
+H_LINE_TO, 13,
+CLOSE,
+R_MOVE_TO, -5, 0,
+R_V_LINE_TO, -1,
+R_ARC_TO, 2, 2, 0, 0, 1, 4, 0,
+R_V_LINE_TO, 1,
+H_LINE_TO, 8,
+CLOSE
diff --git a/components/toolbar/vector_icons/product_20.icon b/components/toolbar/vector_icons/product_20.icon
new file mode 100644
index 0000000..89c0c2e
--- /dev/null
+++ b/components/toolbar/vector_icons/product_20.icon
@@ -0,0 +1,36 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(crbug.com/647286): This is a duplicate of the existing kProductIcon,
+// just sized to 20x20dips for use in Chrome's touch mode. Delete this when
+// vector icons can support multiple icon sizes.
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 10, 6.4f,
+R_H_LINE_TO, 7.12f,
+CUBIC_TO, 15.84f, 3.76f, 13.12f, 2, 10, 2,
+CUBIC_TO, 7.52f, 2, 5.28f, 3.12f, 3.84f, 4.88f,
+R_LINE_TO, 2.64f, 4.56f,
+CUBIC_TO, 6.72f, 7.76f, 8.24f, 6.4f, 10, 6.4f,
+CLOSE,
+R_MOVE_TO, 0, 7.2f,
+R_CUBIC_TO, -1.36f, 0, -2.48f, -0.72f, -3.12f, -1.84f,
+LINE_TO, 3.28f, 5.6f,
+CUBIC_TO, 2.48f, 6.88f, 2, 8.4f, 2, 10,
+R_CUBIC_TO, 0, 4, 2.88f, 7.28f, 6.72f, 7.92f,
+R_LINE_TO, 2.64f, -4.56f,
+R_CUBIC_TO, -0.48f, 0.16f, -0.88f, 0.24f, -1.36f, 0.24f,
+CLOSE,
+R_MOVE_TO, 3.6f, -3.6f,
+R_CUBIC_TO, 0, -1.12f, -0.56f, -2.16f, -1.36f, -2.8f,
+R_H_LINE_TO, 5.28f,
+R_CUBIC_TO, 0.32f, 0.88f, 0.48f, 1.84f, 0.48f, 2.8f,
+R_CUBIC_TO, 0, 4.4f, -3.6f, 8, -8, 8,
+R_H_LINE_TO, -0.48f,
+R_LINE_TO, 3.6f, -6.24f,
+R_CUBIC_TO, 0.32f, -0.48f, 0.48f, -1.12f, 0.48f, -1.76f,
+CLOSE,
+MOVE_TO, 10, 12.8f,
+R_ARC_TO, 2.8f, 2.8f, 0, 1, 1, 0, -5.6f,
+R_ARC_TO, 2.8f, 2.8f, 0, 0, 1, 0, 5.6f,
+CLOSE
diff --git a/components/tracing/common/trace_config_file.cc b/components/tracing/common/trace_config_file.cc
index 3193bb91..ff0745c 100644
--- a/components/tracing/common/trace_config_file.cc
+++ b/components/tracing/common/trace_config_file.cc
@@ -126,6 +126,10 @@
   return is_enabled_;
 }
 
+void TraceConfigFile::SetDisabled() {
+  is_enabled_ = false;
+}
+
 base::trace_event::TraceConfig TraceConfigFile::GetTraceConfig() const {
   DCHECK(IsEnabled());
   return trace_config_;
diff --git a/components/tracing/common/trace_config_file.h b/components/tracing/common/trace_config_file.h
index fab9eeb..145adc2 100644
--- a/components/tracing/common/trace_config_file.h
+++ b/components/tracing/common/trace_config_file.h
@@ -73,7 +73,15 @@
  public:
   static TraceConfigFile* GetInstance();
 
+  // IsEnabled() returns true if a valid trace config file is specified and
+  // either we are passed the trace duration, if it is positive, or startup
+  // tracing is stopped by other means, e.g. via DevTools protocol.
   bool IsEnabled() const;
+
+  // SetDisabled() is used by the tracing controller to indicate that startup
+  // tracing is finished.
+  void SetDisabled();
+
   base::trace_event::TraceConfig GetTraceConfig() const;
   int GetStartupDuration() const;
 #if !defined(OS_ANDROID)
diff --git a/components/tracing/common/trace_startup.cc b/components/tracing/common/trace_startup.cc
index 7a04869..6ad1aeae 100644
--- a/components/tracing/common/trace_startup.cc
+++ b/components/tracing/common/trace_startup.cc
@@ -14,7 +14,7 @@
 
 namespace tracing {
 
-void EnableStartupTracingIfNeeded(bool can_access_file_system) {
+void EnableStartupTracingIfNeeded() {
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
 
@@ -22,14 +22,10 @@
   // https://crbug.com/764357
   base::trace_event::TraceLog::GetInstance();
 
-  // Enables heap profiling if "--enable-heap-profiling" flag is passed.
-  base::trace_event::MemoryDumpManager::GetInstance()
-      ->EnableHeapProfilingIfNeeded();
-
   if (command_line.HasSwitch(switches::kTraceStartup)) {
     base::trace_event::TraceConfig trace_config(
         command_line.GetSwitchValueASCII(switches::kTraceStartup),
-        base::trace_event::RECORD_UNTIL_FULL);
+        command_line.GetSwitchValueASCII(switches::kTraceStartupRecordMode));
     base::trace_event::TraceLog::GetInstance()->SetEnabled(
         trace_config, base::trace_event::TraceLog::RECORDING_MODE);
   } else if (command_line.HasSwitch(switches::kTraceToConsole)) {
@@ -40,8 +36,7 @@
                << trace_config.ToCategoryFilterString() << "'.";
     base::trace_event::TraceLog::GetInstance()->SetEnabled(
         trace_config, base::trace_event::TraceLog::RECORDING_MODE);
-  } else if (can_access_file_system &&
-             tracing::TraceConfigFile::GetInstance()->IsEnabled()) {
+  } else if (tracing::TraceConfigFile::GetInstance()->IsEnabled()) {
     const base::trace_event::TraceConfig& trace_config =
         tracing::TraceConfigFile::GetInstance()->GetTraceConfig();
     uint8_t modes = base::trace_event::TraceLog::RECORDING_MODE;
diff --git a/components/tracing/common/trace_startup.h b/components/tracing/common/trace_startup.h
index 4a388d8..4cb5683 100644
--- a/components/tracing/common/trace_startup.h
+++ b/components/tracing/common/trace_startup.h
@@ -10,9 +10,7 @@
 namespace tracing {
 
 // Enables TraceLog with config based on the command line flags of the process.
-// If |can_access_file_system| is false then TraceLog is not enabled in case it
-// is required to read config file to start tracing.
-void TRACING_EXPORT EnableStartupTracingIfNeeded(bool can_access_file_system);
+void TRACING_EXPORT EnableStartupTracingIfNeeded();
 
 }  // namespace tracing
 
diff --git a/components/tracing/common/tracing_switches.cc b/components/tracing/common/tracing_switches.cc
index 730aef4..a55082a 100644
--- a/components/tracing/common/tracing_switches.cc
+++ b/components/tracing/common/tracing_switches.cc
@@ -46,6 +46,10 @@
 // all events since startup.
 const char kTraceStartupFile[]              = "trace-startup-file";
 
+// If supplied, sets the tracing record mode; otherwise, the default
+// "record-until-full" mode will be used.
+const char kTraceStartupRecordMode[] = "trace-startup-record-mode";
+
 // Sends a pretty-printed version of tracing info to the console.
 const char kTraceToConsole[]                = "trace-to-console";
 
diff --git a/components/tracing/common/tracing_switches.h b/components/tracing/common/tracing_switches.h
index 6d9764b..e8ae2a4 100644
--- a/components/tracing/common/tracing_switches.h
+++ b/components/tracing/common/tracing_switches.h
@@ -15,6 +15,7 @@
 TRACING_EXPORT extern const char kTraceStartup[];
 TRACING_EXPORT extern const char kTraceStartupDuration[];
 TRACING_EXPORT extern const char kTraceStartupFile[];
+TRACING_EXPORT extern const char kTraceStartupRecordMode[];
 TRACING_EXPORT extern const char kTraceToConsole[];
 TRACING_EXPORT extern const char kTraceUploadURL[];
 
diff --git a/components/tracing/docs/heap_profiler_internals.md b/components/tracing/docs/heap_profiler_internals.md
deleted file mode 100644
index a54e7f3..0000000
--- a/components/tracing/docs/heap_profiler_internals.md
+++ /dev/null
@@ -1,2 +0,0 @@
-This document has moved to [//docs/memory-infra/heap_profiler_internals.md](/docs/memory-infra/heap_profiler_internals.md).
-
diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc
index 0a910de..d5072b3 100644
--- a/components/user_manager/user_manager_base.cc
+++ b/components/user_manager/user_manager_base.cc
@@ -915,7 +915,7 @@
   active_user_ =
       RemoveRegularOrSupervisedUserFromList(account_id, false /* notify */);
 
-  if (active_user_)
+  if (active_user_ && active_user_->GetType() != user_type)
     active_user_->UpdateType(user_type);
 
   // If the user was not found on the user list, create a new user.
diff --git a/components/viz/client/client_layer_tree_frame_sink.cc b/components/viz/client/client_layer_tree_frame_sink.cc
index 58c487fce..936b7ab 100644
--- a/components/viz/client/client_layer_tree_frame_sink.cc
+++ b/components/viz/client/client_layer_tree_frame_sink.cc
@@ -133,6 +133,10 @@
   if (!enable_surface_synchronization_) {
     local_surface_id_ =
         local_surface_id_provider_->GetLocalSurfaceIdForFrame(frame);
+  } else {
+    CHECK(local_surface_id_ != last_submitted_local_surface_id_ ||
+          (last_submitted_device_scale_factor_ == frame.device_scale_factor() &&
+           last_submitted_size_in_pixels_ == frame.size_in_pixels()));
   }
 
   TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"),
@@ -145,6 +149,10 @@
   if (hit_test_data_provider_)
     hit_test_region_list = hit_test_data_provider_->GetHitTestData(frame);
 
+  last_submitted_local_surface_id_ = local_surface_id_;
+  last_submitted_device_scale_factor_ = frame.device_scale_factor();
+  last_submitted_size_in_pixels_ = frame.size_in_pixels();
+
   compositor_frame_sink_ptr_->SubmitCompositorFrame(
       local_surface_id_, std::move(frame), std::move(hit_test_region_list),
       tracing_enabled ? base::TimeTicks::Now().since_origin().InMicroseconds()
diff --git a/components/viz/client/client_layer_tree_frame_sink.h b/components/viz/client/client_layer_tree_frame_sink.h
index 06101ea8..151af13e 100644
--- a/components/viz/client/client_layer_tree_frame_sink.h
+++ b/components/viz/client/client_layer_tree_frame_sink.h
@@ -128,6 +128,10 @@
   const bool enable_surface_synchronization_;
   const bool wants_animate_only_begin_frames_;
 
+  LocalSurfaceId last_submitted_local_surface_id_;
+  float last_submitted_device_scale_factor_ = 1.f;
+  gfx::Size last_submitted_size_in_pixels_;
+
   base::WeakPtrFactory<ClientLayerTreeFrameSink> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ClientLayerTreeFrameSink);
diff --git a/components/viz/client/frame_eviction_manager.cc b/components/viz/client/frame_eviction_manager.cc
index f668659..6a9412e 100644
--- a/components/viz/client/frame_eviction_manager.cc
+++ b/components/viz/client/frame_eviction_manager.cc
@@ -165,4 +165,8 @@
   CullUnlockedFrames(std::max(1, (saved_frame_limit * percentage) / 100));
 }
 
+void FrameEvictionManager::PurgeAllUnlockedFrames() {
+  CullUnlockedFrames(0);
+}
+
 }  // namespace viz
diff --git a/components/viz/client/frame_eviction_manager.h b/components/viz/client/frame_eviction_manager.h
index 3504afa..86486913 100644
--- a/components/viz/client/frame_eviction_manager.h
+++ b/components/viz/client/frame_eviction_manager.h
@@ -52,6 +52,9 @@
   void OnMemoryPressure(
       base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
 
+  // Purges all unlocked frames, allowing us to reclaim resources.
+  void PurgeAllUnlockedFrames();
+
  private:
   FrameEvictionManager();
   ~FrameEvictionManager() override;
diff --git a/components/viz/host/server_gpu_memory_buffer_manager_unittest.cc b/components/viz/host/server_gpu_memory_buffer_manager_unittest.cc
index 10774dd..e7d58c5e 100644
--- a/components/viz/host/server_gpu_memory_buffer_manager_unittest.cc
+++ b/components/viz/host/server_gpu_memory_buffer_manager_unittest.cc
@@ -117,6 +117,8 @@
 
   void DestroyAllChannels() override {}
 
+  void OnBackgrounded() override {}
+
   void Crash() override {}
 
   void Hang() override {}
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index 352a41d..b0142f7 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -674,6 +674,21 @@
   gpu_channel_manager_->DestroyAllChannels();
 }
 
+void GpuServiceImpl::OnBackgrounded() {
+// Currently only called on Android.
+#if defined(OS_ANDROID)
+  if (io_runner_->BelongsToCurrentThread()) {
+    main_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&GpuServiceImpl::OnBackgrounded, weak_ptr_));
+    return;
+  }
+  DVLOG(1) << "GPU: Performing background cleanup";
+  gpu_channel_manager_->OnApplicationBackgrounded();
+#else
+  NOTREACHED();
+#endif
+}
+
 void GpuServiceImpl::Crash() {
   DCHECK(io_runner_->BelongsToCurrentThread());
   DVLOG(1) << "GPU: Simulating GPU crash";
diff --git a/components/viz/service/gl/gpu_service_impl.h b/components/viz/service/gl/gpu_service_impl.h
index fe3b098..51d4892 100644
--- a/components/viz/service/gl/gpu_service_impl.h
+++ b/components/viz/service/gl/gpu_service_impl.h
@@ -187,6 +187,7 @@
   void WakeUpGpu() override;
   void GpuSwitched() override;
   void DestroyAllChannels() override;
+  void OnBackgrounded() override;
   void Crash() override;
   void Hang() override;
   void ThrowJavaException() override;
diff --git a/content/app/android/library_loader_hooks.cc b/content/app/android/library_loader_hooks.cc
index 9db19957..82f5c7a 100644
--- a/content/app/android/library_loader_hooks.cc
+++ b/content/app/android/library_loader_hooks.cc
@@ -14,7 +14,7 @@
 
 bool LibraryLoaded(JNIEnv* env, jclass clazz) {
   // Enable startup tracing asap to avoid early TRACE_EVENT calls being ignored.
-  tracing::EnableStartupTracingIfNeeded(true /* can_access_file_system */);
+  tracing::EnableStartupTracingIfNeeded();
 
   // Android's main browser loop is custom so we set the browser
   // name here as early as possible.
diff --git a/content/app/content_main_runner.cc b/content/app/content_main_runner.cc
index 84e2469..65d091ba 100644
--- a/content/app/content_main_runner.cc
+++ b/content/app/content_main_runner.cc
@@ -336,6 +336,10 @@
       command_line.GetSwitchValueASCII(switches::kProcessType);
   ContentClientInitializer::Set(process_type, delegate);
 
+#if !defined(OS_ANDROID)
+  tracing::EnableStartupTracingIfNeeded();
+#endif  // !OS_ANDROID
+
   MainFunctionParams main_params(command_line);
   main_params.zygote_child = true;
 
@@ -544,12 +548,11 @@
     // Enable startup tracing asap to avoid early TRACE_EVENT calls being
     // ignored. For Android, startup tracing is enabled in an even earlier place
     // content/app/android/library_loader_hooks.cc.
-    // Zygote process does not have file thread and renderer process on Win10
-    // cannot access the file system.
-    // TODO(ssid): Check if other processes can enable startup tracing here.
-    bool can_access_file_system = (process_type != switches::kZygoteProcess &&
-                                   process_type != switches::kRendererProcess);
-    tracing::EnableStartupTracingIfNeeded(can_access_file_system);
+    //
+    // Startup tracing flags are not (and should not) passed to Zygote
+    // processes. We will enable tracing when forked, if needed.
+    if (process_type != switches::kZygoteProcess)
+      tracing::EnableStartupTracingIfNeeded();
 #endif  // !OS_ANDROID
 
 #if defined(OS_WIN)
diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc
index 589568ba..3379b8a 100644
--- a/content/browser/browser_child_process_host_impl.cc
+++ b/content/browser/browser_child_process_host_impl.cc
@@ -26,8 +26,10 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "components/tracing/common/trace_config_file.h"
 #include "components/tracing/common/tracing_switches.h"
 #include "content/browser/bad_message.h"
+#include "content/browser/browser_main_loop.h"
 #include "content/browser/histogram_controller.h"
 #include "content/browser/loader/resource_message_filter.h"
 #include "content/browser/service_manager/service_manager_context.h"
@@ -209,6 +211,44 @@
       switches::kDisableFeatures, cmd_line);
 }
 
+// static
+void BrowserChildProcessHostImpl::CopyTraceStartupFlags(
+    base::CommandLine* cmd_line) {
+  const base::CommandLine& browser_cmd_line =
+      *base::CommandLine::ForCurrentProcess();
+
+  if (browser_cmd_line.HasSwitch(switches::kTraceStartup) &&
+      BrowserMainLoop::GetInstance()->is_tracing_startup_for_duration()) {
+    // Pass kTraceStartup switch to renderer only if startup tracing has not
+    // finished.
+    cmd_line->AppendSwitchASCII(
+        switches::kTraceStartup,
+        browser_cmd_line.GetSwitchValueASCII(switches::kTraceStartup));
+    if (browser_cmd_line.HasSwitch(switches::kTraceStartupRecordMode)) {
+      cmd_line->AppendSwitchASCII(switches::kTraceStartupRecordMode,
+                                  browser_cmd_line.GetSwitchValueASCII(
+                                      switches::kTraceStartupRecordMode));
+    }
+  } else if (tracing::TraceConfigFile::GetInstance()->IsEnabled()) {
+    const auto trace_config =
+        tracing::TraceConfigFile::GetInstance()->GetTraceConfig();
+    if (!trace_config.IsArgumentFilterEnabled()) {
+      // The only trace option that we can pass through switches is the record
+      // mode. Other trace options should have the default value.
+      //
+      // TODO(chiniforooshan): Add other trace options to switches if, for
+      // example, they are used in a telemetry test that needs startup trace
+      // events from renderer processes.
+      cmd_line->AppendSwitchASCII(switches::kTraceStartup,
+                                  trace_config.ToCategoryFilterString());
+      cmd_line->AppendSwitchASCII(
+          switches::kTraceStartupRecordMode,
+          base::trace_event::TraceConfig::TraceRecordModeToStr(
+              trace_config.GetTraceRecordMode()));
+    }
+  }
+}
+
 void BrowserChildProcessHostImpl::Launch(
     std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
     std::unique_ptr<base::CommandLine> cmd_line,
diff --git a/content/browser/browser_child_process_host_impl.h b/content/browser/browser_child_process_host_impl.h
index 76923f92..5dc64aa 100644
--- a/content/browser/browser_child_process_host_impl.h
+++ b/content/browser/browser_child_process_host_impl.h
@@ -64,6 +64,10 @@
   // from FieldTrials.
   static void CopyFeatureAndFieldTrialFlags(base::CommandLine* cmd_line);
 
+  // Appends kTraceStartup and kTraceRecordMode flags to the command line, if
+  // needed.
+  static void CopyTraceStartupFlags(base::CommandLine* cmd_line);
+
   // BrowserChildProcessHost implementation:
   bool Send(IPC::Message* message) override;
   void Launch(std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 1db7eb7..70d6ebd 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -164,7 +164,6 @@
 #endif
 
 #if defined(OS_MACOSX)
-#include "base/allocator/allocator_interception_mac.h"
 #include "base/memory/memory_pressure_monitor_mac.h"
 #include "content/browser/cocoa/system_hotkey_helper_mac.h"
 #include "content/browser/mach_broker_mac.h"
@@ -265,7 +264,6 @@
   // to the zygote/renderers.
   static const char* const kForwardSwitches[] = {
       switches::kAndroidFontsPath, switches::kClearKeyCdmPathForTesting,
-      switches::kEnableHeapProfiling,
       switches::kEnableLogging,  // Support, e.g., --enable-logging=stderr.
       // Need to tell the zygote that it is headless so that we don't try to use
       // the wrong type of main delegate.
@@ -839,14 +837,6 @@
 
   InitializeMemoryManagementComponent();
 
-#if defined(OS_MACOSX)
-  if (base::CommandLine::InitializedForCurrentProcess() &&
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableHeapProfiling)) {
-    base::allocator::PeriodicallyShimNewMallocZones();
-  }
-#endif
-
 #if BUILDFLAG(ENABLE_PLUGINS)
   // Prior to any processing happening on the IO thread, we create the
   // plugin service as it is predominantly used from the IO thread,
@@ -1616,7 +1606,8 @@
   if (parsed_command_line_.HasSwitch(switches::kTraceStartup)) {
     base::trace_event::TraceConfig trace_config(
         parsed_command_line_.GetSwitchValueASCII(switches::kTraceStartup),
-        base::trace_event::RECORD_UNTIL_FULL);
+        parsed_command_line_.GetSwitchValueASCII(
+            switches::kTraceStartupRecordMode));
     TracingController::GetInstance()->StartTracing(
         trace_config, TracingController::StartTracingDoneCallback());
   } else if (parsed_command_line_.HasSwitch(switches::kTraceToConsole)) {
diff --git a/content/browser/browser_main_runner.cc b/content/browser/browser_main_runner.cc
index 2563426..febe54e5 100644
--- a/content/browser/browser_main_runner.cc
+++ b/content/browser/browser_main_runner.cc
@@ -26,7 +26,6 @@
 #include "content/browser/browser_shutdown_profile_dumper.h"
 #include "content/browser/notification_service_impl.h"
 #include "content/common/content_switches_internal.h"
-#include "content/public/browser/tracing_controller.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/main_function_params.h"
 #include "third_party/skia/include/core/SkGraphics.h"
@@ -191,8 +190,7 @@
         startup_profiler.reset(
             new BrowserShutdownProfileDumper(main_loop_->startup_trace_file()));
       }
-    } else if (tracing::TraceConfigFile::GetInstance()->IsEnabled() &&
-               TracingController::GetInstance()->IsTracing()) {
+    } else if (tracing::TraceConfigFile::GetInstance()->IsEnabled()) {
       base::FilePath result_file;
 #if defined(OS_ANDROID)
       TracingControllerAndroid::GenerateTracingFilePath(&result_file);
diff --git a/content/browser/devtools/devtools_frame_trace_recorder_for_viz.cc b/content/browser/devtools/devtools_frame_trace_recorder_for_viz.cc
index d2528bfb..ce9d0b3 100644
--- a/content/browser/devtools/devtools_frame_trace_recorder_for_viz.cc
+++ b/content/browser/devtools/devtools_frame_trace_recorder_for_viz.cc
@@ -76,9 +76,8 @@
 
   scoped_refptr<media::VideoFrame> frame;
   frame = media::VideoFrame::WrapExternalData(
-      info->pixel_format, info->coded_size, info->visible_rect,
-      info->visible_rect.size(), static_cast<uint8_t*>(mapping.get()),
-      buffer_size, info->timestamp);
+      info->pixel_format, info->coded_size, content_rect, content_rect.size(),
+      static_cast<uint8_t*>(mapping.get()), buffer_size, info->timestamp);
   if (!frame)
     return;
   frame->AddDestructionObserver(base::BindOnce(
@@ -86,8 +85,7 @@
 
   media::PaintCanvasVideoRenderer renderer;
   SkBitmap skbitmap;
-  skbitmap.allocN32Pixels(info->visible_rect.width(),
-                          info->visible_rect.height());
+  skbitmap.allocN32Pixels(content_rect.width(), content_rect.height());
   cc::SkiaPaintCanvas canvas(skbitmap);
   renderer.Copy(frame, &canvas, media::Context3D());
   callbacks->Done();
diff --git a/content/browser/devtools/protocol/input_handler.cc b/content/browser/devtools/protocol/input_handler.cc
index 689dc38..3556f33c 100644
--- a/content/browser/devtools/protocol/input_handler.cc
+++ b/content/browser/devtools/protocol/input_handler.cc
@@ -10,7 +10,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
-#include "components/viz/common/quads/compositor_frame_metadata.h"
 #include "content/browser/devtools/devtools_session.h"
 #include "content/browser/devtools/protocol/native_input_event_builder.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
@@ -441,9 +440,8 @@
   Input::Dispatcher::wire(dispatcher, this);
 }
 
-void InputHandler::OnSwapCompositorFrame(
-    const viz::CompositorFrameMetadata& frame_metadata) {
-  page_scale_factor_ = frame_metadata.page_scale_factor;
+void InputHandler::OnPageScaleFactorChanged(float page_scale_factor) {
+  page_scale_factor_ = page_scale_factor;
 }
 
 Response InputHandler::Disable() {
diff --git a/content/browser/devtools/protocol/input_handler.h b/content/browser/devtools/protocol/input_handler.h
index 9dce4844..2fd710cc 100644
--- a/content/browser/devtools/protocol/input_handler.h
+++ b/content/browser/devtools/protocol/input_handler.h
@@ -18,10 +18,6 @@
 #include "content/public/browser/render_widget_host.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
 
-namespace viz {
-class CompositorFrameMetadata;
-}
-
 namespace content {
 class DevToolsAgentHostImpl;
 class RenderFrameHostImpl;
@@ -40,8 +36,7 @@
   void SetRenderer(int process_host_id,
                    RenderFrameHostImpl* frame_host) override;
 
-  void OnSwapCompositorFrame(
-      const viz::CompositorFrameMetadata& frame_metadata);
+  void OnPageScaleFactorChanged(float page_scale_factor);
   Response Disable() override;
 
   void DispatchKeyEvent(
diff --git a/content/browser/devtools/protocol/tracing_handler.cc b/content/browser/devtools/protocol/tracing_handler.cc
index 3623748..96336dc8 100644
--- a/content/browser/devtools/protocol/tracing_handler.cc
+++ b/content/browser/devtools/protocol/tracing_handler.cc
@@ -490,8 +490,7 @@
 }
 
 bool TracingHandler::IsStartupTracingActive() {
-  return ::tracing::TraceConfigFile::GetInstance()->IsEnabled() &&
-      TracingController::GetInstance()->IsTracing();
+  return ::tracing::TraceConfigFile::GetInstance()->IsEnabled();
 }
 
 // static
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index ff235b6d..0356a679f 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -701,8 +701,6 @@
           ->last_frame_metadata();
   for (auto* page : protocol::PageHandler::ForAgentHost(this))
     page->OnSwapCompositorFrame(metadata.Clone());
-  for (auto* input : protocol::InputHandler::ForAgentHost(this))
-    input->OnSwapCompositorFrame(metadata);
 
   if (!frame_trace_recorder_)
     return;
@@ -713,6 +711,12 @@
     frame_trace_recorder_->OnSwapCompositorFrame(frame_host_, metadata);
 }
 
+void RenderFrameDevToolsAgentHost::OnPageScaleFactorChanged(
+    float page_scale_factor) {
+  for (auto* input : protocol::InputHandler::ForAgentHost(this))
+    input->OnPageScaleFactorChanged(page_scale_factor);
+}
+
 void RenderFrameDevToolsAgentHost::DisconnectWebContents() {
   navigation_handles_.clear();
   SetFrameTreeNode(nullptr);
@@ -861,8 +865,6 @@
     viz::CompositorFrameMetadata frame_metadata) {
   for (auto* page : protocol::PageHandler::ForAgentHost(this))
     page->OnSynchronousSwapCompositorFrame(frame_metadata.Clone());
-  for (auto* input : protocol::InputHandler::ForAgentHost(this))
-    input->OnSwapCompositorFrame(frame_metadata);
 
   if (!frame_trace_recorder_)
     return;
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h
index 2ddcb13..5ecb275 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.h
+++ b/content/browser/devtools/render_frame_devtools_agent_host.h
@@ -139,6 +139,7 @@
   void DidDetachInterstitialPage() override;
   void OnVisibilityChanged(content::Visibility visibility) override;
   void DidReceiveCompositorFrame() override;
+  void OnPageScaleFactorChanged(float page_scale_factor) override;
 
   bool IsChildFrame();
   bool IsFrameHostAllowedForRestrictedSessions();
diff --git a/content/browser/download/download_item_impl.cc b/content/browser/download/download_item_impl.cc
index 244592b..f4df726 100644
--- a/content/browser/download/download_item_impl.cc
+++ b/content/browser/download/download_item_impl.cc
@@ -54,15 +54,9 @@
 #include "content/browser/download/download_request_handle.h"
 #include "content/browser/download/download_utils.h"
 #include "content/browser/download/parallel_download_utils.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/storage_partition_impl.h"
-#include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/download_item_utils.h"
-#include "content/public/common/content_features.h"
-#include "content/public/common/referrer.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
@@ -84,7 +78,6 @@
     base::WeakPtr<DownloadItemImpl> item,
     const base::Callback<void(bool)>& callback,
     bool success) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (success && item.get())
     item->OnDownloadedFileRemoved();
   callback.Run(success);
@@ -429,7 +422,7 @@
 }
 
 DownloadItemImpl::~DownloadItemImpl() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Should always have been nuked before now, at worst in
   // DownloadManager shutdown.
@@ -443,19 +436,19 @@
 }
 
 void DownloadItemImpl::AddObserver(Observer* observer) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   observers_.AddObserver(observer);
 }
 
 void DownloadItemImpl::RemoveObserver(Observer* observer) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   observers_.RemoveObserver(observer);
 }
 
 void DownloadItemImpl::UpdateObservers() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DVLOG(20) << __func__ << "()";
 
   // Nested updates should not be allowed.
@@ -468,7 +461,7 @@
 }
 
 void DownloadItemImpl::ValidateDangerousDownload() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!IsDone());
   DCHECK(IsDangerous());
 
@@ -496,7 +489,7 @@
     bool delete_file_afterward,
     const AcquireFileCallback& callback) {
   DVLOG(20) << __func__ << "() download = " << DebugString(true);
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(IsDangerous());
   DCHECK(AllDataSaved());
 
@@ -522,7 +515,7 @@
 }
 
 void DownloadItemImpl::Pause() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Ignore irrelevant states.
   if (IsPaused())
@@ -557,7 +550,7 @@
 }
 
 void DownloadItemImpl::Resume() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DVLOG(20) << __func__ << "() download = " << DebugString(true);
   switch (state_) {
     case CANCELLED_INTERNAL:  // Nothing to resume.
@@ -590,7 +583,7 @@
 }
 
 void DownloadItemImpl::Cancel(bool user_cancel) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DVLOG(20) << __func__ << "() download = " << DebugString(true);
   InterruptAndDiscardPartialState(
       user_cancel ? download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
@@ -600,7 +593,7 @@
 
 void DownloadItemImpl::Remove() {
   DVLOG(20) << __func__ << "() download = " << DebugString(true);
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   download::RecordDownloadDeletion(GetEndTime(), GetMimeType());
 
   delegate_->AssertStateConsistent(this);
@@ -615,7 +608,7 @@
 }
 
 void DownloadItemImpl::OpenDownload() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   if (!IsDone()) {
     // We don't honor the open_when_complete_ flag for temporary
@@ -642,7 +635,7 @@
 }
 
 void DownloadItemImpl::ShowDownloadInShell() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   delegate_->ShowDownloadInShell(this);
 }
@@ -672,7 +665,7 @@
 }
 
 bool DownloadItemImpl::CanResume() const {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   switch (state_) {
     case INITIAL_INTERNAL:
     case COMPLETING_INTERNAL:
@@ -840,19 +833,19 @@
 }
 
 void DownloadItemImpl::DeleteFile(const base::Callback<void(bool)>& callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (GetState() != download::DownloadItem::COMPLETE) {
     // Pass a null WeakPtr so it doesn't call OnDownloadedFileRemoved.
-    BrowserThread::PostTask(
-        BrowserThread::UI, FROM_HERE,
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
         base::BindOnce(&DeleteDownloadedFileDone,
                        base::WeakPtr<DownloadItemImpl>(), callback, false));
     return;
   }
   if (GetFullPath().empty() || file_externally_removed_) {
     // Pass a null WeakPtr so it doesn't call OnDownloadedFileRemoved.
-    BrowserThread::PostTask(
-        BrowserThread::UI, FROM_HERE,
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
         base::BindOnce(&DeleteDownloadedFileDone,
                        base::WeakPtr<DownloadItemImpl>(), callback, true));
     return;
@@ -977,7 +970,7 @@
 void DownloadItemImpl::OnContentCheckCompleted(
     download::DownloadDangerType danger_type,
     download::DownloadInterruptReason reason) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(AllDataSaved());
 
   // Danger type is only allowed to be set on an active download after all data
@@ -1073,14 +1066,14 @@
 
 void DownloadItemImpl::SimulateErrorForTesting(
     download::DownloadInterruptReason reason) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   InterruptWithPartialState(GetReceivedBytes(), nullptr, reason);
   UpdateObservers();
 }
 
 download::ResumeMode DownloadItemImpl::GetResumeMode() const {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Only support resumption for HTTP(S).
   if (!GetURL().SchemeIsHTTPOrHTTPS())
@@ -1179,17 +1172,9 @@
   return delegate_->GetBrowserContext();
 }
 
-WebContents* DownloadItemImpl::GetWebContents() const {
-  // TODO(rdsmith): Remove null check after removing GetWebContents() from
-  // paths that might be used by download::DownloadItems created from history
-  // import. Currently such items have null request_handle_s, where other items
-  // (regular and SavePackage downloads) have actual objects off the pointer.
-  return DownloadItemUtils::GetWebContents(this);
-}
-
 void DownloadItemImpl::UpdateValidatorsOnResumption(
     const download::DownloadCreateInfo& new_create_info) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK_EQ(RESUMING_INTERNAL, state_);
   DCHECK(!new_create_info.url_chain.empty());
 
@@ -1263,7 +1248,7 @@
 void DownloadItemImpl::OnAllDataSaved(
     int64_t total_bytes,
     std::unique_ptr<crypto::SecureHash> hash_state) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!AllDataSaved());
   destination_info_.all_data_saved = true;
   SetTotalBytes(total_bytes);
@@ -1292,7 +1277,7 @@
 }
 
 void DownloadItemImpl::MarkAsComplete() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   DCHECK(AllDataSaved());
   destination_info_.end_time = base::Time::Now();
@@ -1304,7 +1289,7 @@
     int64_t bytes_so_far,
     int64_t bytes_per_sec,
     const std::vector<download::DownloadItem::ReceivedSlice>& received_slices) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // If the download is in any other state we don't expect any
   // DownloadDestinationObserver callbacks. An interruption or a cancellation
   // results in a call to ReleaseDownloadFile which invalidates the weak
@@ -1335,7 +1320,7 @@
     download::DownloadInterruptReason reason,
     int64_t bytes_so_far,
     std::unique_ptr<crypto::SecureHash> secure_hash) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // If the download is in any other state we don't expect any
   // DownloadDestinationObserver callbacks. An interruption or a cancellation
   // results in a call to ReleaseDownloadFile which invalidates the weak
@@ -1353,7 +1338,7 @@
 void DownloadItemImpl::DestinationCompleted(
     int64_t total_bytes,
     std::unique_ptr<crypto::SecureHash> secure_hash) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // If the download is in any other state we don't expect any
   // DownloadDestinationObserver callbacks. An interruption or a cancellation
   // results in a call to ReleaseDownloadFile which invalidates the weak
@@ -1372,7 +1357,7 @@
 void DownloadItemImpl::Init(
     bool active,
     download::DownloadItem::DownloadType download_type) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   std::string file_name;
   if (download_type == TYPE_HISTORY_IMPORT) {
@@ -1419,7 +1404,7 @@
     std::unique_ptr<download::DownloadRequestHandleInterface> req_handle,
     const download::DownloadCreateInfo& new_create_info,
     scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!download_file_.get());
   DVLOG(20) << __func__ << "() this=" << DebugString(true);
   download::RecordDownloadCountWithSource(download::START_COUNT,
@@ -1530,7 +1515,7 @@
 void DownloadItemImpl::OnDownloadFileInitialized(
     download::DownloadInterruptReason result,
     int64_t bytes_wasted) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(state_ == TARGET_PENDING_INTERNAL ||
          state_ == INTERRUPTED_TARGET_PENDING_INTERNAL)
       << "Unexpected state: " << DebugDownloadStateString(state_);
@@ -1576,7 +1561,7 @@
 }
 
 void DownloadItemImpl::DetermineDownloadTarget() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DVLOG(20) << __func__ << "() " << DebugString(true);
 
   download::RecordDownloadCountWithSource(
@@ -1593,7 +1578,7 @@
     download::DownloadDangerType danger_type,
     const base::FilePath& intermediate_path,
     download::DownloadInterruptReason interrupt_reason) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (state_ == CANCELLED_INTERNAL)
     return;
 
@@ -1678,7 +1663,7 @@
 void DownloadItemImpl::OnDownloadRenamedToIntermediateName(
     download::DownloadInterruptReason reason,
     const base::FilePath& full_path) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(state_ == TARGET_PENDING_INTERNAL ||
          state_ == INTERRUPTED_TARGET_PENDING_INTERNAL);
   DCHECK(download_file_);
@@ -1696,7 +1681,7 @@
 }
 
 void DownloadItemImpl::OnTargetResolved() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DVLOG(20) << __func__ << "() download=" << DebugString(true);
   DCHECK(
       (state_ == TARGET_PENDING_INTERNAL &&
@@ -1746,7 +1731,7 @@
 // SavePackage completes downloads. SavePackage always uses its own Finish() to
 // mark downloads complete.
 void DownloadItemImpl::MaybeCompleteDownload() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!IsSavePackageDownload());
 
   if (!IsDownloadReadyForCompletion(
@@ -1765,7 +1750,7 @@
 // Called by MaybeCompleteDownload() when it has determined that the download
 // is ready for completion.
 void DownloadItemImpl::OnDownloadCompleting() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   if (state_ != IN_PROGRESS_INTERNAL)
     return;
@@ -1792,7 +1777,7 @@
 void DownloadItemImpl::OnDownloadRenamedToFinalName(
     download::DownloadInterruptReason reason,
     const base::FilePath& full_path) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!IsSavePackageDownload());
 
   // If a cancel or interrupt hit, we'll cancel the DownloadFile, which
@@ -1842,14 +1827,14 @@
 }
 
 void DownloadItemImpl::DelayedDownloadOpened(bool auto_opened) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   auto_opened_ = auto_opened;
   Completed();
 }
 
 void DownloadItemImpl::Completed() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   DVLOG(20) << __func__ << "() " << DebugString(false);
 
@@ -1921,7 +1906,7 @@
     int64_t bytes_so_far,
     std::unique_ptr<crypto::SecureHash> hash_state,
     download::DownloadInterruptReason reason) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK_NE(download::DOWNLOAD_INTERRUPT_REASON_NONE, reason);
   DVLOG(20) << __func__
             << "() reason:" << DownloadInterruptReasonToString(reason)
@@ -2074,15 +2059,12 @@
     received_bytes_at_length_mismatch_ = GetReceivedBytes();
   }
 
-  if (!GetWebContents())
-    download::RecordDownloadCountWithSource(
-        download::INTERRUPTED_WITHOUT_WEBCONTENTS, download_source_);
-
   // TODO(asanka): This is not good. We can transition to interrupted from
   // target-pending, which is something we don't want to do. Perhaps we should
   // explicitly transition to target-resolved prior to switching to interrupted.
   DCHECK_EQ(last_reason_, reason);
   TransitionTo(INTERRUPTED_INTERNAL);
+  delegate_->DownloadInterrupted(this);
   AutoResumeIfValid();
 }
 
@@ -2113,7 +2095,7 @@
 }
 
 void DownloadItemImpl::ReleaseDownloadFile(bool destroy_file) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DVLOG(20) << __func__ << "() destroy_file:" << destroy_file;
 
   if (destroy_file) {
@@ -2171,7 +2153,7 @@
 }
 
 void DownloadItemImpl::TransitionTo(DownloadInternalState new_state) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   if (state_ == new_state)
     return;
@@ -2310,7 +2292,7 @@
 }
 
 void DownloadItemImpl::SetFullPath(const base::FilePath& new_path) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DVLOG(20) << __func__ << "() new_path = \"" << new_path.value() << "\" "
             << DebugString(true);
   DCHECK(!new_path.empty());
@@ -2325,7 +2307,7 @@
 
 void DownloadItemImpl::AutoResumeIfValid() {
   DVLOG(20) << __func__ << "() " << DebugString(true);
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   download::ResumeMode mode = GetResumeMode();
 
   if (mode != download::ResumeMode::IMMEDIATE_RESTART &&
@@ -2340,7 +2322,7 @@
 
 void DownloadItemImpl::ResumeInterruptedDownload(
     ResumptionRequestSource source) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // If we're not interrupted, ignore the request; our caller is drunk.
   if (state_ != INTERRUPTED_INTERNAL)
     return;
diff --git a/content/browser/download/download_item_impl.h b/content/browser/download/download_item_impl.h
index 6c5a8ef..f3b8171 100644
--- a/content/browser/download/download_item_impl.h
+++ b/content/browser/download/download_item_impl.h
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "base/threading/thread_checker_impl.h"
 #include "base/time/time.h"
 #include "components/download/public/common/download_create_info.h"
 #include "components/download/public/common/download_destination_observer.h"
@@ -36,7 +37,6 @@
 namespace content {
 class BrowserContext;
 class DownloadItemImplDelegate;
-class WebContents;
 
 // See download_item.h for usage.
 class CONTENT_EXPORT DownloadItemImpl
@@ -614,8 +614,7 @@
   // INTERRUPTED state.
   download::ResumeMode GetResumeMode() const;
 
-  // Helper method to get WebContents and BrowserContext of the DownloadItem.
-  WebContents* GetWebContents() const;
+  // Helper method to get BrowserContext of the DownloadItem.;
   BrowserContext* GetBrowserContext() const;
 
   static DownloadState InternalToExternalState(
@@ -777,6 +776,8 @@
   // Source of the download, used in metrics.
   download::DownloadSource download_source_ = download::DownloadSource::UNKNOWN;
 
+  THREAD_CHECKER(thread_checker_);
+
   base::WeakPtrFactory<DownloadItemImpl> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(DownloadItemImpl);
diff --git a/content/browser/download/download_item_impl_delegate.cc b/content/browser/download/download_item_impl_delegate.cc
index cb30d4c2..d5201b19 100644
--- a/content/browser/download/download_item_impl_delegate.cc
+++ b/content/browser/download/download_item_impl_delegate.cc
@@ -88,4 +88,7 @@
 void DownloadItemImplDelegate::AssertStateConsistent(
     DownloadItemImpl* download) const {}
 
+void DownloadItemImplDelegate::DownloadInterrupted(DownloadItemImpl* download) {
+}
+
 }  // namespace content
diff --git a/content/browser/download/download_item_impl_delegate.h b/content/browser/download/download_item_impl_delegate.h
index ddc8b25..a2217f7f 100644
--- a/content/browser/download/download_item_impl_delegate.h
+++ b/content/browser/download/download_item_impl_delegate.h
@@ -100,6 +100,9 @@
   // Assert consistent state for delgate object at various transitions.
   virtual void AssertStateConsistent(DownloadItemImpl* download) const;
 
+  // Called when the download is interrupted.
+  virtual void DownloadInterrupted(DownloadItemImpl* download);
+
  private:
   // For "Outlives attached DownloadItemImpl" invariant assertion.
   int count_;
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index 9f2290e..9a00e9a 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -845,6 +845,14 @@
   downloads_.erase(download->GetId());
 }
 
+void DownloadManagerImpl::DownloadInterrupted(DownloadItemImpl* download) {
+  WebContents* web_contents = DownloadItemUtils::GetWebContents(download);
+  if (!web_contents) {
+    download::RecordDownloadCountWithSource(
+        download::INTERRUPTED_WITHOUT_WEBCONTENTS, download->download_source());
+  }
+}
+
 void DownloadManagerImpl::OnUrlDownloadHandlerCreated(
     download::UrlDownloadHandler::UniqueUrlDownloadHandlerPtr downloader) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/content/browser/download/download_manager_impl.h b/content/browser/download/download_manager_impl.h
index 9a4361b..6c29fea 100644
--- a/content/browser/download/download_manager_impl.h
+++ b/content/browser/download/download_manager_impl.h
@@ -234,6 +234,7 @@
   bool IsMostRecentDownloadItemAtFilePath(DownloadItemImpl* download) override;
   void ShowDownloadInShell(DownloadItemImpl* download) override;
   void DownloadRemoved(DownloadItemImpl* download) override;
+  void DownloadInterrupted(DownloadItemImpl* download) override;
 
   // Helper method to start or resume a download.
   void BeginDownloadInternal(
diff --git a/content/browser/frame_host/cross_process_frame_connector.cc b/content/browser/frame_host/cross_process_frame_connector.cc
index 0428388d..8f24d179 100644
--- a/content/browser/frame_host/cross_process_frame_connector.cc
+++ b/content/browser/frame_host/cross_process_frame_connector.cc
@@ -211,13 +211,14 @@
 
   auto* event_router = parent_view->host()->delegate()->GetInputEventRouter();
 
-  gfx::Vector2d offset_from_parent =
-      screen_space_rect_in_dip_.OffsetFromOrigin();
+  // We will only convert the coordinates back to the root here. The
+  // RenderWidgetHostInputEventRouter will determine which ancestor view will
+  // receive a resent gesture event, so it will be responsible for converting to
+  // the coordinates of the target view.
   blink::WebGestureEvent resent_gesture_event(event);
-  // TODO(kenrb, wjmaclean): Do we need to account for transforms here?
-  // See https://crbug.com/626020.
-  resent_gesture_event.SetPositionInWidget(
-      resent_gesture_event.PositionInWidget() + offset_from_parent);
+  const gfx::PointF root_point =
+      view_->TransformPointToRootCoordSpaceF(event.PositionInWidget());
+  resent_gesture_event.SetPositionInWidget(root_point);
 
   if (view_->wheel_scroll_latching_enabled()) {
     if (event.GetType() == blink::WebInputEvent::kGestureScrollBegin) {
diff --git a/content/browser/frame_host/navigation_entry_screenshot_manager.cc b/content/browser/frame_host/navigation_entry_screenshot_manager.cc
index 84ba9ad..0e40ca12 100644
--- a/content/browser/frame_host/navigation_entry_screenshot_manager.cc
+++ b/content/browser/frame_host/navigation_entry_screenshot_manager.cc
@@ -7,6 +7,7 @@
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted_memory.h"
 #include "base/task_scheduler/post_task.h"
 #include "content/browser/frame_host/navigation_controller_impl.h"
 #include "content/browser/frame_host/navigation_entry_impl.h"
@@ -72,7 +73,7 @@
     // Encode the A8 bitmap to grayscale PNG treating alpha as color intensity.
     std::vector<unsigned char> data;
     if (gfx::PNGCodec::EncodeA8SkBitmap(grayscale_bitmap, &data))
-      data_ = new base::RefCountedBytes(data);
+      data_ = base::RefCountedBytes::TakeVector(&data);
   }
 
   scoped_refptr<base::RefCountedBytes> data_;
diff --git a/content/browser/gpu/gpu_ipc_browsertests.cc b/content/browser/gpu/gpu_ipc_browsertests.cc
index e1ec69f..e3717ed3 100644
--- a/content/browser/gpu/gpu_ipc_browsertests.cc
+++ b/content/browser/gpu/gpu_ipc_browsertests.cc
@@ -15,6 +15,7 @@
 #include "content/public/browser/gpu_utils.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/content_browser_test.h"
+#include "content/test/gpu_browsertest_helpers.h"
 #include "gpu/ipc/client/command_buffer_proxy_impl.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "services/ui/public/cpp/gpu/context_provider_command_buffer.h"
@@ -27,50 +28,6 @@
 
 namespace {
 
-scoped_refptr<ui::ContextProviderCommandBuffer> CreateContext(
-    scoped_refptr<gpu::GpuChannelHost> gpu_channel_host) {
-  gpu::GpuChannelEstablishFactory* factory =
-      content::BrowserMainLoop::GetInstance()->gpu_channel_establish_factory();
-  // This is for an offscreen context, so the default framebuffer doesn't need
-  // any alpha, depth, stencil, antialiasing.
-  gpu::ContextCreationAttribs attributes;
-  attributes.alpha_size = -1;
-  attributes.depth_size = 0;
-  attributes.stencil_size = 0;
-  attributes.samples = 0;
-  attributes.sample_buffers = 0;
-  attributes.bind_generates_resource = false;
-  constexpr bool automatic_flushes = false;
-  constexpr bool support_locking = false;
-  return base::MakeRefCounted<ui::ContextProviderCommandBuffer>(
-      std::move(gpu_channel_host), factory->GetGpuMemoryBufferManager(),
-      content::kGpuStreamIdDefault, content::kGpuStreamPriorityDefault,
-      gpu::kNullSurfaceHandle, GURL(), automatic_flushes, support_locking,
-      gpu::SharedMemoryLimits(), attributes, nullptr,
-      ui::command_buffer_metrics::OFFSCREEN_CONTEXT_FOR_TESTING);
-}
-
-void OnEstablishedGpuChannel(
-    const base::Closure& quit_closure,
-    scoped_refptr<gpu::GpuChannelHost>* retvalue,
-    scoped_refptr<gpu::GpuChannelHost> established_host) {
-  if (retvalue)
-    *retvalue = std::move(established_host);
-  quit_closure.Run();
-}
-
-scoped_refptr<gpu::GpuChannelHost> EstablishGpuChannelSyncRunLoop() {
-  gpu::GpuChannelEstablishFactory* factory =
-      content::BrowserMainLoop::GetInstance()->gpu_channel_establish_factory();
-  CHECK(factory);
-  base::RunLoop run_loop;
-  scoped_refptr<gpu::GpuChannelHost> gpu_channel_host;
-  factory->EstablishGpuChannel(base::BindOnce(
-      &OnEstablishedGpuChannel, run_loop.QuitClosure(), &gpu_channel_host));
-  run_loop.Run();
-  return gpu_channel_host;
-}
-
 // RunLoop implementation that runs until it observes OnContextLost().
 class ContextLostRunLoop : public viz::ContextLostObserver {
  public:
@@ -101,10 +58,11 @@
       return;
 
     scoped_refptr<gpu::GpuChannelHost> gpu_channel_host =
-        EstablishGpuChannelSyncRunLoop();
+        content::GpuBrowsertestEstablishGpuChannelSyncRunLoop();
     CHECK(gpu_channel_host);
 
-    provider_ = CreateContext(std::move(gpu_channel_host));
+    provider_ =
+        content::GpuBrowsertestCreateContext(std::move(gpu_channel_host));
     auto result = provider_->BindToCurrentThread();
     CHECK_EQ(result, gpu::ContextResult::kSuccess);
     gl_ = provider_->ContextGL();
@@ -180,7 +138,7 @@
   }
 
   void EstablishAndWait() {
-    gpu_channel_host_ = EstablishGpuChannelSyncRunLoop();
+    gpu_channel_host_ = content::GpuBrowsertestEstablishGpuChannelSyncRunLoop();
   }
 
   gpu::GpuChannelHost* GetGpuChannel() { return gpu_channel_host_.get(); }
@@ -275,7 +233,7 @@
   // Step 2: verify that holding onto the provider's GrContext will
   // retain the host after provider is destroyed.
   scoped_refptr<ui::ContextProviderCommandBuffer> provider =
-      CreateContext(GetGpuChannel());
+      content::GpuBrowsertestCreateContext(GetGpuChannel());
   ASSERT_EQ(provider->BindToCurrentThread(), gpu::ContextResult::kSuccess);
 
   sk_sp<GrContext> gr_context = sk_ref_sp(provider->GrContext());
@@ -322,7 +280,7 @@
   scoped_refptr<gpu::GpuChannelHost> host = GetGpuChannel();
 
   scoped_refptr<ui::ContextProviderCommandBuffer> provider =
-      CreateContext(GetGpuChannel());
+      content::GpuBrowsertestCreateContext(GetGpuChannel());
   ContextLostRunLoop run_loop(provider.get());
   ASSERT_EQ(provider->BindToCurrentThread(), gpu::ContextResult::kSuccess);
   GpuProcessHost::CallOnIO(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 9df5bca7..0126ec1b 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -142,7 +142,6 @@
     switches::kEnableAcceleratedVpxDecode,
 #endif
     switches::kEnableGpuRasterization,
-    switches::kEnableHeapProfiling,
     switches::kEnableLogging,
     switches::kEnableOOPRasterization,
     switches::kEnableVizDevTools,
@@ -153,8 +152,6 @@
     switches::kNoSandbox,
     switches::kRunAllCompositorStagesBeforeDraw,
     switches::kTestGLLib,
-    switches::kTraceConfigFile,
-    switches::kTraceStartup,
     switches::kTraceToConsole,
     switches::kUseFakeJpegDecodeAccelerator,
     switches::kUseGpuInTests,
@@ -1224,6 +1221,7 @@
   cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kGpuProcess);
 
   BrowserChildProcessHostImpl::CopyFeatureAndFieldTrialFlags(cmd_line.get());
+  BrowserChildProcessHostImpl::CopyTraceStartupFlags(cmd_line.get());
 
 #if defined(OS_WIN)
   cmd_line->AppendArg(switches::kPrefetchArgumentGpu);
diff --git a/content/browser/indexed_db/indexed_db_tombstone_sweeper_unittest.cc b/content/browser/indexed_db/indexed_db_tombstone_sweeper_unittest.cc
index 86be8f46..709f18bf 100644
--- a/content/browser/indexed_db/indexed_db_tombstone_sweeper_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_tombstone_sweeper_unittest.cc
@@ -66,7 +66,7 @@
   MockTickClock() {}
   ~MockTickClock() override {}
 
-  MOCK_METHOD0(NowTicks, base::TimeTicks());
+  MOCK_CONST_METHOD0(NowTicks, base::TimeTicks());
 };
 
 class Comparator : public LevelDBComparator {
diff --git a/content/browser/loader/navigation_url_loader_network_service.cc b/content/browser/loader/navigation_url_loader_network_service.cc
index 70ab9b20..2a771ad 100644
--- a/content/browser/loader/navigation_url_loader_network_service.cc
+++ b/content/browser/loader/navigation_url_loader_network_service.cc
@@ -347,7 +347,7 @@
     default_loader_used_ = true;
     if (base::FeatureList::IsEnabled(features::kSignedHTTPExchange)) {
       DCHECK(!default_url_loader_factory_getter_);
-      // It is safa to pass the callback of CreateURLLoaderThrottles with the
+      // It is safe to pass the callback of CreateURLLoaderThrottles with the
       // unretained |this|, because the passed callback will be used by a
       // SignedExchangeHandler which is indirectly owned by |this| until its
       // header is verified and parsed, that's where the getter is used.
@@ -493,7 +493,7 @@
     }
 
     if (base::FeatureList::IsEnabled(features::kSignedHTTPExchange)) {
-      // It is safa to pass the callback of CreateURLLoaderThrottles with the
+      // It is safe to pass the callback of CreateURLLoaderThrottles with the
       // unretained |this|, because the passed callback will be used by a
       // SignedExchangeHandler which is indirectly owned by |this| until its
       // header is verified and parsed, that's where the getter is used.
diff --git a/content/browser/manifest/manifest_icon_downloader.cc b/content/browser/manifest/manifest_icon_downloader.cc
index 1747af9..903eca6 100644
--- a/content/browser/manifest/manifest_icon_downloader.cc
+++ b/content/browser/manifest/manifest_icon_downloader.cc
@@ -59,10 +59,10 @@
       false,  // is_favicon
       0,      // max_bitmap_size - 0 means no maximum size.
       false,  // bypass_cache
-      base::Bind(&ManifestIconDownloader::OnIconFetched, ideal_icon_size_in_px,
-                 minimum_icon_size_in_px,
-                 base::Owned(new DevToolsConsoleHelper(web_contents)),
-                 callback));
+      base::BindOnce(&ManifestIconDownloader::OnIconFetched,
+                     ideal_icon_size_in_px, minimum_icon_size_in_px,
+                     base::Owned(new DevToolsConsoleHelper(web_contents)),
+                     callback));
   return true;
 }
 
diff --git a/content/browser/manifest/manifest_manager_host.cc b/content/browser/manifest/manifest_manager_host.cc
index c74ac86..818db2d 100644
--- a/content/browser/manifest/manifest_manager_host.cc
+++ b/content/browser/manifest/manifest_manager_host.cc
@@ -30,10 +30,10 @@
     OnConnectionError();
 }
 
-void ManifestManagerHost::GetManifest(const GetManifestCallback& callback) {
+void ManifestManagerHost::GetManifest(GetManifestCallback callback) {
   auto& manifest_manager = GetManifestManager();
-  int request_id =
-      callbacks_.Add(std::make_unique<GetManifestCallback>(callback));
+  int request_id = callbacks_.Add(
+      std::make_unique<GetManifestCallback>(std::move(callback)));
   manifest_manager.RequestManifest(
       base::BindOnce(&ManifestManagerHost::OnRequestManifestResponse,
                      base::Unretained(this), request_id));
@@ -67,7 +67,7 @@
   }
   callbacks_.Clear();
   for (auto& callback : callbacks)
-    callback.Run(GURL(), Manifest());
+    std::move(callback).Run(GURL(), Manifest());
 }
 
 void ManifestManagerHost::OnRequestManifestResponse(int request_id,
diff --git a/content/browser/manifest/manifest_manager_host.h b/content/browser/manifest/manifest_manager_host.h
index 0898480..515f1e9e 100644
--- a/content/browser/manifest/manifest_manager_host.h
+++ b/content/browser/manifest/manifest_manager_host.h
@@ -30,12 +30,12 @@
   ~ManifestManagerHost() override;
 
   using GetManifestCallback =
-      base::Callback<void(const GURL&, const Manifest&)>;
+      base::OnceCallback<void(const GURL&, const Manifest&)>;
 
   // Calls the given callback with the manifest associated with the main frame.
   // If the main frame has no manifest or if getting it failed the callback will
   // have an empty manifest.
-  void GetManifest(const GetManifestCallback& callback);
+  void GetManifest(GetManifestCallback callback);
 
   void RequestManifestDebugInfo(
       blink::mojom::ManifestManager::RequestManifestDebugInfoCallback callback);
diff --git a/content/browser/media/capture/desktop_capture_device.cc b/content/browser/media/capture/desktop_capture_device.cc
index ad11272..25f88391 100644
--- a/content/browser/media/capture/desktop_capture_device.cc
+++ b/content/browser/media/capture/desktop_capture_device.cc
@@ -499,8 +499,7 @@
           webrtc::DesktopCapturer::CreateScreenCapturer(options));
       if (screen_capturer && screen_capturer->SelectSource(source.id)) {
         capturer.reset(new webrtc::DesktopAndCursorComposer(
-            screen_capturer.release(),
-            webrtc::MouseCursorMonitor::CreateForScreen(options, source.id)));
+            std::move(screen_capturer), options));
         IncrementDesktopCaptureCounter(SCREEN_CAPTURER_CREATED);
         IncrementDesktopCaptureCounter(
             source.audio_share ? SCREEN_CAPTURER_CREATED_WITH_AUDIO
@@ -515,8 +514,7 @@
       if (window_capturer && window_capturer->SelectSource(source.id)) {
         window_capturer->FocusOnSelectedSource();
         capturer.reset(new webrtc::DesktopAndCursorComposer(
-            window_capturer.release(),
-            webrtc::MouseCursorMonitor::CreateForWindow(options, source.id)));
+            std::move(window_capturer), options));
         IncrementDesktopCaptureCounter(WINDOW_CAPTURER_CREATED);
       }
       break;
diff --git a/content/browser/media/capture/frame_sink_video_capture_device.cc b/content/browser/media/capture/frame_sink_video_capture_device.cc
index d466626..c794b29 100644
--- a/content/browser/media/capture/frame_sink_video_capture_device.cc
+++ b/content/browser/media/capture/frame_sink_video_capture_device.cc
@@ -297,8 +297,7 @@
 }
 
 void FrameSinkVideoCaptureDevice::OnTargetChanged(
-    const viz::FrameSinkId& frame_sink_id,
-    gfx::NativeView native_view) {
+    const viz::FrameSinkId& frame_sink_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   target_ = frame_sink_id;
@@ -308,20 +307,12 @@
   if (capturer_ && frame_sink_id.is_valid()) {
     capturer_->ChangeTarget(frame_sink_id);
   }
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::BindOnce(&CursorRenderer::SetTargetView,
-                     cursor_renderer_->GetWeakPtr(), native_view));
 }
 
 void FrameSinkVideoCaptureDevice::OnTargetPermanentlyLost() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   target_ = viz::FrameSinkId();
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::BindOnce(&CursorRenderer::SetTargetView,
-                     cursor_renderer_->GetWeakPtr(), gfx::NativeView()));
 
   OnFatalError("Capture target has been permanently lost.");
 }
diff --git a/content/browser/media/capture/frame_sink_video_capture_device.h b/content/browser/media/capture/frame_sink_video_capture_device.h
index 10cb288..bca2bf2 100644
--- a/content/browser/media/capture/frame_sink_video_capture_device.h
+++ b/content/browser/media/capture/frame_sink_video_capture_device.h
@@ -22,7 +22,6 @@
 #include "media/capture/video_capture_types.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/viz/privileged/interfaces/compositing/frame_sink_video_capture.mojom.h"
-#include "ui/gfx/native_widget_types.h"
 
 namespace content {
 
@@ -84,17 +83,16 @@
   void OnStopped() final;
 
   // These are called to notify when the capture target has changed or was
-  // permanently lost. |frame_sink_id| specifies a target compositor frame sink,
-  // while |native_view| is the local view to monitor for the puproses of mouse
-  // cursor rendering.
-  void OnTargetChanged(const viz::FrameSinkId& frame_sink_id,
-                       gfx::NativeView native_view);
+  // permanently lost.
+  void OnTargetChanged(const viz::FrameSinkId& frame_sink_id);
   void OnTargetPermanentlyLost();
 
   // Overrides the callback that is run to create the capturer.
   void SetCapturerCreatorForTesting(CapturerCreatorCallback creator);
 
  protected:
+  CursorRenderer* cursor_renderer() const { return cursor_renderer_.get(); }
+
   // Subclasses override these to perform additional start/stop tasks.
   virtual void WillStart();
   virtual void DidStop();
diff --git a/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc b/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc
index f5b4b8f..70b26b5f 100644
--- a/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc
+++ b/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc
@@ -302,7 +302,7 @@
     EXPECT_CALL(capturer_, MockStart(NotNull()));
 
     EXPECT_FALSE(capturer_.is_bound());
-    POST_DEVICE_METHOD_CALL(OnTargetChanged, frame_sink_id, gfx::NativeView());
+    POST_DEVICE_METHOD_CALL(OnTargetChanged, frame_sink_id);
     POST_DEVICE_METHOD_CALL(AllocateAndStartWithReceiver, GetCaptureParams(),
                             std::move(receiver));
     WAIT_FOR_DEVICE_TASKS();
diff --git a/content/browser/media/capture/web_contents_video_capture_device.cc b/content/browser/media/capture/web_contents_video_capture_device.cc
index 500ecc6..c440442 100644
--- a/content/browser/media/capture/web_contents_video_capture_device.cc
+++ b/content/browser/media/capture/web_contents_video_capture_device.cc
@@ -31,11 +31,14 @@
           WebContentsVideoCaptureDevice::FrameTracker> {
  public:
   FrameTracker(base::WeakPtr<WebContentsVideoCaptureDevice> device,
+               CursorRenderer* cursor_renderer,
                int render_process_id,
                int main_render_frame_id)
       : device_(std::move(device)),
-        device_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+        device_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+        cursor_renderer_(cursor_renderer) {
     DCHECK(device_task_runner_);
+    DCHECK(cursor_renderer_);
 
     BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
@@ -152,14 +155,20 @@
         native_view = view_impl->GetNativeView();
       }
 
-      if (frame_sink_id != target_frame_sink_id_ ||
-          native_view != target_native_view_) {
+      if (frame_sink_id != target_frame_sink_id_) {
         target_frame_sink_id_ = frame_sink_id;
-        target_native_view_ = native_view;
         device_task_runner_->PostTask(
             FROM_HERE,
             base::BindOnce(&WebContentsVideoCaptureDevice::OnTargetChanged,
-                           device_, frame_sink_id, native_view));
+                           device_, frame_sink_id));
+      }
+
+      if (native_view != target_native_view_) {
+        target_native_view_ = native_view;
+        // Note: CursorRenderer runs on the UI thread. It's also important that
+        // SetTargetView() be called in the current stack while |native_view| is
+        // known to be a valid pointer. http://crbug.com/818679
+        cursor_renderer_->SetTargetView(native_view);
       }
     } else {
       device_task_runner_->PostTask(
@@ -167,6 +176,7 @@
           base::BindOnce(
               &WebContentsVideoCaptureDevice::OnTargetPermanentlyLost,
               device_));
+      cursor_renderer_->SetTargetView(gfx::NativeView());
     }
   }
 
@@ -174,6 +184,11 @@
   const base::WeakPtr<WebContentsVideoCaptureDevice> device_;
   const scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_;
 
+  // Owned by FrameSinkVideoCaptureDevice. This will be valid for the life of
+  // FrameTracker because the FrameTracker deleter task will be posted to the UI
+  // thread before the CursorRenderer deleter task.
+  CursorRenderer* const cursor_renderer_;
+
   viz::FrameSinkId target_frame_sink_id_;
   gfx::NativeView target_native_view_ = gfx::NativeView();
 
@@ -184,6 +199,7 @@
     int render_process_id,
     int main_render_frame_id)
     : tracker_(new FrameTracker(AsWeakPtr(),
+                                cursor_renderer(),
                                 render_process_id,
                                 main_render_frame_id)) {}
 
diff --git a/content/browser/ppapi_plugin_process_host.cc b/content/browser/ppapi_plugin_process_host.cc
index 456dc18..55d7553 100644
--- a/content/browser/ppapi_plugin_process_host.cc
+++ b/content/browser/ppapi_plugin_process_host.cc
@@ -351,6 +351,7 @@
                               is_broker_ ? switches::kPpapiBrokerProcess
                                          : switches::kPpapiPluginProcess);
   BrowserChildProcessHostImpl::CopyFeatureAndFieldTrialFlags(cmd_line.get());
+  BrowserChildProcessHostImpl::CopyTraceStartupFlags(cmd_line.get());
 
 #if defined(OS_WIN)
   cmd_line->AppendArg(is_broker_ ? switches::kPrefetchArgumentPpapiBroker
diff --git a/content/browser/renderer_host/browser_compositor_view_mac.h b/content/browser/renderer_host/browser_compositor_view_mac.h
index dc518c6..8f77c05 100644
--- a/content/browser/renderer_host/browser_compositor_view_mac.h
+++ b/content/browser/renderer_host/browser_compositor_view_mac.h
@@ -48,6 +48,7 @@
       BrowserCompositorMacClient* client,
       bool render_widget_host_is_hidden,
       bool ns_view_attached_to_window,
+      const display::Display& initial_display,
       const viz::FrameSinkId& frame_sink_id);
   ~BrowserCompositorMac() override;
 
@@ -72,7 +73,8 @@
   // NSView. This will allocate a new SurfaceId if needed. This will return
   // true if any properties that need to be communicated to the
   // RenderWidgetHostImpl have changed.
-  bool UpdateNSViewAndDisplay();
+  bool UpdateNSViewAndDisplay(const gfx::Size& new_size_dip,
+                              const display::Display& new_display);
 
   // Update the renderer's SurfaceId to reflect |size_dip| in anticipation of
   // the NSView resizing during auto-resize.
diff --git a/content/browser/renderer_host/browser_compositor_view_mac.mm b/content/browser/renderer_host/browser_compositor_view_mac.mm
index a85136641..cf9e16d 100644
--- a/content/browser/renderer_host/browser_compositor_view_mac.mm
+++ b/content/browser/renderer_host/browser_compositor_view_mac.mm
@@ -172,9 +172,11 @@
     BrowserCompositorMacClient* client,
     bool render_widget_host_is_hidden,
     bool ns_view_attached_to_window,
+    const display::Display& initial_display,
     const viz::FrameSinkId& frame_sink_id)
     : client_(client),
       accelerated_widget_mac_ns_view_(accelerated_widget_mac_ns_view),
+      dfh_display_(initial_display),
       weak_factory_(this) {
   g_browser_compositors.Get().insert(this);
 
@@ -184,8 +186,6 @@
       base::FeatureList::IsEnabled(features::kVizDisplayCompositor),
       true /* should_register_frame_sink_id */));
 
-  dfh_display_ = display::Screen::GetScreen()->GetDisplayNearestView(nil);
-
   SetRenderWidgetHostIsHidden(render_widget_host_is_hidden);
   SetNSViewAttachedToWindow(ns_view_attached_to_window);
 }
@@ -256,12 +256,9 @@
   }
 }
 
-bool BrowserCompositorMac::UpdateNSViewAndDisplay() {
-  NSView* ns_view =
-      accelerated_widget_mac_ns_view_->AcceleratedWidgetGetNSView();
-  display::Display new_display =
-      display::Screen::GetScreen()->GetDisplayNearestView(ns_view);
-  gfx::Size new_size_dip([ns_view bounds].size);
+bool BrowserCompositorMac::UpdateNSViewAndDisplay(
+    const gfx::Size& new_size_dip,
+    const display::Display& new_display) {
   if (new_size_dip == dfh_size_dip_ && new_display == dfh_display_)
     return false;
 
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index ad3d9c7..d02c3b2 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -35,6 +35,7 @@
 #include "cc/resources/ui_resource_manager.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/layer_tree_settings.h"
+#include "components/viz/client/frame_eviction_manager.h"
 #include "components/viz/common/features.h"
 #include "components/viz/common/gl_helper.h"
 #include "components/viz/common/gpu/context_provider.h"
@@ -54,6 +55,7 @@
 #include "content/browser/browser_main_loop.h"
 #include "content/browser/compositor/surface_utils.h"
 #include "content/browser/gpu/compositor_util.h"
+#include "content/browser/gpu/gpu_process_host.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/common/gpu_stream_constants.h"
 #include "content/public/browser/android/compositor.h"
@@ -659,11 +661,13 @@
           root_window_->GetBeginFrameSource());
     }
     display_.reset();
+    EnqueueLowEndBackgroundCleanup();
   } else {
     host_->SetVisible(true);
     has_submitted_frame_since_became_visible_ = false;
     if (layer_tree_frame_sink_request_pending_)
       HandlePendingLayerTreeFrameSinkRequest();
+    low_end_background_cleanup_task_.Cancel();
   }
 }
 
@@ -1004,4 +1008,35 @@
     host_->SetDeferCommits(locked);
 }
 
+void CompositorImpl::EnqueueLowEndBackgroundCleanup() {
+  if (base::SysInfo::IsLowEndDevice()) {
+    low_end_background_cleanup_task_.Reset(
+        base::BindOnce(&CompositorImpl::DoLowEndBackgroundCleanup,
+                       weak_factory_.GetWeakPtr()));
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, low_end_background_cleanup_task_.callback(),
+        base::TimeDelta::FromSeconds(5));
+  }
+}
+
+void CompositorImpl::DoLowEndBackgroundCleanup() {
+  // When we become visible, we immediately cancel the callback that runs this
+  // code.
+  DCHECK(!host_->IsVisible());
+
+  // First, evict all unlocked frames, allowing resources to be reclaimed.
+  viz::FrameEvictionManager::GetInstance()->PurgeAllUnlockedFrames();
+
+  // Next, notify the GPU process to do background processing, which will
+  // lose all renderer contexts.
+  content::GpuProcessHost::CallOnIO(
+      content::GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
+      false /* force_create */,
+      base::BindRepeating([](content::GpuProcessHost* host) {
+        if (host) {
+          host->gpu_service()->OnBackgrounded();
+        }
+      }));
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h
index 48da07d..6b59f095 100644
--- a/content/browser/renderer_host/compositor_impl_android.h
+++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -77,6 +77,10 @@
   void DeleteUIResource(cc::UIResourceId resource_id) override;
   bool SupportsETC1NonPowerOfTwo() const override;
 
+  // Test functions:
+  bool IsLockedForTesting() const { return lock_manager_.IsLocked(); }
+  void SetVisibleForTesting(bool visible) { SetVisible(visible); }
+
  private:
   // Compositor implementation.
   void SetRootWindow(gfx::NativeWindow root_window) override;
@@ -160,6 +164,11 @@
 
   void DetachRootWindow();
 
+  // Helper functions to perform delayed cleanup after the compositor is no
+  // longer visible on low-end devices.
+  void EnqueueLowEndBackgroundCleanup();
+  void DoLowEndBackgroundCleanup();
+
   viz::FrameSinkId frame_sink_id_;
 
   // root_layer_ is the persistent internal root layer, while subroot_layer_
@@ -206,6 +215,11 @@
       pending_child_frame_sink_ids_;
   ui::CompositorLockManager lock_manager_;
   bool has_submitted_frame_since_became_visible_ = false;
+
+  // A task which runs cleanup tasks on low-end Android after a delay. Enqueued
+  // when we hide, canceled when we're shown.
+  base::CancelableOnceClosure low_end_background_cleanup_task_;
+
   base::WeakPtrFactory<CompositorImpl> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(CompositorImpl);
diff --git a/content/browser/renderer_host/compositor_impl_android_browsertest.cc b/content/browser/renderer_host/compositor_impl_android_browsertest.cc
new file mode 100644
index 0000000..bfaadc5
--- /dev/null
+++ b/content/browser/renderer_host/compositor_impl_android_browsertest.cc
@@ -0,0 +1,170 @@
+// 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.
+
+#include "base/base_switches.h"
+#include "content/browser/browser_main_loop.h"
+#include "content/browser/gpu/gpu_process_host.h"
+#include "content/browser/renderer_host/compositor_impl_android.h"
+#include "content/browser/renderer_host/render_widget_host_view_android.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/gpu_stream_constants.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "content/test/content_browser_test_utils_internal.h"
+#include "content/test/gpu_browsertest_helpers.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/ipc/client/gpu_channel_host.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "ui/android/window_android.h"
+#include "url/gurl.h"
+
+namespace content {
+
+namespace {
+
+class CompositorImplLowEndBrowserTest : public ContentBrowserTest {
+ public:
+  CompositorImplLowEndBrowserTest() {}
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(switches::kEnableLowEndDeviceMode);
+    command_line->AppendSwitch(switches::kInProcessGPU);
+    content::ContentBrowserTest::SetUpCommandLine(command_line);
+  }
+
+ protected:
+  void SetUpOnMainThread() override {
+    ASSERT_TRUE(embedded_test_server()->Start());
+    net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+    https_server.ServeFilesFromSourceDirectory("content/test/data");
+    ASSERT_TRUE(https_server.Start());
+    GURL http_url(embedded_test_server()->GetURL("/title1.html"));
+    ASSERT_TRUE(NavigateToURL(shell(), http_url));
+  }
+
+  ui::WindowAndroid* window() const {
+    return web_contents()->GetTopLevelNativeWindow();
+  }
+
+  CompositorImpl* compositor_impl() const {
+    return static_cast<CompositorImpl*>(window()->GetCompositor());
+  }
+
+  WebContentsImpl* web_contents() const {
+    return static_cast<WebContentsImpl*>(shell()->web_contents());
+  }
+
+  RenderWidgetHostViewAndroid* render_widget_host_view_android() const {
+    return static_cast<RenderWidgetHostViewAndroid*>(
+        web_contents()->GetRenderWidgetHostView());
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CompositorImplLowEndBrowserTest);
+};
+
+// RunLoop implementation that calls glFlush() every second until it observes
+// OnContextLost().
+class ContextLostRunLoop : public viz::ContextLostObserver {
+ public:
+  ContextLostRunLoop(viz::ContextProvider* context_provider)
+      : context_provider_(context_provider) {
+    context_provider_->AddObserver(this);
+  }
+  ~ContextLostRunLoop() override { context_provider_->RemoveObserver(this); }
+
+  void RunUntilContextLost() {
+    CheckForContextLoss();
+    run_loop_.Run();
+  }
+
+  void CheckForContextLoss() {
+    if (did_lose_context_) {
+      run_loop_.Quit();
+      return;
+    }
+    context_provider_->ContextGL()->Flush();
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&ContextLostRunLoop::CheckForContextLoss,
+                       base::Unretained(this)),
+        base::TimeDelta::FromSeconds(1));
+  }
+
+ private:
+  // viz::LostContextProvider:
+  void OnContextLost() override { did_lose_context_ = true; }
+
+  viz::ContextProvider* const context_provider_;
+  bool did_lose_context_ = false;
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContextLostRunLoop);
+};
+
+// RunLoop implementation that runs until it observes a compositor frame.
+class CompositorFrameRunLoop : public ui::WindowAndroidObserver {
+ public:
+  CompositorFrameRunLoop(ui::WindowAndroid* window) : window_(window) {
+    window_->AddObserver(this);
+  }
+  ~CompositorFrameRunLoop() override { window_->RemoveObserver(this); }
+
+  void RunUntilFrame() { run_loop_.Run(); }
+
+ private:
+  // ui::WindowAndroidObserver:
+  void OnCompositingDidCommit() override { run_loop_.Quit(); }
+  void OnRootWindowVisibilityChanged(bool visible) override {}
+  void OnAttachCompositor() override {}
+  void OnDetachCompositor() override {}
+  void OnActivityStopped() override {}
+  void OnActivityStarted() override {}
+
+  ui::WindowAndroid* const window_;
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(CompositorFrameRunLoop);
+};
+
+IN_PROC_BROWSER_TEST_F(CompositorImplLowEndBrowserTest,
+                       CompositorImplDropsResourcesOnBackground) {
+  auto* rwhva = render_widget_host_view_android();
+  auto* compositor = compositor_impl();
+  auto context = GpuBrowsertestCreateContext(
+      GpuBrowsertestEstablishGpuChannelSyncRunLoop());
+  context->BindToCurrentThread();
+
+  CompositorFrameRunLoop(window()).RunUntilFrame();
+  EXPECT_TRUE(rwhva->HasValidFrame());
+
+  ContextLostRunLoop run_loop(context.get());
+  compositor->SetVisibleForTesting(false);
+  rwhva->OnRootWindowVisibilityChanged(false);
+  rwhva->Hide();
+
+  // Ensure that context is eventually dropped and at that point we do not have
+  // a valid frame.
+  run_loop.RunUntilContextLost();
+  EXPECT_FALSE(rwhva->HasValidFrame());
+
+  // Become visible again:
+  compositor->SetVisibleForTesting(true);
+  rwhva->Show();
+  rwhva->OnRootWindowVisibilityChanged(true);
+
+  // We should have taken the compositor lock on resume.
+  EXPECT_TRUE(compositor->IsLockedForTesting());
+  EXPECT_FALSE(rwhva->HasValidFrame());
+
+  // The compositor should eventually be unlocked and produce a frame.
+  CompositorFrameRunLoop(window()).RunUntilFrame();
+  EXPECT_FALSE(compositor->IsLockedForTesting());
+  EXPECT_TRUE(rwhva->HasValidFrame());
+}
+
+}  // namespace
+}  // namespace content
diff --git a/content/browser/renderer_host/display_util.cc b/content/browser/renderer_host/display_util.cc
index 3c1a80d3..204211f 100644
--- a/content/browser/renderer_host/display_util.cc
+++ b/content/browser/renderer_host/display_util.cc
@@ -58,12 +58,9 @@
     *screen_info = ScreenInfo();
     return;
   }
-#if defined(OS_MACOSX) || defined(USE_AURA)
-  // On macOS, we use the display nearest the nullptr view to return the most
-  // recently active screen, instead of the primary screen
-  // https://crbug.com/357443
-  // On Aura, this decision may or may not have been taken intentionally, and
-  // may or may not have any effect.
+#if defined(USE_AURA)
+  // This behavior difference between Aura and other platforms may or may not
+  // be intentional, and may or may not have any effect.
   gfx::NativeView null_native_view = nullptr;
   display::Display display = screen->GetDisplayNearestView(null_native_view);
 #else
@@ -81,14 +78,9 @@
     *screen_info = ScreenInfo();
     return;
   }
-#if defined(OS_MACOSX)
-  // See previous comment regarding https://crbug.com/357443
-  display::Display display = screen->GetDisplayNearestView(native_view);
-#else
   display::Display display = native_view
                                  ? screen->GetDisplayNearestView(native_view)
                                  : screen->GetPrimaryDisplay();
-#endif
   DisplayToScreenInfo(screen_info, display);
 }
 
diff --git a/content/browser/renderer_host/render_process_host_browsertest.cc b/content/browser/renderer_host/render_process_host_browsertest.cc
index fe592d8..d94381b 100644
--- a/content/browser/renderer_host/render_process_host_browsertest.cc
+++ b/content/browser/renderer_host/render_process_host_browsertest.cc
@@ -79,10 +79,6 @@
     command_line->AppendSwitchASCII(
         switches::kAutoplayPolicy,
         switches::autoplay::kNoUserGestureRequiredPolicy);
-    // These flags are necessary to emulate camera input for getUserMedia()
-    // tests.
-    command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream);
-    command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
   }
 
  protected:
@@ -557,6 +553,25 @@
     rph->RemoveObserver(this);
 }
 
+// Test class instance to run specific setup steps for capture streams.
+class CaptureStreamRenderProcessHostTest : public RenderProcessHostTest {
+ public:
+  void SetUp() override {
+    // Pixel output is needed when digging pixel values out of video tags for
+    // verification in VideoCaptureStream tests.
+    EnablePixelOutput();
+    RenderProcessHostTest::SetUp();
+  }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // These flags are necessary to emulate camera input for getUserMedia()
+    // tests.
+    command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream);
+    command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
+    RenderProcessHostTest::SetUpCommandLine(command_line);
+  }
+};
+
 // These tests contain WebRTC calls and cannot be run when it isn't enabled.
 #if !BUILDFLAG(ENABLE_WEBRTC)
 #define GetUserMediaIncrementsVideoCaptureStreams \
@@ -572,7 +587,7 @@
 
 // Tests that video capture stream count increments when getUserMedia() is
 // called.
-IN_PROC_BROWSER_TEST_F(RenderProcessHostTest,
+IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
                        GetUserMediaIncrementsVideoCaptureStreams) {
   ASSERT_TRUE(embedded_test_server()->Start());
   NavigateToURL(shell(),
@@ -588,7 +603,8 @@
 
 // Tests that video capture stream count resets when getUserMedia() is called
 // and stopped.
-IN_PROC_BROWSER_TEST_F(RenderProcessHostTest, StopResetsVideoCaptureStreams) {
+IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
+                       StopResetsVideoCaptureStreams) {
   ASSERT_TRUE(embedded_test_server()->Start());
   NavigateToURL(shell(),
                 embedded_test_server()->GetURL("/media/getusermedia.html"));
@@ -604,7 +620,7 @@
 // Tests that video capture stream counts (used for process priority
 // calculations) are properly set and cleared during media playback and renderer
 // terminations.
-IN_PROC_BROWSER_TEST_F(RenderProcessHostTest,
+IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
                        KillProcessZerosVideoCaptureStreams) {
   ASSERT_TRUE(embedded_test_server()->Start());
   NavigateToURL(shell(),
@@ -650,7 +666,7 @@
 
 // Tests that media stream count increments when getUserMedia() is
 // called with audio only.
-IN_PROC_BROWSER_TEST_F(RenderProcessHostTest,
+IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
                        GetUserMediaAudioOnlyIncrementsMediaStreams) {
   ASSERT_TRUE(embedded_test_server()->Start());
   NavigateToURL(shell(),
@@ -668,7 +684,7 @@
 // Tests that media stream counts (used for process priority
 // calculations) are properly set and cleared during media playback and renderer
 // terminations for audio only streams.
-IN_PROC_BROWSER_TEST_F(RenderProcessHostTest,
+IN_PROC_BROWSER_TEST_F(CaptureStreamRenderProcessHostTest,
                        KillProcessZerosAudioCaptureStreams) {
   ASSERT_TRUE(embedded_test_server()->Start());
   NavigateToURL(shell(),
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 96ffc5b..c2f3e783 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2596,7 +2596,6 @@
     switches::kDomAutomationController,
     switches::kEnableAutomation,
     switches::kEnableExperimentalWebPlatformFeatures,
-    switches::kEnableHeapProfiling,
     switches::kEnableGPUClientLogging,
     switches::kEnableGpuClientTracing,
     switches::kEnableGpuMemoryBufferVideoFrames,
@@ -2656,7 +2655,6 @@
     switches::kTestType,
     switches::kTouchEventFeatureDetection,
     switches::kTouchTextSelectionStrategy,
-    switches::kTraceConfigFile,
     switches::kTraceToConsole,
     switches::kUseFakeUIForMediaStream,
     // This flag needs to be propagated to the renderer process for
@@ -2735,15 +2733,7 @@
                                  arraysize(kSwitchNames));
 
   BrowserChildProcessHostImpl::CopyFeatureAndFieldTrialFlags(renderer_cmd);
-
-  if (browser_cmd.HasSwitch(switches::kTraceStartup) &&
-      BrowserMainLoop::GetInstance()->is_tracing_startup_for_duration()) {
-    // Pass kTraceStartup switch to renderer only if startup tracing has not
-    // finished.
-    renderer_cmd->AppendSwitchASCII(
-        switches::kTraceStartup,
-        browser_cmd.GetSwitchValueASCII(switches::kTraceStartup));
-  }
+  BrowserChildProcessHostImpl::CopyTraceStartupFlags(renderer_cmd);
 
 #if BUILDFLAG(ENABLE_WEBRTC)
   // Only run the Stun trials in the first renderer.
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc
index 4a67197..6aa7d0c2 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.cc
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc
@@ -743,6 +743,22 @@
   base::debug::DumpWithoutCrashing();
 }
 
+namespace {
+
+// Given |event| in root coordinates, return an event in |target_view|'s
+// coordinates.
+blink::WebGestureEvent GestureEventInTarget(
+    const blink::WebGestureEvent& event,
+    RenderWidgetHostViewBase* target_view) {
+  const gfx::PointF point_in_target =
+      target_view->TransformRootPointToViewCoordSpace(event.PositionInWidget());
+  blink::WebGestureEvent event_for_target(event);
+  event_for_target.SetPositionInWidget(point_in_target);
+  return event_for_target;
+}
+
+}  // namespace
+
 void RenderWidgetHostInputEventRouter::BubbleScrollEvent(
     RenderWidgetHostViewBase* target_view,
     const blink::WebGestureEvent& event,
@@ -773,10 +789,14 @@
       // event ack which didn't consume any scroll delta, and so another level
       // of bubbling is needed. This requires a GestureScrollEnd be sent to the
       // last view, which will no longer be the scroll target.
-      if (bubbling_gesture_scroll_target_.target)
-        SendGestureScrollEnd(bubbling_gesture_scroll_target_.target, event);
-      else
+      if (bubbling_gesture_scroll_target_.target) {
+        SendGestureScrollEnd(
+            bubbling_gesture_scroll_target_.target,
+            GestureEventInTarget(event,
+                                 bubbling_gesture_scroll_target_.target));
+      } else {
         first_bubbling_scroll_target_.target = target_view;
+      }
 
       bubbling_gesture_scroll_target_.target = target_view;
     } else {  // !(event.GetType() == blink::WebInputEvent::kGestureScrollBegin)
@@ -808,8 +828,9 @@
       return;
     }
 
-    bubbling_gesture_scroll_target_.target->ProcessGestureEvent(event,
-                                                                latency_info);
+    bubbling_gesture_scroll_target_.target->ProcessGestureEvent(
+        GestureEventInTarget(event, bubbling_gesture_scroll_target_.target),
+        latency_info);
     if (event.GetType() == blink::WebInputEvent::kGestureScrollEnd ||
         event.GetType() == blink::WebInputEvent::kGestureFlingStart) {
       first_bubbling_scroll_target_.target = nullptr;
@@ -829,8 +850,9 @@
   // If target_view is already set up for bubbled scrolls, we forward
   // the event to the current scroll target without further consideration.
   if (target_view == first_bubbling_scroll_target_.target) {
-    bubbling_gesture_scroll_target_.target->ProcessGestureEvent(event,
-                                                                latency_info);
+    bubbling_gesture_scroll_target_.target->ProcessGestureEvent(
+        GestureEventInTarget(event, bubbling_gesture_scroll_target_.target),
+        latency_info);
     if (event.GetType() == blink::WebInputEvent::kGestureScrollEnd ||
         event.GetType() == blink::WebInputEvent::kGestureFlingStart) {
       first_bubbling_scroll_target_.target = nullptr;
@@ -849,8 +871,9 @@
   // have been sent to a renderer before the first one was ACKed, and the ACK
   // caused a bubble retarget. In this case they all get forwarded.
   if (target_view == bubbling_gesture_scroll_target_.target) {
-    bubbling_gesture_scroll_target_.target->ProcessGestureEvent(event,
-                                                                latency_info);
+    bubbling_gesture_scroll_target_.target->ProcessGestureEvent(
+        GestureEventInTarget(event, bubbling_gesture_scroll_target_.target),
+        latency_info);
     return;
   }
 
@@ -867,15 +890,19 @@
   // unused scroll delta, and so another level of bubbling is needed. This
   // requires a GestureScrollEnd be sent to the last view, which will no
   // longer be the scroll target.
-  if (bubbling_gesture_scroll_target_.target)
-    SendGestureScrollEnd(bubbling_gesture_scroll_target_.target, event);
-  else
+  if (bubbling_gesture_scroll_target_.target) {
+    SendGestureScrollEnd(
+        bubbling_gesture_scroll_target_.target,
+        GestureEventInTarget(event, bubbling_gesture_scroll_target_.target));
+  } else {
     first_bubbling_scroll_target_.target = target_view;
+  }
 
   bubbling_gesture_scroll_target_.target = target_view;
 
-  SendGestureScrollBegin(target_view, event);
-  target_view->ProcessGestureEvent(event, latency_info);
+  SendGestureScrollBegin(target_view, GestureEventInTarget(event, target_view));
+  target_view->ProcessGestureEvent(GestureEventInTarget(event, target_view),
+                                   latency_info);
 }
 
 void RenderWidgetHostInputEventRouter::SendGestureScrollBegin(
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.h b/content/browser/renderer_host/render_widget_host_input_event_router.h
index ffb43fc..6ddb6ef 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.h
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.h
@@ -79,6 +79,7 @@
                        blink::WebTouchEvent *event,
                        const ui::LatencyInfo& latency);
 
+  // |event| is in root coordinates.
   void BubbleScrollEvent(RenderWidgetHostViewBase* target_view,
                          const blink::WebGestureEvent& event,
                          const RenderWidgetHostViewBase* resending_view);
diff --git a/content/browser/renderer_host/render_widget_host_ns_view_bridge.mm b/content/browser/renderer_host/render_widget_host_ns_view_bridge.mm
index 458fb65f..85ee334 100644
--- a/content/browser/renderer_host/render_widget_host_ns_view_bridge.mm
+++ b/content/browser/renderer_host/render_widget_host_ns_view_bridge.mm
@@ -11,6 +11,8 @@
 #include "content/browser/renderer_host/render_widget_host_view_mac.h"
 #import "skia/ext/skia_utils_mac.h"
 #import "ui/base/cocoa/animation_utils.h"
+#include "ui/display/display_observer.h"
+#include "ui/display/screen.h"
 
 namespace content {
 
@@ -20,7 +22,8 @@
 // process as the NSView. The caller of this interface may exist in another
 // process.
 class RenderWidgetHostViewNSViewBridgeLocal
-    : public RenderWidgetHostNSViewBridge {
+    : public RenderWidgetHostNSViewBridge,
+      public display::DisplayObserver {
  public:
   explicit RenderWidgetHostViewNSViewBridgeLocal(
       std::unique_ptr<RenderWidgetHostNSViewClient> client);
@@ -31,6 +34,10 @@
   void SetVisible(bool visible) override;
 
  private:
+  // display::DisplayObserver implementation.
+  void OnDisplayMetricsChanged(const display::Display& display,
+                               uint32_t metrics) override;
+
   // Weak, this is owned by |cocoa_view_|'s |client_|, and |cocoa_view_| owns
   // its |client_|.
   RenderWidgetHostViewCocoa* cocoa_view_ = nil;
@@ -43,6 +50,8 @@
 
 RenderWidgetHostViewNSViewBridgeLocal::RenderWidgetHostViewNSViewBridgeLocal(
     std::unique_ptr<RenderWidgetHostNSViewClient> client) {
+  display::Screen::GetScreen()->AddObserver(this);
+
   // Since we autorelease |cocoa_view|, our caller must put |GetNativeView()|
   // into the view hierarchy right after calling us.
   cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc]
@@ -54,7 +63,9 @@
 }
 
 RenderWidgetHostViewNSViewBridgeLocal::
-    ~RenderWidgetHostViewNSViewBridgeLocal() {}
+    ~RenderWidgetHostViewNSViewBridgeLocal() {
+  display::Screen::GetScreen()->RemoveObserver(this);
+}
 
 RenderWidgetHostViewCocoa*
 RenderWidgetHostViewNSViewBridgeLocal::GetRenderWidgetHostViewCocoa() {
@@ -73,6 +84,15 @@
   [cocoa_view_ setHidden:!visible];
 }
 
+void RenderWidgetHostViewNSViewBridgeLocal::OnDisplayMetricsChanged(
+    const display::Display& display,
+    uint32_t changed_metrics) {
+  // Note that -updateScreenProperties is also be called by the notification
+  // NSWindowDidChangeBackingPropertiesNotification (some of these calls
+  // will be redundant).
+  [cocoa_view_ updateScreenProperties];
+}
+
 }  // namespace
 
 // static
diff --git a/content/browser/renderer_host/render_widget_host_ns_view_client.h b/content/browser/renderer_host/render_widget_host_ns_view_client.h
index 525cf56..60bcf374 100644
--- a/content/browser/renderer_host/render_widget_host_ns_view_client.h
+++ b/content/browser/renderer_host/render_widget_host_ns_view_client.h
@@ -23,6 +23,23 @@
   // RenderWidgetHostNSViewBridge, this method is to be removed.
   virtual RenderWidgetHostViewMac* GetRenderWidgetHostViewMac() = 0;
 
+  // Indicates the NSView's bounds in its NSWindow's DIP coordinate system (with
+  // the origin at the upper-left corner), and indicate if the the NSView is
+  // attached to an NSWindow (if it is not, then |view_bounds_in_window_dip|'s
+  // origin is meaningless, but its size is still relevant).
+  virtual void OnNSViewBoundsInWindowChanged(
+      const gfx::Rect& view_bounds_in_window_dip,
+      bool attached_to_window) = 0;
+
+  // Indicates the NSView's NSWindow's frame in the global display::Screen
+  // DIP coordinate system (where the origin the upper-left corner of
+  // Screen::GetPrimaryDisplay).
+  virtual void OnNSViewWindowFrameInScreenChanged(
+      const gfx::Rect& window_frame_in_screen_dip) = 0;
+
+  // Indicate the NSView's NSScreen's properties.
+  virtual void OnNSViewDisplayChanged(const display::Display& display) = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostNSViewClient);
 };
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index febc9a4..79c5f7e 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -108,9 +108,9 @@
 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
 #include "content/browser/accessibility/browser_accessibility_win.h"
 #include "content/browser/renderer_host/legacy_render_widget_host_win.h"
+#include "ui/base/ime/win/osk_display_manager.h"
+#include "ui/base/ime/win/osk_display_observer.h"
 #include "ui/base/win/hidden_window.h"
-#include "ui/base/win/osk_display_manager.h"
-#include "ui/base/win/osk_display_observer.h"
 #include "ui/display/win/screen_win.h"
 #include "ui/gfx/gdi_util.h"
 #endif
diff --git a/content/browser/renderer_host/render_widget_host_view_cocoa.h b/content/browser/renderer_host/render_widget_host_view_cocoa.h
index a01b4f9..addae2e 100644
--- a/content/browser/renderer_host/render_widget_host_view_cocoa.h
+++ b/content/browser/renderer_host/render_widget_host_view_cocoa.h
@@ -72,6 +72,10 @@
   // Is YES if the cursor is hidden by key events.
   BOOL cursorHidden_;
 
+  // Set during -setFrame to avoid spamming client_ with origin and size
+  // changes.
+  BOOL inSetFrame_;
+
   // Variables used by our implementaion of the NSTextInput protocol.
   // An input method of Mac calls the methods of this protocol not only to
   // notify an application of its status, but also to retrieve the status of
@@ -207,6 +211,9 @@
 - (void)showLookUpDictionaryOverlayFromRange:(NSRange)range
                                   targetView:(NSView*)targetView;
 - (BOOL)suppressNextKeyUpForTesting:(int)keyCode;
+// Query the display::Display from the view's NSWindow's NSScreen and forward
+// it to the RenderWidgetHostNSViewClient (only if the screen is non-nil).
+- (void)updateScreenProperties;
 
 // Methods previously marked as private.
 - (id)initWithClient:
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 9741074..a657c2b 100644
--- a/content/browser/renderer_host/render_widget_host_view_cocoa.mm
+++ b/content/browser/renderer_host/render_widget_host_view_cocoa.mm
@@ -32,7 +32,9 @@
 #import "ui/base/cocoa/appkit_utils.h"
 #include "ui/base/cocoa/cocoa_base_utils.h"
 #include "ui/base/cocoa/text_services_context_menu.h"
+#include "ui/display/screen.h"
 #include "ui/events/event_utils.h"
+#include "ui/gfx/mac/coordinate_conversion.h"
 
 using content::BrowserAccessibility;
 using content::BrowserAccessibilityManager;
@@ -143,10 +145,11 @@
 - (void)windowChangedGlobalFrame:(NSNotification*)notification;
 - (void)windowDidBecomeKey:(NSNotification*)notification;
 - (void)windowDidResignKey:(NSNotification*)notification;
-- (void)updateScreenProperties;
 - (void)showLookUpDictionaryOverlayInternal:(NSAttributedString*)string
                               baselinePoint:(NSPoint)baselinePoint
                                  targetView:(NSView*)view;
+- (void)sendViewBoundsInWindowToClient;
+- (void)sendWindowFrameInScreenToClient;
 @end
 
 @implementation RenderWidgetHostViewCocoa
@@ -196,6 +199,36 @@
   [super dealloc];
 }
 
+- (void)sendViewBoundsInWindowToClient {
+  TRACE_EVENT0("browser",
+               "RenderWidgetHostViewCocoa::sendViewBoundsInWindowToClient");
+  if (inSetFrame_)
+    return;
+
+  NSRect viewBoundsInView = [self bounds];
+  NSWindow* enclosingWindow = [self window];
+  if (!enclosingWindow) {
+    client_->OnNSViewBoundsInWindowChanged(gfx::Rect(viewBoundsInView), false);
+    return;
+  }
+
+  NSRect viewBoundsInWindow = [self convertRect:viewBoundsInView toView:nil];
+  gfx::Rect gfxViewBoundsInWindow(viewBoundsInWindow);
+  gfxViewBoundsInWindow.set_y(NSHeight([enclosingWindow frame]) -
+                              NSMaxY(viewBoundsInWindow));
+  client_->OnNSViewBoundsInWindowChanged(gfxViewBoundsInWindow, true);
+}
+
+- (void)sendWindowFrameInScreenToClient {
+  TRACE_EVENT0("browser",
+               "RenderWidgetHostViewCocoa::sendWindowFrameInScreenToClient");
+  NSWindow* enclosingWindow = [self window];
+  if (!enclosingWindow)
+    return;
+  client_->OnNSViewWindowFrameInScreenChanged(
+      gfx::ScreenRectFromNSRect([enclosingWindow frame]));
+}
+
 - (void)setResponderDelegate:
     (NSObject<RenderWidgetHostViewMacDelegate>*)delegate {
   DCHECK(!responderDelegate_);
@@ -1148,7 +1181,7 @@
                                   name:NSWindowDidMoveNotification
                                 object:oldWindow];
     [notificationCenter removeObserver:self
-                                  name:NSWindowDidEndLiveResizeNotification
+                                  name:NSWindowDidResizeNotification
                                 object:oldWindow];
     [notificationCenter removeObserver:self
                                   name:NSWindowDidBecomeKeyNotification
@@ -1169,7 +1202,7 @@
                              object:newWindow];
     [notificationCenter addObserver:self
                            selector:@selector(windowChangedGlobalFrame:)
-                               name:NSWindowDidEndLiveResizeNotification
+                               name:NSWindowDidResizeNotification
                              object:newWindow];
     [notificationCenter addObserver:self
                            selector:@selector(windowDidBecomeKey:)
@@ -1180,45 +1213,60 @@
                                name:NSWindowDidResignKeyNotification
                              object:newWindow];
   }
+
+  [self sendWindowFrameInScreenToClient];
 }
 
 - (void)updateScreenProperties {
-  renderWidgetHostView_->UpdateNSViewAndDisplayProperties();
-  renderWidgetHostView_->UpdateDisplayLink();
+  NSWindow* enclosingWindow = [self window];
+  if (!enclosingWindow)
+    return;
+
+  // TODO(ccameron): This will call [enclosingWindow screen], which may return
+  // nil. Do that call here to avoid sending bogus display info to the client.
+  display::Display display =
+      display::Screen::GetScreen()->GetDisplayNearestView(self);
+  client_->OnNSViewDisplayChanged(display);
 }
 
-// http://developer.apple.com/library/mac/#documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/CapturingScreenContents/CapturingScreenContents.html#//apple_ref/doc/uid/TP40012302-CH10-SW4
+// This will be called when the NSView's NSWindow moves from one NSScreen to
+// another, and makes note of the new screen's color space, scale factor, etc.
+// It is also called when the current NSScreen's properties change (which is
+// redundant with display::DisplayObserver::OnDisplayMetricsChanged).
 - (void)windowDidChangeBackingProperties:(NSNotification*)notification {
-  // Background tabs check if their screen scale factor, color profile, and
-  // vsync properties changed when they are added to a window.
-
-  // Allocating a CGLayerRef with the current scale factor immediately from
-  // this handler doesn't work. Schedule the backing store update on the
-  // next runloop cycle, then things are read for CGLayerRef allocations to
-  // work.
+  // Delay calling updateScreenProperties so that display::ScreenMac can
+  // update our display::Displays first (if applicable).
   [self performSelector:@selector(updateScreenProperties)
              withObject:nil
              afterDelay:0];
 }
 
 - (void)windowChangedGlobalFrame:(NSNotification*)notification {
-  renderWidgetHostView_->UpdateNSViewAndDisplayProperties();
+  [self sendWindowFrameInScreenToClient];
+  // Update the view bounds relative to the window, as they may have changed
+  // during layout, and we don't explicitly listen for re-layout of parent
+  // views.
+  [self sendViewBoundsInWindowToClient];
+}
+
+- (void)setFrame:(NSRect)r {
+  // Note that -setFrame: calls through -setFrameSize: and -setFrameOrigin. To
+  // avoid spamming the client with transiently invalid states, only send one
+  // message at the end.
+  inSetFrame_ = YES;
+  [super setFrame:r];
+  inSetFrame_ = NO;
+  [self sendViewBoundsInWindowToClient];
+}
+
+- (void)setFrameOrigin:(NSPoint)newOrigin {
+  [super setFrameOrigin:newOrigin];
+  [self sendViewBoundsInWindowToClient];
 }
 
 - (void)setFrameSize:(NSSize)newSize {
-  TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::setFrameSize");
-
-  // NB: -[NSView setFrame:] calls through -setFrameSize:, so overriding
-  // -setFrame: isn't neccessary.
   [super setFrameSize:newSize];
-
-  renderWidgetHostView_->UpdateNSViewAndDisplayProperties();
-
-  // Wait for the frame that WasResize might have requested. If the view is
-  // being made visible at a new size, then this call will have no effect
-  // because the view widget is still hidden, and the pause call in WasShown
-  // will have this effect for us.
-  renderWidgetHostView_->PauseForPendingResizeOrRepaintsAndDraw();
+  [self sendViewBoundsInWindowToClient];
 }
 
 - (BOOL)canBecomeKeyView {
@@ -1821,10 +1869,11 @@
   if (!renderWidgetHostView_->browser_compositor_)
     return;
 
-  if ([self window])
-    [self updateScreenProperties];
-  renderWidgetHostView_->browser_compositor_->SetNSViewAttachedToWindow(
-      [self window]);
+  // Update the window's frame, the view's bounds, and the display info, as they
+  // have not been updated while unattached to a window.
+  [self sendWindowFrameInScreenToClient];
+  [self sendViewBoundsInWindowToClient];
+  [self updateScreenProperties];
 
   // If we switch windows (or are removed from the view hierarchy), cancel any
   // open mouse-downs.
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h
index 2810dc6..d2ff2c2 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.h
+++ b/content/browser/renderer_host/render_widget_host_view_mac.h
@@ -26,7 +26,6 @@
 #include "ui/accelerated_widget_mac/accelerated_widget_mac.h"
 #include "ui/accelerated_widget_mac/display_link_mac.h"
 #include "ui/base/cocoa/remote_layer_api.h"
-#include "ui/display/display_observer.h"
 
 namespace content {
 class CursorManager;
@@ -69,8 +68,7 @@
       public BrowserCompositorMacClient,
       public TextInputManager::Observer,
       public ui::AcceleratedWidgetMacNSView,
-      public IPC::Sender,
-      public display::DisplayObserver {
+      public IPC::Sender {
  public:
   // The view will associate itself with the given widget. The native view must
   // be hooked up immediately to the view hierarchy, or else when it is
@@ -207,12 +205,6 @@
   // IPC::Sender implementation.
   bool Send(IPC::Message* message) override;
 
-  // display::DisplayObserver implementation.
-  void OnDisplayAdded(const display::Display& new_display) override;
-  void OnDisplayRemoved(const display::Display& old_display) override;
-  void OnDisplayMetricsChanged(const display::Display& display,
-                               uint32_t metrics) override;
-
   // Forwards the mouse event to the renderer.
   void ForwardMouseEvent(const blink::WebMouseEvent& event);
 
@@ -282,18 +274,20 @@
 
   int window_number() const;
 
-  // Update the size, scale factor, color profile, and any other properties
-  // of the NSView or its NSScreen. Propagate these to the RenderWidgetHostImpl
-  // as well.
+  // Update the size, scale factor, color profile, vsync parameters, and any
+  // other properties of the NSView or its NSScreen. Propagate these to the
+  // RenderWidgetHostImpl as well.
   void UpdateNSViewAndDisplayProperties();
 
-  // Ensure that the display link is associated with the correct display.
-  void UpdateDisplayLink();
-
   void PauseForPendingResizeOrRepaintsAndDraw();
 
   // RenderWidgetHostNSViewClient implementation.
   RenderWidgetHostViewMac* GetRenderWidgetHostViewMac() override;
+  void OnNSViewBoundsInWindowChanged(const gfx::Rect& view_bounds_in_window_dip,
+                                     bool attached_to_window) override;
+  void OnNSViewWindowFrameInScreenChanged(
+      const gfx::Rect& window_frame_in_screen_dip) override;
+  void OnNSViewDisplayChanged(const display::Display& display) override;
 
   // BrowserCompositorMacClient implementation.
   SkColor BrowserCompositorMacGetGutterColor() const override;
@@ -381,6 +375,17 @@
   // State tracked by Show/Hide/IsShowing.
   bool is_visible_ = false;
 
+  // The bounds of the view in its NSWindow's coordinate system (with origin
+  // in the upper-left).
+  gfx::Rect view_bounds_in_window_dip_;
+
+  // The frame of the window in the global display::Screen coordinate system
+  // (where the origin is the upper-left corner of Screen::GetPrimaryDisplay).
+  gfx::Rect window_frame_in_screen_dip_;
+
+  // Cached copy of the display information pushed to us from the NSView.
+  display::Display display_;
+
   // Indicates if the page is loading.
   bool is_loading_;
 
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index a32c48d..2b5dda27 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -48,6 +48,7 @@
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/gfx/geometry/dip_util.h"
+#include "ui/gfx/mac/coordinate_conversion.h"
 #include "ui/gl/gl_switches.h"
 
 using blink::WebInputEvent;
@@ -148,46 +149,6 @@
 // Maximum number of characters we allow in a tooltip.
 const size_t kMaxTooltipLength = 1024;
 
-float FlipYFromRectToScreen(float y, float rect_height) {
-  TRACE_EVENT0("browser", "FlipYFromRectToScreen");
-  display::Screen* screen = display::Screen::GetScreen();
-  display::Display display = screen->GetPrimaryDisplay();
-  CGFloat screen_zero_height = display.bounds().height();
-  if (screen_zero_height == 0.f)
-    return y;
-  return screen_zero_height - y - rect_height;
-}
-
-// Adjusts an NSRect in Cocoa screen coordinates to have an origin in the upper
-// left of the primary screen (Carbon coordinates), and stuffs it into a
-// gfx::Rect.
-gfx::Rect FlipNSRectToRectScreen(const NSRect& rect) {
-  gfx::Rect new_rect(NSRectToCGRect(rect));
-  new_rect.set_y(FlipYFromRectToScreen(new_rect.y(), new_rect.height()));
-  return new_rect;
-}
-
-// Returns the window that visually contains the given view. This is different
-// from [view window] in the case of tab dragging, where the view's owning
-// window is a floating panel attached to the actual browser window that the tab
-// is visually part of.
-NSWindow* ApparentWindowForView(NSView* view) {
-  // TODO(shess): In case of !window, the view has been removed from
-  // the view hierarchy because the tab isn't main.  Could retrieve
-  // the information from the main tab for our window.
-  NSWindow* enclosing_window = [view window];
-
-  // See if this is a tab drag window. The width check is to distinguish that
-  // case from extension popup windows.
-  NSWindow* ancestor_window = [enclosing_window parentWindow];
-  if (ancestor_window && (NSWidth([enclosing_window frame]) ==
-                          NSWidth([ancestor_window frame]))) {
-    enclosing_window = ancestor_window;
-  }
-
-  return enclosing_window;
-}
-
 }  // namespace
 
 namespace content {
@@ -268,14 +229,19 @@
   ns_view_bridge_ = RenderWidgetHostNSViewBridge::Create(
       std::unique_ptr<RenderWidgetHostNSViewClient>(this));
 
+  // Guess that the initial screen we will be on is the screen of the current
+  // window (since that's the best guess that we have, and is usually right).
+  // https://crbug.com/357443
+  display_ =
+      display::Screen::GetScreen()->GetDisplayNearestWindow([NSApp keyWindow]);
+
   viz::FrameSinkId frame_sink_id = is_guest_view_hack_
                                        ? AllocateFrameSinkIdForGuestViewHack()
                                        : host()->GetFrameSinkId();
 
-  browser_compositor_.reset(new BrowserCompositorMac(
-      this, this, host()->is_hidden(), [cocoa_view() window], frame_sink_id));
-
-  display::Screen::GetScreen()->AddObserver(this);
+  browser_compositor_.reset(
+      new BrowserCompositorMac(this, this, host()->is_hidden(),
+                               [cocoa_view() window], display_, frame_sink_id));
 
   if (!is_guest_view_hack_)
     host()->SetView(this);
@@ -315,8 +281,6 @@
 }
 
 RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
-  display::Screen::GetScreen()->RemoveObserver(this);
-
   // |this| is owned by RenderWidgetHostViewCocoa and is destroyed when the
   // RenderWidgetHostViewCocoa is deallocated, so destroy the bridge to the
   // RenderWidgetHostViewCocoa.
@@ -396,12 +360,8 @@
   [cocoa_view() setCloseOnDeactivate:YES];
   [cocoa_view() setCanBeKeyView:activatable ? YES : NO];
 
-  NSPoint origin_global = NSPointFromCGPoint(pos.origin().ToCGPoint());
-  origin_global.y = FlipYFromRectToScreen(origin_global.y, pos.height());
-
   popup_window_.reset([[RenderWidgetPopupWindow alloc]
-      initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,
-                                     pos.width(), pos.height())
+      initWithContentRect:gfx::ScreenRectToNSRect(pos)
                 styleMask:NSBorderlessWindowMask
                   backing:NSBackingStoreBuffered
                     defer:NO]);
@@ -486,26 +446,6 @@
   return [window windowNumber];
 }
 
-void RenderWidgetHostViewMac::UpdateDisplayLink() {
-  static bool is_vsync_disabled =
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kDisableGpuVsync);
-  if (is_vsync_disabled)
-    return;
-
-  NSScreen* screen = [[cocoa_view() window] screen];
-  NSDictionary* screen_description = [screen deviceDescription];
-  NSNumber* screen_number = [screen_description objectForKey:@"NSScreenNumber"];
-  CGDirectDisplayID display_id = [screen_number unsignedIntValue];
-
-  display_link_ = ui::DisplayLinkMac::GetForDisplay(display_id);
-  if (!display_link_.get()) {
-    // Note that on some headless systems, the display link will fail to be
-    // created, so this should not be a fatal error.
-    LOG(ERROR) << "Failed to create display link.";
-  }
-}
-
 void RenderWidgetHostViewMac::UpdateDisplayVSyncParameters() {
   if (!host() || !display_link_.get())
     return;
@@ -542,6 +482,18 @@
   if (!browser_compositor_)
     return;
 
+  static bool is_vsync_disabled =
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableGpuVsync);
+  if (!is_vsync_disabled) {
+    display_link_ = ui::DisplayLinkMac::GetForDisplay(display_.id());
+    if (!display_link_.get()) {
+      // Note that on some headless systems, the display link will fail to be
+      // created, so this should not be a fatal error.
+      LOG(ERROR) << "Failed to create display link.";
+    }
+  }
+
   // During auto-resize it is the responsibility of the caller to ensure that
   // the NSView and RenderWidgetHostImpl are kept in sync.
   if (host()->auto_resize_enabled())
@@ -556,8 +508,10 @@
   // to send to the renderer, so it is required that BrowserCompositorMac be
   // updated first. Only notify RenderWidgetHostImpl of the update if any
   // properties it will query have changed.
-  if (browser_compositor_->UpdateNSViewAndDisplay())
+  if (browser_compositor_->UpdateNSViewAndDisplay(
+          view_bounds_in_window_dip_.size(), display_)) {
     host()->NotifyScreenInfoChanged();
+  }
 }
 
 void RenderWidgetHostViewMac::GetScreenInfo(ScreenInfo* screen_info) const {
@@ -642,12 +596,7 @@
   if (isRelativeToScreen) {
     // The position of |rect| is screen coordinate system and we have to
     // consider Cocoa coordinate system is upside-down and also multi-screen.
-    NSPoint origin_global = NSPointFromCGPoint(rect.origin().ToCGPoint());
-    NSSize size = NSMakeSize(rect.width(), rect.height());
-    size = [cocoa_view() convertSize:size toView:nil];
-    origin_global.y = FlipYFromRectToScreen(origin_global.y, size.height);
-    NSRect frame = NSMakeRect(origin_global.x, origin_global.y,
-                              size.width, size.height);
+    NSRect frame = gfx::ScreenRectToNSRect(rect);
     if (IsPopup())
       [popup_window_ setFrame:frame display:YES];
     else
@@ -691,17 +640,8 @@
 }
 
 gfx::Rect RenderWidgetHostViewMac::GetViewBounds() const {
-  NSRect bounds = [cocoa_view() bounds];
-  // TODO(shess): In case of !window, the view has been removed from
-  // the view hierarchy because the tab isn't main.  Could retrieve
-  // the information from the main tab for our window.
-  NSWindow* enclosing_window = ApparentWindowForView(cocoa_view());
-  if (!enclosing_window)
-    return gfx::Rect(gfx::Size(NSWidth(bounds), NSHeight(bounds)));
-
-  bounds = [cocoa_view() convertRect:bounds toView:nil];
-  bounds = [enclosing_window convertRectToScreen:bounds];
-  return FlipNSRectToRectScreen(bounds);
+  return view_bounds_in_window_dip_ +
+         window_frame_in_screen_dip_.OffsetFromOrigin();
 }
 
 void RenderWidgetHostViewMac::UpdateCursor(const WebCursor& cursor) {
@@ -805,30 +745,17 @@
   if (!region)
     return;
 
-  NSWindow* enclosing_window = ApparentWindowForView(cocoa_view());
-  if (!enclosing_window)
-    return;
-
   // Create a rectangle for the edge of the selection focus, which will be
   // the same as the caret position if the selection is collapsed. That's
   // what we want to try to keep centered on-screen if possible.
   gfx::Rect gfx_caret_rect(region->focus.edge_top_rounded().x(),
                            region->focus.edge_top_rounded().y(),
                            1, region->focus.GetHeight());
+  gfx_caret_rect += view_bounds_in_window_dip_.OffsetFromOrigin();
+  gfx_caret_rect += window_frame_in_screen_dip_.OffsetFromOrigin();
 
-  // Convert the caret rect to CG-style flipped widget-relative coordinates.
+  // Note that UAZoomChangeFocus wants unflipped screen coordinates.
   NSRect caret_rect = NSRectFromCGRect(gfx_caret_rect.ToCGRect());
-  caret_rect.origin.y = NSHeight([cocoa_view() bounds]) -
-                        (caret_rect.origin.y + caret_rect.size.height);
-
-  // Now convert that to screen coordinates.
-  caret_rect = [cocoa_view() convertRect:caret_rect toView:nil];
-  caret_rect = [enclosing_window convertRectToScreen:caret_rect];
-
-  // Finally, flip it again because UAZoomChangeFocus wants unflipped screen
-  // coordinates, and call UAZoomChangeFocus to initiate the scroll.
-  caret_rect.origin.y = FlipYFromRectToScreen(
-      caret_rect.origin.y, caret_rect.size.height);
   UAZoomChangeFocus(&caret_rect, &caret_rect, kUAZoomFocusTypeInsertionPoint);
 }
 
@@ -1320,15 +1247,7 @@
 }
 
 gfx::Rect RenderWidgetHostViewMac::GetBoundsInRootWindow() {
-  // TODO(shess): In case of !window, the view has been removed from
-  // the view hierarchy because the tab isn't main.  Could retrieve
-  // the information from the main tab for our window.
-  NSWindow* enclosing_window = ApparentWindowForView(cocoa_view());
-  if (!enclosing_window)
-    return gfx::Rect();
-
-  NSRect bounds = [enclosing_window frame];
-  return FlipNSRectToRectScreen(bounds);
+  return window_frame_in_screen_dip_;
 }
 
 bool RenderWidgetHostViewMac::LockMouse() {
@@ -1424,7 +1343,7 @@
     gfx::PointF* transformed_point) {
   // Transformations use physical pixels rather than DIP, so conversion
   // is necessary.
-  float scale_factor = ui::GetScaleFactorForNativeView(cocoa_view());
+  float scale_factor = display_.device_scale_factor();
   gfx::PointF point_in_pixels = gfx::ConvertPointToPixel(scale_factor, point);
   if (!browser_compositor_->GetDelegatedFrameHost()
            ->TransformPointToLocalCoordSpace(point_in_pixels, original_surface,
@@ -1584,23 +1503,6 @@
       ->AllocateFrameSinkId();
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// display::DisplayObserver, public:
-
-void RenderWidgetHostViewMac::OnDisplayAdded(const display::Display& display) {}
-
-void RenderWidgetHostViewMac::OnDisplayRemoved(
-    const display::Display& display) {}
-
-void RenderWidgetHostViewMac::OnDisplayMetricsChanged(
-    const display::Display& display,
-    uint32_t changed_metrics) {
-  display::Screen* screen = display::Screen::GetScreen();
-  if (display.id() != screen->GetDisplayNearestView(cocoa_view()).id())
-    return;
-  UpdateNSViewAndDisplayProperties();
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // RenderWidgetHostNSViewClient implementation:
 
@@ -1608,6 +1510,47 @@
   return this;
 }
 
+void RenderWidgetHostViewMac::OnNSViewBoundsInWindowChanged(
+    const gfx::Rect& view_bounds_in_window_dip,
+    bool attached_to_window) {
+  if (!browser_compositor_)
+    return;
+
+  bool view_size_changed =
+      view_bounds_in_window_dip_.size() != view_bounds_in_window_dip.size();
+
+  browser_compositor_->SetNSViewAttachedToWindow(attached_to_window);
+
+  if (attached_to_window) {
+    view_bounds_in_window_dip_ = view_bounds_in_window_dip;
+  } else {
+    // If not attached to a window, do not update the bounds origin (since it is
+    // meaningless, and the last value is the best guess at the next meaningful
+    // value).
+    view_bounds_in_window_dip_.set_size(view_bounds_in_window_dip.size());
+  }
+
+  if (view_size_changed) {
+    UpdateNSViewAndDisplayProperties();
+    // Wait for the frame that WasResize might have requested. If the view is
+    // being made visible at a new size, then this call will have no effect
+    // because the view widget is still hidden, and the pause call in WasShown
+    // will have this effect for us.
+    PauseForPendingResizeOrRepaintsAndDraw();
+  }
+}
+
+void RenderWidgetHostViewMac::OnNSViewWindowFrameInScreenChanged(
+    const gfx::Rect& window_frame_in_screen_dip) {
+  window_frame_in_screen_dip_ = window_frame_in_screen_dip;
+}
+
+void RenderWidgetHostViewMac::OnNSViewDisplayChanged(
+    const display::Display& display) {
+  display_ = display;
+  UpdateNSViewAndDisplayProperties();
+}
+
 Class GetRenderWidgetHostViewCocoaClassForTesting() {
   return [RenderWidgetHostViewCocoa class];
 }
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index 3776b1a..198de9e 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -911,6 +911,76 @@
   scroll_end_observer.Wait();
 }
 
+// When a scroll event is bubbled, ensure that the bubbled event's coordinates
+// are correctly updated to the ancestor's coordinate space. In particular,
+// ensure that the transformation considers CSS scaling of the child where
+// simply applying the ancestor's offset does not produce the correct
+// coordinates in the ancestor's coordinate space.
+// See https://crbug.com/817392
+IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
+                       BubbledScrollEventsTransformedCorrectly) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "/frame_tree/page_with_positioned_scaled_frame.html"));
+  ASSERT_TRUE(NavigateToURL(shell(), main_url));
+
+  // It is safe to obtain the root frame tree node here, as it doesn't change.
+  FrameTreeNode* root = web_contents()->GetFrameTree()->root();
+  ASSERT_EQ(1U, root->child_count());
+
+  FrameTreeNode* iframe_node = root->child_at(0);
+  GURL site_url(embedded_test_server()->GetURL("baz.com", "/title1.html"));
+  EXPECT_EQ(site_url, iframe_node->current_url());
+
+  RenderWidgetHostViewBase* root_rwhv = static_cast<RenderWidgetHostViewBase*>(
+      root->current_frame_host()->GetRenderWidgetHost()->GetView());
+
+  RenderWidgetHostInputEventRouter* router =
+      static_cast<WebContentsImpl*>(shell()->web_contents())
+          ->GetInputEventRouter();
+
+  WaitForChildFrameSurfaceReady(iframe_node->current_frame_host());
+
+  const float scale_factor = GetPageScaleFactor(shell());
+  // Due to the CSS scaling of the iframe, the position in the child view's
+  // coordinates is (96, 96) and not (48, 48) (or approximately these values
+  // if there's rounding due to the scale factor).
+  const gfx::Point position_in_root(gfx::ToCeiledInt(150 * scale_factor),
+                                    gfx::ToCeiledInt(150 * scale_factor));
+
+  auto expect_gsb_with_position = base::BindRepeating(
+      [](const gfx::Point& expected_position, content::InputEventAckSource,
+         content::InputEventAckState, const blink::WebInputEvent& event) {
+        if (event.GetType() != blink::WebInputEvent::kGestureScrollBegin)
+          return false;
+
+        const blink::WebGestureEvent& gesture_event =
+            static_cast<const blink::WebGestureEvent&>(event);
+        EXPECT_NEAR(expected_position.x(), gesture_event.PositionInWidget().x,
+                    1);
+        EXPECT_NEAR(expected_position.y(), gesture_event.PositionInWidget().y,
+                    1);
+        return true;
+      });
+
+  InputEventAckWaiter root_scroll_begin_observer(
+      root_rwhv->GetRenderWidgetHost(),
+      base::BindRepeating(expect_gsb_with_position, position_in_root));
+
+  // Scroll the iframe upward, scroll events get bubbled up to the root.
+  blink::WebMouseWheelEvent scroll_event(
+      blink::WebInputEvent::kMouseWheel, blink::WebInputEvent::kNoModifiers,
+      blink::WebInputEvent::GetStaticTimeStampForTests());
+  SetWebEventPositions(&scroll_event, position_in_root, root_rwhv);
+  scroll_event.delta_x = 0.0f;
+  scroll_event.delta_y = 5.0f;
+  scroll_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
+  scroll_event.has_precise_scrolling_deltas = true;
+
+  router->RouteMouseWheelEvent(root_rwhv, &scroll_event, ui::LatencyInfo());
+
+  root_scroll_begin_observer.Wait();
+}
+
 #if defined(USE_AURA) || defined(OS_ANDROID)
 
 // When unconsumed scrolls in a child bubble to the root and start an
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc
index eb7f0d45..b8bdb742e 100644
--- a/content/browser/tracing/tracing_controller_impl.cc
+++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -21,6 +21,7 @@
 #include "base/trace_event/trace_config.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "components/tracing/common/trace_config_file.h"
 #include "content/browser/tracing/file_tracing_provider_impl.h"
 #include "content/browser/tracing/tracing_ui.h"
 #include "content/public/browser/browser_thread.h"
@@ -326,6 +327,7 @@
     return false;
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
+  tracing::TraceConfigFile::GetInstance()->SetDisabled();
   trace_data_endpoint_ = std::move(trace_data_endpoint);
   is_data_complete_ = false;
   is_metadata_available_ = false;
diff --git a/content/browser/utility_process_host.cc b/content/browser/utility_process_host.cc
index 0358e9d7..cd1e948 100644
--- a/content/browser/utility_process_host.cc
+++ b/content/browser/utility_process_host.cc
@@ -255,6 +255,7 @@
     cmd_line->AppendSwitchASCII(switches::kProcessType,
                                 switches::kUtilityProcess);
     BrowserChildProcessHostImpl::CopyFeatureAndFieldTrialFlags(cmd_line.get());
+    BrowserChildProcessHostImpl::CopyTraceStartupFlags(cmd_line.get());
     std::string locale = GetContentClient()->browser()->GetApplicationLocale();
     cmd_line->AppendSwitchASCII(switches::kLang, locale);
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 7184190d..14a5395 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3634,7 +3634,7 @@
     bool is_favicon,
     uint32_t max_bitmap_size,
     bool bypass_cache,
-    const WebContents::ImageDownloadCallback& callback) {
+    WebContents::ImageDownloadCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   static int next_image_download_id = 0;
   const content::mojom::ImageDownloaderPtr& mojo_image_downloader =
@@ -3643,20 +3643,22 @@
   if (!mojo_image_downloader) {
     // If the renderer process is dead (i.e. crash, or memory pressure on
     // Android), the downloader service will be invalid. Pre-Mojo, this would
-    // hang the callback indefinetly since the IPC would be dropped. Now,
+    // hang the callback indefinitely since the IPC would be dropped. Now,
     // respond with a 400 HTTP error code to indicate that something went wrong.
     BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
         base::BindOnce(&WebContentsImpl::OnDidDownloadImage,
-                       weak_factory_.GetWeakPtr(), callback, download_id, url,
-                       400, std::vector<SkBitmap>(), std::vector<gfx::Size>()));
+                       weak_factory_.GetWeakPtr(), std::move(callback),
+                       download_id, url, 400, std::vector<SkBitmap>(),
+                       std::vector<gfx::Size>()));
     return download_id;
   }
 
   mojo_image_downloader->DownloadImage(
       url, is_favicon, max_bitmap_size, bypass_cache,
       base::BindOnce(&WebContentsImpl::OnDidDownloadImage,
-                     weak_factory_.GetWeakPtr(), callback, download_id, url));
+                     weak_factory_.GetWeakPtr(), std::move(callback),
+                     download_id, url));
   return download_id;
 }
 
@@ -3691,8 +3693,8 @@
   return was_ever_audible_;
 }
 
-void WebContentsImpl::GetManifest(const GetManifestCallback& callback) {
-  manifest_manager_host_->GetManifest(callback);
+void WebContentsImpl::GetManifest(GetManifestCallback callback) {
+  manifest_manager_host_->GetManifest(std::move(callback));
 }
 
 void WebContentsImpl::ExitFullscreen(bool will_cause_resize) {
@@ -5827,13 +5829,14 @@
 #endif
 
 void WebContentsImpl::OnDidDownloadImage(
-    const ImageDownloadCallback& callback,
+    ImageDownloadCallback callback,
     int id,
     const GURL& image_url,
     int32_t http_status_code,
     const std::vector<SkBitmap>& images,
     const std::vector<gfx::Size>& original_image_sizes) {
-  callback.Run(id, http_status_code, image_url, images, original_image_sizes);
+  std::move(callback).Run(id, http_status_code, image_url, images,
+                          original_image_sizes);
 }
 
 void WebContentsImpl::OnDialogClosed(int render_process_id,
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index b97c76a..b6e95b7 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -442,7 +442,7 @@
                     bool is_favicon,
                     uint32_t max_bitmap_size,
                     bool bypass_cache,
-                    const ImageDownloadCallback& callback) override;
+                    ImageDownloadCallback callback) override;
   bool IsSubframe() const override;
   void Find(int request_id,
             const base::string16& search_text,
@@ -450,7 +450,7 @@
   void StopFinding(StopFindAction action) override;
   bool WasRecentlyAudible() override;
   bool WasEverAudible() override;
-  void GetManifest(const GetManifestCallback& callback) override;
+  void GetManifest(GetManifestCallback callback) override;
   bool IsFullscreenForCurrentTab() const override;
   void ExitFullscreen(bool will_cause_resize) override;
   void ResumeLoadingCreatedWebContents() override;
@@ -1080,7 +1080,7 @@
   void DoWasUnOccluded();
 
   // Called with the result of a DownloadImage() request.
-  void OnDidDownloadImage(const ImageDownloadCallback& callback,
+  void OnDidDownloadImage(ImageDownloadCallback callback,
                           int id,
                           const GURL& image_url,
                           int32_t http_status_code,
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index 854b4b4..f29a4b8 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -575,6 +575,7 @@
   EXPECT_EQ(page_url, subresource_load_info->referrer);
   EXPECT_EQ("GET", subresource_load_info->method);
   EXPECT_EQ(content::RESOURCE_TYPE_IMAGE, subresource_load_info->resource_type);
+  EXPECT_EQ("image/jpeg", subresource_load_info->mime_type);
   ASSERT_TRUE(subresource_load_info->ip);
   EXPECT_EQ("127.0.0.1", subresource_load_info->ip->ToString());
 }
@@ -1430,8 +1431,8 @@
   shell->LoadURL(GURL("about:blank"));
   shell->web_contents()->DownloadImage(
       image_url, false, 1024, false,
-      base::Bind(&DownloadImageObserver::OnFinishDownloadImage,
-                 base::Unretained(&download_image_observer)));
+      base::BindOnce(&DownloadImageObserver::OnFinishDownloadImage,
+                     base::Unretained(&download_image_observer)));
 
   // Wait for response.
   loop_runner->Run();
@@ -1483,7 +1484,7 @@
   base::RunLoop run_loop;
   shell()->web_contents()->DownloadImage(
       kImageUrl, false, 2, false,
-      base::Bind(&ExpectNoValidImageCallback, run_loop.QuitClosure()));
+      base::BindOnce(&ExpectNoValidImageCallback, run_loop.QuitClosure()));
 
   run_loop.Run();
 }
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc
index 257106a..dcb1b2c 100644
--- a/content/child/child_thread_impl.cc
+++ b/content/child/child_thread_impl.cc
@@ -75,10 +75,6 @@
 #include "content/public/common/content_descriptors.h"
 #endif
 
-#if defined(OS_MACOSX)
-#include "base/allocator/allocator_interception_mac.h"
-#endif
-
 namespace content {
 namespace {
 
@@ -557,14 +553,6 @@
       connection_timeout = temp;
   }
 
-#if defined(OS_MACOSX)
-  if (base::CommandLine::InitializedForCurrentProcess() &&
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableHeapProfiling)) {
-    base::allocator::PeriodicallyShimNewMallocZones();
-  }
-#endif
-
   message_loop_->task_runner()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&ChildThreadImpl::EnsureConnected,
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 25e820f..3963944 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -512,12 +512,14 @@
   IPC_STRUCT_TRAITS_MEMBER(name)
   IPC_STRUCT_TRAITS_MEMBER(unique_name)
   IPC_STRUCT_TRAITS_MEMBER(feature_policy_header)
+  IPC_STRUCT_TRAITS_MEMBER(active_sandbox_flags)
   IPC_STRUCT_TRAITS_MEMBER(frame_policy)
   IPC_STRUCT_TRAITS_MEMBER(accumulated_csp_headers)
   IPC_STRUCT_TRAITS_MEMBER(scope)
   IPC_STRUCT_TRAITS_MEMBER(insecure_request_policy)
   IPC_STRUCT_TRAITS_MEMBER(insecure_navigations_set)
   IPC_STRUCT_TRAITS_MEMBER(has_potentially_trustworthy_unique_origin)
+  IPC_STRUCT_TRAITS_MEMBER(has_received_user_gesture)
   IPC_STRUCT_TRAITS_MEMBER(has_received_user_gesture_before_nav)
 IPC_STRUCT_TRAITS_END()
 
diff --git a/content/common/frame_replication_state.h b/content/common/frame_replication_state.h
index c969449a..f90c925 100644
--- a/content/common/frame_replication_state.h
+++ b/content/common/frame_replication_state.h
@@ -133,6 +133,9 @@
   // Whether the frame has received a user gesture in a previous navigation so
   // long as a the frame has staying on the same eTLD+1.
   bool has_received_user_gesture_before_nav;
+
+  // IMPORTANT NOTE: When adding a new member to this struct, don't forget to
+  // also add a corresponding entry to the struct traits in frame_messages.h!
 };
 
 }  // namespace content
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCoreImpl.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCoreImpl.java
index 85152da1..6d42fe6 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCoreImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCoreImpl.java
@@ -407,11 +407,6 @@
         nativeResetGestureDetection(mNativeContentViewCore);
     }
 
-    @Override
-    public boolean isAttachedToWindow() {
-        return mAttachedToWindow;
-    }
-
     @SuppressWarnings("javadoc")
     @Override
     public void onAttachedToWindow() {
@@ -684,12 +679,14 @@
         // action mode menu items according to the new rotation. So Chrome
         // has to re-create the action mode.
         if (mWebContents != null) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
-                    && getSelectionPopupController().isActionModeValid()) {
+            SelectionPopupControllerImpl controller = getSelectionPopupController();
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && controller != null
+                    && controller.isActionModeValid()) {
                 hidePopupsAndPreserveSelection();
-                getSelectionPopupController().showActionModeOrClearOnFailure();
+                controller.showActionModeOrClearOnFailure();
             }
-            getTextSuggestionHost().hidePopups();
+            TextSuggestionHost host = getTextSuggestionHost();
+            if (host != null) host.hidePopups();
         }
 
         int rotationDegrees = 0;
diff --git a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
index ead39d3..5c9c271 100644
--- a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
@@ -711,9 +711,11 @@
 
         setPasteAsPlainTextMenuItemTitle(menu);
 
-        if (mClassificationResult != null && mAdditionalMenuItemProvider != null) {
+        Context windowContext = mWindowAndroid.getContext().get();
+        if (mClassificationResult != null && mAdditionalMenuItemProvider != null
+                && windowContext != null) {
             mAdditionalMenuItemProvider.addMenuItems(
-                    mContext, menu, mClassificationResult.textClassification);
+                    windowContext, menu, mClassificationResult.textClassification);
         }
 
         if (!hasSelection() || isSelectionPassword()) return;
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content_public/browser/ContentViewCore.java
index 54d55ca..a74f242 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/ContentViewCore.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/ContentViewCore.java
@@ -160,11 +160,6 @@
     void onHide();
 
     /**
-     * Whether or not the associated ContentView is currently attached to a window.
-     */
-    boolean isAttachedToWindow();
-
-    /**
      * @see View#onAttachedToWindow()
      */
     void onAttachedToWindow();
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index e392d557..38e5383 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -707,17 +707,16 @@
   // Returns the WakeLockContext accociated with this WebContents.
   virtual device::mojom::WakeLockContext* GetWakeLockContext() = 0;
 
-  typedef base::Callback<void(
-      int, /* id */
-      int, /* HTTP status code */
-      const GURL&, /* image_url */
+  using ImageDownloadCallback = base::OnceCallback<void(
+      int,                          /* id */
+      int,                          /* HTTP status code */
+      const GURL&,                  /* image_url */
       const std::vector<SkBitmap>&, /* bitmaps */
       /* The sizes in pixel of the bitmaps before they were resized due to the
          max bitmap size passed to DownloadImage(). Each entry in the bitmaps
          vector corresponds to an entry in the sizes vector. If a bitmap was
          resized, there should be a single returned bitmap. */
-      const std::vector<gfx::Size>&)>
-          ImageDownloadCallback;
+      const std::vector<gfx::Size>&)>;
 
   // Sends a request to download the given image |url| and returns the unique
   // id of the download request. When the download is finished, |callback| will
@@ -734,7 +733,7 @@
                             bool is_favicon,
                             uint32_t max_bitmap_size,
                             bool bypass_cache,
-                            const ImageDownloadCallback& callback) = 0;
+                            ImageDownloadCallback callback) = 0;
 
   // Returns true if the WebContents is responsible for displaying a subframe
   // in a different process from its parent page.
@@ -762,11 +761,11 @@
   // The callback invoked when the renderer responds to a request for the main
   // frame document's manifest. The url will be empty if the document specifies
   // no manifest, and the manifest will be empty if any other failures occurred.
-  typedef base::Callback<void(const GURL&, const Manifest&)>
-      GetManifestCallback;
+  using GetManifestCallback =
+      base::OnceCallback<void(const GURL&, const Manifest&)>;
 
   // Requests the manifest URL and the Manifest of the main frame's document.
-  virtual void GetManifest(const GetManifestCallback& callback) = 0;
+  virtual void GetManifest(GetManifestCallback callback) = 0;
 
   // Returns whether the renderer is in fullscreen mode.
   virtual bool IsFullscreenForCurrentTab() const = 0;
diff --git a/content/public/common/subresource_load_info.mojom b/content/public/common/subresource_load_info.mojom
index 3cb15c9..d4650972 100644
--- a/content/public/common/subresource_load_info.mojom
+++ b/content/public/common/subresource_load_info.mojom
@@ -23,6 +23,9 @@
   // The resource type.
   ResourceType resource_type;
 
+  // The mime type.
+  string mime_type;
+
   // The host IP.
   net.interfaces.IPAddress? ip;
 
diff --git a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestContentViewCore.java b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestContentViewCore.java
index 91e9dbb4..dc751dd6 100644
--- a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestContentViewCore.java
+++ b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestContentViewCore.java
@@ -56,11 +56,6 @@
     public void onHide() {}
 
     @Override
-    public boolean isAttachedToWindow() {
-        return false;
-    }
-
-    @Override
     public void onAttachedToWindow() {}
 
     @Override
diff --git a/content/renderer/fetchers/multi_resolution_image_resource_fetcher.cc b/content/renderer/fetchers/multi_resolution_image_resource_fetcher.cc
index c386583..f0632fc 100644
--- a/content/renderer/fetchers/multi_resolution_image_resource_fetcher.cc
+++ b/content/renderer/fetchers/multi_resolution_image_resource_fetcher.cc
@@ -28,8 +28,8 @@
     int id,
     WebURLRequest::RequestContext request_context,
     blink::mojom::FetchCacheMode cache_mode,
-    const Callback& callback)
-    : callback_(callback),
+    Callback callback)
+    : callback_(std::move(callback)),
       id_(id),
       http_status_code_(0),
       image_url_(image_url) {
@@ -73,17 +73,15 @@
     // If we get here, it means no image from server or couldn't decode the
     // response as an image. The delegate will see an empty vector.
 
-  // Take a reference to the callback as running the callback may lead to our
-  // destruction.
-  Callback callback = callback_;
-  std::move(callback).Run(this, bitmaps);
+  // Take local ownership of the callback as running the callback may lead to
+  // our destruction.
+  base::ResetAndReturn(&callback_).Run(this, bitmaps);
 }
 
 void MultiResolutionImageResourceFetcher::OnRenderFrameDestruct() {
-  // Take a reference to the callback as running the callback may lead to our
-  // destruction.
-  Callback callback = callback_;
-  std::move(callback).Run(this, std::vector<SkBitmap>());
+  // Take local ownership of the callback as running the callback may lead to
+  // our destruction.
+  base::ResetAndReturn(&callback_).Run(this, std::vector<SkBitmap>());
 }
 
 }  // namespace content
diff --git a/content/renderer/fetchers/multi_resolution_image_resource_fetcher.h b/content/renderer/fetchers/multi_resolution_image_resource_fetcher.h
index e748c3f..8fda30c 100644
--- a/content/renderer/fetchers/multi_resolution_image_resource_fetcher.h
+++ b/content/renderer/fetchers/multi_resolution_image_resource_fetcher.h
@@ -30,8 +30,8 @@
 // an image. Useful for favicons.
 class MultiResolutionImageResourceFetcher {
  public:
-  typedef base::Callback<void(MultiResolutionImageResourceFetcher*,
-                              const std::vector<SkBitmap>&)> Callback;
+  using Callback = base::OnceCallback<void(MultiResolutionImageResourceFetcher*,
+                                           const std::vector<SkBitmap>&)>;
 
   MultiResolutionImageResourceFetcher(
       const GURL& image_url,
@@ -39,7 +39,7 @@
       int id,
       blink::WebURLRequest::RequestContext request_context,
       blink::mojom::FetchCacheMode cache_mode,
-      const Callback& callback);
+      Callback callback);
 
   virtual ~MultiResolutionImageResourceFetcher();
 
diff --git a/content/renderer/image_downloader/image_downloader_base.cc b/content/renderer/image_downloader/image_downloader_base.cc
index 9167646..174adcf 100644
--- a/content/renderer/image_downloader/image_downloader_base.cc
+++ b/content/renderer/image_downloader/image_downloader_base.cc
@@ -62,28 +62,27 @@
 void ImageDownloaderBase::DownloadImage(const GURL& image_url,
                                         bool is_favicon,
                                         bool bypass_cache,
-                                        const DownloadCallback& callback) {
-  std::vector<SkBitmap> result_images;
-
-  if (image_url.SchemeIs(url::kDataScheme)) {
-    SkBitmap data_image = ImageFromDataUrl(image_url);
-    // Drop null or empty SkBitmap.
-    if (!data_image.drawsNothing())
-      result_images.push_back(data_image);
-  } else {
-    if (FetchImage(image_url, is_favicon, bypass_cache, callback)) {
-      // Will complete asynchronously via ImageDownloaderBase::DidFetchImage
-      return;
-    }
+                                        DownloadCallback callback) {
+  if (!image_url.SchemeIs(url::kDataScheme)) {
+    FetchImage(image_url, is_favicon, bypass_cache, std::move(callback));
+    // Will complete asynchronously via ImageDownloaderBase::DidFetchImage.
+    return;
   }
 
-  callback.Run(0, result_images);
+  std::vector<SkBitmap> result_images;
+  SkBitmap data_image = ImageFromDataUrl(image_url);
+
+  // Drop null or empty SkBitmap.
+  if (!data_image.drawsNothing())
+    result_images.push_back(data_image);
+
+  std::move(callback).Run(0, result_images);
 }
 
-bool ImageDownloaderBase::FetchImage(const GURL& image_url,
+void ImageDownloaderBase::FetchImage(const GURL& image_url,
                                      bool is_favicon,
                                      bool bypass_cache,
-                                     const DownloadCallback& callback) {
+                                     DownloadCallback callback) {
   blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
   DCHECK(frame);
 
@@ -95,13 +94,12 @@
                      : WebURLRequest::kRequestContextImage,
           bypass_cache ? blink::mojom::FetchCacheMode::kBypassCache
                        : blink::mojom::FetchCacheMode::kDefault,
-          base::Bind(&ImageDownloaderBase::DidFetchImage,
-                     base::Unretained(this), callback)));
-  return true;
+          base::BindOnce(&ImageDownloaderBase::DidFetchImage,
+                         base::Unretained(this), std::move(callback))));
 }
 
 void ImageDownloaderBase::DidFetchImage(
-    const DownloadCallback& callback,
+    DownloadCallback callback,
     MultiResolutionImageResourceFetcher* fetcher,
     const std::vector<SkBitmap>& images) {
   int32_t http_status_code = fetcher->http_status_code();
@@ -121,7 +119,7 @@
   }
 
   // |this| may be destructed after callback is run.
-  callback.Run(http_status_code, images);
+  std::move(callback).Run(http_status_code, images);
 }
 
 void ImageDownloaderBase::OnDestruct() {
diff --git a/content/renderer/image_downloader/image_downloader_base.h b/content/renderer/image_downloader/image_downloader_base.h
index c444bc3..0c479fe 100644
--- a/content/renderer/image_downloader/image_downloader_base.h
+++ b/content/renderer/image_downloader/image_downloader_base.h
@@ -28,13 +28,13 @@
   ~ImageDownloaderBase() override;
 
   using DownloadCallback =
-      base::Callback<void(int32_t, const std::vector<SkBitmap>&)>;
-  // Request to aynchronously download an image. When done, |callback| will be
+      base::OnceCallback<void(int32_t, const std::vector<SkBitmap>&)>;
+  // Request to asynchronously download an image. When done, |callback| will be
   // called.
   void DownloadImage(const GURL& url,
                      bool is_favicon,
                      bool bypass_cache,
-                     const DownloadCallback& callback);
+                     DownloadCallback callback);
 
  protected:
   // RenderFrameObserver implementation.
@@ -42,19 +42,18 @@
 
  private:
   // Requests to fetch an image. When done, the image downloader is notified by
-  // way of DidFetchImage. Returns true if the request was successfully started,
-  // false otherwise. If the image is a favicon, cookies will not be sent nor
-  // accepted during download. If the image has multiple frames, all frames are
-  // returned.
-  bool FetchImage(const GURL& image_url,
+  // way of DidFetchImage. If the image is a favicon, cookies will not be sent
+  // nor accepted during download. If the image has multiple frames, all frames
+  // are returned.
+  void FetchImage(const GURL& image_url,
                   bool is_favicon,
                   bool bypass_cache,
-                  const DownloadCallback& callback);
+                  DownloadCallback callback);
 
   // This callback is triggered when FetchImage completes, either
-  // succesfully or with a failure. See FetchImage for more
+  // successfully or with a failure. See FetchImage for more
   // details.
-  void DidFetchImage(const DownloadCallback& callback,
+  void DidFetchImage(DownloadCallback callback,
                      MultiResolutionImageResourceFetcher* fetcher,
                      const std::vector<SkBitmap>& images);
 
diff --git a/content/renderer/image_downloader/image_downloader_impl.cc b/content/renderer/image_downloader/image_downloader_impl.cc
index 6a70cc4..f20ad65 100644
--- a/content/renderer/image_downloader/image_downloader_impl.cc
+++ b/content/renderer/image_downloader/image_downloader_impl.cc
@@ -119,8 +119,9 @@
 
   ImageDownloaderBase::DownloadImage(
       image_url, is_favicon, bypass_cache,
-      base::Bind(&ImageDownloaderImpl::DidDownloadImage, base::Unretained(this),
-                 max_bitmap_size, base::Passed(&callback)));
+      base::BindOnce(&ImageDownloaderImpl::DidDownloadImage,
+                     base::Unretained(this), max_bitmap_size,
+                     std::move(callback)));
 }
 
 void ImageDownloaderImpl::DidDownloadImage(
diff --git a/content/renderer/loader/resource_dispatcher.cc b/content/renderer/loader/resource_dispatcher.cc
index 4f23b60..5d42418 100644
--- a/content/renderer/loader/resource_dispatcher.cc
+++ b/content/renderer/loader/resource_dispatcher.cc
@@ -178,6 +178,7 @@
   }
 
   if (!IsResourceTypeFrame(request_info->resource_type)) {
+    request_info->mime_type = response_head.mime_type;
     NotifySubresourceStarted(RenderThreadImpl::DeprecatedGetMainTaskRunner(),
                              request_info->render_frame_id,
                              request_info->response_url,
@@ -273,6 +274,7 @@
     subresource_load_info->resource_type = request_info->resource_type;
     if (request_info->parsed_ip.IsValid())
       subresource_load_info->ip = request_info->parsed_ip;
+    subresource_load_info->mime_type = request_info->mime_type;
     subresource_load_info->was_cached = status.exists_in_cache;
     NotifySubresourceLoadComplete(
         RenderThreadImpl::DeprecatedGetMainTaskRunner(),
diff --git a/content/renderer/loader/resource_dispatcher.h b/content/renderer/loader/resource_dispatcher.h
index 4f81b7d7..5ec0311 100644
--- a/content/renderer/loader/resource_dispatcher.h
+++ b/content/renderer/loader/resource_dispatcher.h
@@ -192,6 +192,7 @@
     linked_ptr<base::SharedMemory> buffer;
     int buffer_size;
     net::IPAddress parsed_ip;
+    std::string mime_type;
 
     // For mojo loading.
     std::unique_ptr<ThrottlingURLLoader> url_loader;
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index 6082aad..13de4ec5 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -847,6 +847,14 @@
         base::UmaHistogramSparse("WebRTC.PeerConnection.SslCipherSuite.Data",
                                  counter);
         break;
+      case webrtc::kEnumCounterSrtpUnprotectError:
+        base::UmaHistogramSparse("WebRTC.PeerConnection.SrtpUnprotectError",
+                                 counter);
+        break;
+      case webrtc::kEnumCounterSrtcpUnprotectError:
+        base::UmaHistogramSparse("WebRTC.PeerConnection.SrtcpUnprotectError",
+                                 counter);
+        break;
       default:
         // The default clause is expected to reach when new enum types are
         // added.
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index cc58867..97dcbe4 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -2637,7 +2637,8 @@
 void RenderThreadImpl::OnRendererInterfaceRequest(
     mojom::RendererAssociatedRequest request) {
   DCHECK(!renderer_binding_.is_bound());
-  renderer_binding_.Bind(std::move(request));
+  renderer_binding_.Bind(std::move(request),
+                         GetRendererScheduler()->IPCTaskRunner());
 }
 
 bool RenderThreadImpl::NeedsToRecordFirstActivePaint(
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index a111d58..1d0243a 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -842,6 +842,7 @@
   ]
   public_deps = [
     "//mojo/public/mojom/base",
+    "//skia/public/interfaces",
     "//ui/gfx/geometry/mojo",
     "//url/mojom:url_mojom_gurl",
   ]
diff --git a/content/shell/browser/layout_test/blink_test_controller.cc b/content/shell/browser/layout_test/blink_test_controller.cc
index 4a11137d..31db9799 100644
--- a/content/shell/browser/layout_test/blink_test_controller.cc
+++ b/content/shell/browser/layout_test/blink_test_controller.cc
@@ -177,8 +177,12 @@
 BlinkTestResultPrinter::~BlinkTestResultPrinter() {
 }
 
+void BlinkTestResultPrinter::StartStateDump() {
+  state_ = DURING_STATE_DUMP;
+}
+
 void BlinkTestResultPrinter::PrintTextHeader() {
-  if (state_ != DURING_TEST)
+  if (state_ != DURING_STATE_DUMP)
     return;
   if (!capture_text_only_)
     *output_ << "Content-Type: text/plain\n";
@@ -237,7 +241,7 @@
 }
 
 void BlinkTestResultPrinter::PrintAudioHeader() {
-  DCHECK_EQ(state_, DURING_TEST);
+  DCHECK_EQ(state_, DURING_STATE_DUMP);
   if (!capture_text_only_)
     *output_ << "Content-Type: audio/wav\n";
   state_ = IN_AUDIO_BLOCK;
@@ -284,7 +288,7 @@
 void BlinkTestResultPrinter::AddErrorMessage(const std::string& message) {
   if (!capture_text_only_)
     *error_ << message << "\n";
-  if (state_ != DURING_TEST)
+  if (state_ != DURING_TEST && state_ != DURING_STATE_DUMP)
     return;
   PrintTextHeader();
   *output_ << message << "\n";
@@ -498,6 +502,7 @@
   prefs_ = WebPreferences();
   should_override_prefs_ = false;
   LayoutTestContentBrowserClient::Get()->SetPopupBlockingEnabled(false);
+  navigation_history_dump_ = "";
 
 #if defined(OS_ANDROID)
   // Re-using the shell's main window on Android causes issues with networking
@@ -547,6 +552,36 @@
       main_render_view_host->GetRoutingID()));
 }
 
+void BlinkTestController::OnInitiateCaptureDump(
+    bool capture_navigation_history) {
+  if (capture_navigation_history) {
+    RenderFrameHost* main_rfh = main_window_->web_contents()->GetMainFrame();
+    for (auto* window : Shell::windows()) {
+      WebContents* web_contents = window->web_contents();
+      // Only capture the history from windows in the same process as the main
+      // window. During layout tests, we only use two processes when a devtools
+      // window is open.
+      // TODO(https://crbug.com/771003): Dump history for all WebContentses, not
+      // just ones that happen to be in the same process as the main test
+      // window's main frame.
+      if (main_rfh->GetProcess() != web_contents->GetMainFrame()->GetProcess())
+        continue;
+
+      navigation_history_dump_ +=
+          "\n============== Back Forward List ==============\n";
+      navigation_history_dump_ += DumpHistoryForWebContents(web_contents);
+      navigation_history_dump_ +=
+          "===============================================\n";
+    }
+  }
+
+  RenderFrameHost* rfh = main_window_->web_contents()->GetMainFrame();
+  printer_->StartStateDump();
+  GetLayoutTestControlPtr(rfh)->CaptureDump(
+      base::BindOnce(&BlinkTestController::OnCaptureDumpCompleted,
+                     weak_factory_.GetWeakPtr()));
+}
+
 bool BlinkTestController::IsMainWindow(WebContents* web_contents) const {
   return main_window_ && web_contents == main_window_->web_contents();
 }
@@ -576,16 +611,12 @@
     IPC_MESSAGE_HANDLER(ShellViewHostMsg_PrintMessage, OnPrintMessage)
     IPC_MESSAGE_HANDLER(ShellViewHostMsg_PrintMessageToStderr,
                         OnPrintMessageToStderr)
-    IPC_MESSAGE_HANDLER(ShellViewHostMsg_TextDump, OnTextDump)
     IPC_MESSAGE_HANDLER(ShellViewHostMsg_InitiateLayoutDump,
                         OnInitiateLayoutDump)
-    IPC_MESSAGE_HANDLER(ShellViewHostMsg_ImageDump, OnImageDump)
-    IPC_MESSAGE_HANDLER(ShellViewHostMsg_AudioDump, OnAudioDump)
     IPC_MESSAGE_HANDLER(ShellViewHostMsg_OverridePreferences,
                         OnOverridePreferences)
     IPC_MESSAGE_HANDLER(ShellViewHostMsg_SetPopupBlockingEnabled,
                         OnSetPopupBlockingEnabled)
-    IPC_MESSAGE_HANDLER(ShellViewHostMsg_TestFinished, OnTestFinished)
     IPC_MESSAGE_HANDLER(ShellViewHostMsg_NavigateSecondaryWindow,
                         OnNavigateSecondaryWindow)
     IPC_MESSAGE_HANDLER(ShellViewHostMsg_GoToOffset, OnGoToOffset)
@@ -823,6 +854,17 @@
   }
 }
 
+void BlinkTestController::OnCaptureDumpCompleted(
+    mojom::LayoutTestDumpPtr dump) {
+  if (dump->audio)
+    OnAudioDump(*dump->audio);
+  if (dump->layout)
+    OnTextDump(*dump->layout);
+  if (!dump->actual_pixel_hash.empty())
+    OnImageDump(dump->actual_pixel_hash, dump->pixels);
+  OnTestFinished();
+}
+
 void BlinkTestController::OnImageDump(const std::string& actual_pixel_hash,
                                       const SkBitmap& image) {
   printer_->PrintImageHeader(actual_pixel_hash, expected_pixel_hash_);
@@ -870,30 +912,11 @@
   printer_->PrintAudioFooter();
 }
 
-void BlinkTestController::OnTextDump(const std::string& dump,
-                                     bool should_dump_history) {
+void BlinkTestController::OnTextDump(const std::string& dump) {
   printer_->PrintTextHeader();
   printer_->PrintTextBlock(dump);
-  if (should_dump_history) {
-    RenderFrameHost* main_rfh = main_window_->web_contents()->GetMainFrame();
-    for (auto* window : Shell::windows()) {
-      WebContents* web_contents = window->web_contents();
-      // Only capture the history from windows in the same process as the main
-      // window. During layout tests, we only use two processes when a devtools
-      // window is open.
-      // TODO(https://crbug.com/771003): Dump history for all WebContentses, not
-      // just ones that happen to be in the same process as the main test
-      // window's main frame.
-      if (main_rfh->GetProcess() != web_contents->GetMainFrame()->GetProcess())
-        continue;
-
-      printer_->PrintTextBlock(
-          "\n============== Back Forward List ==============\n");
-      printer_->PrintTextBlock(DumpHistoryForWebContents(web_contents));
-      printer_->PrintTextBlock(
-          "===============================================\n");
-    }
-  }
+  if (!navigation_history_dump_.empty())
+    printer_->PrintTextBlock(navigation_history_dump_);
   printer_->PrintTextFooter();
 }
 
diff --git a/content/shell/browser/layout_test/blink_test_controller.h b/content/shell/browser/layout_test/blink_test_controller.h
index c927c3c..0176658 100644
--- a/content/shell/browser/layout_test/blink_test_controller.h
+++ b/content/shell/browser/layout_test/blink_test_controller.h
@@ -93,12 +93,14 @@
   void AddErrorMessage(const std::string& message);
 
   void CloseStderr();
+  void StartStateDump();
 
  private:
   void PrintEncodedBinaryData(const std::vector<unsigned char>& data);
 
   enum State {
     DURING_TEST,
+    DURING_STATE_DUMP,
     IN_TEXT_BLOCK,
     IN_AUDIO_BLOCK,
     IN_IMAGE_BLOCK,
@@ -138,6 +140,7 @@
       int sender_process_host_id,
       const base::DictionaryValue& changed_layout_test_runtime_flags);
   void OnTestFinishedInSecondaryRenderer();
+  void OnInitiateCaptureDump(bool capture_navigation_history);
   void OnInspectSecondaryWindow();
 
   // Makes sure that the potentially new renderer associated with |frame| is 1)
@@ -202,7 +205,7 @@
   // Message handlers.
   void OnAudioDump(const std::vector<unsigned char>& audio_dump);
   void OnImageDump(const std::string& actual_pixel_hash, const SkBitmap& image);
-  void OnTextDump(const std::string& dump, bool should_dump_history);
+  void OnTextDump(const std::string& dump);
   void OnInitiateLayoutDump();
   void OnDumpFrameLayoutResponse(int frame_tree_node_id,
                                  const std::string& dump);
@@ -228,6 +231,7 @@
 
   void OnAllServiceWorkersCleared();
   void OnAllSharedWorkersDestroyed();
+  void OnCaptureDumpCompleted(mojom::LayoutTestDumpPtr dump);
 
   std::unique_ptr<BlinkTestResultPrinter> printer_;
 
@@ -295,6 +299,8 @@
   // renderer created while test is in progress).
   base::DictionaryValue accumulated_layout_test_runtime_flags_changes_;
 
+  std::string navigation_history_dump_;
+
   // Map from one frame to one mojo pipe.
   std::map<RenderFrameHost*, mojom::LayoutTestControlAssociatedPtr>
       layout_test_control_map_;
diff --git a/content/shell/browser/layout_test/layout_test_message_filter.cc b/content/shell/browser/layout_test/layout_test_message_filter.cc
index 7d7fa7c..160e997 100644
--- a/content/shell/browser/layout_test/layout_test_message_filter.cc
+++ b/content/shell/browser/layout_test/layout_test_message_filter.cc
@@ -65,6 +65,7 @@
     case LayoutTestHostMsg_ResetPermissions::ID:
     case LayoutTestHostMsg_LayoutTestRuntimeFlagsChanged::ID:
     case LayoutTestHostMsg_TestFinishedInSecondaryRenderer::ID:
+    case LayoutTestHostMsg_InitiateCaptureDump::ID:
     case LayoutTestHostMsg_InspectSecondaryWindow::ID:
     case LayoutTestHostMsg_DeleteAllCookiesForNetworkService::ID:
       return BrowserThread::GetTaskRunnerForThread(BrowserThread::UI).get();
@@ -96,6 +97,8 @@
                         OnLayoutTestRuntimeFlagsChanged)
     IPC_MESSAGE_HANDLER(LayoutTestHostMsg_TestFinishedInSecondaryRenderer,
                         OnTestFinishedInSecondaryRenderer)
+    IPC_MESSAGE_HANDLER(LayoutTestHostMsg_InitiateCaptureDump,
+                        OnInitiateCaptureDump);
     IPC_MESSAGE_HANDLER(LayoutTestHostMsg_InspectSecondaryWindow,
                         OnInspectSecondaryWindow)
     IPC_MESSAGE_UNHANDLED(handled = false)
@@ -248,6 +251,15 @@
     BlinkTestController::Get()->OnTestFinishedInSecondaryRenderer();
 }
 
+void LayoutTestMessageFilter::OnInitiateCaptureDump(
+    bool capture_navigation_history) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (BlinkTestController::Get()) {
+    BlinkTestController::Get()->OnInitiateCaptureDump(
+        capture_navigation_history);
+  }
+}
+
 void LayoutTestMessageFilter::OnInspectSecondaryWindow() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (BlinkTestController::Get())
diff --git a/content/shell/browser/layout_test/layout_test_message_filter.h b/content/shell/browser/layout_test/layout_test_message_filter.h
index 5437671..b105a5a 100644
--- a/content/shell/browser/layout_test/layout_test_message_filter.h
+++ b/content/shell/browser/layout_test/layout_test_message_filter.h
@@ -92,6 +92,7 @@
   void OnLayoutTestRuntimeFlagsChanged(
       const base::DictionaryValue& changed_layout_test_runtime_flags);
   void OnTestFinishedInSecondaryRenderer();
+  void OnInitiateCaptureDump(bool capture_navigation_history);
   void OnInspectSecondaryWindow();
 
   int render_process_id_;
diff --git a/content/shell/common/layout_test.mojom b/content/shell/common/layout_test.mojom
index bf75f38..edf9291 100644
--- a/content/shell/common/layout_test.mojom
+++ b/content/shell/common/layout_test.mojom
@@ -5,6 +5,7 @@
 module content.mojom;
 
 import "mojo/public/mojom/base/file_path.mojom";
+import "skia/public/interfaces/bitmap.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 import "url/mojom/url.mojom";
 
@@ -34,7 +35,22 @@
   gfx.mojom.Size initial_size;
 };
 
+// Results of a CaptureDump call.
+struct LayoutTestDump {
+  // Audio dump.
+  array<uint8>? audio;
+
+  // Layout dump.
+  string? layout;
+
+  // Image dump.
+  skia.mojom.Bitmap? pixels;
+  string actual_pixel_hash;
+};
+
 interface LayoutTestControl {
+  CaptureDump() => (LayoutTestDump result);
+
   // Dumps the frame's contents into a string.
   DumpFrameLayout() => (string frame_layout_dump);
 
diff --git a/content/shell/common/layout_test/layout_test_messages.h b/content/shell/common/layout_test/layout_test_messages.h
index 3d94a0cc..0d0617c 100644
--- a/content/shell/common/layout_test/layout_test_messages.h
+++ b/content/shell/common/layout_test/layout_test_messages.h
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Multiply-included file, no traditional include guard.
+// no-include-guard-because-multiply-included
 #include <string>
 #include <vector>
 
@@ -44,6 +44,8 @@
                     GURL /* embedding_origin */)
 IPC_MESSAGE_ROUTED0(LayoutTestHostMsg_ResetPermissions)
 IPC_MESSAGE_ROUTED0(LayoutTestHostMsg_InspectSecondaryWindow)
+IPC_MESSAGE_ROUTED1(LayoutTestHostMsg_InitiateCaptureDump,
+                    bool /* should dump navigation history */)
 
 // Notifies the browser that one of renderers has changed layout test runtime
 // flags (i.e. has set dump_as_text).
diff --git a/content/shell/common/shell_messages.h b/content/shell/common/shell_messages.h
index d5c550b..be22d21a 100644
--- a/content/shell/common/shell_messages.h
+++ b/content/shell/common/shell_messages.h
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Multiply-included file, no traditional include guard.
+// no-include-guard-because-multiply-included
 #include <string>
 #include <vector>
 
@@ -36,28 +36,12 @@
 IPC_MESSAGE_ROUTED1(ShellViewMsg_LayoutDumpCompleted,
                     std::string /* completed/stitched layout dump */)
 
-// Send a text dump of the WebContents to the render host.
-IPC_MESSAGE_ROUTED2(ShellViewHostMsg_TextDump,
-                    std::string /* dump */,
-                    bool /* should_dump_history */)
-
 // Asks the browser process to perform a layout dump spanning all the
 // (potentially cross-process) frames.  This goes through multiple
 // LayoutTestControl.DumpFrameLayout calls and ends with sending of
 // ShellViewMsg_LayoutDumpCompleted.
 IPC_MESSAGE_ROUTED0(ShellViewHostMsg_InitiateLayoutDump)
 
-// Send an image dump of the WebContents to the render host.
-IPC_MESSAGE_ROUTED2(ShellViewHostMsg_ImageDump,
-                    std::string /* actual pixel hash */,
-                    SkBitmap /* image */)
-
-// Send an audio dump to the render host.
-IPC_MESSAGE_ROUTED1(ShellViewHostMsg_AudioDump,
-                    std::vector<unsigned char> /* audio data */)
-
-IPC_MESSAGE_ROUTED0(ShellViewHostMsg_TestFinished)
-
 IPC_MESSAGE_ROUTED0(ShellViewHostMsg_ResetDone)
 
 // WebTestDelegate related.
diff --git a/content/shell/renderer/layout_test/blink_test_runner.cc b/content/shell/renderer/layout_test/blink_test_runner.cc
index d00750ad..e5b415e2 100644
--- a/content/shell/renderer/layout_test/blink_test_runner.cc
+++ b/content/shell/renderer/layout_test/blink_test_runner.cc
@@ -538,14 +538,21 @@
 void BlinkTestRunner::TestFinished() {
   test_runner::WebTestInterfaces* interfaces =
       LayoutTestRenderThreadObserver::GetInstance()->test_interfaces();
+  // We might get multiple TestFinished calls, ensure to only process the dump
+  // once.
+  if (!interfaces->TestIsRunning())
+    return;
   interfaces->SetTestIsRunning(false);
 
+  // If we're not in the main frame, then ask the browser to redirect the call
+  // to the main frame instead.
   if (!is_main_window_ || !render_view()->GetMainRenderFrame()) {
     RenderThread::Get()->Send(
         new LayoutTestHostMsg_TestFinishedInSecondaryRenderer());
     return;
   }
 
+  // Now we know that we're in the main frame, we should generate dump results.
   // Clean out the lifecycle if needed before capturing the layout tree
   // dump and pixels from the compositor.
   render_view()
@@ -554,7 +561,129 @@
       ->ToWebLocalFrame()
       ->FrameWidget()
       ->UpdateAllLifecyclePhases();
-  CaptureDump();
+
+  // Initialize a new dump results object which we will populate in the calls
+  // below.
+  dump_result_ = mojom::LayoutTestDump::New();
+
+  CaptureLocalAudioDump();
+  // TODO(vmpstr): Sometimes the layout isn't stable, which means that if we
+  // just ask the browser to ask us to do a dump, the layout would be different
+  // compared to if we do it now. This probably needs to be rebaselined. But for
+  // now, just capture a local layout first.
+  CaptureLocalLayoutDump();
+  // TODO(vmpstr): This code should move to the browser, but since again some
+  // tests seem to be timing dependent, capture a local pixels dump first. Ask
+  // the browser to initiate a dump.
+  CaptureLocalPixelsDump();
+
+  // Request the browser to send us a callback through which we will return the
+  // results.
+  Send(new LayoutTestHostMsg_InitiateCaptureDump(
+      routing_id(), interfaces->TestRunner()->ShouldDumpBackForwardList()));
+}
+
+void BlinkTestRunner::CaptureLocalAudioDump() {
+  TRACE_EVENT0("shell", "BlinkTestRunner::CaptureLocalAudioDump");
+  test_runner::WebTestInterfaces* interfaces =
+      LayoutTestRenderThreadObserver::GetInstance()->test_interfaces();
+  if (!interfaces->TestRunner()->ShouldDumpAsAudio())
+    return;
+
+  dump_result_->audio.emplace();
+  interfaces->TestRunner()->GetAudioData(&*dump_result_->audio);
+}
+
+void BlinkTestRunner::CaptureLocalLayoutDump() {
+  TRACE_EVENT0("shell", "BlinkTestRunner::CaptureLocalLayoutDump");
+  test_runner::WebTestInterfaces* interfaces =
+      LayoutTestRenderThreadObserver::GetInstance()->test_interfaces();
+
+  if (interfaces->TestRunner()->ShouldDumpAsAudio())
+    return;
+
+  std::string layout;
+  if (interfaces->TestRunner()->HasCustomTextDump(&layout)) {
+    dump_result_->layout.emplace(layout + "\n");
+  } else if (!interfaces->TestRunner()->IsRecursiveLayoutDumpRequested()) {
+    dump_result_->layout.emplace(interfaces->TestRunner()->DumpLayout(
+        render_view()->GetMainRenderFrame()->GetWebFrame()));
+  } else {
+    // TODO(vmpstr): Since CaptureDump is called from the browser, we can be
+    // smart and move this logic directly to the browser.
+    waiting_for_layout_dump_results_ = true;
+    Send(new ShellViewHostMsg_InitiateLayoutDump(routing_id()));
+  }
+}
+
+void BlinkTestRunner::CaptureLocalPixelsDump() {
+  TRACE_EVENT0("shell", "BlinkTestRunner::CaptureLocalPixelsDump");
+  test_runner::WebTestInterfaces* interfaces =
+      LayoutTestRenderThreadObserver::GetInstance()->test_interfaces();
+  if (!test_config_->enable_pixel_dumping ||
+      !interfaces->TestRunner()->ShouldGeneratePixelResults() ||
+      interfaces->TestRunner()->ShouldDumpAsAudio()) {
+    return;
+  }
+
+  CHECK(render_view()->GetWebView()->IsAcceleratedCompositingActive());
+
+  // Test finish should only be processed in the BlinkTestRunner associated
+  // with the current, non-swapped-out RenderView.
+  DCHECK(render_view()->GetWebView()->MainFrame()->IsWebLocalFrame());
+
+  // TODO(vmpstr): We should move pixel capturing to the browser in order to
+  // start implementing OOPIF pixel dump support.
+  waiting_for_pixels_dump_result_ = true;
+  interfaces->TestRunner()->DumpPixelsAsync(
+      render_view()->GetWebView()->MainFrame()->ToWebLocalFrame(),
+      base::BindOnce(&BlinkTestRunner::OnPixelsDumpCompleted,
+                     base::Unretained(this)));
+}
+
+void BlinkTestRunner::OnLayoutDumpCompleted(std::string completed_layout_dump) {
+  dump_result_->layout.emplace(completed_layout_dump);
+  waiting_for_layout_dump_results_ = false;
+  CaptureDumpComplete();
+}
+
+void BlinkTestRunner::OnPixelsDumpCompleted(const SkBitmap& snapshot) {
+  DCHECK_NE(0, snapshot.info().width());
+  DCHECK_NE(0, snapshot.info().height());
+
+  // The snapshot arrives from the GPU process via shared memory. Because MSan
+  // can't track initializedness across processes, we must assure it that the
+  // pixels are in fact initialized.
+  MSAN_UNPOISON(snapshot.getPixels(), snapshot.computeByteSize());
+  base::MD5Digest digest;
+  base::MD5Sum(snapshot.getPixels(), snapshot.computeByteSize(), &digest);
+  std::string actual_pixel_hash = base::MD5DigestToBase16(digest);
+
+  dump_result_->actual_pixel_hash = actual_pixel_hash;
+  if (actual_pixel_hash != test_config_->expected_pixel_hash)
+    dump_result_->pixels = snapshot;
+
+  waiting_for_pixels_dump_result_ = false;
+  CaptureDumpComplete();
+}
+
+void BlinkTestRunner::CaptureDumpComplete() {
+  // Abort if we're still waiting for some results.
+  if (waiting_for_layout_dump_results_ || waiting_for_pixels_dump_result_)
+    return;
+
+  // Once we captured everything, we can stop loading. Note that we can't stop
+  // earlier, since partial load tests will have a different pixel output than
+  // expected.
+  render_view()->GetWebView()->MainFrame()->StopLoading();
+
+  // Abort if the browser didn't ask us for the dump yet.
+  if (!dump_callback_)
+    return;
+
+  std::move(dump_callback_).Run(std::move(dump_result_));
+  dump_callback_.Reset();
+  dump_result_.reset();
 }
 
 void BlinkTestRunner::CloseRemainingWindows() {
@@ -757,101 +886,19 @@
   }
 }
 
+void BlinkTestRunner::CaptureDump(
+    mojom::LayoutTestControl::CaptureDumpCallback callback) {
+  // TODO(vmpstr): This is only called on the main frame. One suggestion is to
+  // split the interface on which this call lives so that it is only accessible
+  // to the main frame (as opposed to all frames).
+  DCHECK(is_main_window_ && render_view()->GetMainRenderFrame());
+
+  dump_callback_ = std::move(callback);
+  CaptureDumpComplete();
+}
+
 // Private methods  -----------------------------------------------------------
 
-void BlinkTestRunner::CaptureDump() {
-  test_runner::WebTestInterfaces* interfaces =
-      LayoutTestRenderThreadObserver::GetInstance()->test_interfaces();
-  TRACE_EVENT0("shell", "BlinkTestRunner::CaptureDump");
-
-  if (interfaces->TestRunner()->ShouldDumpAsAudio()) {
-    std::vector<unsigned char> vector_data;
-    interfaces->TestRunner()->GetAudioData(&vector_data);
-    Send(new ShellViewHostMsg_AudioDump(routing_id(), vector_data));
-    CaptureDumpContinued();
-    return;
-  }
-
-  std::string custom_text_dump;
-  if (interfaces->TestRunner()->HasCustomTextDump(&custom_text_dump)) {
-    Send(new ShellViewHostMsg_TextDump(routing_id(), custom_text_dump + "\n",
-                                       false));
-    CaptureDumpContinued();
-    return;
-  }
-
-  if (!interfaces->TestRunner()->IsRecursiveLayoutDumpRequested()) {
-    std::string layout_dump = interfaces->TestRunner()->DumpLayout(
-        render_view()->GetMainRenderFrame()->GetWebFrame());
-    OnLayoutDumpCompleted(std::move(layout_dump));
-    return;
-  }
-
-  Send(new ShellViewHostMsg_InitiateLayoutDump(routing_id()));
-  // OnLayoutDumpCompleted will be eventually called by an IPC from the browser.
-}
-
-void BlinkTestRunner::OnLayoutDumpCompleted(std::string completed_layout_dump) {
-  test_runner::WebTestInterfaces* interfaces =
-      LayoutTestRenderThreadObserver::GetInstance()->test_interfaces();
-  Send(new ShellViewHostMsg_TextDump(
-      routing_id(), std::move(completed_layout_dump),
-      interfaces->TestRunner()->ShouldDumpBackForwardList()));
-  CaptureDumpContinued();
-}
-
-void BlinkTestRunner::CaptureDumpContinued() {
-  test_runner::WebTestInterfaces* interfaces =
-      LayoutTestRenderThreadObserver::GetInstance()->test_interfaces();
-  if (test_config_->enable_pixel_dumping &&
-      interfaces->TestRunner()->ShouldGeneratePixelResults() &&
-      !interfaces->TestRunner()->ShouldDumpAsAudio()) {
-    CHECK(render_view()->GetWebView()->IsAcceleratedCompositingActive());
-
-    // Test finish should only be processed in the BlinkTestRunner associated
-    // with the current, non-swapped-out RenderView.
-    DCHECK(render_view()->GetWebView()->MainFrame()->IsWebLocalFrame());
-
-    interfaces->TestRunner()->DumpPixelsAsync(
-        render_view()->GetWebView()->MainFrame()->ToWebLocalFrame(),
-        base::BindOnce(&BlinkTestRunner::OnPixelsDumpCompleted,
-                       base::Unretained(this)));
-    return;
-  }
-
-  CaptureDumpComplete();
-}
-
-void BlinkTestRunner::OnPixelsDumpCompleted(const SkBitmap& snapshot) {
-  DCHECK_NE(0, snapshot.info().width());
-  DCHECK_NE(0, snapshot.info().height());
-
-  // The snapshot arrives from the GPU process via shared memory. Because MSan
-  // can't track initializedness across processes, we must assure it that the
-  // pixels are in fact initialized.
-  MSAN_UNPOISON(snapshot.getPixels(), snapshot.computeByteSize());
-  base::MD5Digest digest;
-  base::MD5Sum(snapshot.getPixels(), snapshot.computeByteSize(), &digest);
-  std::string actual_pixel_hash = base::MD5DigestToBase16(digest);
-
-  if (actual_pixel_hash == test_config_->expected_pixel_hash) {
-    SkBitmap empty_image;
-    Send(new ShellViewHostMsg_ImageDump(
-        routing_id(), actual_pixel_hash, empty_image));
-  } else {
-    Send(new ShellViewHostMsg_ImageDump(
-        routing_id(), actual_pixel_hash, snapshot));
-  }
-
-  CaptureDumpComplete();
-}
-
-void BlinkTestRunner::CaptureDumpComplete() {
-  render_view()->GetWebView()->MainFrame()->StopLoading();
-
-  Send(new ShellViewHostMsg_TestFinished(routing_id()));
-}
-
 mojom::LayoutTestBluetoothFakeAdapterSetter&
 BlinkTestRunner::GetBluetoothFakeAdapterSetter() {
   if (!bluetooth_fake_adapter_setter_) {
diff --git a/content/shell/renderer/layout_test/blink_test_runner.h b/content/shell/renderer/layout_test/blink_test_runner.h
index 22cda55..424e28b 100644
--- a/content/shell/renderer/layout_test/blink_test_runner.h
+++ b/content/shell/renderer/layout_test/blink_test_runner.h
@@ -172,6 +172,7 @@
   void OnSetTestConfiguration(mojom::ShellTestConfigurationPtr params);
   void OnReplicateTestConfiguration(mojom::ShellTestConfigurationPtr params);
   void OnSetupSecondaryRenderer();
+  void CaptureDump(mojom::LayoutTestControl::CaptureDumpCallback callback);
 
  private:
   // Message handlers.
@@ -195,11 +196,12 @@
 
   // After finishing the test, retrieves the audio, text, and pixel dumps from
   // the TestRunner library and sends them to the browser process.
-  void CaptureDump();
   void OnLayoutDumpCompleted(std::string completed_layout_dump);
-  void CaptureDumpContinued();
   void OnPixelsDumpCompleted(const SkBitmap& snapshot);
   void CaptureDumpComplete();
+  void CaptureLocalAudioDump();
+  void CaptureLocalLayoutDump();
+  void CaptureLocalPixelsDump();
 
   mojom::LayoutTestBluetoothFakeAdapterSetter&
   GetBluetoothFakeAdapterSetter();
@@ -220,6 +222,11 @@
 
   std::unique_ptr<LeakDetector> leak_detector_;
 
+  mojom::LayoutTestControl::CaptureDumpCallback dump_callback_;
+  mojom::LayoutTestDumpPtr dump_result_;
+  bool waiting_for_layout_dump_results_ = false;
+  bool waiting_for_pixels_dump_result_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(BlinkTestRunner);
 };
 
diff --git a/content/shell/renderer/layout_test/layout_test_render_frame_observer.cc b/content/shell/renderer/layout_test/layout_test_render_frame_observer.cc
index 3ec70f5..ef59351 100644
--- a/content/shell/renderer/layout_test/layout_test_render_frame_observer.cc
+++ b/content/shell/renderer/layout_test/layout_test_render_frame_observer.cc
@@ -42,6 +42,11 @@
   delete this;
 }
 
+void LayoutTestRenderFrameObserver::CaptureDump(CaptureDumpCallback callback) {
+  BlinkTestRunner::Get(render_frame()->GetRenderView())
+      ->CaptureDump(std::move(callback));
+}
+
 void LayoutTestRenderFrameObserver::DumpFrameLayout(
     DumpFrameLayoutCallback callback) {
   std::string dump =
diff --git a/content/shell/renderer/layout_test/layout_test_render_frame_observer.h b/content/shell/renderer/layout_test/layout_test_render_frame_observer.h
index cc86135e..c4c138e 100644
--- a/content/shell/renderer/layout_test/layout_test_render_frame_observer.h
+++ b/content/shell/renderer/layout_test/layout_test_render_frame_observer.h
@@ -22,6 +22,7 @@
   // RenderFrameObserver implementation.
   void OnDestruct() override;
 
+  void CaptureDump(CaptureDumpCallback callback) override;
   void DumpFrameLayout(DumpFrameLayoutCallback callback) override;
   void SetTestConfiguration(mojom::ShellTestConfigurationPtr config) override;
   void ReplicateTestConfiguration(
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index bcb689d..d0f111e 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -200,6 +200,8 @@
     "fake_plugin_service.h",
     "fake_renderer_compositor_frame_sink.cc",
     "fake_renderer_compositor_frame_sink.h",
+    "gpu_browsertest_helpers.cc",
+    "gpu_browsertest_helpers.h",
     "mock_background_sync_controller.cc",
     "mock_background_sync_controller.h",
     "mock_google_streaming_server.cc",
@@ -325,6 +327,7 @@
     "//mojo/edk",
     "//net:test_support",
     "//services/device/public/mojom",
+    "//services/ui/public/cpp/gpu",
 
     # TODO(jam): remove this by adding a public header for the NetworkContext
     # public testing method.
@@ -1021,6 +1024,7 @@
       "../browser/android/render_widget_host_connector_browsertest.cc",
       "../browser/android/render_widget_host_connector_browsertest.h",
       "../browser/media/session/audio_focus_delegate_android_browsertest.cc",
+      "../browser/renderer_host/compositor_impl_android_browsertest.cc",
       "../shell/android/browsertests_apk/content_browser_tests_jni_onload.cc",
     ]
     sources -= [
diff --git a/content/test/DEPS b/content/test/DEPS
index 87aa8cbf..661fab7 100644
--- a/content/test/DEPS
+++ b/content/test/DEPS
@@ -47,4 +47,8 @@
     "+content/shell/common/shell_switches.h",
     "+services/ui/public/cpp/gpu",
   ],
+  "gpu_browsertest_helpers.cc": [
+    "+services/ui/public/cpp/gpu/command_buffer_metrics.h",
+    "+services/ui/public/cpp/gpu/context_provider_command_buffer.h",
+  ],
 }
diff --git a/content/test/data/frame_tree/page_with_positioned_scaled_frame.html b/content/test/data/frame_tree/page_with_positioned_scaled_frame.html
new file mode 100644
index 0000000..db1b86f
--- /dev/null
+++ b/content/test/data/frame_tree/page_with_positioned_scaled_frame.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      iframe {
+        position: absolute;
+        top: 50px;
+        left: 50px;
+        width: 200px;
+        height: 200px;
+        transform: scale(0.5);
+      }
+    </style>
+  </head>
+  <body>
+    <iframe src="/cross-site/baz.com/title1.html"></iframe>
+    This page contains a positioned and scaled cross-origin iframe.
+  </body>
+</html>
diff --git a/content/test/gpu/generate_buildbot_json.py b/content/test/gpu/generate_buildbot_json.py
index 08f64a7..b4e6cbc 100755
--- a/content/test/gpu/generate_buildbot_json.py
+++ b/content/test/gpu/generate_buildbot_json.py
@@ -1015,7 +1015,6 @@
         "gfx_unittests",
         "gn_unittests",
         "google_apis_unittests",
-        "gpu_ipc_service_unittests",
         "gpu_unittests",
         "interactive_ui_tests",
         "ipc_tests",
@@ -2457,6 +2456,8 @@
       {
         'names': [
           'Linux FYI Ozone (Intel)',
+          # anglebug.com/2433
+          'Android FYI Release (Nexus 6)',
         ],
       },
     ],
diff --git a/content/test/gpu_browsertest_helpers.cc b/content/test/gpu_browsertest_helpers.cc
new file mode 100644
index 0000000..e4d6852
--- /dev/null
+++ b/content/test/gpu_browsertest_helpers.cc
@@ -0,0 +1,70 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/test/gpu_browsertest_helpers.h"
+
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "content/browser/browser_main_loop.h"
+#include "content/common/gpu_stream_constants.h"
+#include "gpu/command_buffer/client/shared_memory_limits.h"
+#include "gpu/command_buffer/common/context_creation_attribs.h"
+#include "gpu/ipc/client/gpu_channel_host.h"
+#include "gpu/ipc/common/surface_handle.h"
+#include "services/ui/public/cpp/gpu/command_buffer_metrics.h"
+#include "services/ui/public/cpp/gpu/context_provider_command_buffer.h"
+#include "url/gurl.h"
+
+namespace content {
+
+namespace {
+
+void OnEstablishedGpuChannel(
+    const base::RepeatingClosure& quit_closure,
+    scoped_refptr<gpu::GpuChannelHost>* retvalue,
+    scoped_refptr<gpu::GpuChannelHost> established_host) {
+  if (retvalue)
+    *retvalue = std::move(established_host);
+  quit_closure.Run();
+}
+
+}  // namespace
+
+scoped_refptr<gpu::GpuChannelHost>
+GpuBrowsertestEstablishGpuChannelSyncRunLoop() {
+  gpu::GpuChannelEstablishFactory* factory =
+      content::BrowserMainLoop::GetInstance()->gpu_channel_establish_factory();
+  CHECK(factory);
+  base::RunLoop run_loop;
+  scoped_refptr<gpu::GpuChannelHost> gpu_channel_host;
+  factory->EstablishGpuChannel(base::BindOnce(
+      &OnEstablishedGpuChannel, run_loop.QuitClosure(), &gpu_channel_host));
+  run_loop.Run();
+  return gpu_channel_host;
+}
+
+scoped_refptr<ui::ContextProviderCommandBuffer> GpuBrowsertestCreateContext(
+    scoped_refptr<gpu::GpuChannelHost> gpu_channel_host) {
+  gpu::GpuChannelEstablishFactory* factory =
+      content::BrowserMainLoop::GetInstance()->gpu_channel_establish_factory();
+  // This is for an offscreen context, so the default framebuffer doesn't need
+  // any alpha, depth, stencil, antialiasing.
+  gpu::ContextCreationAttribs attributes;
+  attributes.alpha_size = -1;
+  attributes.depth_size = 0;
+  attributes.stencil_size = 0;
+  attributes.samples = 0;
+  attributes.sample_buffers = 0;
+  attributes.bind_generates_resource = false;
+  constexpr bool automatic_flushes = false;
+  constexpr bool support_locking = false;
+  return base::MakeRefCounted<ui::ContextProviderCommandBuffer>(
+      std::move(gpu_channel_host), factory->GetGpuMemoryBufferManager(),
+      content::kGpuStreamIdDefault, content::kGpuStreamPriorityDefault,
+      gpu::kNullSurfaceHandle, GURL(), automatic_flushes, support_locking,
+      gpu::SharedMemoryLimits(), attributes, nullptr,
+      ui::command_buffer_metrics::OFFSCREEN_CONTEXT_FOR_TESTING);
+}
+
+}  // namespace content
diff --git a/content/test/gpu_browsertest_helpers.h b/content/test/gpu_browsertest_helpers.h
new file mode 100644
index 0000000..bdd2965
--- /dev/null
+++ b/content/test/gpu_browsertest_helpers.h
@@ -0,0 +1,32 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_TEST_GPU_BROWSERTEST_HELPERS_H_
+#define CONTENT_TEST_GPU_BROWSERTEST_HELPERS_H_
+
+#include "base/memory/scoped_refptr.h"
+
+namespace gpu {
+class GpuChannelHost;
+}
+
+namespace ui {
+class ContextProviderCommandBuffer;
+}
+
+namespace content {
+
+// Synchronously establishes a connection to the GPU process and returns the
+// GpuChannelHost.
+scoped_refptr<gpu::GpuChannelHost>
+GpuBrowsertestEstablishGpuChannelSyncRunLoop();
+
+// Creates a new ContextProviderCommandBuffer using the provided
+// GpuChannelHost.
+scoped_refptr<ui::ContextProviderCommandBuffer> GpuBrowsertestCreateContext(
+    scoped_refptr<gpu::GpuChannelHost> gpu_channel_host);
+
+}  // namespace content
+
+#endif  // CONTENT_TEST_GPU_BROWSERTEST_HELPERS_H_
diff --git a/content/test/test_web_contents.cc b/content/test/test_web_contents.cc
index 69a40ee..8f7d027 100644
--- a/content/test/test_web_contents.cc
+++ b/content/test/test_web_contents.cc
@@ -77,11 +77,11 @@
                                    bool is_favicon,
                                    uint32_t max_bitmap_size,
                                    bool bypass_cache,
-                                   const ImageDownloadCallback& callback) {
+                                   ImageDownloadCallback callback) {
   static int g_next_image_download_id = 0;
   ++g_next_image_download_id;
   pending_image_downloads_[url].emplace_back(g_next_image_download_id,
-                                             callback);
+                                             std::move(callback));
   return g_next_image_download_id;
 }
 
@@ -181,7 +181,8 @@
   if (!HasPendingDownloadImage(url))
     return false;
   int id = pending_image_downloads_[url].front().first;
-  ImageDownloadCallback callback = pending_image_downloads_[url].front().second;
+  ImageDownloadCallback callback =
+      std::move(pending_image_downloads_[url].front().second);
   pending_image_downloads_[url].pop_front();
   std::move(callback).Run(id, http_status_code, url, bitmaps,
                           original_bitmap_sizes);
diff --git a/content/test/test_web_contents.h b/content/test/test_web_contents.h
index 73361dd..b11bac2 100644
--- a/content/test/test_web_contents.h
+++ b/content/test/test_web_contents.h
@@ -56,7 +56,7 @@
                     bool is_favicon,
                     uint32_t max_bitmap_size,
                     bool bypass_cache,
-                    const ImageDownloadCallback& callback) override;
+                    ImageDownloadCallback callback) override;
   const GURL& GetLastCommittedURL() const override;
 
   // WebContentsTester implementation.
diff --git a/crypto/nss_util.cc b/crypto/nss_util.cc
index 594507e..6fc56ba 100644
--- a/crypto/nss_util.cc
+++ b/crypto/nss_util.cc
@@ -618,6 +618,11 @@
         initializing_tpm_token_(false),
         chaps_module_(nullptr),
         root_(nullptr) {
+    // Initializing NSS causes us to do blocking IO.
+    // Temporarily allow it until we fix
+    //   http://code.google.com/p/chromium/issues/detail?id=59847
+    base::ThreadRestrictions::ScopedAllowIO allow_io;
+
     // It's safe to construct on any thread, since LazyInstance will prevent any
     // other threads from accessing until the constructor is done.
     thread_checker_.DetachFromThread();
@@ -772,10 +777,6 @@
 }
 
 void EnsureNSSInit() {
-  // Initializing SSL causes us to do blocking IO.
-  // Temporarily allow it until we fix
-  //   http://code.google.com/p/chromium/issues/detail?id=59847
-  base::ThreadRestrictions::ScopedAllowIO allow_io;
   g_nss_singleton.Get();
 }
 
diff --git a/device/geolocation/BUILD.gn b/device/geolocation/BUILD.gn
index 6046c65..1769208 100644
--- a/device/geolocation/BUILD.gn
+++ b/device/geolocation/BUILD.gn
@@ -53,7 +53,6 @@
     "wifi_data_provider_manager.h",
     "wifi_data_provider_win.cc",
     "wifi_data_provider_win.h",
-    "wifi_polling_policy.cc",
     "wifi_polling_policy.h",
   ]
 
diff --git a/device/geolocation/wifi_data_provider_chromeos.cc b/device/geolocation/wifi_data_provider_chromeos.cc
index b53d30ee..b4b4f1a 100644
--- a/device/geolocation/wifi_data_provider_chromeos.cc
+++ b/device/geolocation/wifi_data_provider_chromeos.cc
@@ -36,16 +36,20 @@
 void WifiDataProviderChromeOs::StartDataProvider() {
   DCHECK(CalledOnClientThread());
 
-  if (!WifiPollingPolicy::IsInitialized())
-    WifiPollingPolicy::Initialize(CreatePollingPolicy());
-  DCHECK(WifiPollingPolicy::IsInitialized());
+  DCHECK(polling_policy_ == nullptr);
+  polling_policy_.reset(
+      new GenericWifiPollingPolicy<kDefaultPollingIntervalMilliseconds,
+                                   kNoChangePollingIntervalMilliseconds,
+                                   kTwoNoChangePollingIntervalMilliseconds,
+                                   kNoWifiPollingIntervalMilliseconds>);
 
-  ScheduleStart(WifiPollingPolicy::Get()->PollingInterval());
+  ScheduleStart();
 }
 
 void WifiDataProviderChromeOs::StopDataProvider() {
   DCHECK(CalledOnClientThread());
 
+  polling_policy_.reset();
   ScheduleStop();
 }
 
@@ -79,7 +83,7 @@
   // Schedule next scan if started (StopDataProvider could have been called
   // in between DoWifiScanTaskOnNetworkHandlerThread and this method).
   if (started_)
-    ScheduleNextScan(WifiPollingPolicy::Get()->NoWifiInterval());
+    ScheduleNextScan(polling_policy_->NoWifiInterval());
 }
 
 void WifiDataProviderChromeOs::DidWifiScanTask(const WifiData& new_data) {
@@ -89,8 +93,8 @@
   // Schedule next scan if started (StopDataProvider could have been called
   // in between DoWifiScanTaskOnNetworkHandlerThread and this method).
   if (started_) {
-    WifiPollingPolicy::Get()->UpdatePollingInterval(update_available);
-    ScheduleNextScan(WifiPollingPolicy::Get()->PollingInterval());
+    polling_policy_->UpdatePollingInterval(update_available);
+    ScheduleNextScan(polling_policy_->PollingInterval());
   }
 
   if (update_available || !is_first_scan_complete_) {
@@ -120,7 +124,7 @@
   started_ = false;
 }
 
-void WifiDataProviderChromeOs::ScheduleStart(int interval) {
+void WifiDataProviderChromeOs::ScheduleStart() {
   DCHECK(CalledOnClientThread());
   DCHECK(!started_);
   if (!NetworkHandler::IsInitialized()) {
@@ -128,12 +132,13 @@
     return;
   }
   started_ = true;
-  NetworkHandler::Get()->task_runner()->PostDelayedTask(
+  // Perform first scan ASAP regardless of the polling policy. If this scan
+  // fails we'll retry at a rate in line with the polling policy.
+  NetworkHandler::Get()->task_runner()->PostTask(
       FROM_HERE,
       base::Bind(
           &WifiDataProviderChromeOs::DoWifiScanTaskOnNetworkHandlerThread,
-          this),
-      base::TimeDelta::FromMilliseconds(interval));
+          this));
 }
 
 bool WifiDataProviderChromeOs::GetAccessPointData(
@@ -171,14 +176,6 @@
   return true;
 }
 
-std::unique_ptr<WifiPollingPolicy>
-WifiDataProviderChromeOs::CreatePollingPolicy() {
-  return std::make_unique<GenericWifiPollingPolicy<
-      kDefaultPollingIntervalMilliseconds, kNoChangePollingIntervalMilliseconds,
-      kTwoNoChangePollingIntervalMilliseconds,
-      kNoWifiPollingIntervalMilliseconds>>();
-}
-
 // static
 WifiDataProvider* WifiDataProviderManager::DefaultFactoryFunction() {
   return new WifiDataProviderChromeOs();
diff --git a/device/geolocation/wifi_data_provider_chromeos.h b/device/geolocation/wifi_data_provider_chromeos.h
index 09fcff8..f9630f6f 100644
--- a/device/geolocation/wifi_data_provider_chromeos.h
+++ b/device/geolocation/wifi_data_provider_chromeos.h
@@ -39,7 +39,7 @@
   void ScheduleNextScan(int interval);
 
   // Will schedule starting of the scanning process.
-  void ScheduleStart(int interval);
+  void ScheduleStart();
 
   // Will schedule stopping of the scanning process.
   void ScheduleStop();
@@ -47,8 +47,8 @@
   // Get access point data from chromeos.
   bool GetAccessPointData(WifiData::AccessPointDataSet* data);
 
-  // Create the policy for controlling the polling update interval.
-  std::unique_ptr<WifiPollingPolicy> CreatePollingPolicy();
+  // Controls the polling update interval. (client thread)
+  std::unique_ptr<WifiPollingPolicy> polling_policy_;
 
   // The latest wifi data. (client thread)
   WifiData wifi_data_;
diff --git a/device/geolocation/wifi_data_provider_common.cc b/device/geolocation/wifi_data_provider_common.cc
index d25c992f..615b53c 100644
--- a/device/geolocation/wifi_data_provider_common.cc
+++ b/device/geolocation/wifi_data_provider_common.cc
@@ -35,15 +35,18 @@
     return;
   }
 
-  if (!WifiPollingPolicy::IsInitialized())
-    WifiPollingPolicy::Initialize(CreatePollingPolicy());
-  DCHECK(WifiPollingPolicy::IsInitialized());
+  DCHECK(!polling_policy_);
+  polling_policy_ = CreatePollingPolicy();
+  DCHECK(polling_policy_);
 
-  ScheduleNextScan(WifiPollingPolicy::Get()->PollingInterval());
+  // Perform first scan ASAP regardless of the polling policy. If this scan
+  // fails we'll retry at a rate in line with the polling policy.
+  ScheduleNextScan(0);
 }
 
 void WifiDataProviderCommon::StopDataProvider() {
   wlan_api_.reset();
+  polling_policy_.reset();
 }
 
 bool WifiDataProviderCommon::GetData(WifiData* data) {
@@ -61,12 +64,12 @@
   bool update_available = false;
   WifiData new_data;
   if (!wlan_api_->GetAccessPointData(&new_data.access_point_data)) {
-    ScheduleNextScan(WifiPollingPolicy::Get()->NoWifiInterval());
+    ScheduleNextScan(polling_policy_->NoWifiInterval());
   } else {
     update_available = wifi_data_.DiffersSignificantly(new_data);
     wifi_data_ = new_data;
-    WifiPollingPolicy::Get()->UpdatePollingInterval(update_available);
-    ScheduleNextScan(WifiPollingPolicy::Get()->PollingInterval());
+    polling_policy_->UpdatePollingInterval(update_available);
+    ScheduleNextScan(polling_policy_->PollingInterval());
   }
   if (update_available || !is_first_scan_complete_) {
     is_first_scan_complete_ = true;
diff --git a/device/geolocation/wifi_data_provider_common.h b/device/geolocation/wifi_data_provider_common.h
index ad39a54e..44a312c 100644
--- a/device/geolocation/wifi_data_provider_common.h
+++ b/device/geolocation/wifi_data_provider_common.h
@@ -72,6 +72,9 @@
   // Underlying OS wifi API.
   std::unique_ptr<WlanApiInterface> wlan_api_;
 
+  // Controls the polling update interval.
+  std::unique_ptr<WifiPollingPolicy> polling_policy_;
+
   // Holder for delayed tasks; takes care of cleanup.
   base::WeakPtrFactory<WifiDataProviderCommon> weak_factory_;
 
diff --git a/device/geolocation/wifi_data_provider_common_unittest.cc b/device/geolocation/wifi_data_provider_common_unittest.cc
index 99e6edb..a1cbb62 100644
--- a/device/geolocation/wifi_data_provider_common_unittest.cc
+++ b/device/geolocation/wifi_data_provider_common_unittest.cc
@@ -15,7 +15,6 @@
 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "device/geolocation/wifi_data_provider_manager.h"
-#include "device/geolocation/wifi_polling_policy.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -62,8 +61,7 @@
 class WifiDataProviderCommonWithMock : public WifiDataProviderCommon {
  public:
   WifiDataProviderCommonWithMock()
-      : wlan_api_(new MockWlanApi),
-        polling_policy_(std::make_unique<MockPollingPolicy>()) {}
+      : wlan_api_(new MockWlanApi), polling_policy_(new MockPollingPolicy) {}
 
   // WifiDataProviderCommon
   std::unique_ptr<WlanApiInterface> CreateWlanApi() override {
@@ -100,7 +98,6 @@
   void TearDown() override {
     provider_->RemoveCallback(&wifi_data_callback_);
     provider_->StopDataProvider();
-    WifiPollingPolicy::Shutdown();
   }
 
  protected:
diff --git a/device/geolocation/wifi_polling_policy.cc b/device/geolocation/wifi_polling_policy.cc
deleted file mode 100644
index 01deed3..0000000
--- a/device/geolocation/wifi_polling_policy.cc
+++ /dev/null
@@ -1,38 +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 "device/geolocation/wifi_polling_policy.h"
-
-namespace device {
-
-namespace {
-// Leaks at exit.
-WifiPollingPolicy* g_wifi_polling_policy;
-}  // namespace
-
-// static
-void WifiPollingPolicy::Initialize(std::unique_ptr<WifiPollingPolicy> policy) {
-  DCHECK(!g_wifi_polling_policy);
-  g_wifi_polling_policy = policy.release();
-}
-
-// static
-void WifiPollingPolicy::Shutdown() {
-  if (g_wifi_polling_policy)
-    delete g_wifi_polling_policy;
-  g_wifi_polling_policy = nullptr;
-}
-
-// static
-WifiPollingPolicy* WifiPollingPolicy::Get() {
-  DCHECK(g_wifi_polling_policy);
-  return g_wifi_polling_policy;
-}
-
-// static
-bool WifiPollingPolicy::IsInitialized() {
-  return g_wifi_polling_policy != nullptr;
-}
-
-}  // namespace device
diff --git a/device/geolocation/wifi_polling_policy.h b/device/geolocation/wifi_polling_policy.h
index 226b3852..0ac9203 100644
--- a/device/geolocation/wifi_polling_policy.h
+++ b/device/geolocation/wifi_polling_policy.h
@@ -5,36 +5,21 @@
 #ifndef DEVICE_GEOLOCATION_WIFI_POLLING_POLICY_H_
 #define DEVICE_GEOLOCATION_WIFI_POLLING_POLICY_H_
 
-#include <memory>
-
 #include "base/macros.h"
-#include "base/time/time.h"
-#include "device/geolocation/geolocation_export.h"
 
 namespace device {
 
 // Allows sharing and mocking of the update polling policy function.
-class DEVICE_GEOLOCATION_EXPORT WifiPollingPolicy {
+class WifiPollingPolicy {
  public:
-  virtual ~WifiPollingPolicy() = default;
-
-  // Methods for managing the single instance of WifiPollingPolicy. The WiFi
-  // policy is global so it can outlive the WifiDataProvider instance, which is
-  // shut down and destroyed when no WiFi scanning is active.
-  static void Initialize(std::unique_ptr<WifiPollingPolicy>);
-  static void Shutdown();
-  static WifiPollingPolicy* Get();
-  static bool IsInitialized();
-
+  WifiPollingPolicy() {}
+  virtual ~WifiPollingPolicy() {}
   // Calculates the new polling interval for wiFi scans, given the previous
   // interval and whether the last scan produced new results.
   virtual void UpdatePollingInterval(bool scan_results_differ) = 0;
   virtual int PollingInterval() = 0;
   virtual int NoWifiInterval() = 0;
 
- protected:
-  WifiPollingPolicy() = default;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(WifiPollingPolicy);
 };
@@ -48,7 +33,6 @@
 class GenericWifiPollingPolicy : public WifiPollingPolicy {
  public:
   GenericWifiPollingPolicy() : polling_interval_(DEFAULT_INTERVAL) {}
-
   // WifiPollingPolicy
   void UpdatePollingInterval(bool scan_results_differ) override {
     if (scan_results_differ) {
@@ -61,37 +45,11 @@
       polling_interval_ = TWO_NO_CHANGE_INTERVAL;
     }
   }
-  int PollingInterval() override { return ComputeInterval(polling_interval_); }
-  int NoWifiInterval() override { return ComputeInterval(NO_WIFI_INTERVAL); }
+  int PollingInterval() override { return polling_interval_; }
+  int NoWifiInterval() override { return NO_WIFI_INTERVAL; }
 
  private:
-  int ComputeInterval(int polling_interval) {
-    base::Time now = base::Time::Now();
-
-    int64_t remaining_millis = 0;
-    if (!next_scan_.is_null()) {
-      // Compute the remaining duration of the current interval. If the interval
-      // is not yet complete, we will schedule a scan to occur once it is.
-      base::TimeDelta remaining = next_scan_ - now;
-      remaining_millis = remaining.InMilliseconds();
-    }
-
-    // If the current interval is complete (or if this is our first scan), scan
-    // now and schedule the next scan to occur at |polling_interval|
-    // milliseconds into the future.
-    if (remaining_millis <= 0) {
-      next_scan_ = now + base::TimeDelta::FromMilliseconds(polling_interval);
-      remaining_millis = 0;
-    }
-
-    return remaining_millis;
-  }
-
   int polling_interval_;
-
-  // The scheduled time of the next scan, or a null value if no scan has
-  // occurred yet.
-  base::Time next_scan_;
 };
 
 }  // namespace device
diff --git a/docs/README.md b/docs/README.md
index 975dcb5b..a71feb4 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -287,6 +287,9 @@
 *   [VoiceOver](ios/voiceover.md) - Using Apple's VoiceOver feature with
     Chromium on iOS.
 
+### Memory
+*   [Memory Overview](memory/README.md)
+
 ### Memory Infrastructure Timeline Profiling (MemoryInfra)
 *   [Overview](memory-infra/README.md)
 *   [GPU Profiling](memory-infra/probe-gpu.md)
@@ -295,7 +298,6 @@
 *   [Memory Usage in CC](memory-infra/probe-cc.md)
 *   [Memory Benchmarks](memory-infra/memory_benchmarks.md)
 *   [Heap Profiling](memory-infra/heap_profiler.md)
-*   [Heap Profiling Internals](memory-infra/heap_profiler_internals.md)
 
 ### Misc
 *   [Useful URLs](useful_urls.md) - A collection of links to various tools and
diff --git a/docs/memory-infra/README.md b/docs/memory-infra/README.md
index e0aa5758..dec1038 100644
--- a/docs/memory-infra/README.md
+++ b/docs/memory-infra/README.md
@@ -114,7 +114,6 @@
 
  * [Adding MemoryInfra Tracing to a Component](adding_memory_infra_tracing.md)
  * [GPU Memory Tracing](probe-gpu.md)
- * [Heap Profiler Internals](heap_profiler_internals.md)
  * [Heap Profiling with MemoryInfra](heap_profiler.md)
  * [Startup Tracing with MemoryInfra](memory_infra_startup_tracing.md)
 
diff --git a/docs/memory-infra/heap_profiler_internals.md b/docs/memory-infra/heap_profiler_internals.md
deleted file mode 100644
index d1019c8..0000000
--- a/docs/memory-infra/heap_profiler_internals.md
+++ /dev/null
@@ -1,184 +0,0 @@
-# Heap Profiler Internals
-
-This document describes how the heap profiler works and how to add heap
-profiling support to your allocator. If you just want to know how to use it,
-see [Heap Profiling with MemoryInfra](heap_profiler.md)
-
-[TOC]
-
-## Overview
-
-The heap profiler consists of tree main components:
-
- * **The Context Tracker**: Responsible for providing context (pseudo stack
-   backtrace) when an allocation occurs.
- * **The Allocation Register**: A specialized hash table that stores allocation
-   details by address.
- * **The Heap Dump Writer**: Extracts the most important information from a set
-   of recorded allocations and converts it into a format that can be dumped into
-   the trace log.
-
-These components are designed to work well together, but to be usable
-independently as well.
-
-When there is a way to get notified of all allocations and frees, this is the
-normal flow:
-
- 1. When an allocation occurs, call
-    [`AllocationContextTracker::GetInstanceForCurrentThread()->GetContextSnapshot()`][context-tracker]
-    to get an [`AllocationContext`][alloc-context].
- 2. Insert that context together with the address and size into an
-    [`AllocationRegister`][alloc-register] by calling `Insert()`.
- 3. When memory is freed, remove it from the register with `Remove()`.
- 4. On memory dump, collect the allocations from the register, call
-    [`ExportHeapDump()`][export-heap-dump], and add the generated heap dump to
-    the memory dump.
-
-[context-tracker]:  https://chromium.googlesource.com/chromium/src/+/master/base/trace_event/heap_profiler_allocation_context_tracker.h
-[alloc-context]:    https://chromium.googlesource.com/chromium/src/+/master/base/trace_event/heap_profiler_allocation_context.h
-[alloc-register]:   https://chromium.googlesource.com/chromium/src/+/master/base/trace_event/heap_profiler_allocation_register.h
-[export-heap-dump]: https://chromium.googlesource.com/chromium/src/+/master/base/trace_event/heap_profiler_heap_dump_writer.h
-
-*** aside
-An allocator can skip step 2 and 3 if it is able to store the context itself,
-and if it is able to enumerate all allocations for step 4.
-***
-
-When heap profiling is enabled (the `--enable-heap-profiling` flag is passed),
-the memory dump manager calls `OnHeapProfilingEnabled()` on every
-`MemoryDumpProvider` as early as possible, so allocators can start recording
-allocations. This should be done even when tracing has not been started,
-because these allocations might still be around when a heap dump happens during
-tracing.
-
-## Context Tracker
-
-The [`AllocationContextTracker`][context-tracker] is a thread-local object. Its
-main purpose is to keep track of a pseudo stack of trace events. Chrome has
-been instrumented with lots of `TRACE_EVENT` macros. These trace events push
-their name to a thread-local stack when they go into scope, and pop when they
-go out of scope, if all of the following conditions have been met:
-
- * A trace is being recorded.
- * The category of the event is enabled in the trace config.
- * Heap profiling is enabled (with the `--enable-heap-profiling` flag).
-
-This means that allocations that occur before tracing is started will not have
-backtrace information in their context.
-
-A thread-local instance of the context tracker is initialized lazily when it is
-first accessed. This might be because a trace event pushed or popped, or because
-`GetContextSnapshot()` was called when an allocation occurred.
-
-[`AllocationContext`][alloc-context] is what is used to group and break down
-allocations. Currently `AllocationContext` has the following fields:
-
- * Backtrace: filled by the context tracker, obtained from the thread-local
-   pseudo stack.
- * Type name: to be filled in at a point where the type of a pointer is known,
-   set to _[unknown]_ by default.
-
-It is possible to modify this context after insertion into the register, for
-instance to set the type name if it was not known at the time of allocation.
-
-## Allocation Register
-
-The [`AllocationRegister`][alloc-register] is a hash table specialized for
-storing `(size, AllocationContext)` pairs by address. It has been optimized for
-Chrome's typical number of unfreed allocations, and it is backed by `mmap`
-memory directly so there are no reentrancy issues when using it to record
-`malloc` allocations.
-
-The allocation register is threading-agnostic. Access must be synchronised
-properly.
-
-## Heap Dump Writer
-
-Dumping every single allocation in the allocation register straight into the
-trace log is not an option due to the sheer volume (~300k unfreed allocations).
-The role of the [`ExportHeapDump()`][export-heap-dump] function is to group
-allocations, striking a balance between trace log size and detail.
-
-See the [Heap Dump Format][heap-dump-format] document for more details about the
-structure of the heap dump in the trace log.
-
-[heap-dump-format]: https://docs.google.com/document/d/1NqBg1MzVnuMsnvV1AKLdKaPSPGpd81NaMPVk5stYanQ
-
-## Instrumenting an Allocator
-
-Below is an example of adding heap profiling support to an allocator that has
-an existing memory dump provider.
-
-```cpp
-class FooDumpProvider : public MemoryDumpProvider {
-
-  // Kept as pointer because |AllocationRegister| allocates a lot of virtual
-  // address space when constructed, so only construct it when heap profiling is
-  // enabled.
-  scoped_ptr<AllocationRegister> allocation_register_;
-  Lock allocation_register_lock_;
-
-  static FooDumpProvider* GetInstance();
-
-  void InsertAllocation(void* address, size_t size) {
-    AllocationContext context = AllocationContextTracker::GetInstanceForCurrentThread()->GetContextSnapshot();
-    AutoLock lock(allocation_register_lock_);
-    allocation_register_->Insert(address, size, context);
-  }
-
-  void RemoveAllocation(void* address) {
-    AutoLock lock(allocation_register_lock_);
-    allocation_register_->Remove(address);
-  }
-
-  // Will be called as early as possible by the memory dump manager.
-  void OnHeapProfilingEnabled(bool enabled) override {
-    AutoLock lock(allocation_register_lock_);
-    allocation_register_.reset(new AllocationRegister());
-
-    // At this point, make sure that from now on, for every allocation and
-    // free, |FooDumpProvider::GetInstance()->InsertAllocation()| and
-    // |RemoveAllocation| are called.
-  }
-
-  bool OnMemoryDump(const MemoryDumpArgs& args,
-                    ProcessMemoryDump& pmd) override {
-    // Do regular dumping here.
-
-    // Dump the heap only for detailed dumps.
-    if (args.level_of_detail == MemoryDumpLevelOfDetail::DETAILED) {
-      TraceEventMemoryOverhead overhead;
-      hash_map<AllocationContext, size_t> bytes_by_context;
-
-      {
-        AutoLock lock(allocation_register_lock_);
-        if (allocation_register_) {
-          // Group allocations in the register into |bytes_by_context|, but do
-          // no additional processing inside the lock.
-          for (const auto& alloc_size : *allocation_register_)
-            bytes_by_context[alloc_size.context] += alloc_size.size;
-
-          allocation_register_->EstimateTraceMemoryOverhead(&overhead);
-        }
-      }
-
-      if (!bytes_by_context.empty()) {
-        scoped_refptr<TracedValue> heap_dump = ExportHeapDump(
-            bytes_by_context,
-            pmd->session_state()->stack_frame_deduplicator(),
-            pmb->session_state()->type_name_deduplicator());
-        pmd->AddHeapDump("foo_allocator", heap_dump);
-        overhead.DumpInto("tracing/heap_profiler", pmd);
-      }
-    }
-
-    return true;
-  }
-};
-
-```
-
-*** aside
-The implementation for `malloc` is more complicated because it needs to deal
-with reentrancy.
-***
diff --git a/extensions/browser/api/web_request/web_request_info.cc b/extensions/browser/api/web_request/web_request_info.cc
index 1fc380b..ac1873d 100644
--- a/extensions/browser/api/web_request/web_request_info.cc
+++ b/extensions/browser/api/web_request/web_request_info.cc
@@ -148,7 +148,7 @@
       data_sources->push_back(
           std::make_unique<FileUploadDataSource>(file_reader->path()));
     } else {
-      NOTIMPLEMENTED();
+      DVLOG(1) << "Ignoring upsupported upload data type for WebRequest API.";
     }
   }
 
diff --git a/extensions/browser/app_window/app_window.cc b/extensions/browser/app_window/app_window.cc
index e750b8b..1c1893de 100644
--- a/extensions/browser/app_window/app_window.cc
+++ b/extensions/browser/app_window/app_window.cc
@@ -789,8 +789,8 @@
       true,   // is a favicon
       0,      // no maximum size
       false,  // normal cache policy
-      base::Bind(&AppWindow::DidDownloadFavicon,
-                 image_loader_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&AppWindow::DidDownloadFavicon,
+                     image_loader_ptr_factory_.GetWeakPtr()));
 }
 
 void AppWindow::DidDownloadFavicon(
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 58ab8cf..d580ba1 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1290,6 +1290,7 @@
   VIRTUALKEYBOARDPRIVATE_SETCONTAINERBEHAVIOR,
   QUICKUNLOCKPRIVATE_GETAUTHTOKEN,
   QUICKUNLOCKPRIVATE_SETLOCKSCREENENABLED,
+  LANGUAGESETTINGSPRIVATE_RETRYDOWNLOADDICTIONARY,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/browser/extension_throttle_test_support.cc b/extensions/browser/extension_throttle_test_support.cc
index d5a52289..9ea6988d 100644
--- a/extensions/browser/extension_throttle_test_support.cc
+++ b/extensions/browser/extension_throttle_test_support.cc
@@ -15,7 +15,7 @@
 TestTickClock::~TestTickClock() {
 }
 
-base::TimeTicks TestTickClock::NowTicks() {
+base::TimeTicks TestTickClock::NowTicks() const {
   return now_ticks_;
 }
 
diff --git a/extensions/browser/extension_throttle_test_support.h b/extensions/browser/extension_throttle_test_support.h
index a6fde034..9ab5dfe3 100644
--- a/extensions/browser/extension_throttle_test_support.h
+++ b/extensions/browser/extension_throttle_test_support.h
@@ -20,7 +20,7 @@
   explicit TestTickClock(base::TimeTicks now);
   ~TestTickClock() override;
 
-  base::TimeTicks NowTicks() override;
+  base::TimeTicks NowTicks() const override;
   void set_now(base::TimeTicks now) { now_ticks_ = now; }
 
  private:
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index cc54a566..b09c4d98 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -150,6 +150,7 @@
     "command_buffer/service/gles2_cmd_decoder_mock.h",
     "command_buffer/service/raster_decoder_mock.cc",
     "command_buffer/service/raster_decoder_mock.h",
+    "ipc/service/gpu_memory_buffer_factory_test_template.h",
   ]
 
   public_deps = [
@@ -159,9 +160,12 @@
   ]
   deps = [
     "//gpu/command_buffer/common",
+    "//gpu/ipc/common",
+    "//gpu/ipc/service",
     "//testing/gmock",
     "//testing/gtest",
     "//ui/gl:gl_unittest_utils",
+    "//ui/gl:test_support",
   ]
 }
 
@@ -387,18 +391,32 @@
     "ipc/common/gpu_preferences_util_unittest.cc",
     "ipc/common/struct_traits_unittest.cc",
     "ipc/host/shader_disk_cache_unittest.cc",
+    "ipc/service/gpu_channel_manager_unittest.cc",
+    "ipc/service/gpu_channel_test_common.cc",
+    "ipc/service/gpu_channel_test_common.h",
+    "ipc/service/gpu_channel_unittest.cc",
   ]
 
   if (is_mac) {
-    sources += [ "ipc/common/gpu_memory_buffer_impl_io_surface_unittest.cc" ]
+    sources += [
+      "ipc/common/gpu_memory_buffer_impl_io_surface_unittest.cc",
+      "ipc/service/gpu_memory_buffer_factory_io_surface_unittest.cc",
+    ]
   }
 
   if (is_linux) {
-    sources += [ "ipc/common/gpu_memory_buffer_impl_native_pixmap_unittest.cc" ]
+    sources += [
+      "ipc/common/gpu_memory_buffer_impl_native_pixmap_unittest.cc",
+      "ipc/service/gpu_memory_buffer_factory_native_pixmap_unittest.cc",
+    ]
   }
 
   if (is_win) {
-    sources += [ "ipc/common/gpu_memory_buffer_impl_dxgi_unittest.cc" ]
+    sources += [
+      "ipc/common/gpu_memory_buffer_impl_dxgi_unittest.cc",
+      "ipc/service/gpu_memory_buffer_factory_dxgi_unittest.cc",
+      "ipc/service/gpu_vsync_provider_unittest_win.cc",
+    ]
   }
 
   if (is_android) {
@@ -436,25 +454,32 @@
     "//gpu/command_buffer/client:gles2_c_lib",
     "//gpu/command_buffer/client:gles2_implementation",
     "//gpu/command_buffer/client:raster",
+    "//gpu/command_buffer/common",
     "//gpu/command_buffer/common:gles2_utils",
+    "//gpu/command_buffer/service",
     "//gpu/ipc:gl_in_process_context",
     "//gpu/ipc/client",
+    "//gpu/ipc/common",
     "//gpu/ipc/common:gpu_preferences_util",
     "//gpu/ipc/common:test_interfaces",
     "//gpu/ipc/host",
+    "//gpu/ipc/service",
     "//ipc:test_support",
     "//mojo/edk",
     "//mojo/public/cpp/bindings",
     "//net:test_support",
+    "//skia",
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/angle:translator",
+    "//third_party/mesa:mesa_headers",
     "//ui/gfx",
     "//ui/gfx:test_support",
     "//ui/gfx/geometry",
     "//ui/gl",
     "//ui/gl:gl_unittest_utils",
     "//ui/gl:test_support",
+    "//url",
   ]
 
   if (is_android) {
diff --git a/gpu/command_buffer/tests/gl_manager.cc b/gpu/command_buffer/tests/gl_manager.cc
index 8a19b8fa..b334859 100644
--- a/gpu/command_buffer/tests/gl_manager.cc
+++ b/gpu/command_buffer/tests/gl_manager.cc
@@ -239,7 +239,7 @@
   }
 #endif  // defined(OS_MACOSX)
   std::vector<uint8_t> data(gfx::BufferSizeForBufferFormat(size, format), 0);
-  scoped_refptr<base::RefCountedBytes> bytes(new base::RefCountedBytes(data));
+  auto bytes = base::RefCountedBytes::TakeVector(&data);
   return base::WrapUnique<gfx::GpuMemoryBuffer>(
       new GpuMemoryBufferImpl(bytes.get(), size, format));
 }
diff --git a/gpu/ipc/service/BUILD.gn b/gpu/ipc/service/BUILD.gn
index 6b22443e..7bd0fca 100644
--- a/gpu/ipc/service/BUILD.gn
+++ b/gpu/ipc/service/BUILD.gn
@@ -152,50 +152,3 @@
     "//ui/gl:test_support",
   ]
 }
-
-test("gpu_ipc_service_unittests") {
-  configs += [ "//build/config:precompiled_headers" ]
-  sources = [
-    "gpu_channel_manager_unittest.cc",
-    "gpu_channel_test_common.cc",
-    "gpu_channel_test_common.h",
-    "gpu_channel_unittest.cc",
-    "gpu_vsync_provider_unittest_win.cc",
-  ]
-  deps = [
-    ":service",
-    ":test_support",
-    "//base",
-    "//base/test:test_support",
-    "//gpu/command_buffer/common",
-    "//gpu/command_buffer/service",
-    "//gpu/config",
-    "//gpu/ipc/common",
-    "//ipc:run_all_unittests",
-    "//ipc:test_support",
-    "//skia",
-    "//testing/gmock",
-    "//testing/gtest",
-    "//third_party/mesa:mesa_headers",
-    "//ui/gfx:test_support",
-    "//ui/gl:gl_unittest_utils",
-    "//ui/gl:test_support",
-    "//url",
-  ]
-  libs = []
-  if (is_android) {
-    deps += [ "//ui/android:ui_java" ]
-  }
-  if (is_mac) {
-    sources += [ "gpu_memory_buffer_factory_io_surface_unittest.cc" ]
-  }
-  if (is_linux) {
-    sources += [ "gpu_memory_buffer_factory_native_pixmap_unittest.cc" ]
-  }
-  if (is_win) {
-    sources += [ "gpu_memory_buffer_factory_dxgi_unittest.cc" ]
-  }
-  if (use_ozone) {
-    deps += [ "//ui/ozone" ]
-  }
-}
diff --git a/gpu/ipc/service/gpu_channel_manager.cc b/gpu/ipc/service/gpu_channel_manager.cc
index 1854f3b..e91f73a 100644
--- a/gpu/ipc/service/gpu_channel_manager.cc
+++ b/gpu/ipc/service/gpu_channel_manager.cc
@@ -73,31 +73,15 @@
       shader_translator_cache_(gpu_preferences_),
       gpu_memory_buffer_factory_(gpu_memory_buffer_factory),
       gpu_feature_info_(gpu_feature_info),
-#if defined(OS_ANDROID)
-      is_backgrounded_for_testing_(false),
-#endif
       exiting_for_lost_context_(false),
       activity_flags_(std::move(activity_flags)),
       memory_pressure_listener_(
           base::Bind(&GpuChannelManager::HandleMemoryPressure,
                      base::Unretained(this))),
       weak_factory_(this) {
-  // |application_status_listener_| must be created on the right task runner.
   DCHECK(task_runner->BelongsToCurrentThread());
   DCHECK(io_task_runner);
   DCHECK(scheduler);
-
-#if defined(OS_ANDROID)
-  // ApplicationStatusListener does not currently support being used from
-  // non-browser processes. Enable this optimization only for low-end, where GPU
-  // is run in-process.
-  if (base::SysInfo::IsLowEndDevice()) {
-    // Runs on GPU main thread and unregisters when the listener is destroyed,
-    // So, Unretained is fine here.
-    application_status_listener_.emplace(base::Bind(
-        &GpuChannelManager::OnApplicationStateChange, base::Unretained(this)));
-  }
-#endif
 }
 
 GpuChannelManager::~GpuChannelManager() {
@@ -270,43 +254,7 @@
   DidAccessGpu();
 }
 
-void GpuChannelManager::OnApplicationStateChange(
-    base::android::ApplicationState state) {
-  // TODO(ericrk): Temporarily disable the context release logic due to
-  // https://crbug.com/792120. Re-enable when the fix lands.
-  return;
-
-  if (state != base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES) {
-    return;
-  }
-
-  // Clear the GL context on low-end devices after a 5 second delay, so that we
-  // don't clear in case the user pressed the home or recents button by mistake
-  // and got back to Chrome quickly.
-  const int64_t kDelayToClearContextMs = 5000;
-
-  task_runner_->PostDelayedTask(
-      FROM_HERE,
-      base::Bind(&GpuChannelManager::OnApplicationBackgrounded,
-                 weak_factory_.GetWeakPtr()),
-      base::TimeDelta::FromMilliseconds(kDelayToClearContextMs));
-  return;
-}
-
-void GpuChannelManager::OnApplicationBackgroundedForTesting() {
-  is_backgrounded_for_testing_ = true;
-  OnApplicationBackgrounded();
-}
-
 void GpuChannelManager::OnApplicationBackgrounded() {
-  // Check if the app is still in background after the delay.
-  auto state = base::android::ApplicationStatusListener::GetState();
-  if (state != base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES &&
-      state != base::android::APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES &&
-      !is_backgrounded_for_testing_) {
-    return;
-  }
-
   // Delete all the GL contexts when the channel does not use WebGL and Chrome
   // goes to background on low-end devices.
   std::vector<int> channels_to_clear;
diff --git a/gpu/ipc/service/gpu_channel_manager.h b/gpu/ipc/service/gpu_channel_manager.h
index 83de0cae..598f39e 100644
--- a/gpu/ipc/service/gpu_channel_manager.h
+++ b/gpu/ipc/service/gpu_channel_manager.h
@@ -33,10 +33,6 @@
 #include "ui/gl/gl_surface.h"
 #include "url/gurl.h"
 
-#if defined(OS_ANDROID)
-#include "base/android/application_status_listener.h"
-#endif
-
 namespace gl {
 class GLShareGroup;
 }
@@ -126,10 +122,7 @@
 
 #if defined(OS_ANDROID)
   void DidAccessGpu();
-
-  void OnApplicationStateChange(base::android::ApplicationState state);
-
-  void OnApplicationBackgroundedForTesting();
+  void OnApplicationBackgrounded();
 #endif
 
   bool is_exiting_for_lost_context() { return exiting_for_lost_context_; }
@@ -147,8 +140,6 @@
 #if defined(OS_ANDROID)
   void ScheduleWakeUpGpu();
   void DoWakeUpGpu();
-
-  void OnApplicationBackgrounded();
 #endif
 
   void HandleMemoryPressure(
@@ -189,10 +180,6 @@
   // transport surfaces.
   base::TimeTicks last_gpu_access_time_;
   base::TimeTicks begin_wake_up_time_;
-
-  base::Optional<base::android::ApplicationStatusListener>
-      application_status_listener_;
-  bool is_backgrounded_for_testing_;
 #endif
 
   // Set during intentional GPU process shutdown.
diff --git a/gpu/ipc/service/gpu_channel_manager_unittest.cc b/gpu/ipc/service/gpu_channel_manager_unittest.cc
index 3d92f88..6da864c7 100644
--- a/gpu/ipc/service/gpu_channel_manager_unittest.cc
+++ b/gpu/ipc/service/gpu_channel_manager_unittest.cc
@@ -18,8 +18,8 @@
   ~GpuChannelManagerTest() override = default;
 
 #if defined(OS_ANDROID)
-  void TestOnApplicationStateChange(ContextType type,
-                                    bool should_destroy_channel) {
+  void TestApplicationBackgrounded(ContextType type,
+                                   bool should_destroy_channel) {
     ASSERT_TRUE(channel_manager());
 
     int32_t kClientId = 1;
@@ -47,7 +47,7 @@
     CommandBufferStub* stub = channel->LookupCommandBuffer(kRouteId);
     EXPECT_TRUE(stub);
 
-    channel_manager()->OnApplicationBackgroundedForTesting();
+    channel_manager()->OnApplicationBackgrounded();
 
     channel = channel_manager()->LookupChannel(kClientId);
     if (should_destroy_channel) {
@@ -71,13 +71,14 @@
 }
 
 #if defined(OS_ANDROID)
-TEST_F(GpuChannelManagerTest, OnLowEndBackgroundedWithoutWebGL) {
-  TestOnApplicationStateChange(CONTEXT_TYPE_OPENGLES2, true);
+TEST_F(GpuChannelManagerTest, OnBackgroundedWithoutWebGL) {
+  TestApplicationBackgrounded(CONTEXT_TYPE_OPENGLES2, true);
 }
 
-TEST_F(GpuChannelManagerTest, OnLowEndBackgroundedWithWebGL) {
-  TestOnApplicationStateChange(CONTEXT_TYPE_WEBGL2, false);
+TEST_F(GpuChannelManagerTest, OnBackgroundedWithWebGL) {
+  TestApplicationBackgrounded(CONTEXT_TYPE_WEBGL2, false);
 }
+
 #endif
 
 }  // namespace gpu
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index bfd852c..07fa355 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -486,6 +486,16 @@
     }
 
     builders {
+      name: "Linux ASan LSan Builder"
+      mixins: "linux-ci"
+    }
+
+    builders {
+      name: "Linux ASan LSan Tests (1)"
+      mixins: "linux-ci"
+    }
+
+    builders {
       name: "Linux Builder"
       mixins: "linux-ci"
       dimensions: "cores:32"
diff --git a/infra/config/global/luci-milo-dev.cfg b/infra/config/global/luci-milo-dev.cfg
index 6f6ff7a..265d22d 100644
--- a/infra/config/global/luci-milo-dev.cfg
+++ b/infra/config/global/luci-milo-dev.cfg
@@ -434,7 +434,7 @@
     short_name: "64"
   }
   builders: {
-    name: "buildbot/chromium.linux/Deterministic Linux"
+    name: "buildbucket/luci.chromium.ci/Deterministic Linux"
     category: "chromium.linux"
     short_name: "det"
   }
@@ -887,7 +887,7 @@
     short_name: "64"
   }
   builders: {
-    name: "buildbot/chromium.linux/Deterministic Linux"
+    name: "buildbucket/luci.chromium.ci/Deterministic Linux"
     short_name: "det"
   }
   builders: {
@@ -3763,7 +3763,7 @@
     name: "buildbot/tryserver.chromium.linux/linux_chromium_chromeos_msan_rel_ng"
   }
   builders: {
-    name: "buildbot/tryserver.chromium.linux/linux_chromium_clobber_deterministic"
+    name: "buildbucket/luci.chromium.try/linux_chromium_clobber_deterministic"
   }
   builders: {
     name: "buildbot/tryserver.chromium.linux/linux_chromium_clobber_rel_ng"
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index 624da67..3ededbc 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -439,7 +439,6 @@
     short_name: "64"
   }
   builders: {
-    name: "buildbot/chromium.linux/Deterministic Linux"
     name: "buildbucket/luci.chromium.ci/Deterministic Linux"
     category: "chromium.linux"
     short_name: "det"
@@ -549,11 +548,13 @@
   }
   builders: {
     name: "buildbot/chromium.memory/Linux ASan LSan Builder"
+    name: "buildbucket/luci.chromium.ci/Linux ASan LSan Builder"
     category: "chromium.memory|linux|asan lsan"
     short_name: "bld"
   }
   builders: {
     name: "buildbot/chromium.memory/Linux ASan LSan Tests (1)"
+    name: "buildbucket/luci.chromium.ci/Linux ASan LSan Tests (1)"
     category: "chromium.memory|linux|asan lsan"
     short_name: "tst"
   }
@@ -908,7 +909,6 @@
     short_name: "64"
   }
   builders: {
-    name: "buildbot/chromium.linux/Deterministic Linux"
     name: "buildbucket/luci.chromium.ci/Deterministic Linux"
     short_name: "det"
   }
@@ -1040,11 +1040,13 @@
   }
   builders: {
     name: "buildbot/chromium.memory/Linux ASan LSan Builder"
+    name: "buildbucket/luci.chromium.ci/Linux ASan LSan Builder"
     category: "linux|asan lsan"
     short_name: "bld"
   }
   builders: {
     name: "buildbot/chromium.memory/Linux ASan LSan Tests (1)"
+    name: "buildbucket/luci.chromium.ci/Linux ASan LSan Tests (1)"
     category: "linux|asan lsan"
     short_name: "tst"
   }
@@ -1828,34 +1830,46 @@
   builders: {
     name: "buildbot/chromium.linux/Linux Builder (dbg)(32)"
     category: "chromium.linux|debug|builder"
-    short_name: "buildbot_32"
+    short_name: "bb"
   }
   builders: {
     name: "buildbucket/luci.chromium.ci/Linux Builder (dbg)(32)"
     category: "chromium.linux|debug|builder"
-    short_name: "luci_32"
+    short_name: "lb"
   }
 
   builders: {
-    name: "buildbot/chromium.linux/Deterministic Linux"
-    category: "chromium.linux"
-    short_name: "bb"
-  }
-  builders: {
-    name: "buildbucket/luci.chromium.ci/Deterministic Linux"
-    category: "chromium.linux"
-    short_name: "ci"
-  }
-  builders: {
     name: "buildbot/chromium.linux/Linux Tests (dbg)(1)(32)"
     category: "chromium.linux|debug|tester"
-    short_name: "buildbot_linux_tests_32"
+    short_name: "bt"
   }
   builders: {
     name: "buildbucket/luci.chromium.ci/Linux Tests (dbg)(1)(32)"
     category: "chromium.linux|debug|tester"
-    short_name: "luci_linux_tests_32"
+    short_name: "lt"
   }
+
+  builders: {
+    name: "buildbot/chromium.linux/Linux ASan LSan Builder"
+    category: "chromium.memory|asan|builder"
+    short_name: "bb"
+  }
+  builders: {
+    name: "buildbucket/luci.chromium.ci/Linux ASan LSan Builder"
+    category: "chromium.memory|asan|builder"
+    short_name: "ci"
+  }
+  builders: {
+    name: "buildbot/chromium.linux/Linux ASan LSan Tests (1)"
+    category: "chromium.memory|asan|tester"
+    short_name: "bb"
+  }
+  builders: {
+    name: "buildbucket/luci.chromium.ci/Linux ASan LSan Tests (1)"
+    category: "chromium.memory|asan|tester"
+    short_name: "ci"
+  }
+
 }
 
 # Everything below was generated from buildermap.json.
@@ -4197,9 +4211,6 @@
     name: "buildbot/tryserver.chromium.linux/linux_chromium_chromeos_msan_rel_ng"
   }
   builders: {
-    name: "buildbot/tryserver.chromium.linux/linux_chromium_clobber_deterministic"
-  }
-  builders: {
     name: "buildbot/tryserver.chromium.linux/linux_chromium_clobber_rel_ng"
   }
   builders: {
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 61e39f9..9779a19 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -80,10 +80,10 @@
   triggers: "GPU Linux Builder"
   triggers: "linux-blink-heap-incremental-marking"
   triggers: "linux-blink-heap-verification"
+  triggers: "Linux ASan LSan Builder"
   triggers: "Linux Builder"
   triggers: "Linux Builder (dbg)"
   triggers: "Linux Builder (dbg)(32)"
-  triggers: "Linux Tests (dbg)(1)(32)"
   triggers: "Linux FYI GPU TSAN Release"
 
   # Mac. Sorted alphabetically.
@@ -545,6 +545,27 @@
 }
 
 job {
+  id: "Linux ASan LSan Builder"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "Linux ASan LSan Builder"
+  }
+}
+
+job {
+  id: "Linux ASan LSan Tests (1)"
+  # Triggered by "Linux ASan LSan Builder".
+  acl_sets: "triggered-by-parent-builders"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "Linux ASan LSan Tests (1)"
+  }
+}
+
+job {
   id: "Linux Builder (dbg)"
   acl_sets: "default"
   buildbucket: {
@@ -555,16 +576,6 @@
 }
 
 job {
-  id: "Linux Builder (dbg)(32)"
-  acl_sets: "default"
-  buildbucket: {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "Linux Builder (dbg)(32)"
-  }
-}
-
-job {
   id: "Linux Tests (dbg)(1)"
   # Triggered by "Linux Builder (dbg)".
   acl_sets: "triggered-by-parent-builders"
@@ -576,11 +587,22 @@
 }
 
 job {
-  id: "Linux Tests (dbg)(1)(32)"
+  id: "Linux Builder (dbg)(32)"
   acl_sets: "default"
   buildbucket: {
     server: "cr-buildbucket.appspot.com"
     bucket: "luci.chromium.ci"
+    builder: "Linux Builder (dbg)(32)"
+  }
+}
+
+# Triggered by "Linux Builder (dbg)(32)"
+job {
+  id: "Linux Tests (dbg)(1)(32)"
+  acl_sets: "triggered-by-parent-builders"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
     builder: "Linux Tests (dbg)(1)(32)"
   }
 }
diff --git a/ios/build/bots/chromium.mac/ios-simulator-full-configs.json b/ios/build/bots/chromium.mac/ios-simulator-full-configs.json
index f480771..dbc2f2b5 100644
--- a/ios/build/bots/chromium.mac/ios-simulator-full-configs.json
+++ b/ios/build/bots/chromium.mac/ios-simulator-full-configs.json
@@ -23,106 +23,43 @@
       "include": "eg_tests.json",
       "device type": "iPhone 6s Plus",
       "os": "10.0",
-      "dimensions": [
-        {
-          "os": "Mac-10.12",
-          "pool": "Chrome"
-        },
-        {
-          "os": "Mac-10.13",
-          "pool": "Chrome"
-        }
-      ]
+      "host os": "Mac-10.12"
     },
     {
       "include": "eg_tests.json",
       "device type": "iPhone 7",
       "os": "11.2",
-      "dimensions": [
-        {
-          "os": "Mac-10.12",
-          "pool": "Chrome"
-        },
-        {
-          "os": "Mac-10.13",
-          "pool": "Chrome"
-        }
-      ]
+      "host os": "Mac-10.12"
     },
     {
       "include": "eg_tests.json",
       "device type": "iPad Air 2",
       "os": "11.2",
-      "dimensions": [
-        {
-          "os": "Mac-10.12",
-          "pool": "Chrome"
-        },
-        {
-          "os": "Mac-10.13",
-          "pool": "Chrome"
-        }
-      ]
+      "host os": "Mac-10.12"
     },
     {
       "include": "eg_tests.json",
       "device type": "iPad Air 2",
       "os": "10.0",
-      "dimensions": [
-        {
-          "os": "Mac-10.12",
-          "pool": "Chrome"
-        },
-        {
-          "os": "Mac-10.13",
-          "pool": "Chrome"
-        }
-      ]
+      "host os": "Mac-10.12"
     },
     {
       "include": "eg_tests.json",
       "device type": "iPhone X",
       "os": "11.2",
-      "dimensions": [
-        {
-          "os": "Mac-10.12",
-          "pool": "Chrome"
-        },
-        {
-          "os": "Mac-10.13",
-          "pool": "Chrome"
-        }
-      ]
+      "host os": "Mac-10.13"
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPad Air 2",
       "os": "10.0",
-      "dimensions": [
-        {
-          "os": "Mac-10.12",
-          "pool": "Chrome"
-        },
-        {
-          "os": "Mac-10.13",
-          "pool": "Chrome"
-        }
-      ]
+      "host os": "Mac-10.13"
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPhone X",
       "os": "11.2",
-      "dimensions": [
-        {
-          "os": "Mac-10.12",
-          "pool": "Chrome"
-        },
-        {
-          "os": "Mac-10.13",
-          "pool": "Chrome"
-        }
-      ]
+      "host os": "Mac-10.13"
     }
   ]
 }
diff --git a/ios/build/bots/chromium.mac/ios-simulator.json b/ios/build/bots/chromium.mac/ios-simulator.json
index 044fea8..4f317fe 100644
--- a/ios/build/bots/chromium.mac/ios-simulator.json
+++ b/ios/build/bots/chromium.mac/ios-simulator.json
@@ -24,106 +24,43 @@
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s Plus",
       "os": "11.2",
-      "dimensions": [
-        {
-          "os": "Mac-10.12",
-          "pool": "Chrome"
-        },
-        {
-          "os": "Mac-10.13",
-          "pool": "Chrome"
-        }
-      ]
+      "host os": "Mac-10.12"
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s",
       "os": "11.2",
-      "dimensions": [
-        {
-          "os": "Mac-10.12",
-          "pool": "Chrome"
-        },
-        {
-          "os": "Mac-10.13",
-          "pool": "Chrome"
-        }
-      ]
+      "host os": "Mac-10.12"
     },
     {
       "include": "common_tests.json",
       "device type": "iPhone 6s",
       "os": "11.2",
-      "dimensions": [
-        {
-          "os": "Mac-10.12",
-          "pool": "Chrome"
-        },
-        {
-          "os": "Mac-10.13",
-          "pool": "Chrome"
-        }
-      ]
+      "host os": "Mac-10.12"
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPad Air 2",
       "os": "11.2",
-      "dimensions": [
-        {
-          "os": "Mac-10.12",
-          "pool": "Chrome"
-        },
-        {
-          "os": "Mac-10.13",
-          "pool": "Chrome"
-        }
-      ]
+      "host os": "Mac-10.12"
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPad Air 2",
       "os": "10.0",
-      "dimensions": [
-        {
-          "os": "Mac-10.12",
-          "pool": "Chrome"
-        },
-        {
-          "os": "Mac-10.13",
-          "pool": "Chrome"
-        }
-      ]
+      "host os": "Mac-10.13"
     },
     {
       "include": "common_tests.json",
       "device type": "iPad Air 2",
       "os": "10.0",
-      "dimensions": [
-        {
-          "os": "Mac-10.12",
-          "pool": "Chrome"
-        },
-        {
-          "os": "Mac-10.13",
-          "pool": "Chrome"
-        }
-      ]
+      "host os": "Mac-10.13"
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPhone 6s",
       "os": "11.2",
-      "dimensions": [
-        {
-          "os": "Mac-10.12",
-          "pool": "Chrome"
-        },
-        {
-          "os": "Mac-10.13",
-          "pool": "Chrome"
-        }
-      ]
+      "host os": "Mac-10.13"
     }
   ]
 }
diff --git a/ios/chrome/browser/ui/qr_scanner/BUILD.gn b/ios/chrome/browser/ui/qr_scanner/BUILD.gn
index 40fc17fa..6b7ac077 100644
--- a/ios/chrome/browser/ui/qr_scanner/BUILD.gn
+++ b/ios/chrome/browser/ui/qr_scanner/BUILD.gn
@@ -25,7 +25,7 @@
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/icons",
     "//ios/chrome/browser/ui/qr_scanner/requirements",
-    "//ios/chrome/common:ios_app_bundle_id_prefix_header",
+    "//ios/chrome/common:ios_app_bundle_id_prefix_buildflags",
     "//ios/third_party/material_components_ios",
     "//ui/base",
   ]
diff --git a/ios/chrome/browser/ui/qr_scanner/camera_controller.mm b/ios/chrome/browser/ui/qr_scanner/camera_controller.mm
index b828e724..4f85f0f 100644
--- a/ios/chrome/browser/ui/qr_scanner/camera_controller.mm
+++ b/ios/chrome/browser/ui/qr_scanner/camera_controller.mm
@@ -7,7 +7,7 @@
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
 #include "base/strings/stringprintf.h"
-#include "ios/chrome/common/ios_app_bundle_id_prefix.h"
+#include "ios/chrome/common/ios_app_bundle_id_prefix_buildflags.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_page_control.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_page_control.mm
index a886fbb..bf9b307 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_page_control.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_page_control.mm
@@ -195,6 +195,14 @@
   self.selectedImageView.center =
       [self convertPoint:RectCenter(self.bounds) toView:self.sliderView];
   _sliderPosition = sliderPosition;
+
+  // |_selectedPage| should be kept in sync with the slider position.
+  if (sliderPosition < 0.25)
+    _selectedPage = TabGridPageIncognitoTabs;
+  else if (sliderPosition < 0.75)
+    _selectedPage = TabGridPageRegularTabs;
+  else
+    _selectedPage = TabGridPageRemoteTabs;
 }
 
 // Setters for the control's text values. These need to update three things:
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
index fbcbfc4..7ca9748 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
@@ -164,20 +164,21 @@
 
 - (void)scrollViewDidScroll:(UIScrollView*)scrollView {
   if (scrollView.dragging || scrollView.decelerating) {
+    // Only when user initiates scroll through dragging.
     CGFloat offsetWidth =
         self.scrollView.contentSize.width - self.scrollView.frame.size.width;
     CGFloat offset = scrollView.contentOffset.x / offsetWidth;
     self.topToolbar.pageControl.sliderPosition = offset;
+  }
 
-    // Bookkeeping for the current page.
-    // TODO(crbug.com/822328) : Fix for RTL.
-    CGFloat pageWidth = scrollView.frame.size.width;
-    float fractionalPage = scrollView.contentOffset.x / pageWidth;
-    NSUInteger page = lround(fractionalPage);
-    if (page != self.currentPage) {
-      _currentPage = static_cast<TabGridPage>(page);
-      [self configureButtonsForOriginalAndCurrentPage];
-    }
+  // Bookkeeping for the current page.
+  // TODO(crbug.com/822328) : Fix for RTL.
+  CGFloat pageWidth = scrollView.frame.size.width;
+  float fractionalPage = scrollView.contentOffset.x / pageWidth;
+  NSUInteger page = lround(fractionalPage);
+  if (page != self.currentPage) {
+    _currentPage = static_cast<TabGridPage>(page);
+    [self configureButtonsForOriginalAndCurrentPage];
   }
 }
 
@@ -279,7 +280,7 @@
     _currentPage = currentPage;
   } else {
     [self.scrollView setContentOffset:offset animated:YES];
-    // _currentPage is set in scrollViewDidEndDecelerating:
+    // _currentPage is set in scrollViewDidScroll:
   }
 }
 
diff --git a/ios/chrome/browser/ui/tab_switcher/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/BUILD.gn
index d20d658..317cd62 100644
--- a/ios/chrome/browser/ui/tab_switcher/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/BUILD.gn
@@ -90,7 +90,7 @@
     "//ios/chrome/browser/ui/toolbar:toolbar_ui",
     "//ios/chrome/browser/web",
     "//ios/chrome/browser/web_state_list",
-    "//ios/chrome/common:ios_app_bundle_id_prefix_header",
+    "//ios/chrome/common:ios_app_bundle_id_prefix_buildflags",
     "//ios/public/provider/chrome/browser",
     "//ios/third_party/material_components_ios",
     "//ios/third_party/material_roboto_font_loader_ios",
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_cache.mm b/ios/chrome/browser/ui/tab_switcher/tab_switcher_cache.mm
index 351bcc3..dda92ef 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_switcher_cache.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_cache.mm
@@ -14,7 +14,7 @@
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #import "ios/chrome/browser/web/page_placeholder_tab_helper.h"
-#include "ios/chrome/common/ios_app_bundle_id_prefix.h"
+#include "ios/chrome/common/ios_app_bundle_id_prefix_buildflags.h"
 #include "ios/web/public/navigation_item.h"
 #import "ios/web/public/web_state/web_state.h"
 
diff --git a/ios/chrome/common/BUILD.gn b/ios/chrome/common/BUILD.gn
index ace1da6..7135ebd 100644
--- a/ios/chrome/common/BUILD.gn
+++ b/ios/chrome/common/BUILD.gn
@@ -93,7 +93,7 @@
   ]
 }
 
-buildflag_header("ios_app_bundle_id_prefix_header") {
-  header = "ios_app_bundle_id_prefix.h"
+buildflag_header("ios_app_bundle_id_prefix_buildflags") {
+  header = "ios_app_bundle_id_prefix_buildflags.h"
   flags = [ "IOS_APP_BUNDLE_ID_PREFIX=\"$ios_app_bundle_id_prefix\"" ]
 }
diff --git a/ios/chrome/common/app_group/BUILD.gn b/ios/chrome/common/app_group/BUILD.gn
index 89c9cac3..63b2751b 100644
--- a/ios/chrome/common/app_group/BUILD.gn
+++ b/ios/chrome/common/app_group/BUILD.gn
@@ -18,7 +18,7 @@
   deps = [
     "//base",
     "//components/version_info",
-    "//ios/chrome/common:ios_app_bundle_id_prefix_header",
+    "//ios/chrome/common:ios_app_bundle_id_prefix_buildflags",
   ]
 }
 
diff --git a/ios/chrome/common/app_group/app_group_constants.mm b/ios/chrome/common/app_group/app_group_constants.mm
index 7b8fe22..676179e 100644
--- a/ios/chrome/common/app_group/app_group_constants.mm
+++ b/ios/chrome/common/app_group/app_group_constants.mm
@@ -7,7 +7,7 @@
 #include "base/logging.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/version_info/version_info.h"
-#include "ios/chrome/common/ios_app_bundle_id_prefix.h"
+#include "ios/chrome/common/ios_app_bundle_id_prefix_buildflags.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/net/BUILD.gn b/ios/net/BUILD.gn
index 28b8845..8b514af 100644
--- a/ios/net/BUILD.gn
+++ b/ios/net/BUILD.gn
@@ -8,8 +8,8 @@
 import("//testing/test.gni")
 import("//url/features.gni")
 
-buildflag_header("ios_net_features") {
-  header = "ios_net_features.h"
+buildflag_header("ios_net_buildflags") {
+  header = "ios_net_buildflags.h"
   flags = [ "CRONET_BUILD=$is_cronet_build" ]
 }
 
@@ -22,7 +22,7 @@
 
 source_set("net") {
   deps = [
-    ":ios_net_features",
+    ":ios_net_buildflags",
     "//base",
     "//net",
     "//url:url_features",
diff --git a/ios/net/cookies/cookie_store_ios.mm b/ios/net/cookies/cookie_store_ios.mm
index 3de0cb48..1008ae6 100644
--- a/ios/net/cookies/cookie_store_ios.mm
+++ b/ios/net/cookies/cookie_store_ios.mm
@@ -28,7 +28,7 @@
 #include "ios/net/cookies/cookie_store_ios_client.h"
 #import "ios/net/cookies/ns_http_system_cookie_store.h"
 #import "ios/net/cookies/system_cookie_util.h"
-#include "ios/net/ios_net_features.h"
+#include "ios/net/ios_net_buildflags.h"
 #import "net/base/mac/url_conversions.h"
 #include "net/cookies/cookie_util.h"
 #include "net/cookies/parsed_cookie.h"
diff --git a/ios/net/cookies/system_cookie_store.mm b/ios/net/cookies/system_cookie_store.mm
index 4f4387b1..1931363 100644
--- a/ios/net/cookies/system_cookie_store.mm
+++ b/ios/net/cookies/system_cookie_store.mm
@@ -7,7 +7,7 @@
 #include <memory>
 
 #import "ios/net/cookies/cookie_creation_time_manager.h"
-#include "ios/net/ios_net_features.h"
+#include "ios/net/ios_net_buildflags.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/web/web_state/context_menu_constants.h b/ios/web/web_state/context_menu_constants.h
index 18bfbb8c..c4194f0 100644
--- a/ios/web/web_state/context_menu_constants.h
+++ b/ios/web/web_state/context_menu_constants.h
@@ -16,7 +16,7 @@
 // Represents a unique string request ID that is passed through directly from a
 // call to findElementAtPoint to the response dictionary. The request ID should
 // be used to correlate a response with a previous call to findElementAtPoint.
-extern NSString* const kContextMenuElementRequestID;
+extern NSString* const kContextMenuElementRequestId;
 
 // Optional key. Represents element's href attribute if present or parent's href
 // if element is an image.
diff --git a/ios/web/web_state/context_menu_constants.mm b/ios/web/web_state/context_menu_constants.mm
index a7b3812a..9a7d499 100644
--- a/ios/web/web_state/context_menu_constants.mm
+++ b/ios/web/web_state/context_menu_constants.mm
@@ -10,7 +10,7 @@
 
 namespace web {
 
-NSString* const kContextMenuElementRequestID = @"requestID";
+NSString* const kContextMenuElementRequestId = @"requestId";
 NSString* const kContextMenuElementHyperlink = @"href";
 NSString* const kContextMenuElementSource = @"src";
 NSString* const kContextMenuElementTitle = @"title";
diff --git a/ios/web/web_state/js/context_menu_js_unittest.mm b/ios/web/web_state/js/context_menu_js_unittest.mm
index 1bb613e9..490afb88 100644
--- a/ios/web/web_state/js/context_menu_js_unittest.mm
+++ b/ios/web/web_state/js/context_menu_js_unittest.mm
@@ -488,7 +488,7 @@
 
   id result = FindElementAtPoint(20, 20);
   NSDictionary* expected_value = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
     kContextMenuElementSource : @"file:///bogus",
     kContextMenuElementReferrerPolicy : @"default",
   };
@@ -506,7 +506,7 @@
 
   id result = FindElementAtPoint(20, 20);
   NSDictionary* expected_value = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
     kContextMenuElementSource : @"file:///bogus",
     kContextMenuElementReferrerPolicy : @"default",
     kContextMenuElementTitle : @"Hello world!",
@@ -526,7 +526,7 @@
 
   id result = FindElementAtPoint(0, 0);
   NSDictionary* expected_value = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
   };
   EXPECT_NSEQ(expected_value, result);
 }
@@ -542,7 +542,7 @@
 
   id result = FindElementAtPoint(GetWebViewContentSize().width / 2, 50);
   NSDictionary* expected_value = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
   };
   EXPECT_NSEQ(expected_value, result);
 }
@@ -562,7 +562,7 @@
 
   id result = FindElementAtPoint(20, 20);
   NSDictionary* expected_value = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
     kContextMenuElementSource : @"file:///bogus",
     kContextMenuElementReferrerPolicy : @"default",
     kContextMenuElementHyperlink : @"file:///linky",
@@ -584,7 +584,7 @@
 
   id result = FindElementAtPoint(0, 0);
   NSDictionary* expected_value = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
   };
   EXPECT_NSEQ(expected_value, result);
 }
@@ -603,7 +603,7 @@
 
   id result = FindElementAtPoint(GetWebViewContentSize().width / 2, 50);
   NSDictionary* expected_value = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
   };
   EXPECT_NSEQ(expected_value, result);
 }
@@ -629,7 +629,7 @@
 
   id result = FindElementAtPoint(20, 20);
   NSDictionary* expected_result = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
     kContextMenuElementSource : [NSString stringWithFormat:@"%sfoo", kTestUrl],
     kContextMenuElementReferrerPolicy : @"default",
     kContextMenuElementHyperlink : kLinkDest,
@@ -649,7 +649,7 @@
 
   id result = FindElementAtPoint(20, 20);
   NSDictionary* expected_result = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
     kContextMenuElementSource : [NSString stringWithFormat:@"%sfoo", kTestUrl],
     kContextMenuElementReferrerPolicy : @"default",
     kContextMenuElementHyperlink : @"javascript:console.log(",
@@ -668,7 +668,7 @@
 
   id result = FindElementAtPoint(20, 20);
   NSDictionary* expected_result = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
     kContextMenuElementSource : [NSString stringWithFormat:@"%sfoo", kTestUrl],
     kContextMenuElementReferrerPolicy : @"default",
   };
@@ -688,7 +688,7 @@
 
   id result = FindElementAtPoint(20, 20);
   NSDictionary* expected_result = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
     kContextMenuElementSource : [NSString stringWithFormat:@"%sfoo", kTestUrl],
     kContextMenuElementReferrerPolicy : @"default",
   };
@@ -708,7 +708,7 @@
 
   id result = FindElementAtPoint(20, 20);
   NSDictionary* expected_result = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
     kContextMenuElementSource : [NSString stringWithFormat:@"%sfoo", kTestUrl],
     kContextMenuElementReferrerPolicy : @"default",
   };
@@ -730,7 +730,7 @@
 
   id result = FindElementAtPoint(5, 5);
   NSDictionary* expected_result = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
     kContextMenuElementInnerText : @"",
     kContextMenuElementReferrerPolicy : @"default",
     kContextMenuElementHyperlink : kLinkDest,
@@ -758,7 +758,7 @@
 
   id result = FindElementAtPoint(10, 10);
   NSDictionary* expected_value = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
   };
   EXPECT_NSEQ(expected_value, result);
 }
@@ -809,7 +809,7 @@
   // Link is at bottom of the page content.
   id result = FindElementAtPoint(1, content_height - 5.0);
   NSDictionary* expected_result = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
     kContextMenuElementInnerText : @"link",
     kContextMenuElementReferrerPolicy : @"default",
     kContextMenuElementHyperlink : @"http://destination/",
@@ -829,7 +829,7 @@
 
   id result = FindElementAtPoint(1, 1);
   NSDictionary* expected_result = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
     kContextMenuElementInnerText : @"link",
     kContextMenuElementReferrerPolicy : @"default",
     kContextMenuElementHyperlink : kLinkDest,
@@ -860,7 +860,7 @@
 
   id result = FindElementAtPoint(1, 1);
   NSDictionary* expected_result = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
     kContextMenuElementInnerText : @"link",
     kContextMenuElementReferrerPolicy : @"default",
     kContextMenuElementHyperlink : kLinkDest,
@@ -879,7 +879,7 @@
   ASSERT_TRUE(web::LoadHtml(web_view_, kLinkHtml, GetTestURL()));
 
   id result = FindElementAtPoint(1, 1);
-  EXPECT_NSEQ(@{kContextMenuElementRequestID : kRequestId}, result);
+  EXPECT_NSEQ(@{kContextMenuElementRequestId : kRequestId}, result);
 }
 
 // Tests that -webkit-touch-callout property can be inherited from ancester
@@ -894,7 +894,7 @@
   ASSERT_TRUE(web::LoadHtml(web_view_, kLinkHtml, GetTestURL()));
 
   id result = FindElementAtPoint(1, 1);
-  EXPECT_NSEQ(@{kContextMenuElementRequestID : kRequestId}, result);
+  EXPECT_NSEQ(@{kContextMenuElementRequestId : kRequestId}, result);
 }
 
 // Tests that setting -webkit-touch-callout property can override the value
@@ -912,7 +912,7 @@
 
   id result = FindElementAtPoint(1, 1);
   NSDictionary* expected_result = @{
-    kContextMenuElementRequestID : kRequestId,
+    kContextMenuElementRequestId : kRequestId,
     kContextMenuElementInnerText : @"link",
     kContextMenuElementReferrerPolicy : @"default",
     kContextMenuElementHyperlink : kLinkDest,
diff --git a/ios/web/web_state/js/resources/all_frames_context_menu.js b/ios/web/web_state/js/resources/all_frames_context_menu.js
index 7dba93b..bd94ea897 100644
--- a/ios/web/web_state/js/resources/all_frames_context_menu.js
+++ b/ios/web/web_state/js/resources/all_frames_context_menu.js
@@ -9,6 +9,7 @@
 goog.provide('__crWeb.allFramesContextMenu');
 
 goog.require('__crWeb.base');
+goog.require('__crWeb.common');
 
 /** Beginning of anonymous object */
 (function() {
@@ -19,21 +20,10 @@
  *                 coordinates.
  * @param {number} y Vertical center of the selected point in page
  *                 coordinates.
- * @return {Object} null if no element was found or an object of the form {
- *     href,  // URL of the link under the point
- *     innerText,  // innerText of the link, if the selected element is a link
- *     src,  // src of the image, if the selected element is an image
- *     title,  // title of the image, if the selected
- *     referrerPolicy
- *   }
- *   where:
- *     <ul>
- *     <li>href, innerText are set if the selected element is a link.
- *     <li>src, title are set if the selected element is an image.
- *     <li>href is also set if the selected element is an image with a link.
- *     <li>referrerPolicy is the referrer policy to use for navigations away
- *         from the current page.
- *     </ul>
+ * @return {Object} An object of the same form as returned by
+ *                  {@code getResponseForLinkElement} or
+ *                  {@code getResponseForImageElement} or null if no element was
+ *                  found.
  */
 __gCrWeb['getElementFromPointInPageCoordinates'] = function(x, y) {
   var hitCoordinates = spiralCoordinates_(x, y);
@@ -64,41 +54,11 @@
 
       if (getComputedWebkitTouchCallout_(element) !== 'none') {
         if (tagName === 'a' && element.href) {
-          // Found a link.
-          return {
-            href: element.href,
-            referrerPolicy: getReferrerPolicy_(element),
-            innerText: element.innerText
-          };
+          return getResponseForLinkElement(element);
         }
 
         if (tagName === 'img' && element.src) {
-          // Found an image.
-          var result = {src: element.src, referrerPolicy: getReferrerPolicy_()};
-          // Copy the title, if any.
-          if (element.title) {
-            result.title = element.title;
-          }
-          // Check if the image is also a link.
-          var parent = element.parentNode;
-          while (parent) {
-            if (parent.tagName && parent.tagName.toLowerCase() === 'a' &&
-                parent.href) {
-              // This regex identifies strings like void(0),
-              // void(0)  ;void(0);, ;;;;
-              // which result in a NOP when executed as JavaScript.
-              var regex = RegExp('^javascript:(?:(?:void\\(0\\)|;)\\s*)+$');
-              if (parent.href.match(regex)) {
-                parent = parent.parentNode;
-                continue;
-              }
-              result.href = parent.href;
-              result.referrerPolicy = getReferrerPolicy_(parent);
-              break;
-            }
-            parent = parent.parentNode;
-          }
-          return result;
+          return getResponseForImageElement(element);
         }
       }
       element = element.parentNode;
@@ -108,6 +68,151 @@
 };
 
 /**
+ * Returns an object representing the details of a given link element.
+ * @param {HTMLElement} element The element whose details will be returned.
+ * @return {!Object} An object of the form {
+ *                     {@code href} The URL of the link.
+ *                     {@code referrerPolicy} The referrer policy to use for
+ *                         navigations away from the current page.
+ *                     {@code innerText} The inner text of the link.
+ *                   }.
+ */
+var getResponseForLinkElement = function(element) {
+  return {
+    href: element.href,
+    referrerPolicy: getReferrerPolicy_(element),
+    innerText: element.innerText
+  };
+};
+
+/**
+ * Returns an object representing the details of a given image element.
+ * @param {HTMLElement} element The element whose details will be returned.
+ * @return {!Object} An object of the form {
+ *                     {@code src} The src of the image.
+ *                     {@code referrerPolicy} The referrer policy to use for
+ *                         navigations away from the current page.
+ *                     {@code title} (optional) The title of the image, if one
+ *                         exists.
+ *                     {@code href} (optional) The URL of the link, if one
+ *                         exists.
+ *                   }.
+ */
+var getResponseForImageElement = function(element) {
+  var result = {src: element.src, referrerPolicy: getReferrerPolicy_()};
+  var parent = element.parentNode;
+  // Copy the title, if any.
+  if (element.title) {
+    result.title = element.title;
+  }
+  // Check if the image is also a link.
+  while (parent) {
+    if (parent.tagName && parent.tagName.toLowerCase() === 'a' &&
+        parent.href) {
+      // This regex identifies strings like void(0),
+      // void(0)  ;void(0);, ;;;;
+      // which result in a NOP when executed as JavaScript.
+      var regex = RegExp('^javascript:(?:(?:void\\(0\\)|;)\\s*)+$');
+      if (parent.href.match(regex)) {
+        parent = parent.parentNode;
+        continue;
+      }
+      result.href = parent.href;
+      result.referrerPolicy = getReferrerPolicy_(parent);
+      break;
+    }
+  parent = parent.parentNode;
+  }
+  return result;
+};
+
+/**
+ * Finds the url of the image or link under the selected point. Sends details
+ * about the found element (or an empty object if no links or images are found)
+ * back to the application by posting a 'FindElementResultHandler' message.
+ * The object will be of the same form as returned by
+ * {@code getResponseForLinkElement} or {@code getResponseForImageElement}.
+ * @param {string} requestId An identifier which will be returned in the result
+ *                 dictionary of this request.
+ * @param {number} x Horizontal center of the selected point in page
+ *                 coordinates.
+ * @param {number} y Vertical center of the selected point in page
+ *                 coordinates.
+ */
+__gCrWeb['findElementAtPointInPageCoordinates'] = function(requestId, x, y) {
+  var hitCoordinates = spiralCoordinates_(x, y);
+  for (var index = 0; index < hitCoordinates.length; index++) {
+    var coordinates = hitCoordinates[index];
+
+    var coordinateDetails = newCoordinate(coordinates.x, coordinates.y);
+    var element = elementsFromCoordinates(coordinateDetails);
+    // if element is a frame, tell it to respond to this element request
+    if (element &&
+        (element.tagName.toLowerCase() === 'iframe' ||
+         element.tagName.toLowerCase() === 'frame')) {
+      var payload = {
+        type: 'org.chromium.contextMenuMessage',
+        requestId: requestId,
+        x: x - element.offsetLeft,
+        y: y - element.offsetTop
+      };
+      element.contentWindow.postMessage(payload, element.src);
+      return;
+    }
+
+    if (!element || !element.tagName) {
+      // Nothing under the hit point. Try the next hit point.
+      continue;
+    }
+
+    // Also check element's ancestors. A bound on the level is used here to
+    // avoid large overhead when no links or images are found.
+    var level = 0;
+    while (++level < 8 && element && element != document) {
+      var tagName = element.tagName;
+      if (!tagName) continue;
+      tagName = tagName.toLowerCase();
+
+      if (tagName === 'input' || tagName === 'textarea' ||
+          tagName === 'select' || tagName === 'option') {
+        // If the element is a known input element, stop the spiral search and
+        // return empty results.
+        sendFindElementAtPointResponse(requestId, /*response=*/{});
+        return;
+      }
+
+      if (getComputedWebkitTouchCallout_(element) !== 'none') {
+        if (tagName === 'a' && element.href) {
+          sendFindElementAtPointResponse(requestId,
+                                         getResponseForLinkElement(element));
+          return;
+        }
+
+        if (tagName === 'img' && element.src) {
+          sendFindElementAtPointResponse(requestId,
+                                         getResponseForImageElement(element));
+          return;
+        }
+      }
+      element = element.parentNode;
+    }
+  }
+  sendFindElementAtPointResponse(requestId, /*response=*/{});
+};
+
+/**
+ * Inserts |requestId| into |response| and sends the result as the payload of a
+ * 'FindElementResultHandler' message back to the native application.
+ * @param {string} requestId An identifier which will be returned in the result
+ *                 dictionary of this request.
+ * @param {!Object} response The 'FindElementResultHandler' message payload.
+ */
+var sendFindElementAtPointResponse = function(requestId, response) {
+  response.requestId = requestId;
+  __gCrWeb.common.sendWebKitMessage('FindElementResultHandler', response);
+};
+
+/**
  * Returns whether or not view port coordinates should be used for the given
  * window.
  * @return {boolean} True if the window has been scrolled down or to the right,
@@ -273,4 +378,17 @@
   return 'default';
 };
 
+/**
+ * Processes context menu messages received by the window.
+ */
+window.addEventListener('message', function(message) {
+  var payload = message.data;
+  if (payload.hasOwnProperty('type') &&
+      payload.type == 'org.chromium.contextMenuMessage') {
+    __gCrWeb.findElementAtPointInPageCoordinates(payload.requestId,
+                                                 payload.x,
+                                                 payload.y);
+  }
+});
+
 }());  // End of anonymouse object
diff --git a/ios/web/web_state/js/resources/main_frame_context_menu.js b/ios/web/web_state/js/resources/main_frame_context_menu.js
index efedf28..068ecea 100644
--- a/ios/web/web_state/js/resources/main_frame_context_menu.js
+++ b/ios/web/web_state/js/resources/main_frame_context_menu.js
@@ -19,7 +19,7 @@
  * the application by posting a 'FindElementResultHandler' message.
  * The object returned in the message is of the same form as
  * {@code getElementFromPointInPageCoordinates} result.
- * @param {string} identifier An identifier which be returned in the result
+ * @param {string} requestId An identifier which be returned in the result
  *                 dictionary of this request.
  * @param {number} x Horizontal center of the selected point in web view
  *                 coordinates.
@@ -29,12 +29,11 @@
  * @param {number} webViewHeight the height of web view.
  */
 __gCrWeb['findElementAtPoint'] =
-    function(requestID, x, y, webViewWidth, webViewHeight) {
+    function(requestId, x, y, webViewWidth, webViewHeight) {
       var scale = getPageWidth() / webViewWidth;
-      var result =
-          __gCrWeb.getElementFromPointInPageCoordinates(x * scale, y * scale);
-      result.requestID = requestID;
-      __gCrWeb.common.sendWebKitMessage('FindElementResultHandler', result);
+      __gCrWeb.findElementAtPointInPageCoordinates(requestId,
+                                                   x * scale,
+                                                   y * scale);
     };
 
 /**
diff --git a/ios/web/web_state/ui/crw_context_menu_controller.mm b/ios/web/web_state/ui/crw_context_menu_controller.mm
index 3156c963..1c770ba 100644
--- a/ios/web/web_state/ui/crw_context_menu_controller.mm
+++ b/ios/web/web_state/ui/crw_context_menu_controller.mm
@@ -120,7 +120,7 @@
   // The location of the last reconized long press in the |webView|.
   CGPoint _locationForLastTouch;
   // Details for currently in progress element fetches. The objects are
-  // instances of HTMLElementFetchRequest and are keyed by a unique requestID
+  // instances of HTMLElementFetchRequest and are keyed by a unique requestId
   // string.
   NSMutableDictionary* _pendingElementFetchRequests;
 }
@@ -328,7 +328,7 @@
 
 - (void)didReceiveScriptMessage:(WKScriptMessage*)message {
   NSDictionary* response = message.body;
-  NSString* requestID = response[web::kContextMenuElementRequestID];
+  NSString* requestID = response[web::kContextMenuElementRequestId];
   HTMLElementFetchRequest* fetchRequest =
       _pendingElementFetchRequests[requestID];
   // Do not process the message if a fetch request with a matching |requestID|
diff --git a/ipc/ipc_mojo_bootstrap_unittest.cc b/ipc/ipc_mojo_bootstrap_unittest.cc
index 8528900..4f0598a8 100644
--- a/ipc/ipc_mojo_bootstrap_unittest.cc
+++ b/ipc/ipc_mojo_bootstrap_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/files/file.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "ipc/ipc.mojom.h"
@@ -96,6 +97,12 @@
 
   int32_t peer_pid() const { return peer_pid_; }
 
+  void RunUntilDisconnect() {
+    base::RunLoop run_loop;
+    binding_.set_connection_error_handler(run_loop.QuitClosure());
+    run_loop.Run();
+  }
+
  private:
   mojo::AssociatedBinding<IPC::mojom::Channel> binding_;
   const base::Closure on_peer_pid_set_;
@@ -130,27 +137,7 @@
 
   EXPECT_EQ(kTestClientPid, impl.peer_pid());
 
-  EXPECT_TRUE(helper_.WaitForChildTestShutdown());
-}
-
-// TODO(https://crbug.com/821254): Fix this test and re-enable it.
-TEST_F(IPCMojoBootstrapTest, DISABLED_ReceiveEmptyMessage) {
-  base::MessageLoop message_loop;
-  Connection connection(
-      IPC::MojoBootstrap::Create(
-          helper_.StartChild("IPCMojoBootstrapTestEmptyMessage"),
-          IPC::Channel::MODE_SERVER, base::ThreadTaskRunnerHandle::Get(),
-          base::ThreadTaskRunnerHandle::Get()),
-      kTestServerPid);
-
-  IPC::mojom::ChannelAssociatedRequest receiver;
-  connection.TakeReceiver(&receiver);
-
-  base::RunLoop run_loop;
-  PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure(),
-                       PeerPidReceiver::MessageExpectation::kExpectedInvalid);
-  run_loop.Run();
-
+  impl.RunUntilDisconnect();
   EXPECT_TRUE(helper_.WaitForChildTestShutdown());
 }
 
@@ -178,6 +165,30 @@
   return 0;
 }
 
+TEST_F(IPCMojoBootstrapTest, ReceiveEmptyMessage) {
+  base::MessageLoop message_loop;
+  Connection connection(
+      IPC::MojoBootstrap::Create(
+          helper_.StartChild("IPCMojoBootstrapTestEmptyMessage"),
+          IPC::Channel::MODE_SERVER, base::ThreadTaskRunnerHandle::Get(),
+          base::ThreadTaskRunnerHandle::Get()),
+      kTestServerPid);
+
+  IPC::mojom::ChannelAssociatedRequest receiver;
+  connection.TakeReceiver(&receiver);
+
+  base::RunLoop run_loop;
+  PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure(),
+                       PeerPidReceiver::MessageExpectation::kExpectedInvalid);
+  run_loop.Run();
+
+  // Wait for the Channel to be disconnected so we can reasonably assert that
+  // the child's empty message must have been received before we pass the test.
+  impl.RunUntilDisconnect();
+
+  EXPECT_TRUE(helper_.WaitForChildTestShutdown());
+}
+
 // A long running process that connects to us.
 MULTIPROCESS_TEST_MAIN_WITH_SETUP(
     IPCMojoBootstrapTestEmptyMessageTestChildMain,
diff --git a/media/cast/test/skewed_tick_clock.cc b/media/cast/test/skewed_tick_clock.cc
index 272c61e..0438320 100644
--- a/media/cast/test/skewed_tick_clock.cc
+++ b/media/cast/test/skewed_tick_clock.cc
@@ -17,7 +17,7 @@
       skew_clock_at_last_set_(last_skew_set_time_) {
 }
 
-base::TimeTicks SkewedTickClock::SkewTicks(base::TimeTicks now) {
+base::TimeTicks SkewedTickClock::SkewTicks(base::TimeTicks now) const {
   return base::TimeDelta::FromMicroseconds(
       (now - last_skew_set_time_).InMicroseconds() * skew_) +
       skew_clock_at_last_set_;
@@ -30,7 +30,7 @@
   last_skew_set_time_ = now;
 }
 
-base::TimeTicks SkewedTickClock::NowTicks() {
+base::TimeTicks SkewedTickClock::NowTicks() const {
   return SkewTicks(clock_->NowTicks());
 }
 
diff --git a/media/cast/test/skewed_tick_clock.h b/media/cast/test/skewed_tick_clock.h
index 34b267e..532a866 100644
--- a/media/cast/test/skewed_tick_clock.h
+++ b/media/cast/test/skewed_tick_clock.h
@@ -26,10 +26,10 @@
   // jump forwards or backwards, only changing the offset will
   // do that.
   void SetSkew(double skew, base::TimeDelta offset);
-  base::TimeTicks NowTicks() final;
+  base::TimeTicks NowTicks() const final;
 
  private:
-  base::TimeTicks SkewTicks(base::TimeTicks now);
+  base::TimeTicks SkewTicks(base::TimeTicks now) const;
   base::TickClock* clock_;  // Not owned.
   double skew_;
   base::TimeTicks last_skew_set_time_;
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index 4f0c5093..a2f7714 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -67,13 +67,6 @@
     0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // tracks(size = 0)
 };
 
-// WebM Block bytes that represent a VP8 key frame.
-const uint8_t kVP8Keyframe[] = {0x010, 0x00, 0x00, 0x9d, 0x01, 0x2a,
-                                0x00,  0x10, 0x00, 0x10, 0x00};
-
-// WebM Block bytes that represent a VP8 interframe.
-const uint8_t kVP8Interframe[] = {0x11, 0x00, 0x00};
-
 const uint8_t kCuesHeader[] = {
     0x1C, 0x53, 0xBB, 0x6B,                          // Cues ID
     0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // cues(size = 0)
@@ -547,17 +540,10 @@
         cb.SetClusterTimecode(blocks[i].timestamp_in_ms);
 
       if (blocks[i].duration) {
-        if (blocks[i].track_number == kVideoTrackNum ||
-            blocks[i].track_number == kAlternateVideoTrackNum) {
-          AddVideoBlockGroup(&cb,
-                             blocks[i].track_number, blocks[i].timestamp_in_ms,
-                             blocks[i].duration, blocks[i].flags);
-        } else {
-          cb.AddBlockGroup(blocks[i].track_number, blocks[i].timestamp_in_ms,
-                           blocks[i].duration, blocks[i].flags,
-                           blocks[i].flags & kWebMFlagKeyframe, &data[0],
-                           data.size());
-        }
+        cb.AddBlockGroup(blocks[i].track_number, blocks[i].timestamp_in_ms,
+                         blocks[i].duration, blocks[i].flags,
+                         blocks[i].flags & kWebMFlagKeyframe, &data[0],
+                         data.size());
       } else {
         cb.AddSimpleBlock(blocks[i].track_number, blocks[i].timestamp_in_ms,
                           blocks[i].flags,
@@ -937,19 +923,6 @@
     return GenerateCluster(timecode, timecode, block_count);
   }
 
-  void AddVideoBlockGroup(ClusterBuilder* cb,
-                          int track_num,
-                          int64_t timecode,
-                          int duration,
-                          int flags) {
-    const uint8_t* data =
-        (flags & kWebMFlagKeyframe) != 0 ? kVP8Keyframe : kVP8Interframe;
-    int size = (flags & kWebMFlagKeyframe) != 0 ? sizeof(kVP8Keyframe) :
-        sizeof(kVP8Interframe);
-    cb->AddBlockGroup(track_num, timecode, duration, flags,
-                      flags & kWebMFlagKeyframe, data, size);
-  }
-
   std::unique_ptr<Cluster> GenerateCluster(int first_audio_timecode,
                                            int first_video_timecode,
                                            int block_count) {
@@ -1028,14 +1001,9 @@
       timecode += block_duration;
     }
 
-    if (track_number == kVideoTrackNum) {
-      AddVideoBlockGroup(&cb, track_number, timecode, block_duration,
-                         kWebMFlagKeyframe);
-    } else {
-      cb.AddBlockGroup(track_number, timecode, block_duration,
-                       kWebMFlagKeyframe, static_cast<bool>(kWebMFlagKeyframe),
-                       &data[0], data.size());
-    }
+    cb.AddBlockGroup(track_number, timecode, block_duration, kWebMFlagKeyframe,
+                     static_cast<bool>(kWebMFlagKeyframe), &data[0],
+                     data.size());
 
     return cb.Finish();
   }
diff --git a/media/filters/offloading_video_decoder.cc b/media/filters/offloading_video_decoder.cc
index 29ae6cb..7a528a6 100644
--- a/media/filters/offloading_video_decoder.cc
+++ b/media/filters/offloading_video_decoder.cc
@@ -4,7 +4,9 @@
 
 #include "media/filters/offloading_video_decoder.h"
 
+#include "base/bind_helpers.h"
 #include "base/sequenced_task_runner.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/task_scheduler/post_task.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/decoder_buffer.h"
@@ -12,7 +14,42 @@
 
 namespace media {
 
-static void ReleaseDecoder(std::unique_ptr<VideoDecoder> decoder) {}
+// Helper class which manages cancellation of Decode() after Reset() and makes
+// it easier to destruct on the proper thread.
+class CancellationHelper {
+ public:
+  CancellationHelper(std::unique_ptr<OffloadableVideoDecoder> decoder)
+      : cancellation_flag_(std::make_unique<base::AtomicFlag>()),
+        decoder_(std::move(decoder)) {}
+
+  // Safe to call from any thread.
+  void Cancel() { cancellation_flag_->Set(); }
+
+  void Decode(const scoped_refptr<DecoderBuffer>& buffer,
+              const VideoDecoder::DecodeCB& decode_cb) {
+    if (cancellation_flag_->IsSet()) {
+      decode_cb.Run(DecodeStatus::ABORTED);
+      return;
+    }
+
+    decoder_->Decode(buffer, decode_cb);
+  }
+
+  void Reset() {
+    // OffloadableVideoDecoders are required to have a synchronous Reset(), so
+    // we don't need to wait for the Reset to complete.
+    decoder_->Reset(base::DoNothing());
+    cancellation_flag_.reset(new base::AtomicFlag());
+  }
+
+  OffloadableVideoDecoder* decoder() const { return decoder_.get(); }
+
+ private:
+  std::unique_ptr<base::AtomicFlag> cancellation_flag_;
+  std::unique_ptr<OffloadableVideoDecoder> decoder_;
+
+  DISALLOW_COPY_AND_ASSIGN(CancellationHelper);
+};
 
 OffloadingVideoDecoder::OffloadingVideoDecoder(
     int min_offloading_width,
@@ -20,7 +57,7 @@
     std::unique_ptr<OffloadableVideoDecoder> decoder)
     : min_offloading_width_(min_offloading_width),
       supported_codecs_(std::move(supported_codecs)),
-      decoder_(std::move(decoder)),
+      helper_(std::make_unique<CancellationHelper>(std::move(decoder))),
       weak_factory_(this) {
   DETACH_FROM_THREAD(thread_checker_);
 }
@@ -28,16 +65,15 @@
 OffloadingVideoDecoder::~OffloadingVideoDecoder() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  if (offload_task_runner_) {
-    // Can't use DeleteSoon() here since VideoDecoder has a custom deleter.
-    offload_task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&ReleaseDecoder, base::Passed(&decoder_)));
-  }
+  // The |helper_| must always be destroyed on the |offload_task_runner_| since
+  // we may still have tasks posted to it.
+  if (offload_task_runner_)
+    offload_task_runner_->DeleteSoon(FROM_HERE, std::move(helper_));
 }
 
 std::string OffloadingVideoDecoder::GetDisplayName() const {
   // This call is expected to be static and safe to call from any thread.
-  return decoder_->GetDisplayName();
+  return helper_->decoder()->GetDisplayName();
 }
 
 void OffloadingVideoDecoder::Initialize(
@@ -65,7 +101,7 @@
       offload_task_runner_->PostTaskAndReply(
           FROM_HERE,
           base::BindOnce(&OffloadableVideoDecoder::Detach,
-                         base::Unretained(decoder_.get())),
+                         base::Unretained(helper_->decoder())),
           // We must trampoline back trough OffloadingVideoDecoder because it's
           // possible for this class to be destroyed during Initialize().
           base::BindOnce(&OffloadingVideoDecoder::Initialize,
@@ -78,7 +114,7 @@
     // We're transitioning from no offloading to offloading, so detach from the
     // media thread so we can run on the offloading thread.
     if (!disable_offloading && !offload_task_runner_)
-      decoder_->Detach();
+      helper_->decoder()->Detach();
   }
 
   DCHECK(!initialized_);
@@ -92,8 +128,9 @@
   // If we're not offloading just pass through to the wrapped decoder.
   if (disable_offloading) {
     offload_task_runner_ = nullptr;
-    decoder_->Initialize(config, low_delay, cdm_context, bound_init_cb,
-                         bound_output_cb, waiting_for_decryption_key_cb);
+    helper_->decoder()->Initialize(config, low_delay, cdm_context,
+                                   bound_init_cb, bound_output_cb,
+                                   waiting_for_decryption_key_cb);
     return;
   }
 
@@ -105,7 +142,7 @@
   offload_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&OffloadableVideoDecoder::Initialize,
-                     base::Unretained(decoder_.get()), config, low_delay,
+                     base::Unretained(helper_->decoder()), config, low_delay,
                      cdm_context, bound_init_cb, bound_output_cb,
                      waiting_for_decryption_key_cb));
 }
@@ -118,14 +155,14 @@
 
   DecodeCB bound_decode_cb = BindToCurrentLoop(decode_cb);
   if (!offload_task_runner_) {
-    decoder_->Decode(buffer, bound_decode_cb);
+    helper_->decoder()->Decode(buffer, bound_decode_cb);
     return;
   }
 
   offload_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&OffloadableVideoDecoder::Decode,
-                                base::Unretained(decoder_.get()), buffer,
-                                bound_decode_cb));
+      FROM_HERE,
+      base::BindOnce(&CancellationHelper::Decode,
+                     base::Unretained(helper_.get()), buffer, bound_decode_cb));
 }
 
 void OffloadingVideoDecoder::Reset(const base::Closure& reset_cb) {
@@ -133,14 +170,27 @@
 
   base::Closure bound_reset_cb = BindToCurrentLoop(reset_cb);
   if (!offload_task_runner_) {
-    decoder_->Reset(bound_reset_cb);
-    return;
+    helper_->Reset();
+  } else {
+    helper_->Cancel();
+    offload_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&CancellationHelper::Reset,
+                                  base::Unretained(helper_.get())));
   }
 
-  offload_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&OffloadableVideoDecoder::Reset,
-                     base::Unretained(decoder_.get()), bound_reset_cb));
+  // No need to wait for this to complete since all offloadable decoders are
+  // required to have a synchronous Reset().
+  bound_reset_cb.Run();
+}
+
+int OffloadingVideoDecoder::GetMaxDecodeRequests() const {
+  // If we're offloading, try to parallelize decodes as well. Take care when
+  // adjusting this number as it may dramatically increase memory usage and
+  // reduce seek times. See http://crbug.com/731841.
+  //
+  // The current value of 2 was determined via experimental adjustment until a
+  // 4K60 VP9 playback dropped zero frames.
+  return offload_task_runner_ ? 2 : 1;
 }
 
 }  // namespace media
diff --git a/media/filters/offloading_video_decoder.h b/media/filters/offloading_video_decoder.h
index 959d2d9..48bb567 100644
--- a/media/filters/offloading_video_decoder.h
+++ b/media/filters/offloading_video_decoder.h
@@ -18,7 +18,13 @@
 }  // namespace base
 
 namespace media {
+class CancellationHelper;
 
+// OffloadableVideoDecoder implementations must have synchronous execution of
+// Reset() and Decode() (true for all current software decoders); this allows
+// for serializing these operations on the offloading sequence. With
+// serializing, multiple Decode() events can be queued on the offload thread,
+// and Reset() does not need to wait for |reset_cb| to return.
 class MEDIA_EXPORT OffloadableVideoDecoder : public VideoDecoder {
  public:
   ~OffloadableVideoDecoder() override {}
@@ -33,6 +39,28 @@
 // Wrapper for OffloadableVideoDecoder implementations that runs the wrapped
 // decoder on a task pool other than the caller's thread.
 //
+// Offloading allows us to avoid blocking the media sequence for Decode() when
+// it's known that decoding may take a long time; e.g., high-resolution VP9
+// decodes may occasionally take upwards of > 100ms per frame, which is enough
+// to exhaust the audio buffer and lead to underflow in some circumstances.
+//
+// Offloading also allows better pipelining of Decode() calls. The normal decode
+// sequence is Decode(buffer) -> DecodeComplete() -> WaitFor(buffer)-> (repeat);
+// this sequence generally involves thread hops as well. When offloading we can
+// take advantage of the serialization of operations on the offloading sequence
+// to make this Decode(buffer) -> DecodeComplete() -> Decode(buffer) by queuing
+// the next Decode(buffer) before the previous one completes.
+//
+// I.e., we are no longer wasting cycles waiting for the recipient of the
+// decoded frame to acknowledge that receipt, request the next muxed buffer, and
+// then queue the next decode. Those operations now happen in parallel with the
+// decoding of the previous buffer on the offloading sequence. Improving the
+// total throughput that a decode can achieve.
+//
+// E.g., without parallel offloading, over 4000 frames, a 4K60 VP9 clip spent
+// ~11.7 seconds of aggregate time just waiting for frames. With parallel
+// offloading the same clip spent only ~3.4 seconds.
+//
 // Optionally decoders which are aware of the wrapping may choose to not rebind
 // callbacks to the offloaded thread since they will already be bound by the
 // OffloadingVideoDecoder; this simply avoids extra hops for completed tasks.
@@ -61,6 +89,7 @@
   void Decode(const scoped_refptr<DecoderBuffer>& buffer,
               const DecodeCB& decode_cb) override;
   void Reset(const base::Closure& reset_cb) override;
+  int GetMaxDecodeRequests() const override;
 
  private:
   // VideoDecoderConfigs given to Initialize() with a coded size that has width
@@ -75,8 +104,10 @@
 
   THREAD_CHECKER(thread_checker_);
 
-  // The decoder which will be offloaded.
-  std::unique_ptr<OffloadableVideoDecoder> decoder_;
+  // A helper class for managing Decode() and Reset() calls to the offloaded
+  // decoder; it owns the given OffloadableVideoDecoder and is always destructed
+  // on |offload_task_runner_| when used.
+  std::unique_ptr<CancellationHelper> helper_;
 
   // High resolution decodes may block the media thread for too long, in such
   // cases offload the decoding to a task pool.
diff --git a/media/filters/offloading_video_decoder_unittest.cc b/media/filters/offloading_video_decoder_unittest.cc
index 81f660b..2af913e 100644
--- a/media/filters/offloading_video_decoder_unittest.cc
+++ b/media/filters/offloading_video_decoder_unittest.cc
@@ -96,6 +96,9 @@
     EXPECT_EQ(offloading_decoder_->GetDisplayName(),
               decoder_->GetDisplayName());
 
+    // When offloading decodes should not be parallelized.
+    EXPECT_EQ(offloading_decoder_->GetMaxDecodeRequests(), 1);
+
     // Verify methods are called on the current thread since the offload codec
     // requirement is not satisfied.
     VideoDecoder::OutputCB output_cb;
@@ -129,6 +132,9 @@
     EXPECT_EQ(offloading_decoder_->GetDisplayName(),
               decoder_->GetDisplayName());
 
+    // Prior to Initialize() max decode requests is still 1.
+    EXPECT_EQ(offloading_decoder_->GetMaxDecodeRequests(), 1);
+
     // Since this Initialize() should be happening on another thread, set the
     // expectation after we make the call.
     VideoDecoder::OutputCB output_cb;
@@ -144,6 +150,9 @@
                         RunCallback<3>(true), SaveArg<4>(&output_cb)));
     task_env_.RunUntilIdle();
 
+    // When offloading decodes should be parallelized.
+    EXPECT_GT(offloading_decoder_->GetMaxDecodeRequests(), 1);
+
     // Verify decode works and is called on the right thread.
     offloading_decoder_->Decode(DecoderBuffer::CreateEOSBuffer(),
                                 ExpectDecodeCB(DecodeStatus::OK));
@@ -235,4 +244,88 @@
   TestNoOffloading(TestVideoConfig::Normal(kCodecVP9));
 }
 
+TEST_F(OffloadingVideoDecoderTest, ParallelizedOffloading) {
+  auto offload_config = TestVideoConfig::Large(kCodecVP9);
+  CreateWrapper(offload_config.coded_size().width(), kCodecVP9);
+
+  // Since this Initialize() should be happening on another thread, set the
+  // expectation after we make the call.
+  VideoDecoder::OutputCB output_cb;
+  offloading_decoder_->Initialize(
+      offload_config, false, nullptr, ExpectInitCB(true),
+      base::BindRepeating(&OffloadingVideoDecoderTest::OutputDone,
+                          base::Unretained(this)),
+      VideoDecoder::WaitingForDecryptionKeyCB());
+  EXPECT_CALL(*decoder_, Initialize(_, false, nullptr, _, _, _))
+      .WillOnce(DoAll(VerifyNotOn(task_env_.GetMainThreadTaskRunner()),
+                      RunCallback<3>(true), SaveArg<4>(&output_cb)));
+  task_env_.RunUntilIdle();
+
+  // When offloading decodes should be parallelized.
+  EXPECT_GT(offloading_decoder_->GetMaxDecodeRequests(), 1);
+
+  // Verify decode works and is called on the right thread.
+  VideoDecoder::DecodeCB decode_cb = base::BindRepeating(
+      &OffloadingVideoDecoderTest::DecodeDone, base::Unretained(this));
+  offloading_decoder_->Decode(DecoderBuffer::CreateEOSBuffer(), decode_cb);
+  offloading_decoder_->Decode(DecoderBuffer::CreateEOSBuffer(), decode_cb);
+
+  EXPECT_CALL(*decoder_, Decode(_, _))
+      .Times(2)
+      .WillRepeatedly(DoAll(VerifyNotOn(task_env_.GetMainThreadTaskRunner()),
+                            RunClosure(base::BindRepeating(output_cb, nullptr)),
+                            RunCallback<1>(DecodeStatus::OK)));
+  EXPECT_CALL(*this, DecodeDone(DecodeStatus::OK))
+      .Times(2)
+      .WillRepeatedly(VerifyOn(task_env_.GetMainThreadTaskRunner()));
+  EXPECT_CALL(*this, OutputDone(_))
+      .Times(2)
+      .WillRepeatedly(VerifyOn(task_env_.GetMainThreadTaskRunner()));
+  task_env_.RunUntilIdle();
+
+  // Reset so we can call Initialize() again.
+  offloading_decoder_->Reset(ExpectResetCB());
+  EXPECT_CALL(*decoder_, Reset(_))
+      .WillOnce(DoAll(VerifyNotOn(task_env_.GetMainThreadTaskRunner()),
+                      RunCallback<0>()));
+  task_env_.RunUntilIdle();
+}
+
+TEST_F(OffloadingVideoDecoderTest, ParallelizedOffloadingResetAbortsDecodes) {
+  auto offload_config = TestVideoConfig::Large(kCodecVP9);
+  CreateWrapper(offload_config.coded_size().width(), kCodecVP9);
+
+  // Since this Initialize() should be happening on another thread, set the
+  // expectation after we make the call.
+  VideoDecoder::OutputCB output_cb;
+  offloading_decoder_->Initialize(
+      offload_config, false, nullptr, ExpectInitCB(true),
+      base::BindRepeating(&OffloadingVideoDecoderTest::OutputDone,
+                          base::Unretained(this)),
+      VideoDecoder::WaitingForDecryptionKeyCB());
+  EXPECT_CALL(*decoder_, Initialize(_, false, nullptr, _, _, _))
+      .WillOnce(DoAll(VerifyNotOn(task_env_.GetMainThreadTaskRunner()),
+                      RunCallback<3>(true), SaveArg<4>(&output_cb)));
+  task_env_.RunUntilIdle();
+
+  // When offloading decodes should be parallelized.
+  EXPECT_GT(offloading_decoder_->GetMaxDecodeRequests(), 1);
+
+  // Verify decode works and is called on the right thread.
+  VideoDecoder::DecodeCB decode_cb = base::BindRepeating(
+      &OffloadingVideoDecoderTest::DecodeDone, base::Unretained(this));
+  offloading_decoder_->Decode(DecoderBuffer::CreateEOSBuffer(), decode_cb);
+  offloading_decoder_->Decode(DecoderBuffer::CreateEOSBuffer(), decode_cb);
+
+  EXPECT_CALL(*decoder_, Decode(_, _)).Times(0);
+  EXPECT_CALL(*this, DecodeDone(DecodeStatus::ABORTED))
+      .Times(2)
+      .WillRepeatedly(VerifyOn(task_env_.GetMainThreadTaskRunner()));
+  offloading_decoder_->Reset(ExpectResetCB());
+  EXPECT_CALL(*decoder_, Reset(_))
+      .WillOnce(DoAll(VerifyNotOn(task_env_.GetMainThreadTaskRunner()),
+                      RunCallback<0>()));
+  task_env_.RunUntilIdle();
+}
+
 }  // namespace media
diff --git a/media/gpu/rendering_helper.cc b/media/gpu/rendering_helper.cc
index ee9cf75..80ed11b 100644
--- a/media/gpu/rendering_helper.cc
+++ b/media/gpu/rendering_helper.cc
@@ -56,6 +56,8 @@
 
 namespace media {
 
+bool RenderingHelper::use_gl_ = false;
+
 RenderingHelperParams::RenderingHelperParams()
     : rendering_fps(0), render_as_thumbnails(false) {}
 
@@ -86,10 +88,12 @@
 RenderingHelper::RenderedVideo::~RenderedVideo() {}
 
 // static
-void RenderingHelper::InitializeOneOff(base::WaitableEvent* done) {
+void RenderingHelper::InitializeOneOff(bool use_gl, base::WaitableEvent* done) {
   base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
   cmd_line->AppendSwitchASCII(switches::kUseGL, gl::kGLImplementationEGLName);
 
+  use_gl_ = use_gl;
+
 #if defined(USE_OZONE)
   ui::OzonePlatform::InitParams params;
   params.single_process = true;
@@ -97,6 +101,11 @@
   ui::OzonePlatform::GetInstance()->AfterSandboxEntry();
 #endif
 
+  if (!use_gl_) {
+    done->Signal();
+    return;
+  }
+
   if (!gl::init::InitializeGLOneOff())
     LOG(FATAL) << "Could not initialize GL";
   done->Signal();
@@ -132,13 +141,19 @@
   render_as_thumbnails_ = params.render_as_thumbnails;
   task_runner_ = base::ThreadTaskRunnerHandle::Get();
 
+  videos_.resize(params.num_windows);
+
+  // Skip all the GL stuff if we don't use it
+  if (!use_gl_) {
+    done->Signal();
+    return;
+  }
+
   gl_surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size());
   gl_context_ = gl::init::CreateGLContext(nullptr, gl_surface_.get(),
                                           gl::GLContextAttribs());
   CHECK(gl_context_->MakeCurrent(gl_surface_.get()));
 
-  videos_.resize(params.num_windows);
-
   if (render_as_thumbnails_) {
     CHECK_EQ(videos_.size(), 1U);
 
@@ -275,10 +290,22 @@
 }
 
 void RenderingHelper::UnInitialize(base::WaitableEvent* done) {
+  // We have never been initialized in the first place...
+  if (task_runner_.get() == nullptr) {
+    done->Signal();
+    return;
+  }
+
   CHECK(task_runner_->BelongsToCurrentThread());
 
   render_task_.Cancel();
 
+  if (!use_gl_) {
+    Clear();
+    done->Signal();
+    return;
+  }
+
   if (render_as_thumbnails_) {
     glDeleteTextures(1, &thumbnails_texture_id_);
     glDeleteFramebuffersEXT(1, &thumbnails_fbo_id_);
@@ -305,6 +332,13 @@
                    texture_target, texture_id, size, done));
     return;
   }
+
+  if (!use_gl_) {
+    *texture_id = 0;
+    done->Signal();
+    return;
+  }
+
   glGenTextures(1, texture_id);
   glBindTexture(texture_target, *texture_id);
   if (texture_target == GL_TEXTURE_2D) {
@@ -335,6 +369,8 @@
 void RenderingHelper::RenderThumbnail(uint32_t texture_target,
                                       uint32_t texture_id) {
   CHECK(task_runner_->BelongsToCurrentThread());
+  CHECK(use_gl_);
+
   const int width = thumbnail_size_.width();
   const int height = thumbnail_size_.height();
   const int thumbnails_in_row = thumbnails_fbo_size_.width() / width;
@@ -401,6 +437,10 @@
 
 void RenderingHelper::DeleteTexture(uint32_t texture_id) {
   CHECK(task_runner_->BelongsToCurrentThread());
+
+  if (!use_gl_)
+    return;
+
   glDeleteTextures(1, &texture_id);
   CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR);
 }
@@ -427,7 +467,7 @@
 
 void RenderingHelper::GetThumbnailsAsRGBA(std::vector<unsigned char>* rgba,
                                           base::WaitableEvent* done) {
-  CHECK(render_as_thumbnails_);
+  CHECK(render_as_thumbnails_ && use_gl_);
 
   const size_t num_pixels = thumbnails_fbo_size_.GetArea();
   rgba->resize(num_pixels * 4);
diff --git a/media/gpu/rendering_helper.h b/media/gpu/rendering_helper.h
index ca57f38..ba03eb3 100644
--- a/media/gpu/rendering_helper.h
+++ b/media/gpu/rendering_helper.h
@@ -84,7 +84,7 @@
   ~RenderingHelper();
 
   // Initialize GL. This method must be called on the rendering thread.
-  static void InitializeOneOff(base::WaitableEvent* done);
+  static void InitializeOneOff(bool use_gl, base::WaitableEvent* done);
 
   // Create the render context and windows by the specified
   // dimensions. This method must be called on the rendering thread.
@@ -172,6 +172,7 @@
   gfx::Size thumbnail_size_;
   GLuint vertex_buffer_;
   GLuint program_;
+  static bool use_gl_;
   base::TimeDelta frame_duration_;
   base::TimeTicks scheduled_render_time_;
   base::CancelableClosure render_task_;
diff --git a/media/gpu/video_decode_accelerator_unittest.cc b/media/gpu/video_decode_accelerator_unittest.cc
index 8ef6db71..c9f5a5b7 100644
--- a/media/gpu/video_decode_accelerator_unittest.cc
+++ b/media/gpu/video_decode_accelerator_unittest.cc
@@ -118,6 +118,8 @@
 // The value is set by the switch "--rendering_fps".
 double g_rendering_fps = 60;
 
+bool g_use_gl_renderer = true;
+
 // The value is set by the switch "--num_play_throughs". The video will play
 // the specified number of times. In different test cases, we have different
 // values for |num_play_throughs|. This setting will override the value. A
@@ -263,7 +265,8 @@
     base::WaitableEvent done(base::WaitableEvent::ResetPolicy::AUTOMATIC,
                              base::WaitableEvent::InitialState::NOT_SIGNALED);
     rendering_thread_.task_runner()->PostTask(
-        FROM_HERE, base::Bind(&RenderingHelper::InitializeOneOff, &done));
+        FROM_HERE, base::Bind(&RenderingHelper::InitializeOneOff,
+                              g_use_gl_renderer, &done));
     done.Wait();
 
 #if defined(OS_CHROMEOS)
@@ -612,10 +615,15 @@
     LOG_ASSERT(decoder_->Initialize(config, this));
   } else {
     if (!vda_factory_) {
-      vda_factory_ = GpuVideoDecodeAcceleratorFactory::Create(
-          base::Bind(&RenderingHelper::GetGLContext,
-                     base::Unretained(rendering_helper_)),
-          base::Bind([]() { return true; }), base::Bind(&DummyBindImage));
+      if (g_use_gl_renderer) {
+        vda_factory_ = GpuVideoDecodeAcceleratorFactory::Create(
+            base::Bind(&RenderingHelper::GetGLContext,
+                       base::Unretained(rendering_helper_)),
+            base::Bind([]() { return true; }), base::Bind(&DummyBindImage));
+      } else {
+        vda_factory_ = GpuVideoDecodeAcceleratorFactory::CreateWithNoGL();
+      }
+
       LOG_ASSERT(vda_factory_);
     }
 
@@ -1343,6 +1351,13 @@
   bool test_reuse_delay = std::get<5>(GetParam());
   const bool render_as_thumbnails = std::get<6>(GetParam());
 
+  // We cannot render thumbnails without GL
+  if (!g_use_gl_renderer && render_as_thumbnails) {
+    LOG(WARNING) << "Skipping thumbnail test because GL is deactivated by "
+                    "--disable_rendering";
+    return;
+  }
+
   if (test_video_files_.size() > 1)
     num_concurrent_decoders = test_video_files_.size();
 
@@ -1355,17 +1370,6 @@
   notes_.resize(num_concurrent_decoders);
   clients_.resize(num_concurrent_decoders);
 
-  RenderingHelperParams helper_params;
-  helper_params.rendering_fps = g_rendering_fps;
-  helper_params.render_as_thumbnails = render_as_thumbnails;
-  if (render_as_thumbnails) {
-    // Only one decoder is supported with thumbnail rendering
-    LOG_ASSERT(num_concurrent_decoders == 1U);
-    helper_params.thumbnails_page_size = kThumbnailsPageSize;
-    helper_params.thumbnail_size = kThumbnailSize;
-  }
-
-  helper_params.num_windows = num_concurrent_decoders;
   // First kick off all the decoders.
   for (size_t index = 0; index < num_concurrent_decoders; ++index) {
     TestVideoFile* video_file =
@@ -1391,6 +1395,16 @@
     clients_[index] = std::move(client);
   }
 
+  RenderingHelperParams helper_params;
+  helper_params.rendering_fps = g_rendering_fps;
+  helper_params.render_as_thumbnails = render_as_thumbnails;
+  helper_params.num_windows = num_concurrent_decoders;
+  if (render_as_thumbnails) {
+    // Only one decoder is supported with thumbnail rendering
+    LOG_ASSERT(num_concurrent_decoders == 1U);
+    helper_params.thumbnails_page_size = kThumbnailsPageSize;
+    helper_params.thumbnail_size = kThumbnailSize;
+  }
   InitializeRenderingHelper(helper_params);
 
   for (size_t index = 0; index < num_concurrent_decoders; ++index) {
@@ -1841,9 +1855,8 @@
       LOG_ASSERT(base::StringToDouble(input, &media::g_rendering_fps));
       continue;
     }
-    // TODO(owenlin): Remove this flag once it is not used in autotest.
     if (it->first == "disable_rendering") {
-      media::g_rendering_fps = 0;
+      media::g_use_gl_renderer = false;
       continue;
     }
 
diff --git a/media/gpu/windows/dxva_video_decode_accelerator_win.cc b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
index e5af1ff..85f632f 100644
--- a/media/gpu/windows/dxva_video_decode_accelerator_win.cc
+++ b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
@@ -807,6 +807,10 @@
         ::GetProcAddress(dxgi_manager_dll, "MFCreateDXGIDeviceManager"));
   }
 
+  RETURN_AND_NOTIFY_ON_FAILURE(make_context_current_cb_.Run(),
+                               "Failed to make context current",
+                               PLATFORM_FAILURE, false);
+
   RETURN_AND_NOTIFY_ON_FAILURE(
       gl::g_driver_egl.ext.b_EGL_ANGLE_surface_d3d_texture_2d_share_handle,
       "EGL_ANGLE_surface_d3d_texture_2d_share_handle unavailable",
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc
index 708599b..f6cb31e 100644
--- a/media/renderers/paint_canvas_video_renderer.cc
+++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -477,7 +477,8 @@
   cc::PaintFlags flags;
   flags.setBlendMode(SkBlendMode::kSrc);
   flags.setFilterQuality(kLow_SkFilterQuality);
-  Paint(video_frame, canvas, gfx::RectF(video_frame->visible_rect()), flags,
+  Paint(video_frame, canvas,
+        gfx::RectF(gfx::SizeF(video_frame->visible_rect().size())), flags,
         media::VIDEO_ROTATION_0, context_3d);
 }
 
diff --git a/media/test/pipeline_integration_test_base.cc b/media/test/pipeline_integration_test_base.cc
index 334e7d4..d843f86 100644
--- a/media/test/pipeline_integration_test_base.cc
+++ b/media/test/pipeline_integration_test_base.cc
@@ -680,7 +680,7 @@
   scoped_task_environment_.RunUntilIdle();
 }
 
-base::TimeTicks DummyTickClock::NowTicks() {
+base::TimeTicks DummyTickClock::NowTicks() const {
   now_ += base::TimeDelta::FromSeconds(60);
   return now_;
 }
diff --git a/media/test/pipeline_integration_test_base.h b/media/test/pipeline_integration_test_base.h
index 57503806..cb77140 100644
--- a/media/test/pipeline_integration_test_base.h
+++ b/media/test/pipeline_integration_test_base.h
@@ -49,10 +49,10 @@
  public:
   DummyTickClock() : now_() {}
   ~DummyTickClock() override {}
-  base::TimeTicks NowTicks() override;
+  base::TimeTicks NowTicks() const override;
 
  private:
-  base::TimeTicks now_;
+  mutable base::TimeTicks now_;
 };
 
 class PipelineTestRendererFactory {
diff --git a/net/base/backoff_entry_serializer_unittest.cc b/net/base/backoff_entry_serializer_unittest.cc
index e5d22cf..748e226 100644
--- a/net/base/backoff_entry_serializer_unittest.cc
+++ b/net/base/backoff_entry_serializer_unittest.cc
@@ -34,7 +34,7 @@
   TestTickClock() = default;
   ~TestTickClock() override = default;
 
-  TimeTicks NowTicks() override { return now_ticks_; }
+  TimeTicks NowTicks() const override { return now_ticks_; }
   void set_now(TimeTicks now) { now_ticks_ = now; }
 
  private:
diff --git a/net/base/backoff_entry_unittest.cc b/net/base/backoff_entry_unittest.cc
index 0bed782e..191097a 100644
--- a/net/base/backoff_entry_unittest.cc
+++ b/net/base/backoff_entry_unittest.cc
@@ -22,7 +22,7 @@
   TestTickClock() = default;
   ~TestTickClock() override = default;
 
-  TimeTicks NowTicks() override { return now_ticks_; }
+  TimeTicks NowTicks() const override { return now_ticks_; }
   void set_now(TimeTicks now) { now_ticks_ = now; }
 
  private:
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index 366958a2..bce5390 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -60,6 +60,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/process_memory_dump.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/cookies/cookie_monster_change_dispatcher.h"
@@ -565,6 +566,32 @@
   return store_.get() == nullptr;
 }
 
+void CookieMonster::DumpMemoryStats(
+    base::trace_event::ProcessMemoryDump* pmd,
+    const std::string& parent_absolute_name) const {
+  const char kRelPath[] = "/cookie_monster";
+
+  pmd->CreateAllocatorDump(parent_absolute_name + kRelPath + "/cookies")
+      ->AddScalar(base::trace_event::MemoryAllocatorDump::kNameObjectCount,
+                  base::trace_event::MemoryAllocatorDump::kUnitsObjects,
+                  cookies_.size());
+
+  pmd->CreateAllocatorDump(parent_absolute_name + kRelPath +
+                           "/tasks_pending_global")
+      ->AddScalar(base::trace_event::MemoryAllocatorDump::kNameObjectCount,
+                  base::trace_event::MemoryAllocatorDump::kUnitsObjects,
+                  tasks_pending_.size());
+
+  size_t total_pending_for_key = 0;
+  for (const auto& kv : tasks_pending_for_key_)
+    total_pending_for_key += kv.second.size();
+  pmd->CreateAllocatorDump(parent_absolute_name + kRelPath +
+                           "/tasks_pending_for_key")
+      ->AddScalar(base::trace_event::MemoryAllocatorDump::kNameObjectCount,
+                  base::trace_event::MemoryAllocatorDump::kUnitsObjects,
+                  total_pending_for_key);
+}
+
 CookieMonster::~CookieMonster() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h
index f61eee1..4ad2427 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -207,6 +207,9 @@
 
   bool IsEphemeral() override;
 
+  void DumpMemoryStats(base::trace_event::ProcessMemoryDump* pmd,
+                       const std::string& parent_absolute_name) const override;
+
   // Find a key based on the given domain, which will be used to find all
   // cookies potentially relevant to it. This is used for lookup in cookies_ as
   // well as for PersistentCookieStore::LoadCookiesForKey. See comment on keys
diff --git a/net/cookies/cookie_store.cc b/net/cookies/cookie_store.cc
index 6d273b9..e536dbd 100644
--- a/net/cookies/cookie_store.cc
+++ b/net/cookies/cookie_store.cc
@@ -40,6 +40,10 @@
   return channel_id_service_id_;
 }
 
+void CookieStore::DumpMemoryStats(
+    base::trace_event::ProcessMemoryDump* pmd,
+    const std::string& parent_absolute_name) const {}
+
 CookieStore::CookieStore() : channel_id_service_id_(-1) {}
 
 }  // namespace net
diff --git a/net/cookies/cookie_store.h b/net/cookies/cookie_store.h
index cfe57f7f..980da01 100644
--- a/net/cookies/cookie_store.h
+++ b/net/cookies/cookie_store.h
@@ -21,6 +21,12 @@
 
 class GURL;
 
+namespace base {
+namespace trace_event {
+class ProcessMemoryDump;
+}
+}  // namespace base
+
 namespace net {
 
 class CookieChangeDispatcher;
@@ -146,6 +152,10 @@
   void SetChannelIDServiceID(int id);
   int GetChannelIDServiceID();
 
+  // Reports the estimate of dynamically allocated memory in bytes.
+  virtual void DumpMemoryStats(base::trace_event::ProcessMemoryDump* pmd,
+                               const std::string& parent_absolute_name) const;
+
  protected:
   CookieStore();
   int channel_id_service_id_;
diff --git a/net/extras/sqlite/sqlite_persistent_cookie_store.cc b/net/extras/sqlite/sqlite_persistent_cookie_store.cc
index 37b56ded..4012beb 100644
--- a/net/extras/sqlite/sqlite_persistent_cookie_store.cc
+++ b/net/extras/sqlite/sqlite_persistent_cookie_store.cc
@@ -45,6 +45,7 @@
 enum CookieLoadProblem {
   COOKIE_LOAD_PROBLEM_DECRYPT_FAILED = 0,
   COOKIE_LOAD_PROBLEM_DECRYPT_TIMEOUT = 1,
+  COOKIE_LOAD_PROBLEM_NON_CANONICAL = 2,
   COOKIE_LOAD_PROBLEM_LAST_ENTRY
 };
 
@@ -908,8 +909,11 @@
             static_cast<DBCookiePriority>(smt.ColumnInt(13)))));  // priority
     DLOG_IF(WARNING, cc->CreationDate() > Time::Now())
         << L"CreationDate too recent";
-    if (cc->IsCanonical())
+    if (cc->IsCanonical()) {
       cookies->push_back(std::move(cc));
+    } else {
+      RecordCookieLoadProblem(COOKIE_LOAD_PROBLEM_NON_CANONICAL);
+    }
     ++num_cookies_read_;
   }
 }
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index d07f7352..c733e8a 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -113,7 +113,7 @@
       quic_max_server_configs_stored_in_properties(0u),
       quic_enable_socket_recv_optimization(false),
       mark_quic_broken_when_network_blackholes(false),
-      retry_without_alt_svc_on_quic_errors(false),
+      retry_without_alt_svc_on_quic_errors(true),
       support_ietf_format_quic_altsvc(false),
       quic_close_sessions_on_ip_change(false),
       quic_idle_connection_timeout_seconds(kIdleConnectionTimeoutSeconds),
diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc
index c90a039..fd0ff13c 100644
--- a/net/http/http_stream_factory_impl_job.cc
+++ b/net/http/http_stream_factory_impl_job.cc
@@ -1051,14 +1051,8 @@
         net_log_.AddEvent(
             NetLogEventType::HTTP_STREAM_REQUEST_PROTO,
             base::Bind(&NetLogHttpStreamProtoCallback, negotiated_protocol_));
-        if (negotiated_protocol_ == kProtoHTTP2) {
-          // If request is WebSocket, HTTP/2 must not have been advertised in
-          // the TLS handshake.  The TLS layer must not have accepted the
-          // server choosing HTTP/2.
-          // TODO(bnc): Change to DCHECK once https://crbug.com/819101 is fixed.
-          CHECK(!is_websocket_);
+        if (negotiated_protocol_ == kProtoHTTP2)
           using_spdy_ = true;
-        }
       }
     }
   } else if (proxy_info_.is_https() && connection_->socket() &&
@@ -1219,10 +1213,6 @@
 
   CHECK(!stream_.get());
 
-  // WebSocket over HTTP/2 is only allowed to use existing connections.
-  // TODO(bnc): Change to DCHECK once https://crbug.com/819101 is fixed.
-  CHECK(!is_websocket_ || existing_spdy_session_);
-
   // It is possible that a pushed stream has been opened by a server since last
   // time Job checked above.
   if (!existing_spdy_session_) {
diff --git a/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc b/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc
index 563b7bd..4be593a 100644
--- a/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc
+++ b/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc
@@ -8,6 +8,7 @@
 
 #include "base/logging.h"
 #include "base/trace_event/memory_usage_estimator.h"
+#include "net/http2/platform/api/http2_string.h"
 #include "net/http2/tools/http2_bug_tracker.h"
 
 namespace net {
@@ -166,7 +167,7 @@
   DVLOG(3) << "HpackDecoderStringBuffer::BufferStringIfUnbuffered state="
            << state_ << ", backing=" << backing_;
   if (state_ != State::RESET && backing_ == Backing::UNBUFFERED) {
-    DVLOG(2) << "HpackDecoderStringBuffer buffering string of length "
+    DVLOG(2) << "HpackDecoderStringBuffer buffering Http2String of length "
              << value_.size();
     buffer_.assign(value_.data(), value_.size());
     if (state_ == State::COMPLETE) {
@@ -201,7 +202,7 @@
     if (backing_ == Backing::BUFFERED) {
       return std::move(buffer_);
     } else {
-      return value_.as_string();
+      return Http2String(value_);
     }
   }
   return "";
diff --git a/net/http2/hpack/decoder/hpack_decoder_test.cc b/net/http2/hpack/decoder/hpack_decoder_test.cc
index 7a89e821..3a34f80 100644
--- a/net/http2/hpack/decoder/hpack_decoder_test.cc
+++ b/net/http2/hpack/decoder/hpack_decoder_test.cc
@@ -121,7 +121,7 @@
   // error_message may be used in a GOAWAY frame as the Opaque Data.
   void OnHeaderErrorDetected(Http2StringPiece error_message) override {
     ASSERT_TRUE(saw_start_);
-    error_messages_.push_back(error_message.as_string());
+    error_messages_.push_back(Http2String(error_message));
     // No further callbacks should be made at this point, so replace 'this' as
     // the listener with mock_listener_, which is a strict mock, so will
     // generate an error for any calls.
diff --git a/net/http2/http2_constants.h b/net/http2/http2_constants.h
index a6149f5..05c85026 100644
--- a/net/http2/http2_constants.h
+++ b/net/http2/http2_constants.h
@@ -53,10 +53,10 @@
   return IsSupportedHttp2FrameType(static_cast<uint32_t>(v));
 }
 
-// The return type is 'string' so that they can generate a unique string for
-// each unsupported value. Since these are just used for debugging/error
-// messages, that isn't a cost to we need to worry about.
-// The same applies to the functions later in this file.
+// The return type is 'Http2String' so that they can generate a unique string
+// for each unsupported value. Since these are just used for debugging/error
+// messages, that isn't a cost to we need to worry about. The same applies to
+// the functions later in this file.
 HTTP2_EXPORT_PRIVATE Http2String Http2FrameTypeToString(Http2FrameType v);
 HTTP2_EXPORT_PRIVATE Http2String Http2FrameTypeToString(uint8_t v);
 HTTP2_EXPORT_PRIVATE inline std::ostream& operator<<(std::ostream& out,
diff --git a/net/quic/chromium/quic_chromium_client_session.cc b/net/quic/chromium/quic_chromium_client_session.cc
index 1e270ea..7e6bee3 100644
--- a/net/quic/chromium/quic_chromium_client_session.cc
+++ b/net/quic/chromium/quic_chromium_client_session.cc
@@ -182,6 +182,9 @@
       return "OnMigrateBackToDefaultNetwork";
     case ON_PATH_DEGRADING:
       return "OnPathDegrading";
+    default:
+      QUIC_NOTREACHED();
+      break;
   }
   return "InvalidCause";
 }
@@ -213,6 +216,19 @@
   return std::move(dict);
 }
 
+// TODO(fayang): Remove this when necessary data is collected.
+void LogProbeResultToHistogram(ConnectionMigrationCause cause, bool success) {
+  UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.ConnectionMigrationProbeSuccess",
+                        success);
+  const std::string histogram_name =
+      "Net.QuicSession.ConnectionMigrationProbeSuccess." +
+      ConnectionMigrationCauseToString(cause);
+  STATIC_HISTOGRAM_POINTER_GROUP(
+      histogram_name, cause, MIGRATION_CAUSE_MAX, AddBoolean(success),
+      base::BooleanHistogram::FactoryGet(
+          histogram_name, base::HistogramBase::kUmaTargetedHistogramFlag));
+}
+
 class HpackEncoderDebugVisitor : public QuicHpackDebugVisitor {
   void OnUseEntry(QuicTime::Delta elapsed) override {
     UMA_HISTOGRAM_TIMES(
@@ -1731,6 +1747,8 @@
       NetLogEventType::QUIC_CONNECTION_CONNECTIVITY_PROBING_SUCCEEDED,
       NetLog::Int64Callback("network", network));
 
+  LogProbeResultToHistogram(current_connection_migration_cause_, true);
+
   // Set |this| to listen on socket write events on the packet writer
   // that was used for probing.
   writer->set_delegate(this);
@@ -1769,6 +1787,8 @@
   net_log_.AddEvent(
       NetLogEventType::QUIC_CONNECTION_CONNECTIVITY_PROBING_FAILED,
       NetLog::Int64Callback("network", network));
+
+  LogProbeResultToHistogram(current_connection_migration_cause_, false);
   // Probing failure for default network can be ignored.
   DVLOG(1) << "Connectivity probing failed on NetworkHandle " << network;
   DVLOG_IF(1, network == default_network_ &&
diff --git a/net/quic/chromium/quic_chromium_client_session.h b/net/quic/chromium/quic_chromium_client_session.h
index d58785f..68ef0c9 100644
--- a/net/quic/chromium/quic_chromium_client_session.h
+++ b/net/quic/chromium/quic_chromium_client_session.h
@@ -90,6 +90,7 @@
   ON_NETWORK_MADE_DEFAULT,             // With probing.
   ON_MIGRATE_BACK_TO_DEFAULT_NETWORK,  // With probing.
   ON_PATH_DEGRADING,                   // With probing.
+  MIGRATION_CAUSE_MAX
 };
 
 // Result of connection migration.
diff --git a/net/quic/chromium/quic_network_transaction_unittest.cc b/net/quic/chromium/quic_network_transaction_unittest.cc
index ee8dab6c..3244038 100644
--- a/net/quic/chromium/quic_network_transaction_unittest.cc
+++ b/net/quic/chromium/quic_network_transaction_unittest.cc
@@ -947,6 +947,7 @@
                        ::testing::Bool()));
 
 TEST_P(QuicNetworkTransactionTest, WriteErrorHandshakeConfirmed) {
+  session_params_.retry_without_alt_svc_on_quic_errors = false;
   base::HistogramTester histograms;
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
@@ -978,6 +979,7 @@
 }
 
 TEST_P(QuicNetworkTransactionTest, WriteErrorHandshakeConfirmedAsync) {
+  session_params_.retry_without_alt_svc_on_quic_errors = false;
   base::HistogramTester histograms;
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
@@ -1185,6 +1187,7 @@
 }
 
 TEST_P(QuicNetworkTransactionTest, TooLargeResponseHeaders) {
+  session_params_.retry_without_alt_svc_on_quic_errors = false;
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
@@ -2069,6 +2072,7 @@
 // Verify that if a QUIC connection times out, the QuicHttpStream will
 // return QUIC_PROTOCOL_ERROR.
 TEST_P(QuicNetworkTransactionTest, TimeoutAfterHandshakeConfirmed) {
+  session_params_.retry_without_alt_svc_on_quic_errors = false;
   session_params_.quic_idle_connection_timeout_seconds = 5;
 
   // The request will initially go out over QUIC.
@@ -2162,6 +2166,7 @@
 // Verify that if a QUIC connection RTOs, the QuicHttpStream will
 // return QUIC_PROTOCOL_ERROR.
 TEST_P(QuicNetworkTransactionTest, TooManyRtosAfterHandshakeConfirmed) {
+  session_params_.retry_without_alt_svc_on_quic_errors = false;
   session_params_.quic_connection_options.push_back(k5RTO);
 
   // The request will initially go out over QUIC.
@@ -2364,6 +2369,7 @@
 // Verify that if a QUIC protocol error occurs after the handshake is confirmed
 // the request fails with QUIC_PROTOCOL_ERROR.
 TEST_P(QuicNetworkTransactionTest, ProtocolErrorAfterHandshakeConfirmed) {
+  session_params_.retry_without_alt_svc_on_quic_errors = false;
   // The request will initially go out over QUIC.
   MockQuicData quic_data;
   QuicStreamOffset header_stream_offset = 0;
@@ -2546,7 +2552,6 @@
 // connection times out, then QUIC will be marked as broken and the request
 // retried over TCP.
 TEST_P(QuicNetworkTransactionTest, TimeoutAfterHandshakeConfirmedThenBroken2) {
-  session_params_.retry_without_alt_svc_on_quic_errors = true;
   session_params_.quic_idle_connection_timeout_seconds = 5;
 
   // The request will initially go out over QUIC.
@@ -3016,7 +3021,6 @@
 // retried over TCP and the QUIC will be marked as broken.
 TEST_P(QuicNetworkTransactionTest,
        ProtocolErrorAfterHandshakeConfirmedThenBroken) {
-  session_params_.retry_without_alt_svc_on_quic_errors = true;
   session_params_.quic_idle_connection_timeout_seconds = 5;
 
   // The request will initially go out over QUIC.
@@ -3104,8 +3108,6 @@
 // request is reset from, then QUIC will be marked as broken and the request
 // retried over TCP.
 TEST_P(QuicNetworkTransactionTest, ResetAfterHandshakeConfirmedThenBroken) {
-  session_params_.retry_without_alt_svc_on_quic_errors = true;
-
   // The request will initially go out over QUIC.
   MockQuicData quic_data;
   QuicStreamOffset header_stream_offset = 0;
@@ -3266,7 +3268,6 @@
 TEST_P(QuicNetworkTransactionTest,
        ResetPooledAfterHandshakeConfirmedThenBroken) {
   session_params_.quic_allow_remote_alt_svc = true;
-  session_params_.retry_without_alt_svc_on_quic_errors = true;
 
   GURL origin1 = request_.url;
   GURL origin2("https://www.example.org/");
@@ -4163,6 +4164,7 @@
 
 TEST_P(QuicNetworkTransactionTest,
        LogGranularQuicErrorCodeOnQuicProtocolErrorLocal) {
+  session_params_.retry_without_alt_svc_on_quic_errors = false;
   MockQuicData mock_quic_data;
   QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
@@ -4216,6 +4218,7 @@
 
 TEST_P(QuicNetworkTransactionTest,
        LogGranularQuicErrorCodeOnQuicProtocolErrorRemote) {
+  session_params_.retry_without_alt_svc_on_quic_errors = false;
   MockQuicData mock_quic_data;
   QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
@@ -4334,6 +4337,7 @@
 }
 
 TEST_P(QuicNetworkTransactionTest, RstSteamBeforeHeaders) {
+  session_params_.retry_without_alt_svc_on_quic_errors = false;
   MockQuicData mock_quic_data;
   QuicStreamOffset header_stream_offset = 0;
   mock_quic_data.AddWrite(
@@ -4929,6 +4933,7 @@
 }
 
 TEST_P(QuicNetworkTransactionTest, QuicUploadWriteError) {
+  session_params_.retry_without_alt_svc_on_quic_errors = false;
   ScopedMockNetworkChangeNotifier network_change_notifier;
   MockNetworkChangeNotifier* mock_ncn =
       network_change_notifier.mock_network_change_notifier();
@@ -5034,6 +5039,7 @@
 }
 
 TEST_P(QuicNetworkTransactionTest, MaxRetriesAfterAsyncNoBufferSpace) {
+  session_params_.retry_without_alt_svc_on_quic_errors = false;
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
@@ -5071,6 +5077,7 @@
 }
 
 TEST_P(QuicNetworkTransactionTest, MaxRetriesAfterSynchronousNoBufferSpace) {
+  session_params_.retry_without_alt_svc_on_quic_errors = false;
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
@@ -5108,6 +5115,7 @@
 }
 
 TEST_P(QuicNetworkTransactionTest, NoMigrationForMsgTooBig) {
+  session_params_.retry_without_alt_svc_on_quic_errors = false;
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
   const QuicString error_details =
@@ -5211,6 +5219,7 @@
 // is closed before the pushed headers arrive, but after the connection
 // is closed and before the callbacks are executed.
 TEST_P(QuicNetworkTransactionTest, CancelServerPushAfterConnectionClose) {
+  session_params_.retry_without_alt_svc_on_quic_errors = false;
   session_params_.origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
 
diff --git a/net/quic/platform/impl/quic_test_impl.cc b/net/quic/platform/impl/quic_test_impl.cc
index 739a4951..6e22a9f 100644
--- a/net/quic/platform/impl/quic_test_impl.cc
+++ b/net/quic/platform/impl/quic_test_impl.cc
@@ -4,20 +4,14 @@
 
 #include "net/quic/platform/impl/quic_test_impl.h"
 
-#include "base/logging.h"
-#include "net/quic/platform/api/quic_flags.h"
-
 QuicFlagSaverImpl::QuicFlagSaverImpl() {
-#define QUIC_FLAG(type, flag, value)                                 \
-  CHECK_EQ(value, flag)                                              \
-      << "Flag set to an unexpected value.  A prior test is likely " \
-      << "setting a flag without using a QuicFlagSaver";
+#define QUIC_FLAG(type, flag, value) saved_##flag##_ = flag;
 #include "net/quic/core/quic_flags_list.h"
 #undef QUIC_FLAG
 }
 
 QuicFlagSaverImpl::~QuicFlagSaverImpl() {
-#define QUIC_FLAG(type, flag, value) flag = value;
+#define QUIC_FLAG(type, flag, value) flag = saved_##flag##_;
 #include "net/quic/core/quic_flags_list.h"
 #undef QUIC_FLAG
 }
diff --git a/net/quic/platform/impl/quic_test_impl.h b/net/quic/platform/impl/quic_test_impl.h
index 4f6a418..98ae876 100644
--- a/net/quic/platform/impl/quic_test_impl.h
+++ b/net/quic/platform/impl/quic_test_impl.h
@@ -5,26 +5,49 @@
 #ifndef NET_QUIC_PLATFORM_IMPL_QUIC_TEST_IMPL_H_
 #define NET_QUIC_PLATFORM_IMPL_QUIC_TEST_IMPL_H_
 
+#include "base/logging.h"
+#include "net/quic/platform/api/quic_flags.h"
 #include "testing/gmock/include/gmock/gmock.h"  // IWYU pragma: export
 #include "testing/gtest/include/gtest/gtest.h"  // IWYU pragma: export
 
-// When constructed, checks that all QUIC flags have their correct default
-// values and when destructed, restores those values.
+// When constructed, saves the current values of all QUIC flags. When
+// destructed, restores all QUIC flags to the saved values.
 class QuicFlagSaverImpl {
  public:
   QuicFlagSaverImpl();
   ~QuicFlagSaverImpl();
+
+ private:
+#define QUIC_FLAG(type, flag, value) type saved_##flag##_;
+#include "net/quic/core/quic_flags_list.h"
+#undef QUIC_FLAG
+};
+
+// Checks if all QUIC flags are on their default values on construction.
+class QuicFlagChecker {
+ public:
+  QuicFlagChecker() {
+#define QUIC_FLAG(type, flag, value)                                      \
+  CHECK_EQ(value, flag)                                                   \
+      << "Flag set to an unexpected value.  A prior test is likely "      \
+      << "setting a flag without using a QuicFlagSaver. Use QuicTest to " \
+         "avoid this issue.";
+#include "net/quic/core/quic_flags_list.h"
+#undef QUIC_FLAG
+  }
 };
 
 class QuicTestImpl : public ::testing::Test {
  private:
-  QuicFlagSaverImpl flags_;  // Save/restore all QUIC flag values.
+  QuicFlagChecker checker_;
+  QuicFlagSaverImpl saver_;  // Save/restore all QUIC flag values.
 };
 
 template <class T>
 class QuicTestWithParamImpl : public ::testing::TestWithParam<T> {
  private:
-  QuicFlagSaverImpl flags_;  // Save/restore all QUIC flag values.
+  QuicFlagChecker checker_;
+  QuicFlagSaverImpl saver_;  // Save/restore all QUIC flag values.
 };
 
 #endif  // NET_QUIC_PLATFORM_IMPL_QUIC_TEST_IMPL_H_
diff --git a/net/spdy/chromium/spdy_http_utils.cc b/net/spdy/chromium/spdy_http_utils.cc
index 0cb8c3a..74356b3 100644
--- a/net/spdy/chromium/spdy_http_utils.cc
+++ b/net/spdy/chromium/spdy_http_utils.cc
@@ -114,7 +114,7 @@
     const HttpRequestHeaders& request_headers,
     SpdyHeaderBlock* headers) {
   (*headers)[kHttp2MethodHeader] = "CONNECT";
-  (*headers)[kHttp2AuthorityHeader] = GetHostAndPort(url);
+  (*headers)[kHttp2AuthorityHeader] = GetHostAndOptionalPort(url);
   (*headers)[kHttp2SchemeHeader] = "https";
   (*headers)[kHttp2PathHeader] = url.PathForRequest();
   (*headers)[kHttp2ProtocolHeader] = "websocket";
diff --git a/net/spdy/chromium/spdy_network_transaction_unittest.cc b/net/spdy/chromium/spdy_network_transaction_unittest.cc
index d2055649..9bd8130 100644
--- a/net/spdy/chromium/spdy_network_transaction_unittest.cc
+++ b/net/spdy/chromium/spdy_network_transaction_unittest.cc
@@ -7124,7 +7124,7 @@
 
   SpdyHeaderBlock websocket_request_headers;
   websocket_request_headers[kHttp2MethodHeader] = "CONNECT";
-  websocket_request_headers[kHttp2AuthorityHeader] = "www.example.org:443";
+  websocket_request_headers[kHttp2AuthorityHeader] = "www.example.org";
   websocket_request_headers[kHttp2SchemeHeader] = "https";
   websocket_request_headers[kHttp2PathHeader] = "/";
   websocket_request_headers[kHttp2ProtocolHeader] = "websocket";
diff --git a/net/url_request/url_request_context.cc b/net/url_request/url_request_context.cc
index 381107e..e23b9ec 100644
--- a/net/url_request/url_request_context.cc
+++ b/net/url_request/url_request_context.cc
@@ -172,6 +172,9 @@
     if (http_cache)
       http_cache->DumpMemoryStats(pmd, dump->absolute_name());
   }
+  if (cookie_store_) {
+    cookie_store_->DumpMemoryStats(pmd, dump->absolute_name());
+  }
   return true;
 }
 
diff --git a/net/url_request/url_request_throttler_test_support.cc b/net/url_request/url_request_throttler_test_support.cc
index d1ef6ca..530612b 100644
--- a/net/url_request/url_request_throttler_test_support.cc
+++ b/net/url_request/url_request_throttler_test_support.cc
@@ -14,7 +14,7 @@
 
 TestTickClock::~TestTickClock() = default;
 
-base::TimeTicks TestTickClock::NowTicks() {
+base::TimeTicks TestTickClock::NowTicks() const {
   return now_ticks_;
 }
 
diff --git a/net/url_request/url_request_throttler_test_support.h b/net/url_request/url_request_throttler_test_support.h
index 93e4e2a..80f3383 100644
--- a/net/url_request/url_request_throttler_test_support.h
+++ b/net/url_request/url_request_throttler_test_support.h
@@ -20,7 +20,7 @@
   explicit TestTickClock(base::TimeTicks now);
   ~TestTickClock() override;
 
-  base::TimeTicks NowTicks() override;
+  base::TimeTicks NowTicks() const override;
   void set_now(base::TimeTicks now) { now_ticks_ = now; }
 
  private:
diff --git a/net/websockets/websocket_handshake_stream_create_helper_test.cc b/net/websockets/websocket_handshake_stream_create_helper_test.cc
index 3a5a3392..e0817ba0 100644
--- a/net/websockets/websocket_handshake_stream_create_helper_test.cc
+++ b/net/websockets/websocket_handshake_stream_create_helper_test.cc
@@ -186,7 +186,7 @@
       case HTTP2_HANDSHAKE_STREAM: {
         SpdyTestUtil spdy_util;
         SpdyHeaderBlock request_header_block = WebSocketHttp2Request(
-            kPath, "www.example.org:443", kOrigin, extra_request_headers);
+            kPath, "www.example.org", kOrigin, extra_request_headers);
         SpdySerializedFrame request_headers(spdy_util.ConstructSpdyHeaders(
             1, std::move(request_header_block), DEFAULT_PRIORITY, false));
         MockWrite writes[] = {CreateMockWrite(request_headers, 0)};
diff --git a/net/websockets/websocket_stream_test.cc b/net/websockets/websocket_stream_test.cc
index e0783f1d..d9c5d80 100644
--- a/net/websockets/websocket_stream_test.cc
+++ b/net/websockets/websocket_stream_test.cc
@@ -220,7 +220,7 @@
 
     // WebSocket request.
     SpdyHeaderBlock request_headers =
-        WebSocketHttp2Request(socket_path, "www.example.org:443",
+        WebSocketHttp2Request(socket_path, "www.example.org",
                               "http://www.example.org", extra_request_headers);
     frames_.push_back(spdy_util_.ConstructSpdyHeaders(
         3, std::move(request_headers), DEFAULT_PRIORITY, false));
@@ -1567,6 +1567,10 @@
     base::RunLoop().RunUntilIdle();
     stream_request_.reset();
 
+    EXPECT_TRUE(has_failed());
+    EXPECT_EQ("Stream closed with error: net::ERR_SPDY_PROTOCOL_ERROR",
+              failure_message());
+
     auto samples = histogram_tester.GetHistogramSamplesSinceCreation(
         "Net.WebSocket.HandshakeResult2");
     EXPECT_EQ(1, samples->TotalCount());
diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc
index 1d1a510..3e47b62 100644
--- a/pdf/out_of_process_instance.cc
+++ b/pdf/out_of_process_instance.cc
@@ -112,6 +112,8 @@
 const char kJSSetScrollPositionType[] = "setScrollPosition";
 const char kJSPositionX[] = "x";
 const char kJSPositionY[] = "y";
+// Scroll by (Plugin -> Page)
+const char kJSScrollByType[] = "scrollBy";
 // Cancel the stream URL request (Plugin -> Page)
 const char kJSCancelStreamUrlType[] = "cancelStreamUrl";
 // Navigate to the given URL (Plugin -> Page)
@@ -1242,6 +1244,14 @@
   PostMessage(position);
 }
 
+void OutOfProcessInstance::ScrollBy(const pp::Point& point) {
+  pp::VarDictionary position;
+  position.Set(kType, kJSScrollByType);
+  position.Set(kJSPositionX, pp::Var(point.x() / device_scale_));
+  position.Set(kJSPositionY, pp::Var(point.y() / device_scale_));
+  PostMessage(position);
+}
+
 void OutOfProcessInstance::ScrollToPage(int page) {
   if (engine_->GetNumberOfPages() == 0)
     return;
diff --git a/pdf/out_of_process_instance.h b/pdf/out_of_process_instance.h
index 7a9c966f..edc6cd9 100644
--- a/pdf/out_of_process_instance.h
+++ b/pdf/out_of_process_instance.h
@@ -101,6 +101,7 @@
   void DidScroll(const pp::Point& point) override;
   void ScrollToX(int x_in_screen_coords) override;
   void ScrollToY(int y_in_screen_coords, bool compensate_for_toolbar) override;
+  void ScrollBy(const pp::Point& point) override;
   void ScrollToPage(int page) override;
   void NavigateTo(const std::string& url,
                   WindowOpenDisposition disposition) override;
diff --git a/pdf/pdf_engine.h b/pdf/pdf_engine.h
index 5553336..d6964c6d 100644
--- a/pdf/pdf_engine.h
+++ b/pdf/pdf_engine.h
@@ -146,6 +146,9 @@
     virtual void ScrollToY(int y_in_screen_coords,
                            bool compensate_for_toolbar) = 0;
 
+    // Scroll by a given delta relative to the current position.
+    virtual void ScrollBy(const pp::Point& point) = 0;
+
     // Scroll to zero-based |page|.
     virtual void ScrollToPage(int page) = 0;
 
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index 0019b8a..5ebcff7 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -758,6 +758,7 @@
       in_form_text_area_(false),
       editable_form_text_area_(false),
       mouse_left_button_down_(false),
+      mouse_middle_button_down_(false),
       permissions_(0),
       permissions_handler_revision_(-1),
       fpdf_availability_(nullptr),
@@ -1448,6 +1449,9 @@
     case PP_INPUTEVENT_TYPE_MOUSEMOVE:
       rv = OnMouseMove(pp::MouseInputEvent(event));
       break;
+    case PP_INPUTEVENT_TYPE_MOUSEENTER:
+      OnMouseEnter(pp::MouseInputEvent(event));
+      break;
     case PP_INPUTEVENT_TYPE_KEYDOWN:
       rv = OnKeyDown(pp::KeyboardInputEvent(event));
       break;
@@ -1947,6 +1951,8 @@
 
 bool PDFiumEngine::OnMiddleMouseDown(const pp::MouseInputEvent& event) {
   SetMouseLeftButtonDown(false);
+  mouse_middle_button_down_ = true;
+  mouse_middle_button_last_position_ = event.GetPosition();
 
   SelectionChangeInvalidator selection_invalidator(this);
   selection_.clear();
@@ -2047,6 +2053,8 @@
 
   if (event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_LEFT)
     SetMouseLeftButtonDown(false);
+  else if (event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_MIDDLE)
+    mouse_middle_button_down_ = false;
 
   int page_index = -1;
   int char_index = -1;
@@ -2121,48 +2129,7 @@
     mouse_down_state_.Reset();
 
   if (!selecting_) {
-    PP_CursorType_Dev cursor;
-    switch (area) {
-      case PDFiumPage::TEXT_AREA:
-        cursor = PP_CURSORTYPE_IBEAM;
-        break;
-      case PDFiumPage::WEBLINK_AREA:
-      case PDFiumPage::DOCLINK_AREA:
-        cursor = PP_CURSORTYPE_HAND;
-        break;
-      case PDFiumPage::NONSELECTABLE_AREA:
-      case PDFiumPage::FORM_TEXT_AREA:
-      default:
-        switch (form_type) {
-          case FPDF_FORMFIELD_PUSHBUTTON:
-          case FPDF_FORMFIELD_CHECKBOX:
-          case FPDF_FORMFIELD_RADIOBUTTON:
-          case FPDF_FORMFIELD_COMBOBOX:
-          case FPDF_FORMFIELD_LISTBOX:
-            cursor = PP_CURSORTYPE_HAND;
-            break;
-          case FPDF_FORMFIELD_TEXTFIELD:
-            cursor = PP_CURSORTYPE_IBEAM;
-            break;
-#if defined(PDF_ENABLE_XFA)
-          case FPDF_FORMFIELD_XFA_CHECKBOX:
-          case FPDF_FORMFIELD_XFA_COMBOBOX:
-          case FPDF_FORMFIELD_XFA_IMAGEFIELD:
-          case FPDF_FORMFIELD_XFA_LISTBOX:
-          case FPDF_FORMFIELD_XFA_PUSHBUTTON:
-          case FPDF_FORMFIELD_XFA_SIGNATURE:
-            cursor = PP_CURSORTYPE_HAND;
-            break;
-          case FPDF_FORMFIELD_XFA_TEXTFIELD:
-            cursor = PP_CURSORTYPE_IBEAM;
-            break;
-#endif
-          default:
-            cursor = PP_CURSORTYPE_POINTER;
-            break;
-        }
-        break;
-    }
+    client_->UpdateCursor(DetermineCursorType(area, form_type));
 
     if (page_index != -1) {
       double page_x;
@@ -2171,7 +2138,6 @@
       FORM_OnMouseMove(form_, pages_[page_index]->GetPage(), 0, page_x, page_y);
     }
 
-    client_->UpdateCursor(cursor);
     std::string url = GetLinkAtPosition(event.GetPosition());
     if (url != link_under_cursor_) {
       link_under_cursor_ = url;
@@ -2185,6 +2151,19 @@
       SetFormSelectedText(form_, pages_[last_page_mouse_down_]->GetPage());
     }
 
+    if (mouse_middle_button_down_) {
+      // Subtract (origin - destination) so delta is already the delta for
+      // moving the page, rather than the delta the mouse moved.
+      // GetMovement() does not work here, as small mouse movements are
+      // considered zero.
+      pp::Point page_position_delta =
+          mouse_middle_button_last_position_ - event.GetPosition();
+      if (page_position_delta.x() != 0 || page_position_delta.y() != 0) {
+        client_->ScrollBy(page_position_delta);
+        mouse_middle_button_last_position_ = event.GetPosition();
+      }
+    }
+
     // No need to swallow the event, since this might interfere with the
     // scrollbars if the user is dragging them.
     return false;
@@ -2199,6 +2178,60 @@
   return ExtendSelection(page_index, char_index);
 }
 
+PP_CursorType_Dev PDFiumEngine::DetermineCursorType(PDFiumPage::Area area,
+                                                    int form_type) const {
+  if (mouse_middle_button_down_) {
+    return PP_CURSORTYPE_HAND;
+  }
+
+  switch (area) {
+    case PDFiumPage::TEXT_AREA:
+      return PP_CURSORTYPE_IBEAM;
+    case PDFiumPage::WEBLINK_AREA:
+    case PDFiumPage::DOCLINK_AREA:
+      return PP_CURSORTYPE_HAND;
+    case PDFiumPage::NONSELECTABLE_AREA:
+    case PDFiumPage::FORM_TEXT_AREA:
+    default:
+      switch (form_type) {
+        case FPDF_FORMFIELD_PUSHBUTTON:
+        case FPDF_FORMFIELD_CHECKBOX:
+        case FPDF_FORMFIELD_RADIOBUTTON:
+        case FPDF_FORMFIELD_COMBOBOX:
+        case FPDF_FORMFIELD_LISTBOX:
+          return PP_CURSORTYPE_HAND;
+        case FPDF_FORMFIELD_TEXTFIELD:
+          return PP_CURSORTYPE_IBEAM;
+#if defined(PDF_ENABLE_XFA)
+        case FPDF_FORMFIELD_XFA_CHECKBOX:
+        case FPDF_FORMFIELD_XFA_COMBOBOX:
+        case FPDF_FORMFIELD_XFA_IMAGEFIELD:
+        case FPDF_FORMFIELD_XFA_LISTBOX:
+        case FPDF_FORMFIELD_XFA_PUSHBUTTON:
+        case FPDF_FORMFIELD_XFA_SIGNATURE:
+          return PP_CURSORTYPE_HAND;
+        case FPDF_FORMFIELD_XFA_TEXTFIELD:
+          return PP_CURSORTYPE_IBEAM;
+#endif
+        default:
+          return PP_CURSORTYPE_POINTER;
+      }
+  }
+}
+
+void PDFiumEngine::OnMouseEnter(const pp::MouseInputEvent& event) {
+  if (event.GetModifiers() & PP_INPUTEVENT_MODIFIER_MIDDLEBUTTONDOWN) {
+    if (!mouse_middle_button_down_) {
+      mouse_middle_button_down_ = true;
+      mouse_middle_button_last_position_ = event.GetPosition();
+    }
+  } else {
+    if (mouse_middle_button_down_) {
+      mouse_middle_button_down_ = false;
+    }
+  }
+}
+
 bool PDFiumEngine::ExtendSelection(int page_index, int char_index) {
   // Check if the user has decreased their selection area and we need to remove
   // pages from |selection_|.
diff --git a/pdf/pdfium/pdfium_engine.h b/pdf/pdfium/pdfium_engine.h
index 1c115cc..afbcabd5 100644
--- a/pdf/pdfium/pdfium_engine.h
+++ b/pdf/pdfium/pdfium_engine.h
@@ -295,10 +295,15 @@
   bool OnMouseDown(const pp::MouseInputEvent& event);
   bool OnMouseUp(const pp::MouseInputEvent& event);
   bool OnMouseMove(const pp::MouseInputEvent& event);
+  void OnMouseEnter(const pp::MouseInputEvent& event);
   bool OnKeyDown(const pp::KeyboardInputEvent& event);
   bool OnKeyUp(const pp::KeyboardInputEvent& event);
   bool OnChar(const pp::KeyboardInputEvent& event);
 
+  // Decide what cursor should be displayed.
+  PP_CursorType_Dev DetermineCursorType(PDFiumPage::Area area,
+                                        int form_type) const;
+
   bool ExtendSelection(int page_index, int char_index);
 
   FPDF_DOCUMENT CreateSinglePageRasterPdf(
@@ -699,6 +704,12 @@
   // True if left mouse button is currently being held down.
   bool mouse_left_button_down_;
 
+  // True if middle mouse button is currently being held down.
+  bool mouse_middle_button_down_;
+
+  // Last known position while performing middle mouse button pan.
+  pp::Point mouse_middle_button_last_position_;
+
   // The current text used for searching.
   std::string current_find_text_;
   // The results found.
diff --git a/pdf/preview_mode_client.cc b/pdf/preview_mode_client.cc
index 42af50c..d2fc67b 100644
--- a/pdf/preview_mode_client.cc
+++ b/pdf/preview_mode_client.cc
@@ -31,6 +31,10 @@
   NOTREACHED();
 }
 
+void PreviewModeClient::ScrollBy(const pp::Point& point) {
+  NOTREACHED();
+}
+
 void PreviewModeClient::ScrollToPage(int page) {
   NOTREACHED();
 }
diff --git a/pdf/preview_mode_client.h b/pdf/preview_mode_client.h
index 2bd26571..b12dc3329 100644
--- a/pdf/preview_mode_client.h
+++ b/pdf/preview_mode_client.h
@@ -32,6 +32,7 @@
   void DidScroll(const pp::Point& point) override;
   void ScrollToX(int x_in_screen_coords) override;
   void ScrollToY(int y_in_screen_coords, bool compensate_for_toolbar) override;
+  void ScrollBy(const pp::Point& point) override;
   void ScrollToPage(int page) override;
   void NavigateTo(const std::string& url,
                   WindowOpenDisposition disposition) override;
diff --git a/ppapi/BUILD.gn b/ppapi/BUILD.gn
index 2e46f74..36090e4 100644
--- a/ppapi/BUILD.gn
+++ b/ppapi/BUILD.gn
@@ -348,6 +348,7 @@
     "proxy/plugin_dispatcher_unittest.cc",
     "proxy/plugin_resource_tracker_unittest.cc",
     "proxy/plugin_var_tracker_unittest.cc",
+    "proxy/ppapi_command_buffer_proxy_unittest.cc",
     "proxy/ppb_var_unittest.cc",
     "proxy/ppp_instance_private_proxy_unittest.cc",
     "proxy/ppp_instance_proxy_unittest.cc",
diff --git a/ppapi/proxy/plugin_dispatcher.h b/ppapi/proxy/plugin_dispatcher.h
index 80becd3..7a3fe87 100644
--- a/ppapi/proxy/plugin_dispatcher.h
+++ b/ppapi/proxy/plugin_dispatcher.h
@@ -45,7 +45,7 @@
 namespace proxy {
 
 // Used to keep track of per-instance data.
-struct InstanceData {
+struct PPAPI_PROXY_EXPORT InstanceData {
   InstanceData();
   ~InstanceData();
 
@@ -55,7 +55,7 @@
   scoped_refptr<TrackedCallback> mouse_lock_callback;
 
   // A map of singleton resources which are lazily created.
-  typedef std::map<SingletonResourceID, scoped_refptr<Resource> >
+  typedef std::map<SingletonResourceID, scoped_refptr<Resource>>
       SingletonResourceMap;
   SingletonResourceMap singleton_resources;
 
@@ -71,18 +71,29 @@
   std::unique_ptr<MessageHandler> message_handler;
 
   // Flush info for PpapiCommandBufferProxy::OrderingBarrier().
-  struct FlushInfo {
+  struct PPAPI_PROXY_EXPORT FlushInfo {
     FlushInfo();
     ~FlushInfo();
     bool flush_pending;
     HostResource resource;
     int32_t put_offset;
   };
-  FlushInfo flush_info_;
+  FlushInfo flush_info;
+};
+
+class PPAPI_PROXY_EXPORT LockedSender {
+ public:
+  // Unlike |Send()|, this function continues to hold the Pepper proxy lock
+  // until we are finished sending |msg|, even if it is a synchronous message.
+  virtual bool SendAndStayLocked(IPC::Message* msg) = 0;
+
+ protected:
+  virtual ~LockedSender() {}
 };
 
 class PPAPI_PROXY_EXPORT PluginDispatcher
     : public Dispatcher,
+      public LockedSender,
       public base::SupportsWeakPtr<PluginDispatcher> {
  public:
   class PPAPI_PROXY_EXPORT PluginDelegate : public ProxyChannel::Delegate {
@@ -179,7 +190,7 @@
 
   // Unlike |Send()|, this function continues to hold the Pepper proxy lock
   // until we are finished sending |msg|, even if it is a synchronous message.
-  bool SendAndStayLocked(IPC::Message* msg);
+  bool SendAndStayLocked(IPC::Message* msg) override;
 
   // IPC::Listener implementation.
   bool OnMessageReceived(const IPC::Message& msg) override;
diff --git a/ppapi/proxy/ppapi_command_buffer_proxy.cc b/ppapi/proxy/ppapi_command_buffer_proxy.cc
index 73a743e6..a4d96e5 100644
--- a/ppapi/proxy/ppapi_command_buffer_proxy.cc
+++ b/ppapi/proxy/ppapi_command_buffer_proxy.cc
@@ -17,22 +17,22 @@
 
 PpapiCommandBufferProxy::PpapiCommandBufferProxy(
     const ppapi::HostResource& resource,
-    PluginDispatcher* dispatcher,
+    InstanceData::FlushInfo* flush_info,
+    LockedSender* sender,
     const gpu::Capabilities& capabilities,
     const SerializedHandle& shared_state,
     gpu::CommandBufferId command_buffer_id)
     : command_buffer_id_(command_buffer_id),
       capabilities_(capabilities),
       resource_(resource),
-      dispatcher_(dispatcher),
+      flush_info_(flush_info),
+      sender_(sender),
       next_fence_sync_release_(1),
       pending_fence_sync_release_(0),
       flushed_fence_sync_release_(0),
       validated_fence_sync_release_(0) {
   shared_state_shm_.reset(new base::SharedMemory(shared_state.shmem(), false));
   shared_state_shm_->Map(shared_state.size());
-  InstanceData* data = dispatcher->GetInstanceData(resource.instance());
-  flush_info_ = &data->flush_info_;
 }
 
 PpapiCommandBufferProxy::~PpapiCommandBufferProxy() {
@@ -190,7 +190,10 @@
 }
 
 void PpapiCommandBufferProxy::FlushPendingWork() {
-  // This is only relevant for out-of-process command buffers.
+  if (last_state_.error != gpu::error::kNoError)
+    return;
+  if (flush_info_->flush_pending)
+    FlushInternal();
 }
 
 uint64_t PpapiCommandBufferProxy::GenerateFenceSyncRelease() {
@@ -263,7 +266,7 @@
   // buffer may use a sync IPC with another lock held which could lead to lock
   // and deadlock if we dropped the proxy lock here.
   // http://crbug.com/418651
-  if (dispatcher_->SendAndStayLocked(msg))
+  if (sender_->SendAndStayLocked(msg))
     return true;
 
   last_state_.error = gpu::error::kLostContext;
diff --git a/ppapi/proxy/ppapi_command_buffer_proxy.h b/ppapi/proxy/ppapi_command_buffer_proxy.h
index 8159283..e3b351fb 100644
--- a/ppapi/proxy/ppapi_command_buffer_proxy.h
+++ b/ppapi/proxy/ppapi_command_buffer_proxy.h
@@ -34,7 +34,8 @@
                                                    public gpu::GpuControl {
  public:
   PpapiCommandBufferProxy(const HostResource& resource,
-                          PluginDispatcher* dispatcher,
+                          InstanceData::FlushInfo* flush_info,
+                          LockedSender* sender,
                           const gpu::Capabilities& capabilities,
                           const SerializedHandle& shared_state,
                           gpu::CommandBufferId command_buffer_id);
@@ -98,12 +99,11 @@
   std::unique_ptr<base::SharedMemory> shared_state_shm_;
 
   HostResource resource_;
-  PluginDispatcher* dispatcher_;
+  InstanceData::FlushInfo* flush_info_;
+  LockedSender* sender_;
 
   base::Closure channel_error_callback_;
 
-  InstanceData::FlushInfo *flush_info_;
-
   uint64_t next_fence_sync_release_;
   uint64_t pending_fence_sync_release_;
   uint64_t flushed_fence_sync_release_;
diff --git a/ppapi/proxy/ppapi_command_buffer_proxy_unittest.cc b/ppapi/proxy/ppapi_command_buffer_proxy_unittest.cc
new file mode 100644
index 0000000..19ba930
--- /dev/null
+++ b/ppapi/proxy/ppapi_command_buffer_proxy_unittest.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ppapi/proxy/ppapi_command_buffer_proxy.h"
+
+#include "ipc/ipc_test_sink.h"
+#include "ppapi/proxy/ppapi_messages.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ppapi {
+class PpapiCommandBufferProxyTest : public testing::Test,
+                                    public proxy::LockedSender {
+ public:
+  PpapiCommandBufferProxyTest()
+      : proxy_(HostResource(),
+               &flush_info_,
+               this,
+               gpu::Capabilities(),
+               proxy::SerializedHandle(proxy::SerializedHandle::SHARED_MEMORY),
+               gpu::CommandBufferId()) {}
+
+  ~PpapiCommandBufferProxyTest() override {}
+
+ protected:
+  // We can't verify sync message behavior with this setup.
+  bool SendAndStayLocked(IPC::Message* msg) override { return sink_.Send(msg); }
+
+  IPC::TestSink sink_;
+  proxy::InstanceData::FlushInfo flush_info_;
+  proxy::PpapiCommandBufferProxy proxy_;
+};
+
+TEST_F(PpapiCommandBufferProxyTest, OrderingBarriersAreCoalescedWithFlush) {
+  proxy_.OrderingBarrier(10);
+  proxy_.OrderingBarrier(20);
+  proxy_.OrderingBarrier(30);
+  proxy_.Flush(40);
+
+  EXPECT_EQ(1u, sink_.message_count());
+  const IPC::Message* msg =
+      sink_.GetFirstMessageMatching(PpapiHostMsg_PPBGraphics3D_AsyncFlush::ID);
+  ASSERT_TRUE(msg);
+  PpapiHostMsg_PPBGraphics3D_AsyncFlush::Param params;
+  ASSERT_TRUE(PpapiHostMsg_PPBGraphics3D_AsyncFlush::Read(msg, &params));
+  int32_t sent_put_offset = std::get<1>(params);
+  EXPECT_EQ(40, sent_put_offset);
+  EXPECT_FALSE(flush_info_.flush_pending);
+  EXPECT_EQ(40, flush_info_.put_offset);
+}
+
+TEST_F(PpapiCommandBufferProxyTest, FlushPendingWorkFlushesOrderingBarriers) {
+  proxy_.OrderingBarrier(10);
+  proxy_.OrderingBarrier(20);
+  proxy_.OrderingBarrier(30);
+  proxy_.FlushPendingWork();
+
+  EXPECT_EQ(1u, sink_.message_count());
+  const IPC::Message* msg =
+      sink_.GetFirstMessageMatching(PpapiHostMsg_PPBGraphics3D_AsyncFlush::ID);
+  ASSERT_TRUE(msg);
+  PpapiHostMsg_PPBGraphics3D_AsyncFlush::Param params;
+  ASSERT_TRUE(PpapiHostMsg_PPBGraphics3D_AsyncFlush::Read(msg, &params));
+  int32_t sent_put_offset = std::get<1>(params);
+  EXPECT_EQ(30, sent_put_offset);
+  EXPECT_FALSE(flush_info_.flush_pending);
+  EXPECT_EQ(30, flush_info_.put_offset);
+}
+
+TEST_F(PpapiCommandBufferProxyTest, EnsureWorkVisibleFlushesOrderingBarriers) {
+  proxy_.OrderingBarrier(10);
+  proxy_.OrderingBarrier(20);
+  proxy_.OrderingBarrier(30);
+  proxy_.EnsureWorkVisible();
+
+  EXPECT_EQ(2u, sink_.message_count());
+  const IPC::Message* msg = sink_.GetMessageAt(0);
+  ASSERT_TRUE(msg);
+  EXPECT_EQ(static_cast<uint32_t>(PpapiHostMsg_PPBGraphics3D_AsyncFlush::ID),
+            msg->type());
+  PpapiHostMsg_PPBGraphics3D_AsyncFlush::Param params;
+  ASSERT_TRUE(PpapiHostMsg_PPBGraphics3D_AsyncFlush::Read(msg, &params));
+  int32_t sent_put_offset = std::get<1>(params);
+  EXPECT_EQ(30, sent_put_offset);
+  EXPECT_FALSE(flush_info_.flush_pending);
+  EXPECT_EQ(30, flush_info_.put_offset);
+
+  msg = sink_.GetMessageAt(1);
+  ASSERT_TRUE(msg);
+  EXPECT_EQ(
+      static_cast<uint32_t>(PpapiHostMsg_PPBGraphics3D_EnsureWorkVisible::ID),
+      msg->type());
+}
+
+}  // namespace ppapi
diff --git a/ppapi/proxy/ppb_graphics_3d_proxy.cc b/ppapi/proxy/ppb_graphics_3d_proxy.cc
index ea77cd1..07b1974 100644
--- a/ppapi/proxy/ppb_graphics_3d_proxy.cc
+++ b/ppapi/proxy/ppb_graphics_3d_proxy.cc
@@ -61,9 +61,12 @@
   if (!dispatcher)
     return false;
 
+  InstanceData* data = dispatcher->GetInstanceData(host_resource().instance());
+  DCHECK(data);
+
   command_buffer_.reset(new PpapiCommandBufferProxy(
-      host_resource(), dispatcher, capabilities, shared_state,
-      command_buffer_id));
+      host_resource(), &data->flush_info, dispatcher, capabilities,
+      shared_state, command_buffer_id));
 
   return CreateGLES2Impl(share_gles2);
 }
diff --git a/remoting/base/rate_counter.cc b/remoting/base/rate_counter.cc
index 0d4acf2..1e430084 100644
--- a/remoting/base/rate_counter.cc
+++ b/remoting/base/rate_counter.cc
@@ -26,10 +26,11 @@
   data_points_.push(std::make_pair(now, value));
 }
 
-double RateCounter::Rate() {
+double RateCounter::Rate() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  EvictOldDataPoints(tick_clock_->NowTicks());
+  // This is just to ensure the rate is up to date.
+  const_cast<RateCounter*>(this)->EvictOldDataPoints(tick_clock_->NowTicks());
   return sum_ / time_window_.InSecondsF();
 }
 
diff --git a/remoting/base/rate_counter.h b/remoting/base/rate_counter.h
index f43aba3..c7e08a1e 100644
--- a/remoting/base/rate_counter.h
+++ b/remoting/base/rate_counter.h
@@ -32,7 +32,7 @@
 
   // Returns the rate-per-second of values recorded over the time window.
   // Note that rates reported before |time_window| has elapsed are not accurate.
-  double Rate();
+  double Rate() const;
 
   void set_tick_clock_for_tests(base::TickClock* tick_clock) {
     tick_clock_ = tick_clock;
diff --git a/remoting/client/chromoting_session.cc b/remoting/client/chromoting_session.cc
index fc77c37..feb57de5 100644
--- a/remoting/client/chromoting_session.cc
+++ b/remoting/client/chromoting_session.cc
@@ -515,7 +515,7 @@
 void ChromotingSession::Core::LogPerfStats() {
   DCHECK(network_task_runner()->BelongsToCurrentThread());
 
-  session_context_->logger->LogStatistics(perf_tracker_.get());
+  session_context_->logger->LogStatistics(*perf_tracker_);
 }
 
 // ChromotingSession implementation.
diff --git a/remoting/client/client_telemetry_logger.cc b/remoting/client/client_telemetry_logger.cc
index 23dcf82..44935025 100644
--- a/remoting/client/client_telemetry_logger.cc
+++ b/remoting/client/client_telemetry_logger.cc
@@ -82,7 +82,7 @@
 }
 
 void ClientTelemetryLogger::LogStatistics(
-    protocol::PerformanceTracker* perf_tracker) {
+    const protocol::PerformanceTracker& perf_tracker) {
   DCHECK(thread_checker_.CalledOnValidThread());
   RefreshSessionIdIfOutdated();
 
@@ -97,27 +97,27 @@
 }
 
 void ClientTelemetryLogger::PrintLogStatistics(
-    protocol::PerformanceTracker* perf_tracker) {
+    const protocol::PerformanceTracker& perf_tracker) {
 #if defined(OS_ANDROID)
   __android_log_print(
       ANDROID_LOG_INFO, "stats",
 #else
-  VLOG(1) << base::StringPrintf(
+  VLOG(0) << base::StringPrintf(
 #endif  // OS_ANDROID
       "Bandwidth:%.0f FrameRate:%.1f;"
       " (Avg, Max) Capture:%.1f, %" PRId64 " Encode:%.1f, %" PRId64
       " Decode:%.1f, %" PRId64 " Render:%.1f, %" PRId64 " RTL:%.0f, %" PRId64,
-      perf_tracker->video_bandwidth(), perf_tracker->video_frame_rate(),
-      perf_tracker->video_capture_ms().Average(),
-      perf_tracker->video_capture_ms().Max(),
-      perf_tracker->video_encode_ms().Average(),
-      perf_tracker->video_encode_ms().Max(),
-      perf_tracker->video_decode_ms().Average(),
-      perf_tracker->video_decode_ms().Max(),
-      perf_tracker->video_paint_ms().Average(),
-      perf_tracker->video_paint_ms().Max(),
-      perf_tracker->round_trip_ms().Average(),
-      perf_tracker->round_trip_ms().Max());
+      perf_tracker.video_bandwidth(), perf_tracker.video_frame_rate(),
+      perf_tracker.video_capture_ms().Average(),
+      perf_tracker.video_capture_ms().Max(),
+      perf_tracker.video_encode_ms().Average(),
+      perf_tracker.video_encode_ms().Max(),
+      perf_tracker.video_decode_ms().Average(),
+      perf_tracker.video_decode_ms().Max(),
+      perf_tracker.video_paint_ms().Average(),
+      perf_tracker.video_paint_ms().Max(),
+      perf_tracker.round_trip_ms().Average(),
+      perf_tracker.round_trip_ms().Max());
 }
 
 void ClientTelemetryLogger::SetSessionIdGenerationTimeForTest(
@@ -256,32 +256,32 @@
 }
 
 ChromotingEvent ClientTelemetryLogger::MakeStatsEvent(
-    protocol::PerformanceTracker* perf_tracker) {
+    const protocol::PerformanceTracker& perf_tracker) {
   ChromotingEvent event(ChromotingEvent::Type::CONNECTION_STATISTICS);
   FillEventContext(&event);
 
   event.SetDouble(ChromotingEvent::kVideoBandwidthKey,
-                  perf_tracker->video_bandwidth());
+                  perf_tracker.video_bandwidth());
   event.SetDouble(ChromotingEvent::kCaptureLatencyKey,
-                  perf_tracker->video_capture_ms().Average());
+                  perf_tracker.video_capture_ms().Average());
   event.SetDouble(ChromotingEvent::kEncodeLatencyKey,
-                  perf_tracker->video_encode_ms().Average());
+                  perf_tracker.video_encode_ms().Average());
   event.SetDouble(ChromotingEvent::kDecodeLatencyKey,
-                  perf_tracker->video_decode_ms().Average());
+                  perf_tracker.video_decode_ms().Average());
   event.SetDouble(ChromotingEvent::kRenderLatencyKey,
-                  perf_tracker->video_paint_ms().Average());
+                  perf_tracker.video_paint_ms().Average());
   event.SetDouble(ChromotingEvent::kRoundtripLatencyKey,
-                  perf_tracker->round_trip_ms().Average());
+                  perf_tracker.round_trip_ms().Average());
   event.SetDouble(ChromotingEvent::kMaxCaptureLatencyKey,
-                  perf_tracker->video_capture_ms().Max());
+                  perf_tracker.video_capture_ms().Max());
   event.SetDouble(ChromotingEvent::kMaxEncodeLatencyKey,
-                  perf_tracker->video_encode_ms().Max());
+                  perf_tracker.video_encode_ms().Max());
   event.SetDouble(ChromotingEvent::kMaxDecodeLatencyKey,
-                  perf_tracker->video_decode_ms().Max());
+                  perf_tracker.video_decode_ms().Max());
   event.SetDouble(ChromotingEvent::kMaxRenderLatencyKey,
-                  perf_tracker->video_paint_ms().Max());
+                  perf_tracker.video_paint_ms().Max());
   event.SetDouble(ChromotingEvent::kMaxRoundtripLatencyKey,
-                  perf_tracker->round_trip_ms().Max());
+                  perf_tracker.round_trip_ms().Max());
 
   return event;
 }
diff --git a/remoting/client/client_telemetry_logger.h b/remoting/client/client_telemetry_logger.h
index dd9b2c1..ad6dc5ec 100644
--- a/remoting/client/client_telemetry_logger.h
+++ b/remoting/client/client_telemetry_logger.h
@@ -42,8 +42,7 @@
   void LogSessionStateChange(ChromotingEvent::SessionState state,
                              ChromotingEvent::ConnectionError error);
 
-  // TODO(yuweih): Investigate possibility of making PerformanceTracker const.
-  void LogStatistics(protocol::PerformanceTracker* perf_tracker);
+  void LogStatistics(const protocol::PerformanceTracker& perf_tracker);
 
   const std::string& session_id() const { return session_id_; }
 
@@ -72,7 +71,7 @@
   // Generates a new random session ID.
   void GenerateSessionId();
 
-  void PrintLogStatistics(protocol::PerformanceTracker* perf_tracker);
+  void PrintLogStatistics(const protocol::PerformanceTracker& perf_tracker);
 
   // If not session ID has been set, simply generates a new one without sending
   // any logs, otherwise expire the session ID if the maximum duration has been
@@ -80,7 +79,8 @@
   // change of id.
   void RefreshSessionIdIfOutdated();
 
-  ChromotingEvent MakeStatsEvent(protocol::PerformanceTracker* perf_tracker);
+  ChromotingEvent MakeStatsEvent(
+      const protocol::PerformanceTracker& perf_tracker);
   ChromotingEvent MakeSessionStateChangeEvent(
       ChromotingEvent::SessionState state,
       ChromotingEvent::ConnectionError error);
diff --git a/remoting/client/client_telemetry_logger_unittest.cc b/remoting/client/client_telemetry_logger_unittest.cc
index a78ad68..0a58076 100644
--- a/remoting/client/client_telemetry_logger_unittest.cc
+++ b/remoting/client/client_telemetry_logger_unittest.cc
@@ -115,7 +115,7 @@
   protocol::PerformanceTracker perf_tracker;
   log_writer_->AddExpectedEvent(
       ChromotingEvent(ChromotingEvent::Type::CONNECTION_STATISTICS));
-  logger_->LogStatistics(&perf_tracker);
+  logger_->LogStatistics(perf_tracker);
 }
 
 TEST_F(ClientTelemetryLoggerTest, SessionIdGeneration) {
@@ -154,7 +154,7 @@
   logger_->SetSessionIdGenerationTimeForTest(base::TimeTicks::Now() -
                                              base::TimeDelta::FromDays(2));
   protocol::PerformanceTracker perf_tracker;
-  logger_->LogStatistics(&perf_tracker);
+  logger_->LogStatistics(perf_tracker);
   EXPECT_NE(last_id, logger_->session_id());
 }
 
diff --git a/remoting/protocol/performance_tracker.h b/remoting/protocol/performance_tracker.h
index fb7a0e0c..1f3c6b6 100644
--- a/remoting/protocol/performance_tracker.h
+++ b/remoting/protocol/performance_tracker.h
@@ -42,14 +42,14 @@
   static const int kStatsUpdatePeriodSeconds = 1;
 
   // Return rates and running-averages for different metrics.
-  double video_bandwidth() { return video_bandwidth_.Rate(); }
-  double video_frame_rate() { return video_frame_rate_.Rate(); }
-  double video_packet_rate() { return video_packet_rate_.Rate(); }
-  const RunningSamples& video_capture_ms() { return video_capture_ms_; }
-  const RunningSamples& video_encode_ms() { return video_encode_ms_; }
-  const RunningSamples& video_decode_ms() { return video_decode_ms_; }
-  const RunningSamples& video_paint_ms() { return video_paint_ms_; }
-  const RunningSamples& round_trip_ms() { return round_trip_ms_; }
+  double video_bandwidth() const { return video_bandwidth_.Rate(); }
+  double video_frame_rate() const { return video_frame_rate_.Rate(); }
+  double video_packet_rate() const { return video_packet_rate_.Rate(); }
+  const RunningSamples& video_capture_ms() const { return video_capture_ms_; }
+  const RunningSamples& video_encode_ms() const { return video_encode_ms_; }
+  const RunningSamples& video_decode_ms() const { return video_decode_ms_; }
+  const RunningSamples& video_paint_ms() const { return video_paint_ms_; }
+  const RunningSamples& round_trip_ms() const { return round_trip_ms_; }
 
   // FrameStatsConsumer interface.
   void OnVideoFrameStats(const FrameStats& stats) override;
diff --git a/sandbox/OWNERS b/sandbox/OWNERS
index c7304374..a52767f 100644
--- a/sandbox/OWNERS
+++ b/sandbox/OWNERS
@@ -3,6 +3,7 @@
 palmer@chromium.org
 rsesek@chromium.org
 tsepez@chromium.org
+wfh@chromium.org
 
 # TEAM: security-dev@chromium.org
 # COMPONENT: Internals>Sandbox
diff --git a/services/resource_coordinator/observers/page_signal_generator_impl_unittest.cc b/services/resource_coordinator/observers/page_signal_generator_impl_unittest.cc
index dbb6e2c..dafdd3cf 100644
--- a/services/resource_coordinator/observers/page_signal_generator_impl_unittest.cc
+++ b/services/resource_coordinator/observers/page_signal_generator_impl_unittest.cc
@@ -28,7 +28,7 @@
   ~TickClockWrapper() override {}
 
   // base::TickClock implementation:
-  base::TimeTicks NowTicks() override { return tick_clock_->NowTicks(); }
+  base::TimeTicks NowTicks() const override { return tick_clock_->NowTicks(); }
 
  private:
   base::TickClock* tick_clock_;
diff --git a/services/viz/privileged/interfaces/gl/gpu_service.mojom b/services/viz/privileged/interfaces/gl/gpu_service.mojom
index f060ee1..ff67243 100644
--- a/services/viz/privileged/interfaces/gl/gpu_service.mojom
+++ b/services/viz/privileged/interfaces/gl/gpu_service.mojom
@@ -85,6 +85,11 @@
 
   DestroyAllChannels();
 
+  // Called by the browser when the application is backgrounded. The GPU can use
+  // this message to perform appropriate cleanup. Note that this is currently
+  // only invoked on low-end Android.
+  OnBackgrounded();
+
   Crash();
   Hang();
   ThrowJavaException();
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index a8daf9b..604cab9 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -650,7 +650,6 @@
     }
     if (!is_win) {
       cflags = [
-        "-ffp-contract=fast",
         "-mavx2",
         "-mbmi",
         "-mbmi2",
diff --git a/storage/browser/blob/blob_registry_impl.cc b/storage/browser/blob/blob_registry_impl.cc
index f29a3df..261e60b 100644
--- a/storage/browser/blob/blob_registry_impl.cc
+++ b/storage/browser/blob/blob_registry_impl.cc
@@ -426,6 +426,10 @@
     return;
   }
 
+  // The calls below could delete |this|, so make sure we detect that and don't
+  // try to delete |this| again afterwards.
+  auto weak_this = weak_ptr_factory_.GetWeakPtr();
+
   // The blob might no longer have any references, in which case it may no
   // longer exist. If that happens just skip calling Complete.
   // TODO(mek): Stop building sooner if a blob is no longer referenced.
@@ -442,7 +446,8 @@
     std::move(bad_message_callback_)
         .Run("Received invalid data while transporting blob");
   }
-  MarkAsFinishedAndDeleteSelf();
+  if (weak_this)
+    MarkAsFinishedAndDeleteSelf();
 }
 
 #if DCHECK_IS_ON()
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 89e1ab8..3964e23 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -699,12 +699,6 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
         "test": "gpu_unittests"
       },
       {
@@ -882,12 +876,6 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
         "test": "gpu_unittests"
       },
       {
@@ -1423,32 +1411,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "gpu_ipc_service_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "LMY47W",
-              "device_type": "sprout"
-            }
-          ],
-          "expiration": 14400
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "gpu_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -2630,50 +2592,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "gpu_ipc_service_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "NRD91N",
-              "device_type": "bullhead"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 960,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "gpu_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -3466,12 +3384,6 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
         "test": "gpu_unittests"
       },
       {
@@ -3660,12 +3572,6 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
         "test": "gpu_unittests"
       },
       {
@@ -4347,30 +4253,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "gpu_ipc_service_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_type": "coho"
-            }
-          ]
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "gpu_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -5287,30 +5169,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "gpu_ipc_service_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_type": "gce_x86"
-            }
-          ]
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "gpu_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 0d87789..cf27c98 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -1331,48 +1331,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "gpu_ipc_service_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_type": "hammerhead"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "gpu_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -2972,48 +2930,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "gpu_ipc_service_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_type": "hammerhead"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "gpu_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -4521,50 +4437,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "gpu_ipc_service_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84Z",
-              "device_type": "flo"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 120,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "gpu_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -6029,50 +5901,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "gpu_ipc_service_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "LMY48I",
-              "device_type": "hammerhead"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 960,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "gpu_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -7580,50 +7408,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "gpu_ipc_service_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "LMY49B",
-              "device_type": "flo"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 120,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "gpu_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -9158,49 +8942,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "gpu_ipc_service_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead"
-            }
-          ],
-          "hard_timeout": 960,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "gpu_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -10673,48 +10414,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "gpu_ipc_service_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "gpu_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -12373,50 +12072,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "gpu_ipc_service_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MRA58Z",
-              "device_type": "flo"
-            }
-          ],
-          "expiration": 10800,
-          "hard_timeout": 120,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "gpu_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 4564297..6ac5146 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -182,12 +182,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -580,12 +574,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -1100,12 +1088,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index c83e54f..1e84dc8 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -206,12 +206,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -589,12 +583,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -870,12 +858,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -1136,12 +1118,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -1395,12 +1371,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -1667,12 +1637,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -1969,12 +1933,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -2271,12 +2229,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -2573,12 +2525,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -2875,12 +2821,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -3177,12 +3117,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -3479,12 +3413,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -3781,12 +3709,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -4379,12 +4301,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -5680,48 +5596,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "gpu_ipc_service_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_type": "hammerhead"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "gpu_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -7389,18 +7263,6 @@
             }
           ]
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead"
-            }
-          ]
-        },
         "test": "gpu_unittests"
       },
       {
@@ -7859,12 +7721,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -8190,12 +8046,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -8473,12 +8323,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -8834,12 +8678,6 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
         "test": "gpu_unittests"
       },
       {
@@ -9160,12 +8998,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -9466,12 +9298,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -9819,12 +9645,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -10094,12 +9914,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -10408,12 +10222,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -10710,12 +10518,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -11012,12 +10814,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -11314,12 +11110,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -11616,12 +11406,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -11918,12 +11702,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -12220,12 +11998,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -12546,12 +12318,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -12860,12 +12626,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -13209,12 +12969,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -13617,17 +13371,6 @@
             }
           ]
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10"
-            }
-          ]
-        },
         "test": "gpu_unittests"
       },
       {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index c4943bed..5a9bf97 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -519,12 +519,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -902,12 +896,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -1265,12 +1253,6 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
         "test": "gpu_unittests"
       },
       {
@@ -1661,12 +1643,6 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
         "test": "gpu_unittests"
       },
       {
@@ -2011,12 +1987,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -2458,12 +2428,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -2826,12 +2790,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -3092,12 +3050,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -3351,12 +3303,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -4776,17 +4722,6 @@
             }
           ]
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
         "test": "gpu_unittests"
       },
       {
@@ -6370,17 +6305,6 @@
             }
           ]
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_type": "coho"
-            }
-          ]
-        },
         "test": "gpu_unittests"
       },
       {
@@ -6770,12 +6694,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -7151,12 +7069,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -7492,12 +7404,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -8096,12 +8002,6 @@
         "swarming": {
           "can_use_on_swarming_builders": false
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
         "test": "gpu_unittests"
       },
       {
@@ -8879,12 +8779,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -9174,12 +9068,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 620e4b4f..49c36f5d 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -2395,25 +2395,6 @@
     "isolated_scripts": [
       {
         "args": [
-          "-v",
-          "--one-frame-only"
-        ],
-        "isolate_name": "angle_perftests",
-        "name": "angle_perftests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "L",
-              "device_type": "shamu",
-              "os": "Android",
-              "pool": "Chrome-GPU"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
           "context_lost",
           "--show-stdout",
           "--browser=android-chromium",
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index f6d85ee..82f3baa4 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -110,12 +110,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -325,12 +319,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -980,12 +968,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -1779,12 +1761,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index afaa17a0..3ad26a7 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -181,12 +181,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -648,12 +642,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -1091,12 +1079,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -1559,12 +1541,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -2015,12 +1991,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 0ae0624..536f51a 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -291,18 +291,6 @@
             }
           ]
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead"
-            }
-          ]
-        },
         "test": "gpu_unittests"
       },
       {
@@ -823,16 +811,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "args": [
-          "--test-launcher-batch-limit=1",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -1309,12 +1287,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -1686,12 +1658,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -2036,12 +2002,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -2354,12 +2314,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -2665,12 +2619,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -2919,12 +2867,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 82bb391..b7374650 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -750,6 +750,26 @@
         }
       },
       {
+        "args": [],
+        "isolate_name": "components_perftests",
+        "name": "components_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "id": "build48-b1--device5",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 1200,
+          "upload_test_results": true
+        }
+      },
+      {
         "args": [
           "dromaeo",
           "-v",
@@ -4509,66 +4529,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=android-chromium",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "id": "build14-b1--device3",
-              "os": "Android",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "id": "build14-b1--device3",
-              "os": "Android",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -5463,6 +5423,26 @@
         }
       },
       {
+        "args": [],
+        "isolate_name": "components_perftests",
+        "name": "components_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "id": "build74-b1--device1",
+              "os": "Android",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 1200,
+          "upload_test_results": true
+        }
+      },
+      {
         "args": [
           "dromaeo",
           "-v",
@@ -9242,66 +9222,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=android-chromium",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "id": "build74-b1--device3",
-              "os": "Android",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "id": "build74-b1--device3",
-              "os": "Android",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -11615,36 +11535,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=android-webview",
-          "--output-format=chartjson",
-          "--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk"
-        ],
-        "isolate_name": "telemetry_perf_webview_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_webview_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "id": "build165-b1--device6",
-              "os": "Android",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -16237,66 +16127,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=android-chromium",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "id": "build16-b1--device3",
-              "os": "Android",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "id": "build16-b1--device3",
-              "os": "Android",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -18610,36 +18440,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=android-webview",
-          "--output-format=chartjson",
-          "--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk"
-        ],
-        "isolate_name": "telemetry_perf_webview_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_webview_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "id": "build113-b1--device6",
-              "os": "Android",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -23232,66 +23032,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=android-chromium",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "id": "build10-b1--device3",
-              "os": "Android",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "id": "build10-b1--device3",
-              "os": "Android",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -27903,66 +27643,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=android-chromium",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "id": "build47-b1--device5",
-              "os": "Android",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "id": "build47-b1--device5",
-              "os": "Android",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -31929,68 +31609,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=release",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3",
-              "id": "build30-a9",
-              "os": "Ubuntu-14.04",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3",
-              "id": "build30-a9",
-              "os": "Ubuntu-14.04",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -35950,68 +35568,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=release",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "id": "build161-m1",
-              "os": "Mac-10.12",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "id": "build161-m1",
-              "os": "Mac-10.12",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -39992,68 +39548,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=release",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1626",
-              "id": "build126-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1626",
-              "id": "build126-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -44018,68 +43512,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=release",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "id": "build131-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:6821",
-              "id": "build131-b1",
-              "os": "Mac-10.11",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -47894,68 +47326,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=release_x64",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1616",
-              "id": "build120-b1",
-              "os": "Windows-10",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:1616",
-              "id": "build120-b1",
-              "os": "Windows-10",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -48857,6 +48227,27 @@
         }
       },
       {
+        "args": [],
+        "isolate_name": "components_perftests",
+        "name": "components_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:5912",
+              "id": "build191-a9",
+              "os": "Windows-10",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 1200,
+          "upload_test_results": true
+        }
+      },
+      {
         "args": [
           "dromaeo",
           "-v",
@@ -51791,68 +51182,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=release_x64",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:5912",
-              "id": "build192-a9",
-              "os": "Windows-10",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:5912",
-              "id": "build192-a9",
-              "os": "Windows-10",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -55751,68 +55080,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=release_x64",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:6613",
-              "id": "build104-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "1002:6613",
-              "id": "build104-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -59690,68 +58957,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=release_x64",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:041a",
-              "id": "build167-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:041a",
-              "id": "build167-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -63653,68 +62858,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=release_x64",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3",
-              "id": "build95-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3",
-              "id": "build95-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -64640,6 +63783,27 @@
         }
       },
       {
+        "args": [],
+        "isolate_name": "components_perftests",
+        "name": "components_perftests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "102b:0532",
+              "id": "build189-m1",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "Chrome-perf"
+            }
+          ],
+          "expiration": 36000,
+          "hard_timeout": 10800,
+          "ignore_task_failure": false,
+          "io_timeout": 1200,
+          "upload_test_results": true
+        }
+      },
+      {
         "args": [
           "dromaeo",
           "-v",
@@ -67595,68 +66759,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=release",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build188-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build188-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -71513,68 +70615,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=release_x64",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build141-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build141-m1",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
@@ -75452,68 +74492,6 @@
       },
       {
         "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=release_x64",
-          "--output-format=chartjson"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build146-m1",
-              "os": "Windows-2012ServerR2-SP0",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": false,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
-          "v8.detached_context_age_in_gc",
-          "-v",
-          "--upload-results",
-          "--browser=reference",
-          "--output-format=chartjson",
-          "--max-failures=5",
-          "--output-trace-tag=_ref"
-        ],
-        "isolate_name": "telemetry_perf_tests",
-        "name": "v8.detached_context_age_in_gc.reference",
-        "override_compile_targets": [
-          "telemetry_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "102b:0532",
-              "id": "build146-m1",
-              "os": "Windows-2012ServerR2-SP0",
-              "pool": "Chrome-perf"
-            }
-          ],
-          "expiration": 36000,
-          "hard_timeout": 10800,
-          "ignore_task_failure": true,
-          "io_timeout": 1200,
-          "upload_test_results": true
-        }
-      },
-      {
-        "args": [
           "v8.runtime_stats.top_25",
           "-v",
           "--upload-results",
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index a8145a02..800679e 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -241,12 +241,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -843,12 +837,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -1373,12 +1361,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -2039,12 +2021,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
@@ -2620,12 +2596,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "gpu_ipc_service_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "gpu_unittests"
       },
       {
diff --git a/testing/buildbot/client.v8.fyi.json b/testing/buildbot/client.v8.fyi.json
index 94cf860..7954fdb 100644
--- a/testing/buildbot/client.v8.fyi.json
+++ b/testing/buildbot/client.v8.fyi.json
@@ -1123,7 +1123,6 @@
       "gfx_unittests",
       "gn_unittests",
       "google_apis_unittests",
-      "gpu_ipc_service_unittests",
       "gpu_unittests",
       "interactive_ui_tests",
       "ipc_tests",
diff --git a/testing/buildbot/filters/viz.content_browsertests.filter b/testing/buildbot/filters/viz.content_browsertests.filter
index 6e33ecb..b1f1132 100644
--- a/testing/buildbot/filters/viz.content_browsertests.filter
+++ b/testing/buildbot/filters/viz.content_browsertests.filter
@@ -34,6 +34,7 @@
 -SitePerProcessBrowserTest.TwoSubframesCreatePopupMenuWidgetsSimultaneously
 -SitePerProcessBrowserTest.ViewBoundsInNestedFrameTest
 -SitePerProcessHitTestBrowserTest.AsynchronousHitTestChildTimeout*
+-SitePerProcessHitTestBrowserTest.BubbledScrollEventsTransformedCorrectly*
 -SitePerProcessHitTestBrowserTest.CancelWheelScrollBubblingOnWheelTargetDeletion*
 -SitePerProcessHitTestBrowserTest.CreateContextMenuTest*
 -SitePerProcessHitTestBrowserTest.CrossProcessMouseCapture*
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index c427a11..cab3f28 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -587,10 +587,6 @@
     "label": "//google_apis:google_apis_unittests",
     "type": "console_test_launcher",
   },
-  "gpu_ipc_service_unittests": {
-    "label": "//gpu/ipc/service:gpu_ipc_service_unittests",
-    "type": "windowed_test_launcher",
-  },
   "gpu_unittests": {
     "label": "//gpu:gpu_unittests",
     "type": "windowed_test_launcher",
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 88d63f48..9f733fbe 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1853,28 +1853,6 @@
       'Win7 Tests (dbg)(1)',
     ],
   },
-  'gpu_ipc_service_unittests': {
-    'remove_from': [
-      # chromium.clang
-      'CrWinClngLLD64dbg',
-      # chromium.fyi
-      'Win 10 Fast Ring',
-      # chromium.linux
-      'Linux Tests (dbg)(1)(32)',
-    ],
-    'modifications': {
-      'KitKat Tablet Tester': {
-        'swarming': {
-          'hard_timeout': 120,
-        },
-      },
-      'Marshmallow Tablet Tester': {
-        'swarming': {
-          'hard_timeout': 120,
-        },
-      },
-    },
-  },
   'headless_browsertests': {
     'remove_from': [
       'Linux Tests (dbg)(1)(32)',
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 547e0ef2..d21a0b15 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -417,7 +417,6 @@
     },
     'crypto_unittests': {},
     'google_apis_unittests': {},
-    'gpu_ipc_service_unittests': {},
     'gpu_unittests': {
       'android_swarming': {
         'hard_timeout': 960,
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
index 077bc9f..f4135fa 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
@@ -5,10 +5,6 @@
 crbug.com/802835 external/wpt/fetch/corb/img-png-mislabeled-as-html-nosniff.tentative.sub.html [ Pass ]
 crbug.com/802835 external/wpt/fetch/corb/script-html-correctly-labeled.tentative.sub.html [ Pass ]
 
-# https://crbug.com/794631: OOPIFs VS feature policy VS opaque origins?
-crbug.com/794631 virtual/unified-autoplay/external/wpt/feature-policy/autoplay-allowed-by-feature-policy-attribute.https.sub.html [ Failure ]
-crbug.com/794631 virtual/unified-autoplay/external/wpt/feature-policy/autoplay-allowed-by-feature-policy.https.sub.html [ Failure ]
-
 # https://crbug.com/793127: NOTREACHED() from clamy@ tickled by frame consolidation CL?
 crbug.com/793127 http/tests/security/upgrade-insecure-requests/iframe-upgrade.https.html [ Crash ]
 crbug.com/793127 external/wpt/content-security-policy/securitypolicyviolation/upgrade-insecure-requests-reporting.https.html [ Crash ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 467415ef..8692795 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -59,8 +59,6 @@
 crbug.com/771643 [ Linux ] virtual/disable-spv175/paint/invalidation/compositing/subpixel-offset-scaled-transform-composited.html [ Pass Failure ]
 crbug.com/809102 virtual/disable-spv175/paint/filters/clip-absolute-under-clip-under-filter.html [ Failure ]
 
-crbug.com/808325 virtual/gpu/fast/canvas/canvas-css-clip-path.html [ Failure ]
-
 # Subpixel differences
 crbug.com/771643 external/wpt/css/css-backgrounds/background-attachment-local/attachment-local-clipping-color-5.html [ Failure ]
 crbug.com/771643 external/wpt/css/css-backgrounds/background-attachment-local/attachment-local-clipping-image-5.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/border-radius-clip-subpixel-accumulation-expected.html b/third_party/WebKit/LayoutTests/compositing/overflow/border-radius-clip-subpixel-accumulation-expected.html
new file mode 100644
index 0000000..2a34d0c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/border-radius-clip-subpixel-accumulation-expected.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<style>
+#child-clipping-layer {
+  position: absolute;
+  left: 0;
+  top: 1px;
+  width: 100px;
+  height: 100px;
+  will-change: opacity;
+  border-radius: 10px;
+  overflow: hidden;
+}
+
+#clipped-layer {
+  width: 100px;
+  height: 100px;
+  background: green;
+  will-change: opacity;
+}
+</style>
+<div id="child-clipping-layer">
+  <div id="clipped-layer"></div>
+</div>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/border-radius-clip-subpixel-accumulation.html b/third_party/WebKit/LayoutTests/compositing/overflow/border-radius-clip-subpixel-accumulation.html
new file mode 100644
index 0000000..c6b4a8c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/border-radius-clip-subpixel-accumulation.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<style>
+#child-clipping-layer {
+  position: absolute;
+  left: 0.375px;
+  top: 0.625px;
+  width: 100px;
+  height: 100px;
+  will-change: opacity;
+  border-radius: 10px;
+  overflow: hidden;
+}
+
+#clipped-layer {
+  width: 100px;
+  height: 100px;
+  background: green;
+  will-change: opacity;
+}
+</style>
+<div id="child-clipping-layer">
+  <div id="clipped-layer"></div>
+</div>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index 0dfef3f9..5370c0b 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -39617,6 +39617,18 @@
      {}
     ]
    ],
+   "css/css-flexbox/table-as-item-auto-min-width.html": [
+    [
+     "/css/css-flexbox/table-as-item-auto-min-width.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-flexbox/table-as-item-narrow-content.html": [
     [
      "/css/css-flexbox/table-as-item-narrow-content.html",
@@ -44425,6 +44437,42 @@
      {}
     ]
    ],
+   "css/css-layout-api/fallback-layout-invalid-child.https.html": [
+    [
+     "/css/css-layout-api/fallback-layout-invalid-child.https.html",
+     [
+      [
+       "/css/css-layout-api/fallback-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-layout-api/fallback-layout-invalid-fragment-request.https.html": [
+    [
+     "/css/css-layout-api/fallback-layout-invalid-fragment-request.https.html",
+     [
+      [
+       "/css/css-layout-api/fallback-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-layout-api/fallback-layout-invalid-fragment.https.html": [
+    [
+     "/css/css-layout-api/fallback-layout-invalid-fragment.https.html",
+     [
+      [
+       "/css/css-layout-api/fallback-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-layout-api/fallback-layout-return.https.html": [
     [
      "/css/css-layout-api/fallback-layout-return.https.html",
@@ -44521,6 +44569,54 @@
      {}
     ]
    ],
+   "css/css-layout-api/perform-child-layout-fixed-block-size-vrl.https.html": [
+    [
+     "/css/css-layout-api/perform-child-layout-fixed-block-size-vrl.https.html",
+     [
+      [
+       "/css/css-layout-api/layout-child-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-layout-api/perform-child-layout-fixed-block-size.https.html": [
+    [
+     "/css/css-layout-api/perform-child-layout-fixed-block-size.https.html",
+     [
+      [
+       "/css/css-layout-api/layout-child-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-layout-api/perform-child-layout-fixed-inline-size-vrl.https.html": [
+    [
+     "/css/css-layout-api/perform-child-layout-fixed-inline-size-vrl.https.html",
+     [
+      [
+       "/css/css-layout-api/layout-child-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-layout-api/perform-child-layout-fixed-inline-size.https.html": [
+    [
+     "/css/css-layout-api/perform-child-layout-fixed-inline-size.https.html",
+     [
+      [
+       "/css/css-layout-api/layout-child-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-layout-api/style-map-multi.https.html": [
     [
      "/css/css-layout-api/style-map-multi.https.html",
@@ -49381,6 +49477,30 @@
      {}
     ]
    ],
+   "css/css-sizing/intrinsic-percent-non-replaced-001.html": [
+    [
+     "/css/css-sizing/intrinsic-percent-non-replaced-001.html",
+     [
+      [
+       "/css/css-sizing/intrinsic-percent-non-replaced-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-sizing/intrinsic-percent-non-replaced-003.html": [
+    [
+     "/css/css-sizing/intrinsic-percent-non-replaced-003.html",
+     [
+      [
+       "/css/css-sizing/intrinsic-percent-non-replaced-002-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-style-attr/style-attr-braces-001.xht": [
     [
      "/css/css-style-attr/style-attr-braces-001.xht",
@@ -97176,16 +97296,6 @@
      {}
     ]
    ],
-   "IndexedDB/interfaces.any-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "IndexedDB/interfaces.any.worker-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "IndexedDB/interleaved-cursors-common.js": [
     [
      {}
@@ -100316,11 +100426,6 @@
      {}
     ]
    ],
-   "cookie-store/idlharness.tentative-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "cookie-store/idlharness_serviceworker.js": [
     [
      {}
@@ -117231,6 +117336,11 @@
      {}
     ]
    ],
+   "css/css-layout-api/support/layout-child-fixed-sizes-worklet.js": [
+    [
+     {}
+    ]
+   ],
    "css/css-layout-api/support/layout-child-worklet.js": [
     [
      {}
@@ -118986,6 +119096,16 @@
      {}
     ]
    ],
+   "css/css-sizing/intrinsic-percent-non-replaced-001-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "css/css-sizing/intrinsic-percent-non-replaced-002-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-style-attr/reference/ref-green-on-green.xht": [
     [
      {}
@@ -124776,11 +124896,56 @@
      {}
     ]
    ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-language-override-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-optical-sizing-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-palette-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-presentation-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-size-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-stretch-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "css/css-typed-om/the-stylepropertymap/properties/font-style-expected.txt": [
     [
      {}
     ]
    ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-synthesis-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-variant-alternates-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-variant-emoji-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "css/css-typed-om/the-stylepropertymap/properties/font-weight-expected.txt": [
     [
      {}
@@ -164671,6 +164836,21 @@
      {}
     ]
    ],
+   "workers/modules/resources/dynamic-import-and-then-static-import-worker.js": [
+    [
+     {}
+    ]
+   ],
+   "workers/modules/resources/dynamic-import-worker.js": [
+    [
+     {}
+    ]
+   ],
+   "workers/modules/resources/nested-dynamic-import-worker.js": [
+    [
+     {}
+    ]
+   ],
    "workers/modules/resources/nested-static-import-worker.js": [
     [
      {}
@@ -164681,6 +164861,11 @@
      {}
     ]
    ],
+   "workers/modules/resources/static-import-and-then-dynamic-import-worker.js": [
+    [
+     {}
+    ]
+   ],
    "workers/modules/resources/static-import-worker.js": [
     [
      {}
@@ -165816,11 +166001,6 @@
      {}
     ]
    ],
-   "xhr/send-sync-response-event-order-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "xhr/setrequestheader-content-type-expected.txt": [
     [
      {}
@@ -184178,6 +184358,12 @@
      {}
     ]
    ],
+   "css/css-typed-om/the-stylepropertymap/properties/block-size.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/block-size.html",
+     {}
+    ]
+   ],
    "css/css-typed-om/the-stylepropertymap/properties/border-collapse.html": [
     [
      "/css/css-typed-om/the-stylepropertymap/properties/border-collapse.html",
@@ -184328,30 +184514,120 @@
      {}
     ]
    ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-family.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/font-family.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-language-override.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/font-language-override.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-optical-sizing.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/font-optical-sizing.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-palette.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/font-palette.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-presentation.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/font-presentation.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-size-adjust.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/font-size-adjust.html",
+     {}
+    ]
+   ],
    "css/css-typed-om/the-stylepropertymap/properties/font-size.html": [
     [
      "/css/css-typed-om/the-stylepropertymap/properties/font-size.html",
      {}
     ]
    ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-stretch.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/font-stretch.html",
+     {}
+    ]
+   ],
    "css/css-typed-om/the-stylepropertymap/properties/font-style.html": [
     [
      "/css/css-typed-om/the-stylepropertymap/properties/font-style.html",
      {}
     ]
    ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-synthesis.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/font-synthesis.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-variant-alternates.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/font-variant-alternates.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-variant-emoji.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/font-variant-emoji.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-variant.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/font-variant.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/font-variation-settings.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/font-variation-settings.html",
+     {}
+    ]
+   ],
    "css/css-typed-om/the-stylepropertymap/properties/font-weight.html": [
     [
      "/css/css-typed-om/the-stylepropertymap/properties/font-weight.html",
      {}
     ]
    ],
+   "css/css-typed-om/the-stylepropertymap/properties/font.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/font.html",
+     {}
+    ]
+   ],
+   "css/css-typed-om/the-stylepropertymap/properties/gap.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/gap.html",
+     {}
+    ]
+   ],
    "css/css-typed-om/the-stylepropertymap/properties/height.html": [
     [
      "/css/css-typed-om/the-stylepropertymap/properties/height.html",
      {}
     ]
    ],
+   "css/css-typed-om/the-stylepropertymap/properties/inline-size.html": [
+    [
+     "/css/css-typed-om/the-stylepropertymap/properties/inline-size.html",
+     {}
+    ]
+   ],
    "css/css-typed-om/the-stylepropertymap/properties/isolation.html": [
     [
      "/css/css-typed-om/the-stylepropertymap/properties/isolation.html",
@@ -236052,15 +236328,15 @@
      {}
     ]
    ],
-   "workers/modules/dedicated-worker-options-type.html": [
+   "workers/modules/dedicated-worker-import.html": [
     [
-     "/workers/modules/dedicated-worker-options-type.html",
+     "/workers/modules/dedicated-worker-import.html",
      {}
     ]
    ],
-   "workers/modules/dedicated-worker-static-import.html": [
+   "workers/modules/dedicated-worker-options-type.html": [
     [
-     "/workers/modules/dedicated-worker-static-import.html",
+     "/workers/modules/dedicated-worker-options-type.html",
      {}
     ]
    ],
@@ -243054,6 +243330,12 @@
      {}
     ]
    ],
+   "css/css-sizing/intrinsic-percent-non-replaced-002.html": [
+    [
+     "/css/css-sizing/intrinsic-percent-non-replaced-002.html",
+     {}
+    ]
+   ],
    "css/css-text-decor/text-decoration-visibility-001.xht": [
     [
      "/css/css-text-decor/text-decoration-visibility-001.xht",
@@ -249415,18 +249697,10 @@
    "251a828d333bdd3face9d20a2a28ddf0c0ffeb49",
    "testharness"
   ],
-  "IndexedDB/interfaces.any-expected.txt": [
-   "2606ec37ce4ca007703f69f75e9fb74c97c8b3e2",
-   "support"
-  ],
   "IndexedDB/interfaces.any.js": [
-   "df07f5da63c34969a24fe43bc4268418ab0a5132",
+   "ae562d6b568c1005c5eef5a230b8869729719dff",
    "testharness"
   ],
-  "IndexedDB/interfaces.any.worker-expected.txt": [
-   "2606ec37ce4ca007703f69f75e9fb74c97c8b3e2",
-   "support"
-  ],
   "IndexedDB/interleaved-cursors-common.js": [
    "6744105fe1a15a91058e3e5993f8a1f88548e3a3",
    "support"
@@ -252484,7 +252758,7 @@
    "support"
   ],
   "common/worklet-reftest.js": [
-   "29a9c4f2f6187fc64ecd1c77bcccfa1aef278ed8",
+   "11ef28419b58983d473606865c0d7a47be5e4056",
    "support"
   ],
   "compat/OWNERS": [
@@ -255263,12 +255537,8 @@
    "11c763dd1b7b3a1bff14b9f65538fb33ca97b81b",
    "testharness"
   ],
-  "cookie-store/idlharness.tentative-expected.txt": [
-   "59204fdf907e3d3d5e230dd9a4d808f09ae31ab8",
-   "support"
-  ],
   "cookie-store/idlharness.tentative.html": [
-   "24a4e59e8d3c0a481c4b448bd3fb677f30d0208f",
+   "8654087597cc0c11b880b0144897dab26691d12a",
    "testharness"
   ],
   "cookie-store/idlharness_serviceworker.js": [
@@ -278047,6 +278317,10 @@
    "078e1dd6dd61d36cec239ed75d02051f61fe60a5",
    "support"
   ],
+  "css/css-flexbox/table-as-item-auto-min-width.html": [
+   "d70cb1447c942c9f66f6503b6c769b2db188b5dc",
+   "reftest"
+  ],
   "css/css-flexbox/table-as-item-narrow-content.html": [
    "ccee1a24278c3177809a09009c2f15973908fa83",
    "reftest"
@@ -287847,6 +288121,18 @@
    "3d279f42a9b8e8ea0f6dc120d36ca0597372ef9b",
    "reftest"
   ],
+  "css/css-layout-api/fallback-layout-invalid-child.https.html": [
+   "f2fa9fefa56073f42a0537fa3140f37ca0c316da",
+   "reftest"
+  ],
+  "css/css-layout-api/fallback-layout-invalid-fragment-request.https.html": [
+   "24aed5d6b0f7ea7095324ce1f59adb0498277bdf",
+   "reftest"
+  ],
+  "css/css-layout-api/fallback-layout-invalid-fragment.https.html": [
+   "991b73a63246ae1d2676df32c3a56028f5a9583b",
+   "reftest"
+  ],
   "css/css-layout-api/fallback-layout-return.https.html": [
    "159662be847f8657d33a0607cb0e6561d57ef4e1",
    "reftest"
@@ -287891,6 +288177,22 @@
    "567ff67317cae9906e6d159dc232c41464a4e7c6",
    "reftest"
   ],
+  "css/css-layout-api/perform-child-layout-fixed-block-size-vrl.https.html": [
+   "341711737d74fb068bb3a2e348e47820c236fa49",
+   "reftest"
+  ],
+  "css/css-layout-api/perform-child-layout-fixed-block-size.https.html": [
+   "486fe671afaa9275bc6b32c49ca4c8143290f9be",
+   "reftest"
+  ],
+  "css/css-layout-api/perform-child-layout-fixed-inline-size-vrl.https.html": [
+   "3c3b4c800af40a85eb9ddb588a07fc0d8ceec5cf",
+   "reftest"
+  ],
+  "css/css-layout-api/perform-child-layout-fixed-inline-size.https.html": [
+   "a25a85095781de557edde6dd02b82ee052642bf1",
+   "reftest"
+  ],
   "css/css-layout-api/style-map-multi-ref.html": [
    "d33f700e795484641d3cc7db1c26e09dca952209",
    "support"
@@ -287907,8 +288209,12 @@
    "361b3c82c37c0068d23ae23e96d8e9185d3765b0",
    "reftest"
   ],
+  "css/css-layout-api/support/layout-child-fixed-sizes-worklet.js": [
+   "5ddda72e3c9d077508622511e8685249c7803028",
+   "support"
+  ],
   "css/css-layout-api/support/layout-child-worklet.js": [
-   "bea931d2a111e173494ec3bf2adaa9cc42b73a1e",
+   "87af0bfedbe9d4ea23e904edc5b22382a5d5d56c",
    "support"
   ],
   "css/css-layout-api/supports.https.html": [
@@ -291599,6 +291905,26 @@
    "5bb192165bcb7d9a619d86dbff61831fc8de71cb",
    "support"
   ],
+  "css/css-sizing/intrinsic-percent-non-replaced-001-ref.html": [
+   "67a4540f6b7de22155226b39e948c7a7e2c33f1b",
+   "support"
+  ],
+  "css/css-sizing/intrinsic-percent-non-replaced-001.html": [
+   "58ce478fe083f05e3bdbb3f24988341f3e7095b5",
+   "reftest"
+  ],
+  "css/css-sizing/intrinsic-percent-non-replaced-002-ref.html": [
+   "8c6cde1d06cd3ba83b3049edbb0016033fbb62b4",
+   "support"
+  ],
+  "css/css-sizing/intrinsic-percent-non-replaced-002.html": [
+   "9d9c514c876bb9aa2c577dbd15c14358e7b0d0c4",
+   "visual"
+  ],
+  "css/css-sizing/intrinsic-percent-non-replaced-003.html": [
+   "43d44ad3a75e6abb9d66d06c3cdc71dbcb366651",
+   "reftest"
+  ],
   "css/css-style-attr/reference/ref-green-on-green.xht": [
    "3db4dec22e96cce52c575f4adb7a05f79917d4ea",
    "support"
@@ -305083,6 +305409,10 @@
    "32d684e452a4bafd7b58a0b33d42d32aa51ac091",
    "testharness"
   ],
+  "css/css-typed-om/the-stylepropertymap/properties/block-size.html": [
+   "4dc26a97d56a24c053efac27b5988456d750d186",
+   "testharness"
+  ],
   "css/css-typed-om/the-stylepropertymap/properties/border-collapse.html": [
    "93e71097d3b28c88f51210a2f2a9fcf433f80cba",
    "testharness"
@@ -305191,8 +305521,60 @@
    "1dfca0045c2b57f36d5165139087301ffe54c63a",
    "testharness"
   ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-family.html": [
+   "72e8dd4dd54081fe9b2278c860f2752e2d17de74",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-language-override-expected.txt": [
+   "1ef9ac7d0bd41d1b2cb5246a6969be839c8eb77d",
+   "support"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-language-override.html": [
+   "e43730c10c85fc5bbed8e6a32c594f5c643e4786",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-optical-sizing-expected.txt": [
+   "2ea2f2b21839818a86600f942f5caef0cd2dd3c9",
+   "support"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-optical-sizing.html": [
+   "7183f6fcb2fd4e713d3b2d6cbc7211a8b2a071a6",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-palette-expected.txt": [
+   "873257b774e6ce5e2120695c497da0dea941baef",
+   "support"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-palette.html": [
+   "8ec2ff5bbc9913680fd5fab16949f4aaf4eca740",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-presentation-expected.txt": [
+   "620e30bc90d63e01da9d55a8f1d40014937213eb",
+   "support"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-presentation.html": [
+   "fe359fc963e95d1ca539bb9f217164292e5a13fb",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-size-adjust.html": [
+   "1a8fa0ace8426d7cfecaaa79e2f4fbd77c5265ab",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-size-expected.txt": [
+   "93cf46c3f4a7e14679daee7b0725abd76c6af6ac",
+   "support"
+  ],
   "css/css-typed-om/the-stylepropertymap/properties/font-size.html": [
-   "bb31e5cb88492bece2c54ff106d01e789e3f08a6",
+   "5b405bd6bb5ea140bf6a3d62b670e2924fcc6378",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-stretch-expected.txt": [
+   "47b680cac637feec0057efcce618d1f027797a07",
+   "support"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-stretch.html": [
+   "52438b3be5d9c1037863880834e766dda7d07d5a",
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/font-style-expected.txt": [
@@ -305203,6 +305585,38 @@
    "c4a42872dca602b71f021799d29e404cfa7ed90e",
    "testharness"
   ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-synthesis-expected.txt": [
+   "6e9f8092f2396dfd8c900ce77aafc240bbe23d8b",
+   "support"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-synthesis.html": [
+   "de9400e7f238dbc1dc47fb5f483b3ba01eefe934",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-variant-alternates-expected.txt": [
+   "1619f7d2402cdd1f64d21d3a4201acb019f291cf",
+   "support"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-variant-alternates.html": [
+   "141eab85e1a4bcbc051d61dc502bb90613eae996",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-variant-emoji-expected.txt": [
+   "ecddc70a6de41087d76f49371a2495010b00115e",
+   "support"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-variant-emoji.html": [
+   "737a0494625e9d22c16f791fbb437c47d4a2923b",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-variant.html": [
+   "b538f2ed407c8cfd4718c28082da3de237cd9953",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/font-variation-settings.html": [
+   "566a8c49d7a7e71e960061fee88c9b17d33f1e45",
+   "testharness"
+  ],
   "css/css-typed-om/the-stylepropertymap/properties/font-weight-expected.txt": [
    "3a608fb1d954b478d7e61f726cdf0582e39b72d3",
    "support"
@@ -305211,10 +305625,22 @@
    "6429fa7266a227e93cb0a5d3eb19468b0f73fce8",
    "testharness"
   ],
+  "css/css-typed-om/the-stylepropertymap/properties/font.html": [
+   "6f3f672ecb0ad0b4391b7c7fbd8bb666b77a1efc",
+   "testharness"
+  ],
+  "css/css-typed-om/the-stylepropertymap/properties/gap.html": [
+   "e113dfd152548c6ba612d40af13ba5877673b043",
+   "testharness"
+  ],
   "css/css-typed-om/the-stylepropertymap/properties/height.html": [
    "617ec941ab1cbd02b31b8a9bb7ce6da311109476",
    "testharness"
   ],
+  "css/css-typed-om/the-stylepropertymap/properties/inline-size.html": [
+   "531ad3bba92bfdb60377dc755d40f3300d0843e0",
+   "testharness"
+  ],
   "css/css-typed-om/the-stylepropertymap/properties/isolation.html": [
    "883b36434d3e3d8fee25f0ae0fee9884b5ceef44",
    "testharness"
@@ -321732,7 +322158,7 @@
    "testharness"
   ],
   "dom/nodes/Element-classlist.html": [
-   "e0a3126453af3e138c322ae0074d7ee73d604a8d",
+   "db5f4b3f723db026b90e299b2c7be5e5e4f081f5",
    "testharness"
   ],
   "dom/nodes/Element-closest-expected.txt": [
@@ -328036,11 +328462,11 @@
    "testharness"
   ],
   "hr-time/idlharness-expected.txt": [
-   "2606ec37ce4ca007703f69f75e9fb74c97c8b3e2",
+   "e064a696afab015c130966f2d5a389572f978543",
    "support"
   ],
   "hr-time/idlharness.html": [
-   "f2cdcba041df089206cc9c811167c41a771905df",
+   "579176cad14656ca5cdc616dd1323dc38a5b62ba",
    "testharness"
   ],
   "hr-time/monotonic-clock.any.js": [
@@ -349060,7 +349486,7 @@
    "testharness"
   ],
   "mediacapture-streams/MediaDevices-enumerateDevices.https.html": [
-   "892229b737d157845d9b2e24170f3bc820b10036",
+   "c44f824ed1836c272125b0b5b10653cb86db2a7e",
    "testharness"
   ],
   "mediacapture-streams/MediaDevices-getUserMedia.https.html": [
@@ -349144,7 +349570,7 @@
    "manual"
   ],
   "mediacapture-streams/MediaStreamTrack-getCapabilities.https.html": [
-   "77afad651acf85273270f1b75f660e7e800c0ff1",
+   "23863eb0835fb86d556e6ea95692f52572a38170",
    "testharness"
   ],
   "mediacapture-streams/MediaStreamTrack-getSettings.https.html": [
@@ -374044,7 +374470,7 @@
    "testharness"
   ],
   "web-animations/interfaces/KeyframeEffect/idlharness-expected.txt": [
-   "7a3fcbe2552c1778b73aa834d92dd91422ee063e",
+   "2275f35927d277133447e28a425d4688b19d3cac",
    "support"
   ],
   "web-animations/interfaces/KeyframeEffect/idlharness.html": [
@@ -374472,11 +374898,11 @@
    "testharness"
   ],
   "webaudio/idlharness.https-expected.txt": [
-   "2606ec37ce4ca007703f69f75e9fb74c97c8b3e2",
+   "fb80b7f73175870f1424df7fdfefb32053029983",
    "support"
   ],
   "webaudio/idlharness.https.html": [
-   "7876e16ea643f69315e18c3e17af0d7f95769420",
+   "f42681173fdf4cb8dad2049351f55f3a7ff0fcac",
    "testharness"
   ],
   "webaudio/js/buffer-loader.js": [
@@ -381291,24 +381717,40 @@
    "6bffa3be83d81e2faa93119e710e4fee93fb855e",
    "testharness"
   ],
+  "workers/modules/dedicated-worker-import.html": [
+   "752698b4f8f7298ca2ecc74d41887117a636118a",
+   "testharness"
+  ],
   "workers/modules/dedicated-worker-options-type.html": [
    "9f6f1be759beb885e2baa746e36ace83685f649b",
    "testharness"
   ],
-  "workers/modules/dedicated-worker-static-import.html": [
-   "d0d3dc37a8c061a1dc5213f8fe79e7f985c48b81",
-   "testharness"
+  "workers/modules/resources/dynamic-import-and-then-static-import-worker.js": [
+   "f4df69196f64cd81e92705186325004ac94db659",
+   "support"
+  ],
+  "workers/modules/resources/dynamic-import-worker.js": [
+   "444e313fe51923097e3672d88d0afd30aac5ecab",
+   "support"
+  ],
+  "workers/modules/resources/nested-dynamic-import-worker.js": [
+   "4ee05f3be8a1b41a9e2fa3e24d40ec5103f67eac",
+   "support"
   ],
   "workers/modules/resources/nested-static-import-worker.js": [
-   "eb76ec7e8a0f9df6de7114e3aa9100f8374fea8f",
+   "38ca86e8e32df51af7d848474f31a7b8ff9dc3aa",
    "support"
   ],
   "workers/modules/resources/post-message-on-load-worker.js": [
    "c67a79ade775435a67e5999d17e7cdda450c8e50",
    "support"
   ],
+  "workers/modules/resources/static-import-and-then-dynamic-import-worker.js": [
+   "f69987442b6a223a868e6c1a7ca6d9cee2976068",
+   "support"
+  ],
   "workers/modules/resources/static-import-worker.js": [
-   "f3020118d7e499e0e910abc7a733e0b9c3cf1e5a",
+   "6d5fb2c553d2f32cdd16722a85bd65e0a172768c",
    "support"
   ],
   "workers/name-property-expected.txt": [
@@ -383415,10 +383857,6 @@
    "bf76cb8987608b7bb9f59627032826d21936f450",
    "testharness"
   ],
-  "xhr/send-sync-response-event-order-expected.txt": [
-   "70c00c61fbfff76b3f6042dac646624464ccb3f0",
-   "support"
-  ],
   "xhr/send-sync-response-event-order.htm": [
    "3e2d0154469dcdf3a04376c2c350dab681ff8fe7",
    "testharness"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-htb-ltr.https.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-htb-ltr.https.html
new file mode 100644
index 0000000..b09cb7067
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-htb-ltr.https.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<link rel="help" href="https://drafts.css-houdini.org/css-layout-api/#layoutfragment">
+<link rel="match" href="position-fragment-ref.html">
+<meta name="assert" content="This test checks that child fragments get positioned correctly." />
+<style>
+.test {
+  background: red;
+  width: 100px;
+  height: 100px;
+}
+
+.test {
+  writing-mode: horizontal-tb;
+  direction: ltr;
+}
+
+.child-1 {
+  background: rebeccapurple;
+  width: 10px;
+  height: 20px;
+
+  --inline-offset: 5;
+  --block-offset: 25;
+}
+
+.child-2 {
+  writing-mode: vertical-rl;
+  background: rebeccapurple;
+  width: 15px;
+  height: 25px;
+
+  --inline-offset: 50;
+  --block-offset: 60;
+}
+
+@supports (display: layout(test)) {
+  .test {
+    background: green;
+    display: layout(test);
+  }
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/worklet-reftest.js"></script>
+
+<div class="test">
+  <div class="child-1"></div>
+  <div class="child-2"></div>
+</div>
+
+<script>
+importWorkletAndTerminateTestAfterAsyncPaint(CSS.layoutWorklet, {url: 'support/layout-position-child-worklet.js'});
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-htb-rtl.https.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-htb-rtl.https.html
new file mode 100644
index 0000000..2d65b8cb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-htb-rtl.https.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<link rel="help" href="https://drafts.css-houdini.org/css-layout-api/#layoutfragment">
+<link rel="match" href="position-fragment-ref.html">
+<meta name="assert" content="This test checks that child fragments get positioned correctly." />
+<style>
+.test {
+  background: red;
+  width: 100px;
+  height: 100px;
+}
+
+.test {
+  writing-mode: horizontal-tb;
+  direction: rtl;
+}
+
+.child-1 {
+  background: rebeccapurple;
+  width: 10px;
+  height: 20px;
+
+  --inline-offset: 85;
+  --block-offset: 25;
+}
+
+.child-2 {
+  writing-mode: vertical-rl;
+  background: rebeccapurple;
+  width: 15px;
+  height: 25px;
+
+  --inline-offset: 35;
+  --block-offset: 60;
+}
+
+@supports (display: layout(test)) {
+  .test {
+    background: green;
+    display: layout(test);
+  }
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/worklet-reftest.js"></script>
+
+<div class="test">
+  <div class="child-1"></div>
+  <div class="child-2"></div>
+</div>
+
+<script>
+importWorkletAndTerminateTestAfterAsyncPaint(CSS.layoutWorklet, {url: 'support/layout-position-child-worklet.js'});
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-ref.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-ref.html
new file mode 100644
index 0000000..4ce0a6e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-ref.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<style>
+.result {
+  position: relative;
+  background: green;
+  width: 100px;
+  height: 100px;
+}
+
+.result-child-1 {
+  background: rebeccapurple;
+  width: 10px;
+  height: 20px;
+
+  position: absolute;
+  top: 25px;
+  left: 5px;
+}
+
+.result-child-2 {
+  background: rebeccapurple;
+  width: 15px;
+  height: 25px;
+
+  position: absolute;
+  top: 60px;
+  left: 50px;
+}
+</style>
+<div class="result">
+  <div class="result-child-1"></div>
+  <div class="result-child-2"></div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-vlr-ltr.https.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-vlr-ltr.https.html
new file mode 100644
index 0000000..62a5980
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-vlr-ltr.https.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<link rel="help" href="https://drafts.css-houdini.org/css-layout-api/#layoutfragment">
+<link rel="match" href="position-fragment-ref.html">
+<meta name="assert" content="This test checks that child fragments get positioned correctly." />
+<style>
+.test {
+  background: red;
+  width: 100px;
+  height: 100px;
+}
+
+.test {
+  writing-mode: vertical-lr;
+  direction: ltr;
+}
+
+.child-1 {
+  background: rebeccapurple;
+  width: 10px;
+  height: 20px;
+
+  --inline-offset: 25;
+  --block-offset: 5;
+}
+
+.child-2 {
+  writing-mode: vertical-rl;
+  background: rebeccapurple;
+  width: 15px;
+  height: 25px;
+
+  --inline-offset: 60;
+  --block-offset: 50;
+}
+
+@supports (display: layout(test)) {
+  .test {
+    background: green;
+    display: layout(test);
+  }
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/worklet-reftest.js"></script>
+
+<div class="test">
+  <div class="child-1"></div>
+  <div class="child-2"></div>
+</div>
+
+<script>
+importWorkletAndTerminateTestAfterAsyncPaint(CSS.layoutWorklet, {url: 'support/layout-position-child-worklet.js'});
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-vlr-rtl.https.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-vlr-rtl.https.html
new file mode 100644
index 0000000..a9983b1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-vlr-rtl.https.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<link rel="help" href="https://drafts.css-houdini.org/css-layout-api/#layoutfragment">
+<link rel="match" href="position-fragment-ref.html">
+<meta name="assert" content="This test checks that child fragments get positioned correctly." />
+<style>
+.test {
+  background: red;
+  width: 100px;
+  height: 100px;
+}
+
+.test {
+  writing-mode: vertical-lr;
+  direction: rtl;
+}
+
+.child-1 {
+  background: rebeccapurple;
+  width: 10px;
+  height: 20px;
+
+  --inline-offset: 55;
+  --block-offset: 5;
+}
+
+.child-2 {
+  writing-mode: vertical-rl;
+  background: rebeccapurple;
+  width: 15px;
+  height: 25px;
+
+  --inline-offset: 15;
+  --block-offset: 50;
+}
+
+@supports (display: layout(test)) {
+  .test {
+    background: green;
+    display: layout(test);
+  }
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/worklet-reftest.js"></script>
+
+<div class="test">
+  <div class="child-1"></div>
+  <div class="child-2"></div>
+</div>
+
+<script>
+importWorkletAndTerminateTestAfterAsyncPaint(CSS.layoutWorklet, {url: 'support/layout-position-child-worklet.js'});
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-vrl-ltr.https.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-vrl-ltr.https.html
new file mode 100644
index 0000000..0901285
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-vrl-ltr.https.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<link rel="help" href="https://drafts.css-houdini.org/css-layout-api/#layoutfragment">
+<link rel="match" href="position-fragment-ref.html">
+<meta name="assert" content="This test checks that child fragments get positioned correctly." />
+<style>
+.test {
+  background: red;
+  width: 100px;
+  height: 100px;
+}
+
+.test {
+  writing-mode: vertical-rl;
+  direction: ltr;
+}
+
+.child-1 {
+  background: rebeccapurple;
+  width: 10px;
+  height: 20px;
+
+  --inline-offset: 25;
+  --block-offset: 85;
+}
+
+.child-2 {
+  writing-mode: vertical-rl;
+  background: rebeccapurple;
+  width: 15px;
+  height: 25px;
+
+  --inline-offset: 60;
+  --block-offset: 35;
+}
+
+@supports (display: layout(test)) {
+  .test {
+    background: green;
+    display: layout(test);
+  }
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/worklet-reftest.js"></script>
+
+<div class="test">
+  <div class="child-1"></div>
+  <div class="child-2"></div>
+</div>
+
+<script>
+importWorkletAndTerminateTestAfterAsyncPaint(CSS.layoutWorklet, {url: 'support/layout-position-child-worklet.js'});
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-vrl-rtl.https.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-vrl-rtl.https.html
new file mode 100644
index 0000000..e16f26b1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/position-fragment-vrl-rtl.https.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<link rel="help" href="https://drafts.css-houdini.org/css-layout-api/#layoutfragment">
+<link rel="match" href="position-fragment-ref.html">
+<meta name="assert" content="This test checks that child fragments get positioned correctly." />
+<style>
+.test {
+  background: red;
+  width: 100px;
+  height: 100px;
+}
+
+.test {
+  writing-mode: vertical-rl;
+  direction: rtl;
+}
+
+.child-1 {
+  background: rebeccapurple;
+  width: 10px;
+  height: 20px;
+
+  --inline-offset: 55;
+  --block-offset: 85;
+}
+
+.child-2 {
+  writing-mode: vertical-rl;
+  background: rebeccapurple;
+  width: 15px;
+  height: 25px;
+
+  --inline-offset: 15;
+  --block-offset: 35;
+}
+
+@supports (display: layout(test)) {
+  .test {
+    background: green;
+    display: layout(test);
+  }
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/worklet-reftest.js"></script>
+
+<div class="test">
+  <div class="child-1"></div>
+  <div class="child-2"></div>
+</div>
+
+<script>
+importWorkletAndTerminateTestAfterAsyncPaint(CSS.layoutWorklet, {url: 'support/layout-position-child-worklet.js'});
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/support/layout-position-child-worklet.js b/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/support/layout-position-child-worklet.js
new file mode 100644
index 0000000..1ccfeab5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-layout-api/support/layout-position-child-worklet.js
@@ -0,0 +1,22 @@
+registerLayout('test', class {
+  static get childInputProperties() {
+    return [
+      '--inline-offset',
+      '--block-offset',
+    ];
+  }
+
+  *intrinsicSizes() {}
+  *layout(children, edges, constraints, styleMap) {
+    const childFragments = yield children.map((child) => {
+      return child.layoutNextFragment({});
+    });
+
+    for (let i = 0; i < children.length; i++) {
+      childFragments[i].inlineOffset = parseInt(children[i].styleMap.get('--inline-offset').toString());
+      childFragments[i].blockOffset = parseInt(children[i].styleMap.get('--block-offset').toString());
+    }
+
+    return {autoBlockSize: 0, childFragments};
+  }
+});
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-sizing/intrinsic-percent-non-replaced-001-ref.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-sizing/intrinsic-percent-non-replaced-001-ref.html
new file mode 100644
index 0000000..0b217dd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-sizing/intrinsic-percent-non-replaced-001-ref.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<title>Reference</title>
+
+<style>
+  /* establish context */
+  .container {
+    clear: both;
+    padding: 10px;
+    color: blue;
+    font: 20px/1 Ahem;
+  }
+  .zero {
+    width: 0;
+  }
+  .infinite {
+    width: 400px; /* close enough */
+  }
+
+  /* visualize size contribution */
+  .container > div {
+    float: left;
+    border: solid orange 20px;
+    border-style: none solid;
+  }
+  .container > div > div {
+    border-right: solid 20px aqua;
+  }
+
+  /* controls for min-width */
+  /* content = 100% = 80px = 4ch + border */
+  /* choose sizes that are larger than content to see if how they take effect */
+  .control {
+    width: 60px;
+  }
+  .raw-percent,
+  .calc-percent {
+    width: 160px;
+    margin-right: -100px;
+  }
+  .no-percent {
+    width: 160px;
+  }
+</style>
+
+<!-- calculating min-content widths -->
+<div class='zero container'>
+  <div><div class="control">ppp pp</div></div>
+</div>
+<div class='zero container'>
+  <div><div class="raw-percent">ppp pp</div></div>
+</div>
+<div class='zero container'>
+  <div><div class="calc-percent">ppp pp</div></div>
+</div>
+<div class='zero container'>
+  <div><div class="no-percent">ppp pp</div></div>
+</div>
+
+<!-- calculating max-content widths -->
+<div class='infinite container'>
+  <div><div class="control">p p</div></div>
+</div>
+<div class='infinite container'>
+  <div><div class="raw-percent">p p</div></div>
+</div>
+<div class='infinite container'>
+  <div><div class="calc-percent">p p</div></div>
+</div>
+<div class='infinite container'>
+  <div><div class="no-percent">p p</div></div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-sizing/intrinsic-percent-non-replaced-001.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-sizing/intrinsic-percent-non-replaced-001.html
new file mode 100644
index 0000000..19b0a94
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-sizing/intrinsic-percent-non-replaced-001.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<title>Percentages of min-width on non-replaced blocks are ignored for intrinsic sizing and resolved afterwards</title>
+<link rel="help" href="https://www.w3.org/TR/css-sizing-3/#intrinsic-contribution">
+<link rel="match" href="intrinsic-percent-non-replaced-001-ref.html">
+
+<style>
+  /* establish context */
+  .container {
+    clear: both;
+    padding: 10px;
+    color: blue;
+    font: 20px/1 Ahem;
+  }
+  .zero {
+    width: 0;
+  }
+  .infinite {
+    width: 400px; /* close enough */
+  }
+
+  /* visualize size contribution */
+  .container > div {
+    float: left;
+    border: solid orange 20px;
+    border-style: none solid;
+  }
+  .container > div > div {
+    border-right: solid 20px aqua;
+  }
+
+  /* test min-width */
+  /* content = 100% = 80px = 4ch + border */
+  /* choose sizes that are larger than content to see if how they take effect */
+  .raw-percent {
+    min-width: 200%;
+  }
+  .calc-percent {
+    min-width: calc(160px + 0%);
+  }
+  .no-percent {
+    min-width: 160px;
+  }
+</style>
+
+<!-- calculating min-content widths -->
+<div class='zero container'>
+  <div><div class="control">ppp pp</div></div>
+</div>
+<div class='zero container'>
+  <div><div class="raw-percent">ppp pp</div></div>
+</div>
+<div class='zero container'>
+  <div><div class="calc-percent">ppp pp</div></div>
+</div>
+<div class='zero container'>
+  <div><div class="no-percent">ppp pp</div></div>
+</div>
+
+<!-- calculating max-content widths -->
+<div class='infinite container'>
+  <div><div class="control">p p</div></div>
+</div>
+<div class='infinite container'>
+  <div><div class="raw-percent">p p</div></div>
+</div>
+<div class='infinite container'>
+  <div><div class="calc-percent">p p</div></div>
+</div>
+<div class='infinite container'>
+  <div><div class="no-percent">p p</div></div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-sizing/intrinsic-percent-non-replaced-002-ref.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-sizing/intrinsic-percent-non-replaced-002-ref.html
new file mode 100644
index 0000000..bb818918
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-sizing/intrinsic-percent-non-replaced-002-ref.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<title>Reference</title>
+
+<style>
+  /* establish context */
+  .container {
+    clear: both;
+    padding: 10px;
+    color: blue;
+    font: 20px/1 Ahem;
+  }
+  .zero {
+    width: 0;
+  }
+  .infinite {
+    width: 400px; /* close enough */
+  }
+
+  /* visualize size contribution */
+  .container > div {
+    float: left; /* shrinkwrap */
+    border: solid orange 20px;
+    border-style: none solid;
+  }
+  .container > div > div {
+    border-right: solid 20px aqua;
+  }
+
+  /* controls for width, max-width */
+  /* content = 100% = 80px = 4ch + border */
+  /* choose sizes that are larger than content to see if how they take effect */
+  .control {
+    width: 60px;
+  }
+  .raw-percent,
+  .calc-percent {
+    width: 40px;
+    margin-right: 20px;
+  }
+  .no-percent {
+    width: 40px;
+  }
+</style>
+
+<!-- calculating min-content widths -->
+<div class='zero container'>
+  <div><div class="control">ppp pp</div></div>
+</div>
+<div class='zero container'>
+  <div><div class="raw-percent">ppp pp</div></div>
+</div>
+<div class='zero container'>
+  <div><div class="calc-percent">ppp pp</div></div>
+</div>
+<div class='zero container'>
+  <div><div class="no-percent">ppp pp</div></div>
+</div>
+
+<!-- calculating max-content widths -->
+<div class='infinite container'>
+  <div><div class="control">p p</div></div>
+</div>
+<div class='infinite container'>
+  <div><div class="raw-percent">p p</div></div>
+</div>
+<div class='infinite container'>
+  <div><div class="calc-percent">p p</div></div>
+</div>
+<div class='infinite container'>
+  <div><div class="no-percent">p p</div></div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-sizing/intrinsic-percent-non-replaced-002.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-sizing/intrinsic-percent-non-replaced-002.html
new file mode 100644
index 0000000..763549f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-sizing/intrinsic-percent-non-replaced-002.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<title>Percentages of max-width on non-replaced blocks are ignored for intrinsic sizing and resolved afterwards</title>
+<link rel="help" href="https://www.w3.org/TR/css-sizing-3/#intrinsic-contribution">
+
+<style>
+  /* establish context */
+  .container {
+    clear: both;
+    padding: 10px;
+    color: blue;
+    font: 20px/1 Ahem;
+  }
+  .zero {
+    width: 0;
+  }
+  .infinite {
+    width: 400px; /* close enough */
+  }
+
+  /* visualize size contribution */
+  .container > div {
+    float: left; /* shrinkwrap */
+    border: solid orange 20px;
+    border-style: none solid;
+  }
+  .container > div > div {
+    border-right: solid 20px aqua;
+  }
+
+  /* test max-width */
+  /* content = 100% = 80px = 4ch + border */
+  /* choose sizes that are smaller than content to see if how they take effect */
+  .raw-percent {
+    max-width: 50%;
+  }
+  .calc-percent {
+    max-width: calc(40px + 0%);
+  }
+  .no-percent {
+    max-width: 40px;
+  }
+</style>
+
+<!-- calculating min-content widths -->
+<div class='zero container'>
+  <div><div class="control">ppp pp</div></div>
+</div>
+<div class='zero container'>
+  <div><div class="raw-percent">ppp pp</div></div>
+</div>
+<div class='zero container'>
+  <div><div class="calc-percent">ppp pp</div></div>
+</div>
+<div class='zero container'>
+  <div><div class="no-percent">ppp pp</div></div>
+</div>
+
+<!-- calculating max-content widths -->
+<div class='infinite container'>
+  <div><div class="control">p p</div></div>
+</div>
+<div class='infinite container'>
+  <div><div class="raw-percent">p p</div></div>
+</div>
+<div class='infinite container'>
+  <div><div class="calc-percent">p p</div></div>
+</div>
+<div class='infinite container'>
+  <div><div class="no-percent">p p</div></div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-sizing/intrinsic-percent-non-replaced-003.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-sizing/intrinsic-percent-non-replaced-003.html
new file mode 100644
index 0000000..2dd247c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-sizing/intrinsic-percent-non-replaced-003.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<title>Percentages of width on non-replaced blocks are ignored for intrinsic sizing and resolved afterwards</title>
+<link rel="help" href="https://www.w3.org/TR/css-sizing-3/#intrinsic-contribution">
+<link rel="match" href="intrinsic-percent-non-replaced-002-ref.html">
+
+<style>
+  /* establish context */
+  .container {
+    clear: both;
+    padding: 10px;
+    color: blue;
+    font: 20px/1 Ahem;
+  }
+  .zero {
+    width: 0;
+  }
+  .infinite {
+    width: 400px; /* close enough */
+  }
+
+  /* visualize size contribution */
+  .container > div {
+    float: left;
+    border: solid orange 20px;
+    border-style: none solid;
+  }
+  .container > div > div {
+    border-right: solid 20px aqua;
+  }
+
+  /* test width */
+  /* content = 100% = 80px = 4ch + border */
+  /* choose sizes that are different than content to see if how they take effect */
+  .raw-percent {
+    width: 50%;
+  }
+  .calc-percent {
+    width: calc(40px + 0%);
+  }
+  .no-percent {
+    width: 40px;
+  }
+</style>
+
+<!-- calculating min-content widths -->
+<div class='zero container'>
+  <div><div class="control">ppp pp</div></div>
+</div>
+<div class='zero container'>
+  <div><div class="raw-percent">ppp pp</div></div>
+</div>
+<div class='zero container'>
+  <div><div class="calc-percent">ppp pp</div></div>
+</div>
+<div class='zero container'>
+  <div><div class="no-percent">ppp pp</div></div>
+</div>
+
+<!-- calculating max-content widths -->
+<div class='infinite container'>
+  <div><div class="control">p p</div></div>
+</div>
+<div class='infinite container'>
+  <div><div class="raw-percent">p p</div></div>
+</div>
+<div class='infinite container'>
+  <div><div class="calc-percent">p p</div></div>
+</div>
+<div class='infinite container'>
+  <div><div class="no-percent">p p</div></div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/gap.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/gap.html
new file mode 100644
index 0000000..e07d1cd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/gap.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>'*-gap' properties</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om/#dom-stylepropertymapreadonly-get">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om/#dom-stylepropertymap-set">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om/#reify-stylevalue">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/testhelper.js"></script>
+<script src="resources/testsuite.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+for (const prefix of ['column', 'row']) {
+  runPropertyTests(`${prefix}-gap`, [
+    { syntax: 'normal' },
+    {
+      syntax: '<length>',
+      specified: assert_is_equal_with_range_handling
+    },
+    {
+      syntax: '<percentage>',
+      specified: assert_is_equal_with_range_handling
+    },
+  ]);
+}
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/Element-classlist.html b/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/Element-classlist.html
index 2b376d4..5453da9 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/Element-classlist.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/Element-classlist.html
@@ -31,6 +31,13 @@
     }
     setClass(e, before);
 
+    var obs;
+    // If we have MutationObservers available,  do some checks to make
+    // sure attribute sets are happening at sane times.
+    if (self.MutationObserver) {
+      obs = new MutationObserver(() => {});
+      obs.observe(e, { attributes: true });
+    }
     if (shouldThrow) {
       assert_throws(expectedException, function() {
         var list = e.classList;
@@ -40,6 +47,21 @@
       var list = e.classList;
       var res = list[funcName].apply(list, args);
     }
+    if (obs) {
+      var mutationRecords = obs.takeRecords();
+      obs.disconnect();
+      if (shouldThrow) {
+        assert_equals(mutationRecords.length, 0,
+                      "There should have been no mutation");
+      } else if (funcName == "replace") {
+        assert_equals(mutationRecords.length == 1,
+                      expectedRes,
+                      "Should have a mutation exactly when replace() returns true");
+      } else {
+        // For other functions, would need to check when exactly
+        // mutations are supposed to happen.
+      }
+    }
     if (!shouldThrow) {
       assert_equals(res, expectedRes, "wrong return value");
     }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/navigation-timing.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/navigation-timing.https-expected.txt
new file mode 100644
index 0000000..ead8b008
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/navigation-timing.https-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+FAIL Service worker controlled navigation timing assert_unreached: unexpected rejection: assert_true: Expected workerStart <= fetchStart expected true got false Reached unreachable code
+FAIL Service worker controlled navigation timing network fallback assert_unreached: unexpected rejection: assert_true: Expected workerStart <= fetchStart expected true got false Reached unreachable code
+FAIL Service worker controlled navigation timing redirect assert_unreached: unexpected rejection: assert_true: Expected fetchStart <= requestStart expected true got false Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/navigation-timing.https.html b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/navigation-timing.https.html
new file mode 100644
index 0000000..ac3456e9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/navigation-timing.https.html
@@ -0,0 +1,111 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+
+<script>
+const timingEventOrder = [
+    'startTime',
+    'workerStart',
+    'fetchStart',
+    'requestStart',
+    'responseStart',
+    'responseEnd',
+];
+
+function verify(timing) {
+    for (let i = 0; i < timingEventOrder.length - 1; i++) {
+        assert_true(timing[timingEventOrder[i]] <= timing[timingEventOrder[i + 1]],
+                `Expected ${timingEventOrder[i]} <= ${timingEventOrder[i + 1]}`);
+    }
+}
+
+function navigate_in_frame(frame, url) {
+    frame.contentWindow.location = url;
+    return new Promise((resolve) => {
+        frame.addEventListener('load', () => {
+            const timing = frame.contentWindow.performance.getEntriesByType('navigation')[0];
+            resolve(timing);
+        });
+    });
+}
+
+const worker_url = 'resources/navigation-timing-worker.js';
+
+promise_test(t => {
+    const scope = 'resources/empty.html';
+    let frame;
+
+    return service_worker_unregister_and_register(t, worker_url, scope)
+      .then(r => {
+        return wait_for_state(t, r.installing, 'activated');
+      })
+      .then(() => with_iframe(scope))
+      .then(f => {
+        frame = f;
+        return navigate_in_frame(frame, 'resources/empty.html');
+      })
+      .then(timing => {
+        verify(timing);
+      })
+      .catch(unreached_rejection(t))
+      .then(() => {
+        if (frame)
+            frame.remove();
+        return service_worker_unregister(t, scope);
+      });
+}, 'Service worker controlled navigation timing');
+
+promise_test(t => {
+    const scope = 'resources/empty.html?network-fallback';
+    let frame;
+
+    return service_worker_unregister_and_register(t, worker_url, scope)
+      .then(r => {
+        return wait_for_state(t, r.installing, 'activated');
+      })
+      .then(() => with_iframe(scope))
+      .then(f => {
+        frame = f;
+        return navigate_in_frame(frame, 'resources/empty.html?network-fallback');
+      })
+      .then(timing => {
+        verify(timing);
+      })
+      .catch(unreached_rejection(t))
+      .then(() => {
+        if (frame)
+            frame.remove();
+        return service_worker_unregister(t, scope);
+      });
+}, 'Service worker controlled navigation timing network fallback');
+
+promise_test(t => {
+    const scope = 'resources/redirect.py?Redirect=empty.html';
+    let frame;
+
+    return service_worker_unregister_and_register(t, worker_url, scope)
+      .then(r => {
+        return wait_for_state(t, r.installing, 'activated');
+      })
+      .then(() => with_iframe(scope))
+      .then(f => {
+        frame = f;
+        return navigate_in_frame(frame, 'resources/redirect.py?Redirect=empty.html');
+      })
+      .then(timing => {
+        verify(timing);
+        // Additional checks for redirected navigation.
+        assert_true(timing.redirectStart <= timing.redirectEnd,
+            'Expected redirectStart <= redirectEnd');
+        assert_true(timing.redirectEnd <= timing.fetchStart,
+            'Expected redirectEnd <= fetchStart');
+      })
+      .catch(unreached_rejection(t))
+      .then(() => {
+        if (frame)
+            frame.remove();
+        return service_worker_unregister(t, scope);
+      });
+}, 'Service worker controlled navigation timing redirect');
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/navigation-timing-worker.js b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/navigation-timing-worker.js
new file mode 100644
index 0000000..8539b40
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/navigation-timing-worker.js
@@ -0,0 +1,15 @@
+self.addEventListener('fetch', (event) => {
+    const url = event.request.url;
+
+    // Network fallback.
+    if (url.indexOf('network-fallback') >= 0) {
+        return;
+    }
+
+    // Don't intercept redirect.
+    if (url.indexOf('redirect.py') >= 0) {
+        return;
+    }
+
+    event.respondWith(fetch(url));
+});
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webaudio/idlharness.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webaudio/idlharness.https-expected.txt
index 85f45c8..18ece05 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webaudio/idlharness.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webaudio/idlharness.https-expected.txt
@@ -1242,9 +1242,9 @@
 PASS AudioWorkletNode interface: existence and properties of interface prototype object
 PASS AudioWorkletNode interface: existence and properties of interface prototype object's "constructor" property
 PASS AudioWorkletNode interface: existence and properties of interface prototype object's @@unscopables property
-FAIL AudioWorkletNode interface: attribute parameters assert_true: The prototype object must have a property "parameters" expected true got false
+PASS AudioWorkletNode interface: attribute parameters
 PASS Unscopable handled correctly for parameters property on AudioWorkletNode
-FAIL AudioWorkletNode interface: attribute port assert_true: The prototype object must have a property "port" expected true got false
+PASS AudioWorkletNode interface: attribute port
 PASS Unscopable handled correctly for port property on AudioWorkletNode
 FAIL AudioWorkletNode interface: attribute processorState assert_true: The prototype object must have a property "processorState" expected true got false
 PASS Unscopable handled correctly for processorState property on AudioWorkletNode
@@ -1252,8 +1252,8 @@
 PASS Unscopable handled correctly for onprocessorstatechange property on AudioWorkletNode
 PASS AudioWorkletNode must be primary interface of [object AudioWorkletNode]
 PASS Stringification of [object AudioWorkletNode]
-FAIL AudioWorkletNode interface: [object AudioWorkletNode] must inherit property "parameters" with the proper type assert_inherits: property "parameters" found on object expected in prototype chain
-FAIL AudioWorkletNode interface: [object AudioWorkletNode] must inherit property "port" with the proper type assert_inherits: property "port" found on object expected in prototype chain
+PASS AudioWorkletNode interface: [object AudioWorkletNode] must inherit property "parameters" with the proper type
+PASS AudioWorkletNode interface: [object AudioWorkletNode] must inherit property "port" with the proper type
 FAIL AudioWorkletNode interface: [object AudioWorkletNode] must inherit property "processorState" with the proper type assert_inherits: property "processorState" not found in prototype chain
 FAIL AudioWorkletNode interface: [object AudioWorkletNode] must inherit property "onprocessorstatechange" with the proper type assert_inherits: property "onprocessorstatechange" not found in prototype chain
 PASS AudioNode interface: [object AudioWorkletNode] must inherit property "connect(AudioNode, unsigned long, unsigned long)" with the proper type
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-features=NetworkService/external/wpt/service-workers/service-worker/navigation-timing.https-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-features=NetworkService/external/wpt/service-workers/service-worker/navigation-timing.https-expected.txt
new file mode 100644
index 0000000..cedf448
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-features=NetworkService/external/wpt/service-workers/service-worker/navigation-timing.https-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+FAIL Service worker controlled navigation timing assert_unreached: unexpected rejection: assert_true: Expected workerStart <= fetchStart expected true got false Reached unreachable code
+PASS Service worker controlled navigation timing network fallback
+FAIL Service worker controlled navigation timing redirect assert_unreached: unexpected rejection: assert_true: Expected fetchStart <= requestStart expected true got false Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/input/dispatchMouseEvent-emulated-viewport-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/input/dispatchMouseEvent-emulated-viewport-expected.txt
new file mode 100644
index 0000000..230b5021
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/input/dispatchMouseEvent-emulated-viewport-expected.txt
@@ -0,0 +1,3 @@
+Tests Input.dispatchMouseEvent method after emulating a viewport.
+Clicked
+
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/input/dispatchMouseEvent-emulated-viewport.js b/third_party/WebKit/LayoutTests/inspector-protocol/input/dispatchMouseEvent-emulated-viewport.js
new file mode 100644
index 0000000..59afb16
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/input/dispatchMouseEvent-emulated-viewport.js
@@ -0,0 +1,38 @@
+(async function(testRunner) {
+  var {page, session, dp} = await testRunner.startBlank(`Tests Input.dispatchMouseEvent method after emulating a viewport.`);
+
+  await session.evaluate(`
+    window.result = 'Not Clicked';
+    window.button = document.createElement('button');
+    document.body.appendChild(button);
+    button.style.position = 'absolute';
+    button.style.top = '50px';
+    button.style.left = '50px';
+    button.style.width = '10px';
+    button.style.height = '10px';
+    button.onmousedown = () => window.result = 'Clicked';
+  `);
+
+  function dumpError(message) {
+    if (message.error)
+      testRunner.log('Error: ' + message.error.message);
+  }
+
+  dumpError(await dp.Emulation.setDeviceMetricsOverride({
+    deviceScaleFactor: 5,
+    width: 400,
+    height: 300,
+    mobile: true
+  }));
+
+  dumpError(await dp.Input.dispatchMouseEvent({
+    type: 'mousePressed',
+    button: 'left',
+    clickCount: 1,
+    x: 55,
+    y: 55
+  }));
+
+  testRunner.log(await session.evaluate(`window.result`));
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-radius-mask-canvas-all-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-radius-mask-canvas-all-expected.png
index 734b6ad4..87ddac7 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-radius-mask-canvas-all-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-radius-mask-canvas-all-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-radius-mask-canvas-with-mask-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-radius-mask-canvas-with-mask-expected.png
index 1e03bede..c384a04 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-radius-mask-canvas-with-mask-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-radius-mask-canvas-with-mask-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-radius-mask-canvas-with-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-radius-mask-canvas-with-shadow-expected.png
index 19a4730..111ae3a 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-radius-mask-canvas-with-shadow-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/borders/border-radius-mask-canvas-with-shadow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/prefer_compositing_to_lcd_text/compositing/overflow/border-radius-composited-subframe-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/prefer_compositing_to_lcd_text/compositing/overflow/border-radius-composited-subframe-expected.png
deleted file mode 100644
index 9dc62ee..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/prefer_compositing_to_lcd_text/compositing/overflow/border-radius-composited-subframe-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/overflow/border-radius-composited-subframe-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/overflow/border-radius-composited-subframe-expected.png
index 9dc62ee..d0ef886 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/overflow/border-radius-composited-subframe-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/overflow/border-radius-composited-subframe-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-radius-mask-canvas-all-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-radius-mask-canvas-all-expected.png
index ba6a4c6..8a816c15 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-radius-mask-canvas-all-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-radius-mask-canvas-all-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-radius-mask-canvas-with-mask-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-radius-mask-canvas-with-mask-expected.png
index a6854185..59ed12a 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-radius-mask-canvas-with-mask-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-radius-mask-canvas-with-mask-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-radius-mask-canvas-with-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-radius-mask-canvas-with-shadow-expected.png
index c0a8a18..5acfa55 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-radius-mask-canvas-with-shadow-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/borders/border-radius-mask-canvas-with-shadow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/overflow/border-radius-composited-subframe-expected.png b/third_party/WebKit/LayoutTests/platform/win/compositing/overflow/border-radius-composited-subframe-expected.png
index 134c61e..8540490 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/overflow/border-radius-composited-subframe-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/overflow/border-radius-composited-subframe-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-radius-mask-canvas-all-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-radius-mask-canvas-all-expected.png
index b90577b..59019a0 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-radius-mask-canvas-all-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-radius-mask-canvas-all-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-radius-mask-canvas-with-mask-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-radius-mask-canvas-with-mask-expected.png
index 09b8664..6fe4a29 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-radius-mask-canvas-with-mask-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-radius-mask-canvas-with-mask-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-radius-mask-canvas-with-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-radius-mask-canvas-with-shadow-expected.png
index 4f5592d..91538834 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-radius-mask-canvas-with-shadow-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/borders/border-radius-mask-canvas-with-shadow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 6e36eaf0..bdf4fb7 100644
--- a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -194,7 +194,11 @@
     method constructor
 interface AudioWorkletNode : AudioNode
     attribute @@toStringTag
+    getter onprocessorerror
+    getter parameters
+    getter port
     method constructor
+    setter onprocessorerror
 interface BarProp
     attribute @@toStringTag
     getter visible
diff --git a/third_party/WebKit/LayoutTests/webaudio/audio-worklet/audio-worklet-node-idl-expected.txt b/third_party/WebKit/LayoutTests/webaudio/audio-worklet/audio-worklet-node-idl-expected.txt
index 966ff28..2f9356ce 100644
--- a/third_party/WebKit/LayoutTests/webaudio/audio-worklet/audio-worklet-node-idl-expected.txt
+++ b/third_party/WebKit/LayoutTests/webaudio/audio-worklet/audio-worklet-node-idl-expected.txt
@@ -11,7 +11,7 @@
 FAIL AudioWorkletNode interface: existence and properties of interface prototype object Cannot read property 'has_extended_attribute' of undefined
 PASS AudioWorkletNode interface: existence and properties of interface prototype object's "constructor" property
 PASS AudioWorkletNode interface: existence and properties of interface prototype object's @@unscopables property
-FAIL AudioWorkletNode interface: attribute parameters assert_true: The prototype object must have a property "parameters" expected true got false
+PASS AudioWorkletNode interface: attribute parameters
 PASS Unscopable handled correctly for parameters property on AudioWorkletNode
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index 63aac22..69338a6 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -418,7 +418,11 @@
     method constructor
 interface AudioWorkletNode : AudioNode
     attribute @@toStringTag
+    getter onprocessorerror
+    getter parameters
+    getter port
     method constructor
+    setter onprocessorerror
 interface AuthenticatorAssertionResponse : AuthenticatorResponse
     attribute @@toStringTag
     getter authenticatorData
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.json5 b/third_party/WebKit/Source/core/css/CSSProperties.json5
index 8545a6a..be18958 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.json5
+++ b/third_party/WebKit/Source/core/css/CSSProperties.json5
@@ -3128,6 +3128,8 @@
       default_value: "GapLength()",
       type_name: "GapLength",
       converter: "ConvertGapLength",
+      keywords: ["normal"],
+      typedom_types: ["Keyword", "Length", "Percentage"],
     },
     {
       name: "row-gap",
@@ -3139,6 +3141,8 @@
       default_value: "GapLength()",
       type_name: "GapLength",
       converter: "ConvertGapLength",
+      keywords: ["normal"],
+      typedom_types: ["Keyword", "Length", "Percentage"],
     },
     {
       name: "column-rule-color",
diff --git a/third_party/WebKit/Source/core/editing/LocalCaretRectTest.cpp b/third_party/WebKit/Source/core/editing/LocalCaretRectTest.cpp
index cd40080b..984c267 100644
--- a/third_party/WebKit/Source/core/editing/LocalCaretRectTest.cpp
+++ b/third_party/WebKit/Source/core/editing/LocalCaretRectTest.cpp
@@ -695,8 +695,7 @@
 
 TEST_P(ParameterizedLocalCaretRectTest, AfterLineBreak) {
   LoadAhem();
-  SetBodyContent(
-      "<div style='font: 10px/10px Ahem;' contenteditable>foo<br><br></div>");
+  SetBodyContent("<div style='font: 10px/10px Ahem;'>foo<br><br></div>");
   const Node* div = GetDocument().body()->firstChild();
   const Node* foo = div->firstChild();
   const Node* first_br = foo->nextSibling();
@@ -704,14 +703,62 @@
   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), LayoutRect(30, 0, 1, 10)),
             LocalCaretRectOfPosition(PositionWithAffinity(
                 Position::AfterNode(*foo), TextAffinity::kDownstream)));
-  // TODO(yoichio): Following should return valid rect: crbug.com/812535.
-  EXPECT_EQ(LocalCaretRect(first_br->GetLayoutObject(), LayoutRect(0, 0, 0, 0)),
+  // TODO(yoichio): Legacy should return valid rect: crbug.com/812535.
+  EXPECT_EQ(LayoutNGEnabled() ? LocalCaretRect(second_br->GetLayoutObject(),
+                                               LayoutRect(0, 10, 1, 10))
+                              : LocalCaretRect(first_br->GetLayoutObject(),
+                                               LayoutRect(0, 0, 0, 0)),
             LocalCaretRectOfPosition(PositionWithAffinity(
                 Position::AfterNode(*first_br), TextAffinity::kDownstream)));
+  EXPECT_EQ(LocalCaretRect(second_br->GetLayoutObject(),
+                           LayoutNGEnabled() ? LayoutRect(0, 10, 1, 10)
+                                             : LayoutRect(0, 0, 0, 0)),
+            LocalCaretRectOfPosition(PositionWithAffinity(
+                Position::AfterNode(*second_br), TextAffinity::kDownstream)));
+}
+
+TEST_P(ParameterizedLocalCaretRectTest, AfterLineBreakInPre) {
+  LoadAhem();
+  SetBodyContent("<pre style='font: 10px/10px Ahem;'>foo\n\n</pre>");
+  const Node* pre = GetDocument().body()->firstChild();
+  const Node* foo = pre->firstChild();
+  EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), LayoutRect(30, 0, 1, 10)),
+            LocalCaretRectOfPosition(PositionWithAffinity(
+                Position(foo, 3), TextAffinity::kDownstream)));
+  EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), LayoutRect(0, 10, 1, 10)),
+            LocalCaretRectOfPosition(PositionWithAffinity(
+                Position(foo, 4), TextAffinity::kDownstream)));
+  // TODO(yoichio): Legacy should return valid rect: crbug.com/812535.
+  EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(),
+                           LayoutNGEnabled() ? LayoutRect(0, 10, 1, 10)
+                                             : LayoutRect(0, 0, 0, 0)),
+            LocalCaretRectOfPosition(PositionWithAffinity(
+                Position(foo, 5), TextAffinity::kDownstream)));
+}
+
+TEST_P(ParameterizedLocalCaretRectTest, AfterLineBreakInPre2) {
+  LoadAhem();
+  // This test case simulates the rendering of the inner editor of
+  // <textarea>foo\n</textarea> without using text control element.
+  SetBodyContent("<pre style='font: 10px/10px Ahem;'>foo\n<br></pre>");
+  const Node* pre = GetDocument().body()->firstChild();
+  const Node* foo = pre->firstChild();
+  const Node* br = foo->nextSibling();
+  EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), LayoutRect(30, 0, 1, 10)),
+            LocalCaretRectOfPosition(PositionWithAffinity(
+                Position(foo, 3), TextAffinity::kDownstream)));
+  // TODO(yoichio): Legacy should return valid rect: crbug.com/812535.
   EXPECT_EQ(
-      LocalCaretRect(second_br->GetLayoutObject(), LayoutRect(0, 0, 0, 0)),
-      LocalCaretRectOfPosition(PositionWithAffinity(
-          Position::AfterNode(*second_br), TextAffinity::kDownstream)));
+      LayoutNGEnabled()
+          ? LocalCaretRect(br->GetLayoutObject(), LayoutRect(0, 10, 1, 10))
+          : LocalCaretRect(foo->GetLayoutObject(), LayoutRect(0, 0, 0, 0)),
+      LocalCaretRectOfPosition(
+          PositionWithAffinity(Position(foo, 4), TextAffinity::kDownstream)));
+  EXPECT_EQ(LocalCaretRect(br->GetLayoutObject(), LayoutNGEnabled()
+                                                      ? LayoutRect(0, 10, 1, 10)
+                                                      : LayoutRect(0, 0, 0, 0)),
+            LocalCaretRectOfPosition(PositionWithAffinity(
+                Position::AfterNode(*br), TextAffinity::kDownstream)));
 }
 
 TEST_P(ParameterizedLocalCaretRectTest, AfterLineBreakTextArea) {
@@ -747,14 +794,11 @@
   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), LayoutRect(30, 0, 1, 10)),
             LocalCaretRectOfPosition(PositionWithAffinity(
                 Position(foo, 3), TextAffinity::kDownstream)));
+  EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), LayoutRect(30, 0, 1, 10)),
+            LocalCaretRectOfPosition(PositionWithAffinity(
+                Position::AfterNode(*foo), TextAffinity::kDownstream)));
   // TODO(yoichio): Following should return valid rect: crbug.com/812535.
   EXPECT_EQ(
-      LayoutNGEnabled()
-          ? LocalCaretRect(foo->GetLayoutObject(), LayoutRect(0, 0, 0, 0))
-          : LocalCaretRect(foo->GetLayoutObject(), LayoutRect(30, 0, 1, 10)),
-      LocalCaretRectOfPosition(PositionWithAffinity(
-          Position::AfterNode(*foo), TextAffinity::kDownstream)));
-  EXPECT_EQ(
       LocalCaretRect(first_span->GetLayoutObject(), LayoutRect(0, 0, 0, 0)),
       LocalCaretRectOfPosition(PositionWithAffinity(
           Position(first_span, PositionAnchorType::kAfterChildren),
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTest.cpp b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTest.cpp
index 89b1622..f62cf2f 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTest.cpp
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTest.cpp
@@ -1067,6 +1067,24 @@
   EXPECT_EQ("[b]", IteratePartial<DOMTree>(start, end));
 }
 
+TEST_P(ParameterizedTextIteratorTest, FloatLeft) {
+  SetBodyContent("abc<span style='float:left'>DEF</span>ghi");
+  EXPECT_EQ("[abc][DEF][ghi]", Iterate<DOMTree>())
+      << "float doesn't affect text iteration";
+}
+
+TEST_P(ParameterizedTextIteratorTest, FloatRight) {
+  SetBodyContent("abc<span style='float:right'>DEF</span>ghi");
+  EXPECT_EQ("[abc][DEF][ghi]", Iterate<DOMTree>())
+      << "float doesn't affect text iteration";
+}
+
+TEST_P(ParameterizedTextIteratorTest, InlineBlock) {
+  SetBodyContent("abc<span style='display:inline-block'>DEF<br>GHI</span>jkl");
+  EXPECT_EQ("[abc][DEF][\n][GHI][jkl]", Iterate<DOMTree>())
+      << "inline-block doesn't insert newline around itself.";
+}
+
 TEST_P(ParameterizedTextIteratorTest, NoZWSForSpaceAfterNoWrapSpace) {
   SetBodyContent("<span style='white-space: nowrap'>foo </span> bar");
   EXPECT_EQ("[foo ][bar]", Iterate<DOMTree>());
diff --git a/third_party/WebKit/Source/core/layout/CounterNode.cpp b/third_party/WebKit/Source/core/layout/CounterNode.cpp
index b3d2230..8c582be 100644
--- a/third_party/WebKit/Source/core/layout/CounterNode.cpp
+++ b/third_party/WebKit/Source/core/layout/CounterNode.cpp
@@ -368,19 +368,14 @@
 
   scoped_refptr<CounterNode> cur_node = first_node;
   scoped_refptr<CounterNode> old_parent = first_node->Parent();
-  while (cur_node && !cur_node->ActsAsReset()) {
-    scoped_refptr<CounterNode> next = cur_node->NextSibling();
-    old_parent->RemoveChild(cur_node.get());
-    new_parent.InsertAfter(cur_node.get(), new_parent.LastChild(), identifier);
-    cur_node = next;
-  }
-
-  // We assume that a reset node cannot have a non-reset node as its next
-  // sibling, but we're not sure this is always true, so we add a DCHECK to be
-  // safe.
   while (cur_node) {
-    DCHECK(cur_node->ActsAsReset());
-    cur_node = cur_node->NextSibling();
+    scoped_refptr<CounterNode> next = cur_node->NextSibling();
+    if (!cur_node->ActsAsReset()) {
+      old_parent->RemoveChild(cur_node.get());
+      new_parent.InsertAfter(cur_node.get(), new_parent.LastChild(),
+                             identifier);
+    }
+    cur_node = next;
   }
 }
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.cpp b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
index c4f0333e2..45bf5a9 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
@@ -95,6 +95,7 @@
 #include "platform/geometry/TransformState.h"
 #include "platform/graphics/GraphicsLayer.h"
 #include "platform/graphics/TouchAction.h"
+#include "platform/graphics/paint/GeometryMapper.h"
 #include "platform/graphics/paint/PropertyTreeState.h"
 #include "platform/instrumentation/tracing/TracedValue.h"
 #include "platform/runtime_enabled_features.h"
@@ -1550,10 +1551,40 @@
   return LayoutRect();
 }
 
+bool LayoutObject::MapToVisualRectInAncestorSpaceInternalFastPath(
+    const LayoutBoxModelObject* ancestor,
+    LayoutRect& rect,
+    VisualRectFlags visual_rect_flags) const {
+  if (!(visual_rect_flags & kUseGeometryMapper) ||
+      !RuntimeEnabledFeatures::SlimmingPaintV175Enabled() ||
+      (visual_rect_flags & kEdgeInclusive) ||
+      !FirstFragment().HasLocalBorderBoxProperties() || !ancestor ||
+      !ancestor->FirstFragment().HasLocalBorderBoxProperties()) {
+    return false;
+  }
+
+  if (ancestor == this)
+    return true;
+
+  rect.MoveBy(FirstFragment().PaintOffset());
+  FloatClipRect clip_rect((FloatRect(rect)));
+  GeometryMapper::LocalToAncestorVisualRect(
+      FirstFragment().LocalBorderBoxProperties(),
+      ancestor->FirstFragment().ContentsProperties(), clip_rect);
+  rect = LayoutRect(clip_rect.Rect());
+  rect.MoveBy(-ancestor->FirstFragment().PaintOffset());
+
+  return true;
+}
+
 bool LayoutObject::MapToVisualRectInAncestorSpace(
     const LayoutBoxModelObject* ancestor,
     LayoutRect& rect,
     VisualRectFlags visual_rect_flags) const {
+  if (MapToVisualRectInAncestorSpaceInternalFastPath(ancestor, rect,
+                                                     visual_rect_flags))
+    return !rect.IsEmpty();
+
   TransformState transform_state(TransformState::kApplyTransformDirection,
                                  FloatQuad(FloatRect(rect)));
   bool retval = MapToVisualRectInAncestorSpaceInternal(
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.h b/third_party/WebKit/Source/core/layout/LayoutObject.h
index 5139ed0..db56846 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.h
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.h
@@ -80,7 +80,11 @@
 struct PaintInvalidatorContext;
 struct WebScrollIntoViewParams;
 
-enum VisualRectFlags { kDefaultVisualRectFlags = 0, kEdgeInclusive = 1 };
+enum VisualRectFlags {
+  kDefaultVisualRectFlags = 0,
+  kEdgeInclusive = 1,
+  kUseGeometryMapper = 2,  // Use the GeometryMapper fast-path, if possible.
+};
 
 enum CursorDirective { kSetCursorBasedOnStyle, kSetCursor, kDoNotSetCursor };
 
@@ -2034,7 +2038,13 @@
   // changes at all).
   virtual bool AnonymousHasStylePropagationOverride() { return false; }
 
- protected:
+  // A fast path for MapToVisualRectInAncestorSpace for when GeometryMapper
+  // can be used.
+  bool MapToVisualRectInAncestorSpaceInternalFastPath(
+      const LayoutBoxModelObject* ancestor,
+      LayoutRect&,
+      VisualRectFlags) const;
+
   // This function is called before calling the destructor so that some clean-up
   // can happen regardless of whether they call a virtual function or not. As a
   // rule of thumb, this function should be preferred to the destructor. See
diff --git a/third_party/WebKit/Source/core/layout/LayoutText.cpp b/third_party/WebKit/Source/core/layout/LayoutText.cpp
index 7da03cf..2f37ace 100644
--- a/third_party/WebKit/Source/core/layout/LayoutText.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutText.cpp
@@ -1957,9 +1957,12 @@
   if (position.IsNull() || position.AnchorNode() != GetNode())
     return WTF::nullopt;
   if (GetNode()->IsTextNode()) {
-    // TODO(xiaochengh): Consider Before/AfterAnchor.
-    DCHECK(position.IsOffsetInAnchor()) << position;
+    if (position.IsBeforeAnchor())
+      return 0;
     // TODO(layout-dev): Support offset change due to text-transform.
+    if (position.IsAfterAnchor())
+      return TextLength();
+    DCHECK(position.IsOffsetInAnchor()) << position;
     DCHECK_LE(position.OffsetInContainerNode(), static_cast<int>(TextLength()))
         << position;
     return position.OffsetInContainerNode();
diff --git a/third_party/WebKit/Source/core/layout/LayoutTextFragment.cpp b/third_party/WebKit/Source/core/layout/LayoutTextFragment.cpp
index 360770e9..78dc906 100644
--- a/third_party/WebKit/Source/core/layout/LayoutTextFragment.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutTextFragment.cpp
@@ -187,10 +187,17 @@
     const Position& position) const {
   if (position.IsNull() || position.AnchorNode() != AssociatedTextNode())
     return WTF::nullopt;
-  // TODO(xiaochengh): Consider Before/AfterAnchor.
-  DCHECK(position.IsOffsetInAnchor()) << position;
-  // TODO(layout-dev): Support offset change due to text-transform.
-  unsigned dom_offset = position.OffsetInContainerNode();
+  unsigned dom_offset;
+  if (position.IsBeforeAnchor()) {
+    dom_offset = 0;
+  } else if (position.IsAfterAnchor()) {
+    // TODO(layout-dev): Support offset change due to text-transform.
+    dom_offset = Start() + FragmentLength();
+  } else {
+    DCHECK(position.IsOffsetInAnchor()) << position;
+    // TODO(layout-dev): Support offset change due to text-transform.
+    dom_offset = position.OffsetInContainerNode();
+  }
   if (dom_offset < Start() || dom_offset > Start() + FragmentLength())
     return WTF::nullopt;
   return dom_offset - Start();
diff --git a/third_party/WebKit/Source/core/layout/LayoutView.cpp b/third_party/WebKit/Source/core/layout/LayoutView.cpp
index d4c5da6..31beecbc 100644
--- a/third_party/WebKit/Source/core/layout/LayoutView.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutView.cpp
@@ -492,6 +492,10 @@
     LayoutRect& rect,
     MapCoordinatesFlags mode,
     VisualRectFlags visual_rect_flags) const {
+  if (MapToVisualRectInAncestorSpaceInternalFastPath(ancestor, rect,
+                                                     visual_rect_flags))
+    return !rect.IsEmpty();
+
   TransformState transform_state(TransformState::kApplyTransformDirection,
                                  FloatQuad(FloatRect(rect)));
   bool retval = MapToVisualRectInAncestorSpaceInternal(
diff --git a/third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp b/third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp
index ff1a1260..bb92f989 100644
--- a/third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp
+++ b/third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp
@@ -57,12 +57,10 @@
     FloatClipRect geometry_mapper_rect((FloatRect(local_rect)));
     const FragmentData& fragment_data = object.FirstFragment();
     if (fragment_data.HasLocalBorderBoxProperties()) {
-      geometry_mapper_rect.MoveBy(FloatPoint(fragment_data.PaintOffset()));
-      GeometryMapper::LocalToAncestorVisualRect(
-          fragment_data.LocalBorderBoxProperties(),
-          ancestor.FirstFragment().ContentsProperties(), geometry_mapper_rect);
-      geometry_mapper_rect.MoveBy(
-          -FloatPoint(ancestor.FirstFragment().PaintOffset()));
+      LayoutRect local_rect_copy(local_rect);
+      object.MapToVisualRectInAncestorSpace(&ancestor, local_rect_copy,
+                                            kUseGeometryMapper);
+      geometry_mapper_rect.SetRect(FloatRect(local_rect_copy));
     }
 
     if (expected_visual_rect_in_ancestor.IsEmpty()) {
diff --git a/third_party/WebKit/Source/core/layout/custom/CustomLayoutFragment.h b/third_party/WebKit/Source/core/layout/custom/CustomLayoutFragment.h
index 89d37437..db779b15 100644
--- a/third_party/WebKit/Source/core/layout/custom/CustomLayoutFragment.h
+++ b/third_party/WebKit/Source/core/layout/custom/CustomLayoutFragment.h
@@ -37,6 +37,12 @@
   double inlineSize() const { return inline_size_; }
   double blockSize() const { return block_size_; }
 
+  double inlineOffset() const { return inline_offset_; }
+  double blockOffset() const { return block_offset_; }
+
+  void setInlineOffset(double inline_offset) { inline_offset_ = inline_offset; }
+  void setBlockOffset(double block_offset) { block_offset_ = block_offset; }
+
   LayoutBox* GetLayoutBox() const;
   bool IsValid() const;
 
@@ -59,6 +65,10 @@
   // The inline and block size on this object should never change.
   const double inline_size_;
   const double block_size_;
+
+  // The offset is relative to our parent, and in the parent's writing mode.
+  double inline_offset_ = 0;
+  double block_offset_ = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/custom/CustomLayoutFragmentRequest.cpp b/third_party/WebKit/Source/core/layout/custom/CustomLayoutFragmentRequest.cpp
index 8ef366e..c9b020a 100644
--- a/third_party/WebKit/Source/core/layout/custom/CustomLayoutFragmentRequest.cpp
+++ b/third_party/WebKit/Source/core/layout/custom/CustomLayoutFragmentRequest.cpp
@@ -43,12 +43,12 @@
   if (options_.hasFixedInlineSize()) {
     if (is_parallel_writing_mode) {
       box->SetOverrideLogicalContentWidth(
-          (LayoutUnit(options_.fixedInlineSize()) -
+          (LayoutUnit::FromDoubleRound(options_.fixedInlineSize()) -
            box->BorderAndPaddingLogicalWidth())
               .ClampNegativeToZero());
     } else {
       box->SetOverrideLogicalContentHeight(
-          (LayoutUnit(options_.fixedInlineSize()) -
+          (LayoutUnit::FromDoubleRound(options_.fixedInlineSize()) -
            box->BorderAndPaddingLogicalHeight())
               .ClampNegativeToZero());
     }
@@ -57,12 +57,12 @@
   if (options_.hasFixedBlockSize()) {
     if (is_parallel_writing_mode) {
       box->SetOverrideLogicalContentHeight(
-          (LayoutUnit(options_.fixedBlockSize()) -
+          (LayoutUnit::FromDoubleRound(options_.fixedBlockSize()) -
            box->BorderAndPaddingLogicalHeight())
               .ClampNegativeToZero());
     } else {
       box->SetOverrideLogicalContentWidth(
-          (LayoutUnit(options_.fixedBlockSize()) -
+          (LayoutUnit::FromDoubleRound(options_.fixedBlockSize()) -
            box->BorderAndPaddingLogicalWidth())
               .ClampNegativeToZero());
     }
diff --git a/third_party/WebKit/Source/core/layout/custom/LayoutCustom.cpp b/third_party/WebKit/Source/core/layout/custom/LayoutCustom.cpp
index 4880e48..c3ae6f5 100644
--- a/third_party/WebKit/Source/core/layout/custom/LayoutCustom.cpp
+++ b/third_party/WebKit/Source/core/layout/custom/LayoutCustom.cpp
@@ -170,6 +170,25 @@
       // TODO(ikilpatrick): At this stage we may need to perform a re-layout on
       // the given child. (The LayoutFragment may have been produced from a
       // different LayoutFragmentRequest).
+
+      // Update the position of the child.
+      bool is_parallel_writing_mode = IsParallelWritingMode(
+          StyleRef().GetWritingMode(), child->StyleRef().GetWritingMode());
+      LayoutUnit inline_size = is_parallel_writing_mode
+                                   ? child->LogicalWidth()
+                                   : child->LogicalHeight();
+
+      LayoutUnit inline_offset =
+          LayoutUnit::FromDoubleRound(fragment->inlineOffset());
+      LayoutUnit block_offset =
+          LayoutUnit::FromDoubleRound(fragment->blockOffset());
+      LayoutUnit logical_left =
+          StyleRef().IsLeftToRightDirection()
+              ? inline_offset
+              : LogicalWidth() - inline_size - inline_offset;
+      child->SetLocationAndUpdateOverflowControlsIfNeeded(
+          IsHorizontalWritingMode() ? LayoutPoint(logical_left, block_offset)
+                                    : LayoutPoint(block_offset, logical_left));
     }
 
     // Currently we only support
@@ -182,7 +201,8 @@
       return false;
     }
 
-    SetLogicalHeight(LayoutUnit(fragment_result_options.autoBlockSize()));
+    SetLogicalHeight(
+        LayoutUnit::FromDoubleRound(fragment_result_options.autoBlockSize()));
 
     LayoutUnit old_client_after_edge = ClientLogicalBottom();
     UpdateLogicalHeight();
diff --git a/third_party/WebKit/Source/core/layout/custom/LayoutFragment.idl b/third_party/WebKit/Source/core/layout/custom/LayoutFragment.idl
index 0517ac1..997f257 100644
--- a/third_party/WebKit/Source/core/layout/custom/LayoutFragment.idl
+++ b/third_party/WebKit/Source/core/layout/custom/LayoutFragment.idl
@@ -13,8 +13,8 @@
   readonly attribute double inlineSize;
   readonly attribute double blockSize;
 
-  // attribute double inlineOffset;
-  // attribute double blockOffset;
+  attribute double inlineOffset;
+  attribute double blockOffset;
 
   // readonly attribute any data;
 };
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping.cc
index 7c4598f..dfb3bc33 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping.cc
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping.cc
@@ -33,8 +33,14 @@
 
 std::pair<const Node&, unsigned> ToNodeOffsetPair(const Position& position) {
   DCHECK(NGOffsetMapping::AcceptsPosition(position)) << position;
-  if (position.AnchorNode()->IsTextNode())
-    return {*position.AnchorNode(), position.OffsetInContainerNode()};
+  if (position.AnchorNode()->IsTextNode()) {
+    if (position.IsOffsetInAnchor())
+      return {*position.AnchorNode(), position.OffsetInContainerNode()};
+    if (position.IsBeforeAnchor())
+      return {*position.AnchorNode(), 0};
+    DCHECK(position.IsAfterAnchor());
+    return {*position.AnchorNode(), ToText(position.AnchorNode())->length()};
+  }
   if (position.IsBeforeAnchor())
     return {*position.AnchorNode(), 0};
   return {*position.AnchorNode(), 1};
@@ -127,7 +133,10 @@
   if (position.IsNull())
     return false;
   if (position.AnchorNode()->IsTextNode()) {
-    return position.IsOffsetInAnchor();
+    // Position constructor should have rejected other anchor types.
+    DCHECK(position.IsOffsetInAnchor() || position.IsBeforeAnchor() ||
+           position.IsAfterAnchor());
+    return true;
   }
   if (!position.IsBeforeAnchor() && !position.IsAfterAnchor())
     return false;
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping.h b/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping.h
index da692b5e..d7d00eae 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping.h
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping.h
@@ -109,7 +109,7 @@
 
   // NGOffsetMapping APIs only accept the following positions:
   // 1. Offset-in-anchor in a text node;
-  // 2. Before/After-anchor of an inline-level element.
+  // 2. Before/After-anchor of an inline-level node.
   static bool AcceptsPosition(const Position&);
 
   // Returns the mapping object of the inline formatting context laying out the
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.cpp b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
index 925bcf9..fe175ae 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.cpp
@@ -101,7 +101,7 @@
 
 struct SameSizeAsPaintLayer : DisplayItemClient {
   int bit_fields;
-  void* pointers[10];
+  void* pointers[11];
   LayoutUnit layout_units[4];
   IntSize size;
   Persistent<PaintLayerScrollableArea> scrollable_area;
@@ -109,11 +109,6 @@
     IntSize size;
     LayoutRect rect;
   } previous_paint_status;
-
-  struct {
-    void* pointers[10];
-    IntRect int_rects[2];
-  } ancestor_dependent_compositing_inputs;
 };
 
 static_assert(sizeof(PaintLayer) == sizeof(SameSizeAsPaintLayer),
@@ -990,15 +985,15 @@
   return nullptr;
 }
 
-LayoutPoint PaintLayer::ComputeOffsetFromTransformedAncestor() const {
+LayoutPoint PaintLayer::ComputeOffsetFromAncestor(
+    const PaintLayer& ancestor_layer) const {
   TransformState transform_state(TransformState::kApplyTransformDirection,
                                  FloatPoint());
-  const LayoutBoxModelObject& ancestor =
-      TransformAncestorOrRoot().GetLayoutObject();
-
-  GetLayoutObject().MapLocalToAncestor(&ancestor, transform_state, 0);
-  if (ancestor.UsesCompositedScrolling())
-    transform_state.Move(ToLayoutBox(ancestor).ScrolledContentOffset());
+  const LayoutBoxModelObject& ancestor_object =
+      ancestor_layer.GetLayoutObject();
+  GetLayoutObject().MapLocalToAncestor(&ancestor_object, transform_state, 0);
+  if (ancestor_object.UsesCompositedScrolling())
+    transform_state.Move(ToLayoutBox(ancestor_object).ScrolledContentOffset());
   transform_state.Flatten();
   return LayoutPoint(transform_state.LastPlanarPoint());
 }
@@ -1099,7 +1094,7 @@
 
 void PaintLayer::UpdateAncestorDependentCompositingInputs(
     const AncestorDependentCompositingInputs& compositing_inputs) {
-  ancestor_dependent_compositing_inputs_ = compositing_inputs;
+  EnsureAncestorDependentCompositingInputs() = compositing_inputs;
   needs_ancestor_dependent_compositing_inputs_update_ = false;
 }
 
diff --git a/third_party/WebKit/Source/core/paint/PaintLayer.h b/third_party/WebKit/Source/core/paint/PaintLayer.h
index 995862a65..e746292 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayer.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayer.h
@@ -752,7 +752,7 @@
   const AncestorDependentCompositingInputs&
   GetAncestorDependentCompositingInputs() const {
     DCHECK(!needs_ancestor_dependent_compositing_inputs_update_);
-    return ancestor_dependent_compositing_inputs_;
+    return EnsureAncestorDependentCompositingInputs();
   }
   const IntRect& ClippedAbsoluteBoundingBox() const {
     return GetAncestorDependentCompositingInputs()
@@ -851,7 +851,7 @@
 
   // Returned value does not include any composited scroll offset of
   // the transform ancestor.
-  LayoutPoint ComputeOffsetFromTransformedAncestor() const;
+  LayoutPoint ComputeOffsetFromAncestor(const PaintLayer& ancestor_layer) const;
 
   void DidUpdateScrollsOverflow();
 
@@ -1157,6 +1157,15 @@
 
   LayoutPoint LocationInternal() const;
 
+  AncestorDependentCompositingInputs& EnsureAncestorDependentCompositingInputs()
+      const {
+    if (!ancestor_dependent_compositing_inputs_) {
+      ancestor_dependent_compositing_inputs_ =
+          std::make_unique<AncestorDependentCompositingInputs>();
+    }
+    return *ancestor_dependent_compositing_inputs_;
+  }
+
   // Self-painting layer is an optimization where we avoid the heavy Layer
   // painting machinery for a Layer allocated only to handle the overflow clip
   // case.
@@ -1260,7 +1269,8 @@
   // The first ancestor having a non visible overflow.
   const PaintLayer* ancestor_overflow_layer_;
 
-  AncestorDependentCompositingInputs ancestor_dependent_compositing_inputs_;
+  mutable std::unique_ptr<AncestorDependentCompositingInputs>
+      ancestor_dependent_compositing_inputs_;
 
   Persistent<PaintLayerScrollableArea> scrollable_area_;
 
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
index 79fb0c3..ac44de0 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
@@ -1395,7 +1395,6 @@
               context.GetPaintController(), state, client,
               DisplayItem::PaintPhaseToDrawingType(PaintPhase::kClippingMask));
           ClipRect mask_rect = fragment.background_rect;
-          mask_rect.MoveBy(fragment.fragment_data->PaintOffset());
           FillMaskingFragment(context, mask_rect, client);
         });
     return;
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp b/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp
index c90cbf1e..45fbdf7 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerTest.cpp
@@ -1272,8 +1272,8 @@
       squashed->GetLayoutObject(), rect);
   EXPECT_EQ(LayoutRect(0, 0, 200, 200), rect);
 
-  EXPECT_EQ(LayoutPoint(0, 0),
-            squashed->ComputeOffsetFromTransformedAncestor());
+  EXPECT_EQ(LayoutPoint(0, 0), squashed->ComputeOffsetFromAncestor(
+                                   squashed->TransformAncestorOrRoot()));
 
   GetDocument().View()->LayoutViewportScrollableArea()->ScrollBy(
       ScrollOffset(0, 25), kUserScroll);
@@ -1287,8 +1287,8 @@
       squashed->GetLayoutObject(), rect);
   EXPECT_EQ(LayoutRect(0, 0, 200, 200), rect);
 
-  EXPECT_EQ(LayoutPoint(0, 0),
-            squashed->ComputeOffsetFromTransformedAncestor());
+  EXPECT_EQ(LayoutPoint(0, 0), squashed->ComputeOffsetFromAncestor(
+                                   squashed->TransformAncestorOrRoot()));
 }
 
 TEST_P(PaintLayerTest, HitTestWithIgnoreClipping) {
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
index 0729467..72a3cd8 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
@@ -469,7 +469,8 @@
       // column spans when finding their containing blocks.
       // TODO(crbug.com/780242): This can be avoided if we have fully correct
       // paint property tree states for floating objects and column spans.
-      object.IsLayoutBlock() && object.HasLayer() &&
+      (object.IsLayoutBlock() || object.IsLayoutReplaced()) &&
+      object.HasLayer() &&
       !ToLayoutBoxModelObject(object).Layer()->EnclosingPaginationLayer() &&
       object.GetCompositingState() == kPaintsIntoOwnBacking)
     return true;
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
index 7f80d98..b748351 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
@@ -89,17 +89,12 @@
 
 #define CHECK_VISUAL_RECT(expected, source_object, ancestor, slop_factor)      \
   do {                                                                         \
-    if ((source_object)->HasLayer() && (ancestor)->HasLayer()) {               \
-      LayoutRect source((source_object)->LocalVisualRect());                   \
-      source.MoveBy((source_object)->FirstFragment().PaintOffset());           \
-      auto contents_properties =                                               \
-          (ancestor)->FirstFragment().ContentsProperties();                    \
-      FloatClipRect actual_float_rect((FloatRect(source)));                    \
-      GeometryMapper::LocalToAncestorVisualRect(                               \
-          (source_object)->FirstFragment().LocalBorderBoxProperties(),         \
-          contents_properties, actual_float_rect);                             \
-      LayoutRect actual(actual_float_rect.Rect());                             \
-      actual.MoveBy(-(ancestor)->FirstFragment().PaintOffset());               \
+    if ((source_object)->HasLayer() && (ancestor)->HasLayer() &&               \
+        RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {                  \
+      LayoutRect actual((source_object)->LocalVisualRect());                   \
+      (source_object)                                                          \
+          ->MapToVisualRectInAncestorSpace(ancestor, actual,                   \
+                                           kUseGeometryMapper);                \
       SCOPED_TRACE("GeometryMapper: ");                                        \
       EXPECT_EQ(expected, actual);                                             \
     }                                                                          \
@@ -1626,7 +1621,7 @@
       }
     </style>
     <div id='divWithTransform'>
-      <iframe style='border: 7px solid black'></iframe>
+      <iframe id='iframe' style='border: 7px solid black'></iframe>
     </div>
   )HTML");
   SetChildFrameHTML(R"HTML(
@@ -1669,8 +1664,8 @@
   // Ensure that the inner div's transform is correctly rooted in the root
   // frame's transform tree.
   // This asserts that we have the following tree structure:
-  // ...
-  //   Transform transform=translation=1.000000,2.000000,3.000000
+  // Transform transform=translation=1.000000,2.000000,3.000000
+  //   PaintOffsetTranslation transform=Identity
   //     PreTranslation transform=translation=7.000000,7.000000,0.000000
   //       PaintOffsetTranslation transform=Identity
   //         ScrollTranslation transform=translation=0.000000,0.000000,0.000000
@@ -1684,8 +1679,22 @@
   EXPECT_EQ(FloatSize(), paint_offset_translation->Matrix().To2DTranslation());
   EXPECT_EQ(TransformationMatrix().Translate3d(7, 7, 0),
             iframe_pre_translation->Matrix());
-  EXPECT_EQ(div_with_transform_properties->Transform(),
-            iframe_pre_translation->Parent());
+  // SPv1 composited elements always create paint offset translation,
+  // where in SPv2 they don't.
+  if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
+    EXPECT_EQ(div_with_transform_properties->Transform(),
+              iframe_pre_translation->Parent());
+  } else {
+    LayoutObject* iframe_element = GetLayoutObjectByElementId("iframe");
+    const ObjectPaintProperties* iframe_element_properties =
+        iframe_element->FirstFragment().PaintProperties();
+    EXPECT_EQ(iframe_element_properties->PaintOffsetTranslation(),
+              iframe_pre_translation->Parent());
+    EXPECT_EQ(TransformationMatrix(),
+              iframe_element_properties->PaintOffsetTranslation()->Matrix());
+    EXPECT_EQ(div_with_transform_properties->Transform(),
+              iframe_element_properties->PaintOffsetTranslation()->Parent());
+  }
 }
 
 TEST_P(PaintPropertyTreeBuilderTest, TransformNodesInTransformedSubframes) {
diff --git a/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp
index 236768e..531a99c 100644
--- a/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp
@@ -1004,21 +1004,22 @@
         compositing_container->SubpixelAccumulation();
   }
 
-#if 0 && DCHECK_IS_ON()
-  // TODO(trchen): We should enable this for below comment out |DCHECK()| once
-  // we have simple reproduce case and fix it. See http://crbug.com/646437 for
-  // details.
-  const PaintLayer* commonTransformAncestor = nullptr;
-  if (compositingContainer && compositingContainer->transform())
-    commonTransformAncestor = compositingContainer;
-  else if (compositingContainer)
-    commonTransformAncestor = compositingContainer->transformAncestor();
-#endif
+  const PaintLayer* common_transform_ancestor = nullptr;
+  if (compositing_container && compositing_container->Transform()) {
+    common_transform_ancestor = compositing_container;
+  } else if (compositing_container) {
+    common_transform_ancestor =
+        &compositing_container->TransformAncestorOrRoot();
+  }
+
+  // What about a null compositing container?
+
   // FIXME: Cache these offsets.
   LayoutPoint compositing_container_offset_from_transformed_ancestor;
-  if (compositing_container && !compositing_container->Transform()) {
+  if (compositing_container) {
     compositing_container_offset_from_transformed_ancestor =
-        compositing_container->ComputeOffsetFromTransformedAncestor();
+        compositing_container->ComputeOffsetFromAncestor(
+            *common_transform_ancestor);
   }
 
   LayoutRect total_squash_bounds;
@@ -1029,14 +1030,12 @@
     // Store the local bounds of the Layer subtree before applying the offset.
     layers[i].composited_bounds = squashed_bounds;
 
-#if 0 && DCHECK_IS_ON()
-    // TODO(trchen): We should enable this |DCHECK()| once we have simple
-    // reproduce case and fix it. See http://crbug.com/646437 for details.
-    DCHECK(layers[i].paintLayer->transformAncestor() ==
-           commonTransformAncestor);
-#endif
+    DCHECK(&layers[i].paint_layer->TransformAncestorOrRoot() ==
+           common_transform_ancestor);
+
     LayoutPoint squashed_layer_offset_from_transformed_ancestor =
-        layers[i].paint_layer->ComputeOffsetFromTransformedAncestor();
+        layers[i].paint_layer->ComputeOffsetFromAncestor(
+            *common_transform_ancestor);
     LayoutSize squashed_layer_offset_from_compositing_container =
         squashed_layer_offset_from_transformed_ancestor -
         compositing_container_offset_from_transformed_ancestor;
@@ -1071,7 +1070,8 @@
   // painting code expects the offset to be.
   for (size_t i = 0; i < layers.size(); ++i) {
     const LayoutPoint squashed_layer_offset_from_transformed_ancestor =
-        layers[i].paint_layer->ComputeOffsetFromTransformedAncestor();
+        layers[i].paint_layer->ComputeOffsetFromAncestor(
+            *common_transform_ancestor);
     const LayoutSize offset_from_squash_layer_origin =
         (squashed_layer_offset_from_transformed_ancestor -
          compositing_container_offset_from_transformed_ancestor) -
diff --git a/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMappingTest.cpp b/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMappingTest.cpp
index 3065ce1..53885275 100644
--- a/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMappingTest.cpp
+++ b/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMappingTest.cpp
@@ -2614,4 +2614,41 @@
             squashed->GroupedMapping()->SquashingLayer()->InterestRect());
 }
 
+TEST_P(CompositedLayerMappingTest,
+       SquashingBoundsUnderCompositedScrollingWithTransform) {
+  if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
+    return;
+
+  SetHtmlInnerHTML(R"HTML(
+    <div id=scroller style="transform: translateZ(0); overflow: scroll;
+    width: 200px; height: 400px;">
+      <div id=squashing
+          style='width: 200px; height: 200px; position: relative; will-change:
+transform'></div>
+      <div id=squashed style="width: 200px; height: 6000px; top: -100px;
+          position: relative;">
+      </div>
+    </div>
+    )HTML");
+  Element* scroller_element = GetDocument().getElementById("scroller");
+  auto* scroller = scroller_element->GetLayoutObject();
+  EXPECT_EQ(kPaintsIntoOwnBacking, scroller->GetCompositingState());
+
+  auto* squashing =
+      ToLayoutBoxModelObject(GetLayoutObjectByElementId("squashing"))->Layer();
+  EXPECT_EQ(kPaintsIntoOwnBacking, squashing->GetCompositingState());
+
+  auto* squashed =
+      ToLayoutBoxModelObject(GetLayoutObjectByElementId("squashed"))->Layer();
+  EXPECT_EQ(kPaintsIntoGroupedBacking, squashed->GetCompositingState());
+
+  scroller_element->setScrollTop(300);
+
+  GetDocument().View()->UpdateAllLifecyclePhases();
+
+  // 100px down from squashing's main graphics layer.
+  EXPECT_EQ(FloatPoint(0, 100),
+            squashed->GraphicsLayerBacking()->GetPosition());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js b/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js
index 827c3987..70c10f35 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js
@@ -716,12 +716,23 @@
     }
 
     /**
+     * @param {!Event} event
+     */
+    const keydownListener = event => {
+      if (event.key !== ' ')
+        return;
+      this._editing.commit();
+      event.consume(true);
+    };
+
+    /**
      * @param {!Element} element
      * @param {string} newTagName
      * @this {Elements.ElementsTreeElement}
      */
     function editingComitted(element, newTagName) {
       tagNameElement.removeEventListener('keyup', keyupListener, false);
+      tagNameElement.removeEventListener('keydown', keydownListener, false);
       this._tagNameEditingCommitted.apply(this, arguments);
     }
 
@@ -730,10 +741,12 @@
      */
     function editingCancelled() {
       tagNameElement.removeEventListener('keyup', keyupListener, false);
+      tagNameElement.removeEventListener('keydown', keydownListener, false);
       this._editingCancelled.apply(this, arguments);
     }
 
     tagNameElement.addEventListener('keyup', keyupListener, false);
+    tagNameElement.addEventListener('keydown', keydownListener, false);
 
     const config = new UI.InplaceEditor.Config(editingComitted.bind(this), editingCancelled.bind(this), tagName);
     this._editing = UI.InplaceEditor.startEditing(tagNameElement, config);
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
index 7bb2b6e..3c09699 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
@@ -661,9 +661,6 @@
     const learnMoreNode = UI.XLink.create(
         'https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/',
         Common.UIString('Learn\xa0more'));
-    const learnMoreMigrationNode = UI.XLink.create(
-        'https://developers.google.com/web/updates/2016/12/devtools-javascript-cpu-profile-migration',
-        Common.UIString('Learn\xa0more'));
 
     const recordKey =
         encloseWithTag('b', UI.shortcutRegistry.shortcutDescriptorsForAction('timeline.toggle-recording')[0].name);
@@ -688,22 +685,6 @@
         'Then, zoom and pan the timeline with the mousewheel or %s keys.\n%s',
         [navigateNode, learnMoreNode]));
 
-    const cpuProfilerHintSetting = Common.settings.createSetting('timelineShowProfilerHint', true);
-    if (cpuProfilerHintSetting.get()) {
-      const warning = centered.createChild('p', 'timeline-landing-warning');
-      const closeButton = warning.createChild('div', 'timeline-landing-warning-close', 'dt-close-button');
-      closeButton.addEventListener('click', () => {
-        warning.style.visibility = 'hidden';
-        cpuProfilerHintSetting.set(false);
-      }, false);
-      const performanceSpan = encloseWithTag('b', Common.UIString('Performance'));
-      warning.createChild('div').appendChild(UI.formatLocalized(
-          `The %s panel provides the combined functionality of Timeline and JavaScript CPU profiler. %s%s` +
-          `The JavaScript CPU profiler will be removed shortly. Meanwhile, it's available under ` +
-          `%s \u2192 More Tools \u2192 JavaScript Profiler.`,
-          [performanceSpan, learnMoreMigrationNode, createElement('p'), UI.Icon.create('largeicon-menu')]));
-    }
-
     this._landingPage.show(this._statusPaneContainer);
   }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/timelinePanel.css b/third_party/WebKit/Source/devtools/front_end/timeline/timelinePanel.css
index 64c24ba..f55f4df1 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/timelinePanel.css
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/timelinePanel.css
@@ -660,24 +660,3 @@
     flex: none;
     white-space: pre-line;
 }
-
-.timeline-landing-warning {
-    background-color: #fffde7;
-    padding: 16px 20px;
-    margin-top: 40px;
-    box-shadow: 1px 1px 3px rgba(0,0,0,0.2);
-}
-
-.-theme-with-dark-background .timeline-landing-warning {
-    background: black;
-}
-
-.timeline-landing-warning-close {
-    float: right;
-    margin-top: -10px;
-    margin-right: -13px;
-}
-
-.timeline-landing-warning span[is=ui-icon] {
-    margin: -8px;
-}
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js b/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
index 31a0d5b..b64ab0c3 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/UIUtils.js
@@ -1736,7 +1736,7 @@
       return;
 
     if (this._themeName === 'dark')
-      document.body.classList.add('-theme-with-dark-background');
+      document.documentElement.classList.add('-theme-with-dark-background');
 
     const styleSheets = document.styleSheets;
     const result = [];
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
index 43965dc..2483dc7 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
@@ -23,6 +23,7 @@
 #include "core/frame/Deprecation.h"
 #include "core/frame/FrameOwner.h"
 #include "core/frame/Settings.h"
+#include "core/frame/UseCounter.h"
 #include "core/frame/WebFeature.h"
 #include "core/html/HTMLIFrameElement.h"
 #include "core/inspector/ConsoleMessage.h"
@@ -52,6 +53,7 @@
 #include "platform/wtf/text/StringBuilder.h"
 #include "public/platform/Platform.h"
 #include "public/platform/TaskType.h"
+#include "public/platform/web_feature.mojom-blink.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 
 namespace {
@@ -813,6 +815,13 @@
                                            "Cannot show the payment request"));
   }
 
+  // TODO(crbug.com/825270): Reject with SecurityError DOMException if triggered
+  // without user activation.
+  if (!Frame::HasTransientUserActivation(GetFrame())) {
+    UseCounter::Count(GetExecutionContext(),
+                      WebFeature::kPaymentRequestShowWithoutGesture);
+  }
+
   // TODO(crbug.com/779126): add support for handling payment requests in
   // immersive mode.
   if (GetFrame()->GetDocument()->GetSettings()->GetImmersiveModeEnabled()) {
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.idl b/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.idl
index 180c3a2..b7f58d0f 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.idl
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletNode.idl
@@ -8,7 +8,6 @@
     ActiveScriptWrappable,
     Constructor(BaseAudioContext context, DOMString name, optional AudioWorkletNodeOptions options),
     ConstructorCallWith=ScriptState,
-    Global=(Worklet,AudioWorklet),
     MeasureAs=AudioWorkletNodeConstructor,
     RaisesException=Constructor
 ] interface AudioWorkletNode : AudioNode {
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessor.idl b/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessor.idl
index d6702bc..395dcbbf 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessor.idl
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletProcessor.idl
@@ -7,8 +7,7 @@
 [
     Constructor,
     ConstructorCallWith=ExecutionContext,
-    Exposed=AudioWorklet,
-    Global=(Worklet,AudioWorklet)
+    Exposed=AudioWorklet
 ] interface AudioWorkletProcessor {
   readonly attribute MessagePort port;
 };
diff --git a/third_party/WebKit/Source/platform/LayoutUnit.h b/third_party/WebKit/Source/platform/LayoutUnit.h
index bc5bc18..ba69cb2c 100644
--- a/third_party/WebKit/Source/platform/LayoutUnit.h
+++ b/third_party/WebKit/Source/platform/LayoutUnit.h
@@ -101,6 +101,12 @@
     return v;
   }
 
+  static LayoutUnit FromDoubleRound(double value) {
+    LayoutUnit v;
+    v.value_ = base::saturated_cast<int>(round(value * kFixedPointDenominator));
+    return v;
+  }
+
   int ToInt() const { return value_ / kFixedPointDenominator; }
   float ToFloat() const {
     return static_cast<float>(value_) / kFixedPointDenominator;
diff --git a/third_party/WebKit/Source/platform/graphics/paint/FloatClipRect.h b/third_party/WebKit/Source/platform/graphics/paint/FloatClipRect.h
index 2f858e2..3b75adbf 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/FloatClipRect.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/FloatClipRect.h
@@ -24,8 +24,7 @@
         is_tight_(true),
         is_infinite_(true) {}
 
-  explicit FloatClipRect(const FloatRect& rect)
-      : rect_(rect), has_radius_(false), is_tight_(true), is_infinite_(false) {}
+  explicit FloatClipRect(const FloatRect& rect) { SetRect(rect); }
 
   explicit FloatClipRect(const FloatRoundedRect& rect)
       : rect_(rect.Rect()),
@@ -37,6 +36,13 @@
 
   FloatRect& Rect() { return rect_; }
 
+  void SetRect(const FloatRect& rect) {
+    rect_ = rect;
+    has_radius_ = false;
+    is_tight_ = true;
+    is_infinite_ = false;
+  }
+
   void Intersect(const FloatClipRect& other) {
     if (is_infinite_) {
       is_infinite_ = other.is_infinite_;
diff --git a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.h b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.h
index dc74b84e..9b2ae2f 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.h
@@ -48,9 +48,8 @@
       const TransformPaintPropertyNode* source,
       const TransformPaintPropertyNode* destination);
 
-  // Same as SourceToDestinationVisualRect() except that only transforms are
-  // applied.
-  //
+  // Same as SourceToDestinationProjection() except that it maps the rect
+  // rather than returning the matrix.
   // |mapping_rect| is both input and output.
   static void SourceToDestinationRect(
       const TransformPaintPropertyNode* source_transform_node,
diff --git a/third_party/WebKit/Source/platform/heap/HeapTest.cpp b/third_party/WebKit/Source/platform/heap/HeapTest.cpp
index 47d041ee..c816040 100644
--- a/third_party/WebKit/Source/platform/heap/HeapTest.cpp
+++ b/third_party/WebKit/Source/platform/heap/HeapTest.cpp
@@ -4007,7 +4007,6 @@
   // to allocate anything again. We do this by forcing a GC after doing the
   // checkAndMarkPointer tests.
   {
-    ThreadState::GCForbiddenScope gc_scope(ThreadState::Current());
     TestGCScope scope(BlinkGC::kHeapPointersOnStack);
     MarkingVisitor visitor(ThreadState::Current(),
                            MarkingVisitor::kGlobalMarking);
@@ -4032,7 +4031,6 @@
   // however we don't rely on that below since we don't have any allocations.
   ClearOutOldGarbage();
   {
-    ThreadState::GCForbiddenScope gc_scope(ThreadState::Current());
     TestGCScope scope(BlinkGC::kHeapPointersOnStack);
     MarkingVisitor visitor(ThreadState::Current(),
                            MarkingVisitor::kGlobalMarking);
diff --git a/third_party/WebKit/Source/platform/heap/MarkingVisitor.cpp b/third_party/WebKit/Source/platform/heap/MarkingVisitor.cpp
index 092f2ca..7a0863f22 100644
--- a/third_party/WebKit/Source/platform/heap/MarkingVisitor.cpp
+++ b/third_party/WebKit/Source/platform/heap/MarkingVisitor.cpp
@@ -25,9 +25,7 @@
       weak_callback_worklist_(Heap().GetWeakCallbackWorklist(),
                               WorklistTaskId::MainThread),
       marking_mode_(marking_mode) {
-  // See ThreadState::runScheduledGC() why we need to already be in a
-  // GCForbiddenScope before any safe point is entered.
-  DCHECK(state->IsGCForbidden());
+  DCHECK(state->InAtomicMarkingPause());
 #if DCHECK_IS_ON()
   DCHECK(state->CheckThread());
 #endif  // DCHECK_IS_ON
diff --git a/third_party/WebKit/Source/platform/heap/ThreadState.cpp b/third_party/WebKit/Source/platform/heap/ThreadState.cpp
index 1264147..c3a2470 100644
--- a/third_party/WebKit/Source/platform/heap/ThreadState.cpp
+++ b/third_party/WebKit/Source/platform/heap/ThreadState.cpp
@@ -1302,8 +1302,6 @@
   RUNTIME_CALL_TIMER_SCOPE_IF_ISOLATE_EXISTS(
       GetIsolate(), RuntimeCallStats::CounterId::kCollectGarbage);
 
-  GCForbiddenScope gc_forbidden_scope(this);
-
   {
     AtomicPauseScope atomic_pause_scope(this);
     {
@@ -1403,9 +1401,6 @@
 void ThreadState::MarkPhaseVisitRoots() {
   double start_time = WTF::CurrentTimeTicksInMilliseconds();
 
-  // Disallow allocation during garbage collection (but not during the
-  // finalization that happens when the visitorScope is torn down).
-  NoAllocationScope no_allocation_scope(this);
   // StackFrameDepth should be disabled so we don't trace most of the object
   // graph in one incremental marking step.
   DCHECK(!Heap().GetStackFrameDepth().IsEnabled());
@@ -1428,9 +1423,6 @@
 bool ThreadState::MarkPhaseAdvanceMarking(double deadline_seconds) {
   double start_time = WTF::CurrentTimeTicksInMilliseconds();
 
-  // Disallow allocation during garbage collection (but not during the
-  // finalization that happens when the visitorScope is torn down).
-  NoAllocationScope no_allocation_scope(this);
   StackFrameDepthScope stack_depth_scope(&Heap().GetStackFrameDepth());
 
   // 3. Transitive closure to trace objects including ephemerons.
diff --git a/third_party/WebKit/Source/platform/heap/ThreadState.h b/third_party/WebKit/Source/platform/heap/ThreadState.h
index 48b223e..72715002 100644
--- a/third_party/WebKit/Source/platform/heap/ThreadState.h
+++ b/third_party/WebKit/Source/platform/heap/ThreadState.h
@@ -320,7 +320,11 @@
 
   // Support for disallowing allocation. Mainly used for sanity
   // checks asserts.
-  bool IsAllocationAllowed() const { return !no_allocation_count_; }
+  bool IsAllocationAllowed() const {
+    // Allocation is not allowed during atomic marking pause, but it is allowed
+    // during atomic sweeping pause.
+    return !InAtomicMarkingPause() && !no_allocation_count_;
+  }
   void EnterNoAllocationScope() { no_allocation_count_++; }
   void LeaveNoAllocationScope() { no_allocation_count_--; }
   bool IsWrapperTracingForbidden() { return IsMixinInConstruction(); }
@@ -406,7 +410,7 @@
   class AtomicPauseScope final {
    public:
     explicit AtomicPauseScope(ThreadState* thread_state)
-        : thread_state_(thread_state) {
+        : thread_state_(thread_state), gc_forbidden_scope(thread_state) {
       thread_state_->EnterAtomicPause();
     }
     ~AtomicPauseScope() { thread_state_->LeaveAtomicPause(); }
@@ -414,6 +418,7 @@
    private:
     ThreadState* const thread_state_;
     ScriptForbiddenScope script_forbidden_scope;
+    GCForbiddenScope gc_forbidden_scope;
   };
 
   void FlushHeapDoesNotContainCacheIfNeeded();
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_delegate_for_test.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_delegate_for_test.cc
index b6be124b..ca8c081 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_delegate_for_test.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_delegate_for_test.cc
@@ -57,7 +57,7 @@
 void TaskQueueManagerDelegateForTest::RemoveNestingObserver(
     base::RunLoop::NestingObserver* observer) {}
 
-base::TimeTicks TaskQueueManagerDelegateForTest::NowTicks() {
+base::TimeTicks TaskQueueManagerDelegateForTest::NowTicks() const {
   return time_source_->NowTicks();
 }
 
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_delegate_for_test.h b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_delegate_for_test.h
index f66ee0d2..449e443b 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_delegate_for_test.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager_delegate_for_test.h
@@ -38,7 +38,7 @@
   void RemoveNestingObserver(base::RunLoop::NestingObserver* observer) override;
 
   // TickClock:
-  base::TimeTicks NowTicks() override;
+  base::TimeTicks NowTicks() const override;
 
  protected:
   ~TaskQueueManagerDelegateForTest() override;
diff --git a/third_party/WebKit/Source/platform/scheduler/base/test_count_uses_time_source.cc b/third_party/WebKit/Source/platform/scheduler/base/test_count_uses_time_source.cc
index e61164e7..cbd33e26 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/test_count_uses_time_source.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/test_count_uses_time_source.cc
@@ -12,7 +12,7 @@
 
 TestCountUsesTimeSource::~TestCountUsesTimeSource() = default;
 
-base::TimeTicks TestCountUsesTimeSource::NowTicks() {
+base::TimeTicks TestCountUsesTimeSource::NowTicks() const {
   now_calls_count_++;
   // Don't return 0, as it triggers some assertions.
   return base::TimeTicks() + base::TimeDelta::FromSeconds(1);
diff --git a/third_party/WebKit/Source/platform/scheduler/base/test_count_uses_time_source.h b/third_party/WebKit/Source/platform/scheduler/base/test_count_uses_time_source.h
index cdefe0e..41bd3fb9 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/test_count_uses_time_source.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/test_count_uses_time_source.h
@@ -16,13 +16,13 @@
   explicit TestCountUsesTimeSource();
   ~TestCountUsesTimeSource() override;
 
-  base::TimeTicks NowTicks() override;
-  int now_calls_count() { return now_calls_count_; }
+  base::TimeTicks NowTicks() const override;
+  int now_calls_count() const { return now_calls_count_; }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestCountUsesTimeSource);
 
-  int now_calls_count_;
+  mutable int now_calls_count_;
 };
 
 }  // namespace scheduler
diff --git a/third_party/WebKit/Source/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc b/third_party/WebKit/Source/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc
index a44d33b..6d4968f 100644
--- a/third_party/WebKit/Source/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc
+++ b/third_party/WebKit/Source/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc
@@ -57,8 +57,8 @@
       : advancing_interval_(interval) {}
   ~AutoAdvancingTestClock() override = default;
 
-  base::TimeTicks NowTicks() override {
-    Advance(advancing_interval_);
+  base::TimeTicks NowTicks() const override {
+    const_cast<AutoAdvancingTestClock*>(this)->Advance(advancing_interval_);
     return SimpleTestTickClock::NowTicks();
   }
 
diff --git a/third_party/WebKit/public/platform/web_feature.mojom b/third_party/WebKit/public/platform/web_feature.mojom
index 7f495ed..a251a3bd 100644
--- a/third_party/WebKit/public/platform/web_feature.mojom
+++ b/third_party/WebKit/public/platform/web_feature.mojom
@@ -1887,6 +1887,7 @@
   kKeyboardApiUnlock = 2395,
   kPPAPIURLRequestStreamToFile = 2396,
   kPaymentHandler = 2397,
+  kPaymentRequestShowWithoutGesture = 2398,
 
   // 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/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js
index 732f2cff2..d1faf2e 100644
--- a/third_party/closure_compiler/externs/file_manager_private.js
+++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -515,12 +515,12 @@
  * The files must be under the directory specified by |parentEntry|. |destName|
  * Name of the destination zip file. The zip file will be created under the
  * directory specified by |parentEntry|.
- * @param {!DirectoryEntry} parentEntry
  * @param {!Array<!Entry>} entries
+ * @param {!DirectoryEntry} parentEntry
  * @param {string} destName
  * @param {function((boolean|undefined))} callback
  */
-chrome.fileManagerPrivate.zipSelection = function(parentEntry, entries,
+chrome.fileManagerPrivate.zipSelection = function(entries, parentEntry,
     destName, callback) {};
 
 /**
diff --git a/third_party/closure_compiler/externs/language_settings_private.js b/third_party/closure_compiler/externs/language_settings_private.js
index a0278b2..165a677 100644
--- a/third_party/closure_compiler/externs/language_settings_private.js
+++ b/third_party/closure_compiler/externs/language_settings_private.js
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -172,6 +172,13 @@
 chrome.languageSettingsPrivate.removeInputMethod = function(inputMethodId) {};
 
 /**
+ * Tries to download the dictionary after a failed download.
+ * @param {string} languageCode
+ * @see https://developer.chrome.com/extensions/languageSettingsPrivate#method-retryDownloadDictionary
+ */
+chrome.languageSettingsPrivate.retryDownloadDictionary = function(languageCode) {};
+
+/**
  * Called when the pref for the dictionaries used for spell checking changes or
  * the status of one of the spell check dictionaries changes.
  * @type {!ChromeEvent}
diff --git a/third_party/ink/README.md b/third_party/ink/README.md
new file mode 100644
index 0000000..a2f6a63
--- /dev/null
+++ b/third_party/ink/README.md
@@ -0,0 +1,6 @@
+# Ink
+
+Ink is a software library enabling Google applications to let their users
+express themselves using freehand drawing and handwriting.
+
+go/ink
diff --git a/third_party/polymer/v1_0/bower.json b/third_party/polymer/v1_0/bower.json
index 61ea684a..9317cf5 100644
--- a/third_party/polymer/v1_0/bower.json
+++ b/third_party/polymer/v1_0/bower.json
@@ -47,7 +47,7 @@
     "paper-ripple": "PolymerElements/paper-ripple#1.0.9",
     "paper-slider": "PolymerElements/paper-slider#2.0.2",
     "paper-spinner": "PolymerElements/paper-spinner#2.0.0",
-    "paper-styles": "PolymerElements/paper-styles#1.3.1",
+    "paper-styles": "PolymerElements/paper-styles#2.1.0",
     "paper-tabs": "PolymerElements/paper-tabs#2.0.0",
     "paper-toggle-button": "PolymerElements/paper-toggle-button#2.0.0",
     "paper-tooltip": "PolymerElements/paper-tooltip#2.0.0",
diff --git a/third_party/polymer/v1_0/components-chromium/paper-styles/bower.json b/third_party/polymer/v1_0/components-chromium/paper-styles/bower.json
index daaab79..2d1e3de 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-styles/bower.json
+++ b/third_party/polymer/v1_0/components-chromium/paper-styles/bower.json
@@ -1,6 +1,6 @@
 {
   "name": "paper-styles",
-  "version": "1.3.1",
+  "version": "2.0.0",
   "description": "Common (global) styles for Material Design elements.",
   "authors": [
     "The Polymer Authors"
@@ -21,13 +21,35 @@
     "/.*"
   ],
   "dependencies": {
-    "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+    "iron-flex-layout": "PolymerElements/iron-flex-layout#1 - 2",
     "font-roboto": "PolymerElements/font-roboto#^1.0.1",
-    "polymer": "Polymer/polymer#^1.0.0"
+    "polymer": "Polymer/polymer#1.9 - 2"
   },
   "devDependencies": {
-    "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
-    "iron-component-page": "polymerelements/iron-component-page#^1.0.0",
-    "web-component-tester": "^4.0.0"
+    "iron-component-page": "PolymerElements/iron-component-page#1 - 2",
+    "iron-demo-helpers": "PolymerElements/iron-demo-helpers#1 - 2",
+    "webcomponentsjs": "webcomponents/webcomponentsjs#^1.0.0",
+    "web-component-tester": "^6.0.0"
+  },
+  "variants": {
+    "1.x": {
+      "dependencies": {
+        "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
+        "font-roboto": "PolymerElements/font-roboto#^1.0.0",
+        "polymer": "Polymer/polymer#^1.9"
+      },
+      "devDependencies": {
+        "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
+        "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0",
+        "web-component-tester": "^4.0.0",
+        "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
+      },
+      "resolutions": {
+        "webcomponentsjs": "^0.7"
+      }
+    }
+  },
+  "resolutions": {
+    "webcomponentsjs": "^1.0.0"
   }
 }
diff --git a/third_party/polymer/v1_0/components-chromium/paper-styles/classes/global.html b/third_party/polymer/v1_0/components-chromium/paper-styles/classes/global.html
deleted file mode 100644
index 6f0d5dd..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-styles/classes/global.html
+++ /dev/null
@@ -1,96 +0,0 @@
-<!--
-@license
-Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
--->
-<link rel="import" href="../paper-styles-classes.html">
-
-<!--
-A set of base styles that are applied to the document and standard elements that
-match the Material Design spec.
--->
-<style>
-/*
-Note that there is a lot of style duplication here. The hope is that the Polymer
-0.8 styling solution will allow for inheritance of properties so that we can
-eventually avoid it.
-*/
-
-/* Mixins */
-
-/* [paper-font] */
-body {
-  font-family: 'Roboto', 'Noto', sans-serif;
-  -webkit-font-smoothing: antialiased;  /* OS X subpixel AA bleed bug */
-}
-
-/* [paper-font=display2] */
-h1 {
-  font-size: 45px;
-  font-weight: 400;
-  letter-spacing: -.018em;
-  line-height: 48px;
-}
-
-/* [paper-font=display1] */
-h2 {
-  font-size: 34px;
-  font-weight: 400;
-  letter-spacing: -.01em;
-  line-height: 40px;
-}
-
-/* [paper-font=headline] */
-h3 {
-  font-size: 24px;
-  font-weight: 400;
-  letter-spacing: -.012em;
-  line-height: 32px;
-}
-
-/* [paper-font=subhead] */
-h4 {
-  font-size: 16px;
-  font-weight: 400;
-  line-height: 24px;
-}
-
-/* [paper-font=body2] */
-h5, h6 {
-  font-size: 14px;
-  font-weight: 500;
-  line-height: 24px;
-}
-
-/* [paper-font=button] */
-a {
-  font-size: 14px;
-  font-weight: 500;
-  letter-spacing: 0.018em;
-  line-height: 24px;
-  text-transform: uppercase;
-}
-
-/* Overrides */
-
-body, a {
-  color: #212121;
-}
-
-h1, h2, h3, h4, h5, h6, p {
-  margin: 0 0 20px 0;
-}
-
-h1, h2, h3, h4, h5, h6, a {
-  text-rendering: optimizeLegibility;
-}
-
-a {
-  text-decoration: none;
-}
-
-</style>
diff --git a/third_party/polymer/v1_0/components-chromium/paper-styles/classes/shadow.html b/third_party/polymer/v1_0/components-chromium/paper-styles/classes/shadow.html
index 4c40a14..5ab04b1 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-styles/classes/shadow.html
+++ b/third_party/polymer/v1_0/components-chromium/paper-styles/classes/shadow.html
@@ -8,6 +8,14 @@
 subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
 -->
 
+<!--
+Note that this file probably doesn't do what you expect it to do. It's not
+a `<style is=custom-style include="..."` type of style include, which mean
+these styles will only apply to the main document, regardless of where
+you import this file.
+
+For a set of styles that can be applied to an element, check paper-styles/shadow.html.
+-->
 <style>
 .shadow-transition {
   transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
diff --git a/third_party/polymer/v1_0/components-chromium/paper-styles/classes/typography.html b/third_party/polymer/v1_0/components-chromium/paper-styles/classes/typography.html
index b399395..5586adc 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-styles/classes/typography.html
+++ b/third_party/polymer/v1_0/components-chromium/paper-styles/classes/typography.html
@@ -10,17 +10,12 @@
 <link rel="import" href="../../font-roboto/roboto.html">
 
 <!--
-Typographic styles are provided matching the Material Design standard styles:
-http://www.google.com/design/spec/style/typography.html#typography-standard-styles
+Note that this file probably doesn't do what you expect it to do. It's not
+a `<style is=custom-style include="..."` type of style include, which mean
+these styles will only apply to the main document, regardless of where
+you import this file.
 
-To make use of them, apply a `paper-font-<style>` class to elements, matching
-the font style you wish it to inherit.
-
-  <h1 class="paper-font-display2">Hey there!</h1>
-
-Note that these are English/Latin centric styles. You may need to further adjust
-line heights and weights for CJK typesetting. See the notes in the Material
-Design typography section.
+For a set of styles that can be applied to an element, check paper-styles/typography.html.
 -->
 <style>
 
diff --git a/third_party/polymer/v1_0/components-chromium/paper-styles/color.html b/third_party/polymer/v1_0/components-chromium/paper-styles/color.html
index 5188790..67eae3e2 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-styles/color.html
+++ b/third_party/polymer/v1_0/components-chromium/paper-styles/color.html
@@ -10,324 +10,325 @@
 
 <link rel="import" href="../polymer/polymer.html">
 
-<style is="custom-style">
+<custom-style>
+  <style is="custom-style">
+    html {
 
-  :root {
+      /* Material Design color palette for Google products */
 
-    /* Material Design color palette for Google products */
+      --google-red-100: #f4c7c3;
+      --google-red-300: #e67c73;
+      --google-red-500: #db4437;
+      --google-red-700: #c53929;
 
-    --google-red-100: #f4c7c3;
-    --google-red-300: #e67c73;
-    --google-red-500: #db4437;
-    --google-red-700: #c53929;
+      --google-blue-100: #c6dafc;
+      --google-blue-300: #7baaf7;
+      --google-blue-500: #4285f4;
+      --google-blue-700: #3367d6;
 
-    --google-blue-100: #c6dafc;
-    --google-blue-300: #7baaf7;
-    --google-blue-500: #4285f4;
-    --google-blue-700: #3367d6;
+      --google-green-100: #b7e1cd;
+      --google-green-300: #57bb8a;
+      --google-green-500: #0f9d58;
+      --google-green-700: #0b8043;
 
-    --google-green-100: #b7e1cd;
-    --google-green-300: #57bb8a;
-    --google-green-500: #0f9d58;
-    --google-green-700: #0b8043;
+      --google-yellow-100: #fce8b2;
+      --google-yellow-300: #f7cb4d;
+      --google-yellow-500: #f4b400;
+      --google-yellow-700: #f09300;
 
-    --google-yellow-100: #fce8b2;
-    --google-yellow-300: #f7cb4d;
-    --google-yellow-500: #f4b400;
-    --google-yellow-700: #f09300;
+      --google-grey-100: #f5f5f5;
+      --google-grey-300: #e0e0e0;
+      --google-grey-500: #9e9e9e;
+      --google-grey-700: #616161;
 
-    --google-grey-100: #f5f5f5;
-    --google-grey-300: #e0e0e0;
-    --google-grey-500: #9e9e9e;
-    --google-grey-700: #616161;
-    
-    /* Material Design color palette from online spec document */
+      /* Material Design color palette from online spec document */
 
-    --paper-red-50: #ffebee;
-    --paper-red-100: #ffcdd2;
-    --paper-red-200: #ef9a9a;
-    --paper-red-300: #e57373;
-    --paper-red-400: #ef5350;
-    --paper-red-500: #f44336;
-    --paper-red-600: #e53935;
-    --paper-red-700: #d32f2f;
-    --paper-red-800: #c62828;
-    --paper-red-900: #b71c1c;
-    --paper-red-a100: #ff8a80;
-    --paper-red-a200: #ff5252;
-    --paper-red-a400: #ff1744;
-    --paper-red-a700: #d50000;
- 
-    --paper-pink-50: #fce4ec;
-    --paper-pink-100: #f8bbd0;
-    --paper-pink-200: #f48fb1;
-    --paper-pink-300: #f06292;
-    --paper-pink-400: #ec407a;
-    --paper-pink-500: #e91e63;
-    --paper-pink-600: #d81b60;
-    --paper-pink-700: #c2185b;
-    --paper-pink-800: #ad1457;
-    --paper-pink-900: #880e4f;
-    --paper-pink-a100: #ff80ab;
-    --paper-pink-a200: #ff4081;
-    --paper-pink-a400: #f50057;
-    --paper-pink-a700: #c51162;
- 
-    --paper-purple-50: #f3e5f5;
-    --paper-purple-100: #e1bee7;
-    --paper-purple-200: #ce93d8;
-    --paper-purple-300: #ba68c8;
-    --paper-purple-400: #ab47bc;
-    --paper-purple-500: #9c27b0;
-    --paper-purple-600: #8e24aa;
-    --paper-purple-700: #7b1fa2;
-    --paper-purple-800: #6a1b9a;
-    --paper-purple-900: #4a148c;
-    --paper-purple-a100: #ea80fc;
-    --paper-purple-a200: #e040fb;
-    --paper-purple-a400: #d500f9;
-    --paper-purple-a700: #aa00ff;
- 
-    --paper-deep-purple-50: #ede7f6;
-    --paper-deep-purple-100: #d1c4e9;
-    --paper-deep-purple-200: #b39ddb;
-    --paper-deep-purple-300: #9575cd;
-    --paper-deep-purple-400: #7e57c2;
-    --paper-deep-purple-500: #673ab7;
-    --paper-deep-purple-600: #5e35b1;
-    --paper-deep-purple-700: #512da8;
-    --paper-deep-purple-800: #4527a0;
-    --paper-deep-purple-900: #311b92;
-    --paper-deep-purple-a100: #b388ff;
-    --paper-deep-purple-a200: #7c4dff;
-    --paper-deep-purple-a400: #651fff;
-    --paper-deep-purple-a700: #6200ea;
- 
-    --paper-indigo-50: #e8eaf6;
-    --paper-indigo-100: #c5cae9;
-    --paper-indigo-200: #9fa8da;
-    --paper-indigo-300: #7986cb;
-    --paper-indigo-400: #5c6bc0;
-    --paper-indigo-500: #3f51b5;
-    --paper-indigo-600: #3949ab;
-    --paper-indigo-700: #303f9f;
-    --paper-indigo-800: #283593;
-    --paper-indigo-900: #1a237e;
-    --paper-indigo-a100: #8c9eff;
-    --paper-indigo-a200: #536dfe;
-    --paper-indigo-a400: #3d5afe;
-    --paper-indigo-a700: #304ffe;
- 
-    --paper-blue-50: #e3f2fd;
-    --paper-blue-100: #bbdefb;
-    --paper-blue-200: #90caf9;
-    --paper-blue-300: #64b5f6;
-    --paper-blue-400: #42a5f5;
-    --paper-blue-500: #2196f3;
-    --paper-blue-600: #1e88e5;
-    --paper-blue-700: #1976d2;
-    --paper-blue-800: #1565c0;
-    --paper-blue-900: #0d47a1;
-    --paper-blue-a100: #82b1ff;
-    --paper-blue-a200: #448aff;
-    --paper-blue-a400: #2979ff;
-    --paper-blue-a700: #2962ff;
- 
-    --paper-light-blue-50: #e1f5fe;
-    --paper-light-blue-100: #b3e5fc;
-    --paper-light-blue-200: #81d4fa;
-    --paper-light-blue-300: #4fc3f7;
-    --paper-light-blue-400: #29b6f6;
-    --paper-light-blue-500: #03a9f4;
-    --paper-light-blue-600: #039be5;
-    --paper-light-blue-700: #0288d1;
-    --paper-light-blue-800: #0277bd;
-    --paper-light-blue-900: #01579b;
-    --paper-light-blue-a100: #80d8ff;
-    --paper-light-blue-a200: #40c4ff;
-    --paper-light-blue-a400: #00b0ff;
-    --paper-light-blue-a700: #0091ea;
- 
-    --paper-cyan-50: #e0f7fa;
-    --paper-cyan-100: #b2ebf2;
-    --paper-cyan-200: #80deea;
-    --paper-cyan-300: #4dd0e1;
-    --paper-cyan-400: #26c6da;
-    --paper-cyan-500: #00bcd4;
-    --paper-cyan-600: #00acc1;
-    --paper-cyan-700: #0097a7;
-    --paper-cyan-800: #00838f;
-    --paper-cyan-900: #006064;
-    --paper-cyan-a100: #84ffff;
-    --paper-cyan-a200: #18ffff;
-    --paper-cyan-a400: #00e5ff;
-    --paper-cyan-a700: #00b8d4;
- 
-    --paper-teal-50: #e0f2f1;
-    --paper-teal-100: #b2dfdb;
-    --paper-teal-200: #80cbc4;
-    --paper-teal-300: #4db6ac;
-    --paper-teal-400: #26a69a;
-    --paper-teal-500: #009688;
-    --paper-teal-600: #00897b;
-    --paper-teal-700: #00796b;
-    --paper-teal-800: #00695c;
-    --paper-teal-900: #004d40;
-    --paper-teal-a100: #a7ffeb;
-    --paper-teal-a200: #64ffda;
-    --paper-teal-a400: #1de9b6;
-    --paper-teal-a700: #00bfa5;
- 
-    --paper-green-50: #e8f5e9;
-    --paper-green-100: #c8e6c9;
-    --paper-green-200: #a5d6a7;
-    --paper-green-300: #81c784;
-    --paper-green-400: #66bb6a;
-    --paper-green-500: #4caf50;
-    --paper-green-600: #43a047;
-    --paper-green-700: #388e3c;
-    --paper-green-800: #2e7d32;
-    --paper-green-900: #1b5e20;
-    --paper-green-a100: #b9f6ca;
-    --paper-green-a200: #69f0ae;
-    --paper-green-a400: #00e676;
-    --paper-green-a700: #00c853;
- 
-    --paper-light-green-50: #f1f8e9;
-    --paper-light-green-100: #dcedc8;
-    --paper-light-green-200: #c5e1a5;
-    --paper-light-green-300: #aed581;
-    --paper-light-green-400: #9ccc65;
-    --paper-light-green-500: #8bc34a;
-    --paper-light-green-600: #7cb342;
-    --paper-light-green-700: #689f38;
-    --paper-light-green-800: #558b2f;
-    --paper-light-green-900: #33691e;
-    --paper-light-green-a100: #ccff90;
-    --paper-light-green-a200: #b2ff59;
-    --paper-light-green-a400: #76ff03;
-    --paper-light-green-a700: #64dd17;
- 
-    --paper-lime-50: #f9fbe7;
-    --paper-lime-100: #f0f4c3;
-    --paper-lime-200: #e6ee9c;
-    --paper-lime-300: #dce775;
-    --paper-lime-400: #d4e157;
-    --paper-lime-500: #cddc39;
-    --paper-lime-600: #c0ca33;
-    --paper-lime-700: #afb42b;
-    --paper-lime-800: #9e9d24;
-    --paper-lime-900: #827717;
-    --paper-lime-a100: #f4ff81;
-    --paper-lime-a200: #eeff41;
-    --paper-lime-a400: #c6ff00;
-    --paper-lime-a700: #aeea00;
- 
-    --paper-yellow-50: #fffde7;
-    --paper-yellow-100: #fff9c4;
-    --paper-yellow-200: #fff59d;
-    --paper-yellow-300: #fff176;
-    --paper-yellow-400: #ffee58;
-    --paper-yellow-500: #ffeb3b;
-    --paper-yellow-600: #fdd835;
-    --paper-yellow-700: #fbc02d;
-    --paper-yellow-800: #f9a825;
-    --paper-yellow-900: #f57f17;
-    --paper-yellow-a100: #ffff8d;
-    --paper-yellow-a200: #ffff00;
-    --paper-yellow-a400: #ffea00;
-    --paper-yellow-a700: #ffd600;
- 
-    --paper-amber-50: #fff8e1;
-    --paper-amber-100: #ffecb3;
-    --paper-amber-200: #ffe082;
-    --paper-amber-300: #ffd54f;
-    --paper-amber-400: #ffca28;
-    --paper-amber-500: #ffc107;
-    --paper-amber-600: #ffb300;
-    --paper-amber-700: #ffa000;
-    --paper-amber-800: #ff8f00;
-    --paper-amber-900: #ff6f00;
-    --paper-amber-a100: #ffe57f;
-    --paper-amber-a200: #ffd740;
-    --paper-amber-a400: #ffc400;
-    --paper-amber-a700: #ffab00;
- 
-    --paper-orange-50: #fff3e0;
-    --paper-orange-100: #ffe0b2;
-    --paper-orange-200: #ffcc80;
-    --paper-orange-300: #ffb74d;
-    --paper-orange-400: #ffa726;
-    --paper-orange-500: #ff9800;
-    --paper-orange-600: #fb8c00;
-    --paper-orange-700: #f57c00;
-    --paper-orange-800: #ef6c00;
-    --paper-orange-900: #e65100;
-    --paper-orange-a100: #ffd180;
-    --paper-orange-a200: #ffab40;
-    --paper-orange-a400: #ff9100;
-    --paper-orange-a700: #ff6500;
- 
-    --paper-deep-orange-50: #fbe9e7;
-    --paper-deep-orange-100: #ffccbc;
-    --paper-deep-orange-200: #ffab91;
-    --paper-deep-orange-300: #ff8a65;
-    --paper-deep-orange-400: #ff7043;
-    --paper-deep-orange-500: #ff5722;
-    --paper-deep-orange-600: #f4511e;
-    --paper-deep-orange-700: #e64a19;
-    --paper-deep-orange-800: #d84315;
-    --paper-deep-orange-900: #bf360c;
-    --paper-deep-orange-a100: #ff9e80;
-    --paper-deep-orange-a200: #ff6e40;
-    --paper-deep-orange-a400: #ff3d00;
-    --paper-deep-orange-a700: #dd2c00;
- 
-    --paper-brown-50: #efebe9;
-    --paper-brown-100: #d7ccc8;
-    --paper-brown-200: #bcaaa4;
-    --paper-brown-300: #a1887f;
-    --paper-brown-400: #8d6e63;
-    --paper-brown-500: #795548;
-    --paper-brown-600: #6d4c41;
-    --paper-brown-700: #5d4037;
-    --paper-brown-800: #4e342e;
-    --paper-brown-900: #3e2723;
- 
-    --paper-grey-50: #fafafa;
-    --paper-grey-100: #f5f5f5;
-    --paper-grey-200: #eeeeee;
-    --paper-grey-300: #e0e0e0;
-    --paper-grey-400: #bdbdbd;
-    --paper-grey-500: #9e9e9e;
-    --paper-grey-600: #757575;
-    --paper-grey-700: #616161;
-    --paper-grey-800: #424242;
-    --paper-grey-900: #212121;
- 
-    --paper-blue-grey-50: #eceff1;
-    --paper-blue-grey-100: #cfd8dc;
-    --paper-blue-grey-200: #b0bec5;
-    --paper-blue-grey-300: #90a4ae;
-    --paper-blue-grey-400: #78909c;
-    --paper-blue-grey-500: #607d8b;
-    --paper-blue-grey-600: #546e7a;
-    --paper-blue-grey-700: #455a64;
-    --paper-blue-grey-800: #37474f;
-    --paper-blue-grey-900: #263238;
+      --paper-red-50: #ffebee;
+      --paper-red-100: #ffcdd2;
+      --paper-red-200: #ef9a9a;
+      --paper-red-300: #e57373;
+      --paper-red-400: #ef5350;
+      --paper-red-500: #f44336;
+      --paper-red-600: #e53935;
+      --paper-red-700: #d32f2f;
+      --paper-red-800: #c62828;
+      --paper-red-900: #b71c1c;
+      --paper-red-a100: #ff8a80;
+      --paper-red-a200: #ff5252;
+      --paper-red-a400: #ff1744;
+      --paper-red-a700: #d50000;
 
-    /* opacity for dark text on a light background */
-    --dark-divider-opacity: 0.12;
-    --dark-disabled-opacity: 0.38; /* or hint text or icon */
-    --dark-secondary-opacity: 0.54;
-    --dark-primary-opacity: 0.87;
+      --paper-pink-50: #fce4ec;
+      --paper-pink-100: #f8bbd0;
+      --paper-pink-200: #f48fb1;
+      --paper-pink-300: #f06292;
+      --paper-pink-400: #ec407a;
+      --paper-pink-500: #e91e63;
+      --paper-pink-600: #d81b60;
+      --paper-pink-700: #c2185b;
+      --paper-pink-800: #ad1457;
+      --paper-pink-900: #880e4f;
+      --paper-pink-a100: #ff80ab;
+      --paper-pink-a200: #ff4081;
+      --paper-pink-a400: #f50057;
+      --paper-pink-a700: #c51162;
 
-    /* opacity for light text on a dark background */
-    --light-divider-opacity: 0.12;
-    --light-disabled-opacity: 0.3; /* or hint text or icon */
-    --light-secondary-opacity: 0.7;
-    --light-primary-opacity: 1.0;
+      --paper-purple-50: #f3e5f5;
+      --paper-purple-100: #e1bee7;
+      --paper-purple-200: #ce93d8;
+      --paper-purple-300: #ba68c8;
+      --paper-purple-400: #ab47bc;
+      --paper-purple-500: #9c27b0;
+      --paper-purple-600: #8e24aa;
+      --paper-purple-700: #7b1fa2;
+      --paper-purple-800: #6a1b9a;
+      --paper-purple-900: #4a148c;
+      --paper-purple-a100: #ea80fc;
+      --paper-purple-a200: #e040fb;
+      --paper-purple-a400: #d500f9;
+      --paper-purple-a700: #aa00ff;
 
-  }
+      --paper-deep-purple-50: #ede7f6;
+      --paper-deep-purple-100: #d1c4e9;
+      --paper-deep-purple-200: #b39ddb;
+      --paper-deep-purple-300: #9575cd;
+      --paper-deep-purple-400: #7e57c2;
+      --paper-deep-purple-500: #673ab7;
+      --paper-deep-purple-600: #5e35b1;
+      --paper-deep-purple-700: #512da8;
+      --paper-deep-purple-800: #4527a0;
+      --paper-deep-purple-900: #311b92;
+      --paper-deep-purple-a100: #b388ff;
+      --paper-deep-purple-a200: #7c4dff;
+      --paper-deep-purple-a400: #651fff;
+      --paper-deep-purple-a700: #6200ea;
 
-</style>
+      --paper-indigo-50: #e8eaf6;
+      --paper-indigo-100: #c5cae9;
+      --paper-indigo-200: #9fa8da;
+      --paper-indigo-300: #7986cb;
+      --paper-indigo-400: #5c6bc0;
+      --paper-indigo-500: #3f51b5;
+      --paper-indigo-600: #3949ab;
+      --paper-indigo-700: #303f9f;
+      --paper-indigo-800: #283593;
+      --paper-indigo-900: #1a237e;
+      --paper-indigo-a100: #8c9eff;
+      --paper-indigo-a200: #536dfe;
+      --paper-indigo-a400: #3d5afe;
+      --paper-indigo-a700: #304ffe;
+
+      --paper-blue-50: #e3f2fd;
+      --paper-blue-100: #bbdefb;
+      --paper-blue-200: #90caf9;
+      --paper-blue-300: #64b5f6;
+      --paper-blue-400: #42a5f5;
+      --paper-blue-500: #2196f3;
+      --paper-blue-600: #1e88e5;
+      --paper-blue-700: #1976d2;
+      --paper-blue-800: #1565c0;
+      --paper-blue-900: #0d47a1;
+      --paper-blue-a100: #82b1ff;
+      --paper-blue-a200: #448aff;
+      --paper-blue-a400: #2979ff;
+      --paper-blue-a700: #2962ff;
+
+      --paper-light-blue-50: #e1f5fe;
+      --paper-light-blue-100: #b3e5fc;
+      --paper-light-blue-200: #81d4fa;
+      --paper-light-blue-300: #4fc3f7;
+      --paper-light-blue-400: #29b6f6;
+      --paper-light-blue-500: #03a9f4;
+      --paper-light-blue-600: #039be5;
+      --paper-light-blue-700: #0288d1;
+      --paper-light-blue-800: #0277bd;
+      --paper-light-blue-900: #01579b;
+      --paper-light-blue-a100: #80d8ff;
+      --paper-light-blue-a200: #40c4ff;
+      --paper-light-blue-a400: #00b0ff;
+      --paper-light-blue-a700: #0091ea;
+
+      --paper-cyan-50: #e0f7fa;
+      --paper-cyan-100: #b2ebf2;
+      --paper-cyan-200: #80deea;
+      --paper-cyan-300: #4dd0e1;
+      --paper-cyan-400: #26c6da;
+      --paper-cyan-500: #00bcd4;
+      --paper-cyan-600: #00acc1;
+      --paper-cyan-700: #0097a7;
+      --paper-cyan-800: #00838f;
+      --paper-cyan-900: #006064;
+      --paper-cyan-a100: #84ffff;
+      --paper-cyan-a200: #18ffff;
+      --paper-cyan-a400: #00e5ff;
+      --paper-cyan-a700: #00b8d4;
+
+      --paper-teal-50: #e0f2f1;
+      --paper-teal-100: #b2dfdb;
+      --paper-teal-200: #80cbc4;
+      --paper-teal-300: #4db6ac;
+      --paper-teal-400: #26a69a;
+      --paper-teal-500: #009688;
+      --paper-teal-600: #00897b;
+      --paper-teal-700: #00796b;
+      --paper-teal-800: #00695c;
+      --paper-teal-900: #004d40;
+      --paper-teal-a100: #a7ffeb;
+      --paper-teal-a200: #64ffda;
+      --paper-teal-a400: #1de9b6;
+      --paper-teal-a700: #00bfa5;
+
+      --paper-green-50: #e8f5e9;
+      --paper-green-100: #c8e6c9;
+      --paper-green-200: #a5d6a7;
+      --paper-green-300: #81c784;
+      --paper-green-400: #66bb6a;
+      --paper-green-500: #4caf50;
+      --paper-green-600: #43a047;
+      --paper-green-700: #388e3c;
+      --paper-green-800: #2e7d32;
+      --paper-green-900: #1b5e20;
+      --paper-green-a100: #b9f6ca;
+      --paper-green-a200: #69f0ae;
+      --paper-green-a400: #00e676;
+      --paper-green-a700: #00c853;
+
+      --paper-light-green-50: #f1f8e9;
+      --paper-light-green-100: #dcedc8;
+      --paper-light-green-200: #c5e1a5;
+      --paper-light-green-300: #aed581;
+      --paper-light-green-400: #9ccc65;
+      --paper-light-green-500: #8bc34a;
+      --paper-light-green-600: #7cb342;
+      --paper-light-green-700: #689f38;
+      --paper-light-green-800: #558b2f;
+      --paper-light-green-900: #33691e;
+      --paper-light-green-a100: #ccff90;
+      --paper-light-green-a200: #b2ff59;
+      --paper-light-green-a400: #76ff03;
+      --paper-light-green-a700: #64dd17;
+
+      --paper-lime-50: #f9fbe7;
+      --paper-lime-100: #f0f4c3;
+      --paper-lime-200: #e6ee9c;
+      --paper-lime-300: #dce775;
+      --paper-lime-400: #d4e157;
+      --paper-lime-500: #cddc39;
+      --paper-lime-600: #c0ca33;
+      --paper-lime-700: #afb42b;
+      --paper-lime-800: #9e9d24;
+      --paper-lime-900: #827717;
+      --paper-lime-a100: #f4ff81;
+      --paper-lime-a200: #eeff41;
+      --paper-lime-a400: #c6ff00;
+      --paper-lime-a700: #aeea00;
+
+      --paper-yellow-50: #fffde7;
+      --paper-yellow-100: #fff9c4;
+      --paper-yellow-200: #fff59d;
+      --paper-yellow-300: #fff176;
+      --paper-yellow-400: #ffee58;
+      --paper-yellow-500: #ffeb3b;
+      --paper-yellow-600: #fdd835;
+      --paper-yellow-700: #fbc02d;
+      --paper-yellow-800: #f9a825;
+      --paper-yellow-900: #f57f17;
+      --paper-yellow-a100: #ffff8d;
+      --paper-yellow-a200: #ffff00;
+      --paper-yellow-a400: #ffea00;
+      --paper-yellow-a700: #ffd600;
+
+      --paper-amber-50: #fff8e1;
+      --paper-amber-100: #ffecb3;
+      --paper-amber-200: #ffe082;
+      --paper-amber-300: #ffd54f;
+      --paper-amber-400: #ffca28;
+      --paper-amber-500: #ffc107;
+      --paper-amber-600: #ffb300;
+      --paper-amber-700: #ffa000;
+      --paper-amber-800: #ff8f00;
+      --paper-amber-900: #ff6f00;
+      --paper-amber-a100: #ffe57f;
+      --paper-amber-a200: #ffd740;
+      --paper-amber-a400: #ffc400;
+      --paper-amber-a700: #ffab00;
+
+      --paper-orange-50: #fff3e0;
+      --paper-orange-100: #ffe0b2;
+      --paper-orange-200: #ffcc80;
+      --paper-orange-300: #ffb74d;
+      --paper-orange-400: #ffa726;
+      --paper-orange-500: #ff9800;
+      --paper-orange-600: #fb8c00;
+      --paper-orange-700: #f57c00;
+      --paper-orange-800: #ef6c00;
+      --paper-orange-900: #e65100;
+      --paper-orange-a100: #ffd180;
+      --paper-orange-a200: #ffab40;
+      --paper-orange-a400: #ff9100;
+      --paper-orange-a700: #ff6500;
+
+      --paper-deep-orange-50: #fbe9e7;
+      --paper-deep-orange-100: #ffccbc;
+      --paper-deep-orange-200: #ffab91;
+      --paper-deep-orange-300: #ff8a65;
+      --paper-deep-orange-400: #ff7043;
+      --paper-deep-orange-500: #ff5722;
+      --paper-deep-orange-600: #f4511e;
+      --paper-deep-orange-700: #e64a19;
+      --paper-deep-orange-800: #d84315;
+      --paper-deep-orange-900: #bf360c;
+      --paper-deep-orange-a100: #ff9e80;
+      --paper-deep-orange-a200: #ff6e40;
+      --paper-deep-orange-a400: #ff3d00;
+      --paper-deep-orange-a700: #dd2c00;
+
+      --paper-brown-50: #efebe9;
+      --paper-brown-100: #d7ccc8;
+      --paper-brown-200: #bcaaa4;
+      --paper-brown-300: #a1887f;
+      --paper-brown-400: #8d6e63;
+      --paper-brown-500: #795548;
+      --paper-brown-600: #6d4c41;
+      --paper-brown-700: #5d4037;
+      --paper-brown-800: #4e342e;
+      --paper-brown-900: #3e2723;
+
+      --paper-grey-50: #fafafa;
+      --paper-grey-100: #f5f5f5;
+      --paper-grey-200: #eeeeee;
+      --paper-grey-300: #e0e0e0;
+      --paper-grey-400: #bdbdbd;
+      --paper-grey-500: #9e9e9e;
+      --paper-grey-600: #757575;
+      --paper-grey-700: #616161;
+      --paper-grey-800: #424242;
+      --paper-grey-900: #212121;
+
+      --paper-blue-grey-50: #eceff1;
+      --paper-blue-grey-100: #cfd8dc;
+      --paper-blue-grey-200: #b0bec5;
+      --paper-blue-grey-300: #90a4ae;
+      --paper-blue-grey-400: #78909c;
+      --paper-blue-grey-500: #607d8b;
+      --paper-blue-grey-600: #546e7a;
+      --paper-blue-grey-700: #455a64;
+      --paper-blue-grey-800: #37474f;
+      --paper-blue-grey-900: #263238;
+
+      /* opacity for dark text on a light background */
+      --dark-divider-opacity: 0.12;
+      --dark-disabled-opacity: 0.38; /* or hint text or icon */
+      --dark-secondary-opacity: 0.54;
+      --dark-primary-opacity: 0.87;
+
+      /* opacity for light text on a dark background */
+      --light-divider-opacity: 0.12;
+      --light-disabled-opacity: 0.3; /* or hint text or icon */
+      --light-secondary-opacity: 0.7;
+      --light-primary-opacity: 1.0;
+
+    }
+
+  </style>
+</custom-style>
diff --git a/third_party/polymer/v1_0/components-chromium/paper-styles/default-theme.html b/third_party/polymer/v1_0/components-chromium/paper-styles/default-theme.html
index cc697281..8242076e 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-styles/default-theme.html
+++ b/third_party/polymer/v1_0/components-chromium/paper-styles/default-theme.html
@@ -13,60 +13,59 @@
 
 <!-- Taken from https://www.google.com/design/spec/style/color.html#color-ui-color-application -->
 
-<style is="custom-style">
+<custom-style>
+  <style is="custom-style">
+    html {
+      /*
+       * You can use these generic variables in your elements for easy theming.
+       * For example, if all your elements use `--primary-text-color` as its main
+       * color, then switching from a light to a dark theme is just a matter of
+       * changing the value of `--primary-text-color` in your application.
+       */
+      --primary-text-color: var(--light-theme-text-color);
+      --primary-background-color: var(--light-theme-background-color);
+      --secondary-text-color: var(--light-theme-secondary-color);
+      --disabled-text-color: var(--light-theme-disabled-color);
+      --divider-color: var(--light-theme-divider-color);
+      --error-color: var(--paper-deep-orange-a700);
 
-  :root {
-    /*
-     * You can use these generic variables in your elements for easy theming.
-     * For example, if all your elements use `--primary-text-color` as its main
-     * color, then switching from a light to a dark theme is just a matter of
-     * changing the value of `--primary-text-color` in your application.
-     */
-    --primary-text-color: var(--light-theme-text-color);
-    --primary-background-color: var(--light-theme-background-color);
-    --secondary-text-color: var(--light-theme-secondary-color);
-    --disabled-text-color: var(--light-theme-disabled-color);
-    --divider-color: var(--light-theme-divider-color);
-    --error-color: var(--paper-deep-orange-a700);
+      /*
+       * Primary and accent colors. Also see color.html for more colors.
+       */
+      --primary-color: var(--paper-indigo-500);
+      --light-primary-color: var(--paper-indigo-100);
+      --dark-primary-color: var(--paper-indigo-700);
 
-    /*
-     * Primary and accent colors. Also see color.html for more colors.
-     */
-    --primary-color: var(--paper-indigo-500);
-    --light-primary-color: var(--paper-indigo-100);
-    --dark-primary-color: var(--paper-indigo-700);
-
-    --accent-color: var(--paper-pink-a200);
-    --light-accent-color: var(--paper-pink-a100);
-    --dark-accent-color: var(--paper-pink-a400);
+      --accent-color: var(--paper-pink-a200);
+      --light-accent-color: var(--paper-pink-a100);
+      --dark-accent-color: var(--paper-pink-a400);
 
 
-    /*
-     * Material Design Light background theme
-     */
-    --light-theme-background-color: #ffffff;
-    --light-theme-base-color: #000000;
-    --light-theme-text-color: var(--paper-grey-900);
-    --light-theme-secondary-color: #737373;  /* for secondary text and icons */
-    --light-theme-disabled-color: #9b9b9b;  /* disabled/hint text */
-    --light-theme-divider-color: #dbdbdb;
+      /*
+       * Material Design Light background theme
+       */
+      --light-theme-background-color: #ffffff;
+      --light-theme-base-color: #000000;
+      --light-theme-text-color: var(--paper-grey-900);
+      --light-theme-secondary-color: #737373;  /* for secondary text and icons */
+      --light-theme-disabled-color: #9b9b9b;  /* disabled/hint text */
+      --light-theme-divider-color: #dbdbdb;
 
-    /*
-     * Material Design Dark background theme
-     */
-    --dark-theme-background-color: var(--paper-grey-900);
-    --dark-theme-base-color: #ffffff;
-    --dark-theme-text-color: #ffffff;
-    --dark-theme-secondary-color: #bcbcbc;  /* for secondary text and icons */
-    --dark-theme-disabled-color: #646464;  /* disabled/hint text */
-    --dark-theme-divider-color: #3c3c3c;
+      /*
+       * Material Design Dark background theme
+       */
+      --dark-theme-background-color: var(--paper-grey-900);
+      --dark-theme-base-color: #ffffff;
+      --dark-theme-text-color: #ffffff;
+      --dark-theme-secondary-color: #bcbcbc;  /* for secondary text and icons */
+      --dark-theme-disabled-color: #646464;  /* disabled/hint text */
+      --dark-theme-divider-color: #3c3c3c;
 
-    /*
-     * Deprecated values because of their confusing names.
-     */
-    --text-primary-color: var(--dark-theme-text-color);
-    --default-primary-color: var(--primary-color);
-
-  }
-
-</style>
+      /*
+       * Deprecated values because of their confusing names.
+       */
+      --text-primary-color: var(--dark-theme-text-color);
+      --default-primary-color: var(--primary-color);
+    }
+  </style>
+</custom-style>
diff --git a/third_party/polymer/v1_0/components-chromium/paper-styles/demo.css b/third_party/polymer/v1_0/components-chromium/paper-styles/demo.css
deleted file mode 100644
index efd8b471..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-styles/demo.css
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
-@license
-Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-
-*/
-body {
-  font-family: 'Roboto', 'Noto', sans-serif;
-  font-size: 14px;
-  margin: 0;
-  padding: 24px;
-}
-
-section {
-  padding: 20px 0;
-}
-
-section > div {
-  padding: 14px;
-  font-size: 16px;
-}
diff --git a/third_party/polymer/v1_0/components-chromium/paper-styles/element-styles/paper-item-styles.html b/third_party/polymer/v1_0/components-chromium/paper-styles/element-styles/paper-item-styles.html
deleted file mode 100644
index 796cd24b..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-styles/element-styles/paper-item-styles.html
+++ /dev/null
@@ -1,90 +0,0 @@
-<!--
-@license
-Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
--->
-
-<link rel="import" href="../../polymer/polymer.html">
-<link rel="import" href="../color.html">
-<link rel="import" href="../default-theme.html">
-<link rel="import" href="../typography.html">
-
-<!--
-Material design: [Lists](https://www.google.com/design/spec/components/lists.html)
-
-Shared styles for a native `button` to be used as an item in a `paper-listbox` element:
-
-    <custom-style>
-      <style is="custom-style" include="paper-item-styles"></style>
-    </custom-style>
-
-    <paper-listbox>
-      <button class="paper-item" role="option">Inbox</button>
-      <button class="paper-item" role="option">Starred</button>
-      <button class="paper-item" role="option">Sent mail</button>
-    </paper-listbox>
-
-@group Paper Elements
-@demo demo/index.html
--->
-
-<dom-module id="paper-item-styles">
-  <template>
-    <style>
-      :host, html {
-        --paper-item: {
-          display: block;
-          position: relative;
-          min-height: var(--paper-item-min-height, 48px);
-          padding: 0px 16px;
-          @apply --paper-font-subhead;
-          border:none;
-          outline: none;
-          background: white;
-          width: 100%;
-          text-align: left;
-        };
-      }
-      .paper-item {
-        @apply --paper-item;
-      }
-
-      .paper-item[hidden] {
-        display: none !important;
-      }
-
-      .paper-item.iron-selected {
-        font-weight: var(--paper-item-selected-weight, bold);
-        @apply --paper-item-selected;
-      }
-
-      .paper-item[disabled] {
-        color: var(--paper-item-disabled-color, var(--disabled-text-color));
-        @apply --paper-item-disabled;
-      }
-
-      .paper-item:focus {
-        position: relative;
-        outline: 0;
-        @apply --paper-item-focused;
-      }
-
-      .paper-item:focus:before {
-        position: absolute;
-        top: 0;
-        left: 0;
-        right: 0;
-        bottom: 0;
-        background: currentColor;
-        content: '';
-        opacity: var(--dark-divider-opacity);
-        pointer-events: none;
-        @apply --paper-item-focused-before;
-      }
-    </style>
-  </template>
-</dom-module>
diff --git a/third_party/polymer/v1_0/components-chromium/paper-styles/element-styles/paper-material-styles.html b/third_party/polymer/v1_0/components-chromium/paper-styles/element-styles/paper-material-styles.html
index 83aee8d5..8f5f637 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-styles/element-styles/paper-material-styles.html
+++ b/third_party/polymer/v1_0/components-chromium/paper-styles/element-styles/paper-material-styles.html
@@ -23,7 +23,7 @@
       <style is="custom-style" include="paper-material-styles"></style>
     </custom-style>
 
-    <div class="paper-material elevation-1">
+    <div class="paper-material" elevation="1">
       ... content ...
     </div>
 
diff --git a/third_party/polymer/v1_0/components-chromium/paper-styles/paper-styles-classes.html b/third_party/polymer/v1_0/components-chromium/paper-styles/paper-styles-classes.html
deleted file mode 100644
index ae315a5..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-styles/paper-styles-classes.html
+++ /dev/null
@@ -1,14 +0,0 @@
-<!--
-@license
-Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
--->
-
-<link rel="import" href="../iron-flex-layout/classes/iron-flex-layout.html">
-
-<link rel="import" href="classes/typography.html">
-<link rel="import" href="classes/shadow.html">
diff --git a/third_party/polymer/v1_0/components-chromium/paper-styles/paper-styles.html b/third_party/polymer/v1_0/components-chromium/paper-styles/paper-styles.html
deleted file mode 100644
index 9eca03a7..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-styles/paper-styles.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<!--
-@license
-Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
--->
-
-<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
-<link rel="import" href="../iron-flex-layout/classes/iron-flex-layout.html">
-
-<!--
-The `<paper-styles>` component provides simple ways to use Material Design CSS styles
-in your application. The following imports are available:
-
-1. [color.html](https://github.com/PolymerElements/paper-styles/blob/master/color.html):
-a complete list of the colors defined in the Material Design [palette](https://www.google.com/design/spec/style/color.html)
-
-2. [default-theme.html](https://github.com/PolymerElements/paper-styles/blob/master/default-theme.html): text,
-background and accent colors that match the default Material Design theme
-
-3. [shadow.html](https://github.com/PolymerElements/paper-styles/blob/master/shadow.html): Material Design
-[elevation](https://www.google.com/design/spec/what-is-material/elevation-shadows.html) and shadow styles
-
-4. [typography.html](https://github.com/PolymerElements/paper-styles/blob/master/typography.html):
-Material Design [font](http://www.google.com/design/spec/style/typography.html#typography-styles) styles and sizes
-
-5. [demo-pages.html](https://github.com/PolymerElements/paper-styles/blob/master/demo-pages.html): generic styles
-used in the PolymerElements demo pages
-
-We recommend importing each of these individual files, and using the style mixins
-available in each ones, rather than the aggregated `paper-styles.html` as a whole.
-
-@group Paper Elements
-@pseudoElement paper-styles
-@demo demo/index.html
--->
-
-<link rel="import" href="color.html">
-<link rel="import" href="default-theme.html">
-<link rel="import" href="shadow.html">
-<link rel="import" href="typography.html">
diff --git a/third_party/polymer/v1_0/components-chromium/paper-styles/shadow.html b/third_party/polymer/v1_0/components-chromium/paper-styles/shadow.html
index 7e0546e..0e4e3cb 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-styles/shadow.html
+++ b/third_party/polymer/v1_0/components-chromium/paper-styles/shadow.html
@@ -10,67 +10,67 @@
 
 <link rel="import" href="../polymer/polymer.html">
 
-<style is="custom-style">
+<custom-style>
+  <style is="custom-style">
+    html {
 
-  :root {
+      --shadow-transition: {
+        transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
+      };
 
-    --shadow-transition: {
-      transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
-    };
+      --shadow-none: {
+        box-shadow: none;
+      };
 
-    --shadow-none: {
-      box-shadow: none;
-    };
+      /* from http://codepen.io/shyndman/pen/c5394ddf2e8b2a5c9185904b57421cdb */
 
-    /* from http://codepen.io/shyndman/pen/c5394ddf2e8b2a5c9185904b57421cdb */
+      --shadow-elevation-2dp: {
+        box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14),
+                    0 1px 5px 0 rgba(0, 0, 0, 0.12),
+                    0 3px 1px -2px rgba(0, 0, 0, 0.2);
+      };
 
-    --shadow-elevation-2dp: {
-      box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14),
-                  0 1px 5px 0 rgba(0, 0, 0, 0.12),
-                  0 3px 1px -2px rgba(0, 0, 0, 0.2);
-    };
+      --shadow-elevation-3dp: {
+        box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14),
+                    0 1px 8px 0 rgba(0, 0, 0, 0.12),
+                    0 3px 3px -2px rgba(0, 0, 0, 0.4);
+      };
 
-    --shadow-elevation-3dp: {
-      box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14),
-                  0 1px 8px 0 rgba(0, 0, 0, 0.12),
-                  0 3px 3px -2px rgba(0, 0, 0, 0.4);
-    };
+      --shadow-elevation-4dp: {
+        box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14),
+                    0 1px 10px 0 rgba(0, 0, 0, 0.12),
+                    0 2px 4px -1px rgba(0, 0, 0, 0.4);
+      };
 
-    --shadow-elevation-4dp: {
-      box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14),
-                  0 1px 10px 0 rgba(0, 0, 0, 0.12),
-                  0 2px 4px -1px rgba(0, 0, 0, 0.4);
-    };
+      --shadow-elevation-6dp: {
+        box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14),
+                    0 1px 18px 0 rgba(0, 0, 0, 0.12),
+                    0 3px 5px -1px rgba(0, 0, 0, 0.4);
+      };
 
-    --shadow-elevation-6dp: {
-      box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14),
-                  0 1px 18px 0 rgba(0, 0, 0, 0.12),
-                  0 3px 5px -1px rgba(0, 0, 0, 0.4);
-    };
+      --shadow-elevation-8dp: {
+        box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14),
+                    0 3px 14px 2px rgba(0, 0, 0, 0.12),
+                    0 5px 5px -3px rgba(0, 0, 0, 0.4);
+      };
 
-    --shadow-elevation-8dp: {
-      box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14),
-                  0 3px 14px 2px rgba(0, 0, 0, 0.12),
-                  0 5px 5px -3px rgba(0, 0, 0, 0.4);
-    };
+      --shadow-elevation-12dp: {
+        box-shadow: 0 12px 16px 1px rgba(0, 0, 0, 0.14),
+                    0 4px 22px 3px rgba(0, 0, 0, 0.12),
+                    0 6px 7px -4px rgba(0, 0, 0, 0.4);
+      };
 
-    --shadow-elevation-12dp: {
-      box-shadow: 0 12px 16px 1px rgba(0, 0, 0, 0.14),
-                  0 4px 22px 3px rgba(0, 0, 0, 0.12),
-                  0 6px 7px -4px rgba(0, 0, 0, 0.4);
-    };
+      --shadow-elevation-16dp: {
+        box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14),
+                    0  6px 30px 5px rgba(0, 0, 0, 0.12),
+                    0  8px 10px -5px rgba(0, 0, 0, 0.4);
+      };
 
-    --shadow-elevation-16dp: {
-      box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14),
-                  0  6px 30px 5px rgba(0, 0, 0, 0.12),
-                  0  8px 10px -5px rgba(0, 0, 0, 0.4);
-    };
-
-    --shadow-elevation-24dp: {
-      box-shadow: 0 24px 38px 3px rgba(0, 0, 0, 0.14),
-                  0 9px 46px 8px rgba(0, 0, 0, 0.12),
-                  0 11px 15px -7px rgba(0, 0, 0, 0.4);
-    };
-  }
-
-</style>
+      --shadow-elevation-24dp: {
+        box-shadow: 0 24px 38px 3px rgba(0, 0, 0, 0.14),
+                    0 9px 46px 8px rgba(0, 0, 0, 0.12),
+                    0 11px 15px -7px rgba(0, 0, 0, 0.4);
+      };
+    }
+  </style>
+</custom-style>
diff --git a/third_party/polymer/v1_0/components-chromium/paper-styles/typography.html b/third_party/polymer/v1_0/components-chromium/paper-styles/typography.html
index 055b5f9..689ae97 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-styles/typography.html
+++ b/third_party/polymer/v1_0/components-chromium/paper-styles/typography.html
@@ -11,159 +11,168 @@
 <link rel="import" href="../polymer/polymer.html">
 <link rel="import" href="../font-roboto/roboto.html">
 
-<style is="custom-style">
+<!--
+Typographic styles are provided matching the Material Design standard styles:
+http://www.google.com/design/spec/style/typography.html#typography-standard-styles
 
-  :root {
+Note that these are English/Latin centric styles. You may need to further adjust
+line heights and weights for CJK typesetting. See the notes in the Material
+Design typography section.
+-->
+<custom-style>
+  <style is="custom-style">
+    html {
 
-    /* Shared Styles */
-    --paper-font-common-base: {
-      font-family: 'Roboto', 'Noto', sans-serif;
-      -webkit-font-smoothing: antialiased;
-    };
+      /* Shared Styles */
+      --paper-font-common-base: {
+        font-family: 'Roboto', 'Noto', sans-serif;
+        -webkit-font-smoothing: antialiased;
+      };
 
-    --paper-font-common-code: {
-      font-family: 'Roboto Mono', 'Consolas', 'Menlo', monospace;
-      -webkit-font-smoothing: antialiased;
-    };
+      --paper-font-common-code: {
+        font-family: 'Roboto Mono', 'Consolas', 'Menlo', monospace;
+        -webkit-font-smoothing: antialiased;
+      };
 
-    --paper-font-common-expensive-kerning: {
-      text-rendering: optimizeLegibility;
-    };
+      --paper-font-common-expensive-kerning: {
+        text-rendering: optimizeLegibility;
+      };
 
-    --paper-font-common-nowrap: {
-      white-space: nowrap;
-      overflow: hidden;
-      text-overflow: ellipsis;
-    };
+      --paper-font-common-nowrap: {
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      };
 
-    /* Material Font Styles */
+      /* Material Font Styles */
 
-    --paper-font-display4: {
-      @apply(--paper-font-common-base);
-      @apply(--paper-font-common-nowrap);
+      --paper-font-display4: {
+        @apply --paper-font-common-base;
+        @apply --paper-font-common-nowrap;
 
-      font-size: 112px;
-      font-weight: 300;
-      letter-spacing: -.044em;
-      line-height: 120px;
-    };
+        font-size: 112px;
+        font-weight: 300;
+        letter-spacing: -.044em;
+        line-height: 120px;
+      };
 
-    --paper-font-display3: {
-      @apply(--paper-font-common-base);
-      @apply(--paper-font-common-nowrap);
+      --paper-font-display3: {
+        @apply --paper-font-common-base;
+        @apply --paper-font-common-nowrap;
 
-      font-size: 56px;
-      font-weight: 400;
-      letter-spacing: -.026em;
-      line-height: 60px;
-    };
+        font-size: 56px;
+        font-weight: 400;
+        letter-spacing: -.026em;
+        line-height: 60px;
+      };
 
-    --paper-font-display2: {
-      @apply(--paper-font-common-base);
+      --paper-font-display2: {
+        @apply --paper-font-common-base;
 
-      font-size: 45px;
-      font-weight: 400;
-      letter-spacing: -.018em;
-      line-height: 48px;
-    };
+        font-size: 45px;
+        font-weight: 400;
+        letter-spacing: -.018em;
+        line-height: 48px;
+      };
 
-    --paper-font-display1: {
-      @apply(--paper-font-common-base);
+      --paper-font-display1: {
+        @apply --paper-font-common-base;
 
-      font-size: 34px;
-      font-weight: 400;
-      letter-spacing: -.01em;
-      line-height: 40px;
-    };
+        font-size: 34px;
+        font-weight: 400;
+        letter-spacing: -.01em;
+        line-height: 40px;
+      };
 
-    --paper-font-headline: {
-      @apply(--paper-font-common-base);
+      --paper-font-headline: {
+        @apply --paper-font-common-base;
 
-      font-size: 24px;
-      font-weight: 400;
-      letter-spacing: -.012em;
-      line-height: 32px;
-    };
+        font-size: 24px;
+        font-weight: 400;
+        letter-spacing: -.012em;
+        line-height: 32px;
+      };
 
-    --paper-font-title: {
-      @apply(--paper-font-common-base);
-      @apply(--paper-font-common-nowrap);
+      --paper-font-title: {
+        @apply --paper-font-common-base;
+        @apply --paper-font-common-nowrap;
 
-      font-size: 20px;
-      font-weight: 500;
-      line-height: 28px;
-    };
+        font-size: 20px;
+        font-weight: 500;
+        line-height: 28px;
+      };
 
-    --paper-font-subhead: {
-      @apply(--paper-font-common-base);
+      --paper-font-subhead: {
+        @apply --paper-font-common-base;
 
-      font-size: 16px;
-      font-weight: 400;
-      line-height: 24px;
-    };
+        font-size: 16px;
+        font-weight: 400;
+        line-height: 24px;
+      };
 
-    --paper-font-body2: {
-      @apply(--paper-font-common-base);
+      --paper-font-body2: {
+        @apply --paper-font-common-base;
 
-      font-size: 14px;
-      font-weight: 500;
-      line-height: 24px;
-    };
+        font-size: 14px;
+        font-weight: 500;
+        line-height: 24px;
+      };
 
-    --paper-font-body1: {
-      @apply(--paper-font-common-base);
+      --paper-font-body1: {
+        @apply --paper-font-common-base;
 
-      font-size: 14px;
-      font-weight: 400;
-      line-height: 20px;
-    };
+        font-size: 14px;
+        font-weight: 400;
+        line-height: 20px;
+      };
 
-    --paper-font-caption: {
-      @apply(--paper-font-common-base);
-      @apply(--paper-font-common-nowrap);
+      --paper-font-caption: {
+        @apply --paper-font-common-base;
+        @apply --paper-font-common-nowrap;
 
-      font-size: 12px;
-      font-weight: 400;
-      letter-spacing: 0.011em;
-      line-height: 20px;
-    };
+        font-size: 12px;
+        font-weight: 400;
+        letter-spacing: 0.011em;
+        line-height: 20px;
+      };
 
-    --paper-font-menu: {
-      @apply(--paper-font-common-base);
-      @apply(--paper-font-common-nowrap);
+      --paper-font-menu: {
+        @apply --paper-font-common-base;
+        @apply --paper-font-common-nowrap;
 
-      font-size: 13px;
-      font-weight: 500;
-      line-height: 24px;
-    };
+        font-size: 13px;
+        font-weight: 500;
+        line-height: 24px;
+      };
 
-    --paper-font-button: {
-      @apply(--paper-font-common-base);
-      @apply(--paper-font-common-nowrap);
+      --paper-font-button: {
+        @apply --paper-font-common-base;
+        @apply --paper-font-common-nowrap;
 
-      font-size: 14px;
-      font-weight: 500;
-      letter-spacing: 0.018em;
-      line-height: 24px;
-      text-transform: uppercase;
-    };
+        font-size: 14px;
+        font-weight: 500;
+        letter-spacing: 0.018em;
+        line-height: 24px;
+        text-transform: uppercase;
+      };
 
-    --paper-font-code2: {
-      @apply(--paper-font-common-code);
+      --paper-font-code2: {
+        @apply --paper-font-common-code;
 
-      font-size: 14px;
-      font-weight: 700;
-      line-height: 20px;
-    };
+        font-size: 14px;
+        font-weight: 700;
+        line-height: 20px;
+      };
 
-    --paper-font-code1: {
-      @apply(--paper-font-common-code);
+      --paper-font-code1: {
+        @apply --paper-font-common-code;
 
-      font-size: 14px;
-      font-weight: 500;
-      line-height: 20px;
-    };
+        font-size: 14px;
+        font-weight: 500;
+        line-height: 20px;
+      };
 
-  }
+    }
 
-</style>
+  </style>
+</custom-style>
diff --git a/third_party/polymer/v1_0/components_summary.txt b/third_party/polymer/v1_0/components_summary.txt
index f9905d49..41292c8 100644
--- a/third_party/polymer/v1_0/components_summary.txt
+++ b/third_party/polymer/v1_0/components_summary.txt
@@ -270,9 +270,9 @@
 
 Name: paper-styles
 Repository: https://github.com/PolymerElements/paper-styles.git
-Tree: v1.3.1
-Revision: 1ed324102daa36458d153dd20eac80be9758b00e
-Tree link: https://github.com/PolymerElements/paper-styles/tree/v1.3.1
+Tree: v2.1.0
+Revision: fc4b85ab853f107fc8bb18ea6cda1c2e9ac5a75f
+Tree link: https://github.com/PolymerElements/paper-styles/tree/v2.1.0
 
 Name: paper-tabs
 Repository: https://github.com/PolymerElements/paper-tabs.git
diff --git a/third_party/polymer/v1_0/rsync_exclude.txt b/third_party/polymer/v1_0/rsync_exclude.txt
index fdb5dacb..0cde7a0 100644
--- a/third_party/polymer/v1_0/rsync_exclude.txt
+++ b/third_party/polymer/v1_0/rsync_exclude.txt
@@ -33,4 +33,8 @@
 paper-spinner/paper-spinner.html
 
 # paper-styles
-paper-styles/classes/shadow-layout.html
+paper-styles/classes/global.html
+paper-styles/element-styles/paper-item-styles.html
+paper-styles/gen-tsd.json
+paper-styles/paper-styles-classes.html
+paper-styles/paper-styles.html
diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn
index 789ad62..cf339d4 100644
--- a/third_party/zlib/BUILD.gn
+++ b/third_party/zlib/BUILD.gn
@@ -152,13 +152,13 @@
         "contrib/optimizations/inflate.c",
       ]
 
-      # TODO(772870) back off from -O3 while investigating Android
-      # One perf bot PNG decode regression.
-      # if (!is_debug) {
-      #  # Use optimize_speed (-O3) to output the _smallest_ code.
-      #  configs -= [ "//build/config/compiler:default_optimization" ]
-      #  configs += [ "//build/config/compiler:optimize_speed" ]
-      # }
+      if (!is_debug) {
+        # Here we trade better performance on newer/bigger ARMv8 cores
+        # for less perf on ARMv7. For details, check:
+        # https://bugs.chromium.org/p/chromium/issues/detail?id=772870#c40
+        configs -= [ "//build/config/compiler:default_optimization" ]
+        configs += [ "//build/config/compiler:optimize_speed" ]
+      }
     }
   }
 
diff --git a/tools/binary_size/libsupersize/apkanalyzer.py b/tools/binary_size/libsupersize/apkanalyzer.py
index f25ca6e..e69e843 100644
--- a/tools/binary_size/libsupersize/apkanalyzer.py
+++ b/tools/binary_size/libsupersize/apkanalyzer.py
@@ -110,6 +110,8 @@
       else:
         # Sibling or higher nodes
         break
+    assert total_child_size <= size, (
+        'Child node total size exceeded parent node total size')
     node_size = size - total_child_size
     nodes.append((name, node_size))
     return next_idx, size
@@ -128,7 +130,7 @@
   total_node_size = sum(map(lambda x: x[1], nodes))
   assert dex_expected_size >= total_node_size, (
       'Node size too large, check for node processing errors.')
-  # We have 1+MB of just ids for methods, strings
+  # We have more than 100KB of ids for methods, strings
   id_metadata_overhead_size = dex_expected_size - total_node_size
   symbols = []
   for name, node_size in nodes:
diff --git a/tools/binary_size/libsupersize/archive.py b/tools/binary_size/libsupersize/archive.py
index f822def..bcd3491 100644
--- a/tools/binary_size/libsupersize/archive.py
+++ b/tools/binary_size/libsupersize/archive.py
@@ -456,15 +456,15 @@
   seen_sections = []
   for i, symbol in enumerate(raw_symbols[1:]):
     prev_symbol = raw_symbols[i]
+    if symbol.IsOverhead():
+      # Overhead symbols are not actionable so should be padding-only.
+      symbol.padding = symbol.size
+      continue
     if prev_symbol.section_name != symbol.section_name:
       assert symbol.section_name not in seen_sections, (
           'Input symbols must be sorted by section, then address.')
       seen_sections.append(symbol.section_name)
       continue
-    if symbol.full_name.startswith('Overhead: '):
-      # Overhead symbols are not actionable so should be padding-only.
-      symbol.padding = symbol.size
-      continue
     if (symbol.address <= 0 or prev_symbol.address <= 0 or
         not symbol.IsNative() or not prev_symbol.IsNative()):
       continue
diff --git a/tools/binary_size/libsupersize/file_format.py b/tools/binary_size/libsupersize/file_format.py
index 3ad8b53a..6ee2465 100644
--- a/tools/binary_size/libsupersize/file_format.py
+++ b/tools/binary_size/libsupersize/file_format.py
@@ -72,8 +72,9 @@
 
   write_numeric(lambda s: s.address, delta=True)
   _LogSize(file_obj, 'addresses')  # For libchrome, adds 300kb.
-  # Do not write padding, it will be recalcualted from addresses on load.
-  write_numeric(lambda s: s.size_without_padding)
+  # Do not write padding except for overhead symbols, it will be recalculated
+  # from addresses on load.
+  write_numeric(lambda s: s.size if s.IsOverhead() else s.size_without_padding)
   _LogSize(file_obj, 'sizes')  # For libchrome, adds 300kb
   write_numeric(lambda s: path_tuples[(s.object_path, s.source_path)],
                 delta=True)
diff --git a/tools/binary_size/libsupersize/models.py b/tools/binary_size/libsupersize/models.py
index 02fb888..1c98514 100644
--- a/tools/binary_size/libsupersize/models.py
+++ b/tools/binary_size/libsupersize/models.py
@@ -291,6 +291,9 @@
   def IsNative(self):
     return self.section_name in NATIVE_SECTIONS
 
+  def IsOverhead(self):
+    return self.full_name.startswith('Overhead: ')
+
   def IsGroup(self):
     return False
 
diff --git a/tools/binary_size/libsupersize/testdata/Archive_Elf.golden b/tools/binary_size/libsupersize/testdata/Archive_Elf.golden
index f15f974..01b6cef 100644
--- a/tools/binary_size/libsupersize/testdata/Archive_Elf.golden
+++ b/tools/binary_size/libsupersize/testdata/Archive_Elf.golden
@@ -32,7 +32,7 @@
 * Contains 0 aliases
 * 0 symbols have shared ownership
 Section .other: has 100.0% of 33984171 bytes accounted for from 1 symbols. 0 bytes are unaccounted for.
-* Padding accounts for 0 bytes (0.0%)
+* Padding accounts for 33984171 bytes (100.0%)
 * Contains 0 aliases
 * 0 symbols have shared ownership
 .data@2de7000(size_without_padding=4,padding=0,full_name=google::protobuf::internal::pLinuxKernelCmpxchg,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1)
@@ -50,7 +50,7 @@
 .data.rel.ro.local@2c17740(size_without_padding=789904,padding=0,full_name=chrome::mojom::FieldTrialRecorderProxy [vtable],object_path=third_party/WebKit.a/ContiguousContainer.o,source_path=third_party/container.c,flags={},num_aliases=1)
 .data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1)
 .data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1)
-.other@0(size_without_padding=33984171,padding=0,full_name=Overhead: ELF file,object_path=,source_path=,flags={},num_aliases=1)
+.other@0(size_without_padding=0,padding=33984171,full_name=Overhead: ELF file,object_path=,source_path=,flags={},num_aliases=1)
 .rodata@266e600(size_without_padding=5,padding=0,full_name=string literal,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=2)
 .rodata@266e600(size_without_padding=5,padding=0,full_name=string literal,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=2)
 .rodata@266e605(size_without_padding=16,padding=0,full_name=string literal,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1)
diff --git a/tools/binary_size/libsupersize/testdata/Archive_Pak.golden b/tools/binary_size/libsupersize/testdata/Archive_Pak.golden
index 5d6a1f7..ac6faf10 100644
--- a/tools/binary_size/libsupersize/testdata/Archive_Pak.golden
+++ b/tools/binary_size/libsupersize/testdata/Archive_Pak.golden
@@ -36,7 +36,7 @@
 * Contains 0 aliases
 * 0 symbols have shared ownership
 Section .other: has 100.0% of 33984171 bytes accounted for from 1 symbols. 0 bytes are unaccounted for.
-* Padding accounts for 0 bytes (0.0%)
+* Padding accounts for 33984171 bytes (100.0%)
 * Contains 0 aliases
 * 0 symbols have shared ownership
 .data@2de7000(size_without_padding=4,padding=0,full_name=google::protobuf::internal::pLinuxKernelCmpxchg,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=1)
@@ -54,7 +54,7 @@
 .data.rel.ro.local@2c17740(size_without_padding=789904,padding=0,full_name=chrome::mojom::FieldTrialRecorderProxy [vtable],object_path=third_party/WebKit.a/ContiguousContainer.o,source_path=third_party/container.c,flags={},num_aliases=1)
 .data.rel.ro.local@2cd84e0(size_without_padding=16,padding=16,full_name=.Lswitch.table.45,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libcontroller_api_impl.a_controller_api_impl.o,source_path=,flags={},num_aliases=1)
 .data.rel.ro.local@2cd84f0(size_without_padding=8,padding=0,full_name=kSystemClassPrefixes,object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o,source_path=,flags={anon},num_aliases=1)
-.other@0(size_without_padding=33984171,padding=0,full_name=Overhead: ELF file,object_path=,source_path=,flags={},num_aliases=1)
+.other@0(size_without_padding=0,padding=33984171,full_name=Overhead: ELF file,object_path=,source_path=,flags={},num_aliases=1)
 .rodata@266e600(size_without_padding=5,padding=0,full_name=string literal,object_path=base/base/page_allocator.o,source_path=base/page_allocator.cc,flags={},num_aliases=2)
 .rodata@266e600(size_without_padding=5,padding=0,full_name=string literal,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=2)
 .rodata@266e605(size_without_padding=16,padding=0,full_name=string literal,object_path=third_party/icu/icuuc/ucnv_ext.o,source_path=third_party/icu/ucnv_ext.c,flags={gen},num_aliases=1)
diff --git a/tools/binary_size/libsupersize/testdata/Console.golden b/tools/binary_size/libsupersize/testdata/Console.golden
index e4b6002..c09ee5c 100644
--- a/tools/binary_size/libsupersize/testdata/Console.golden
+++ b/tools/binary_size/libsupersize/testdata/Console.golden
@@ -2,7 +2,7 @@
 Entering interactive Python shell. Quick reference:
 
 SizeInfo: metadata, native_symbols, pak_symbols, raw_symbols, section_sizes, size_path, symbols
-Symbol: FlagsString, IsBss, IsDelta, IsDex, IsGeneratedByToolchain, IsGroup, IsNative, IsOther, IsPak, IsStringLiteral, IterLeafSymbols, address, aliases, end_address, flags, full_name, generated_source, is_anonymous, name, num_aliases, object_path, padding, padding_pss, pss, pss_without_padding, section, section_name, size, size_without_padding, source_path, template_name
+Symbol: FlagsString, IsBss, IsDelta, IsDex, IsGeneratedByToolchain, IsGroup, IsNative, IsOther, IsOverhead, IsPak, IsStringLiteral, IterLeafSymbols, address, aliases, end_address, flags, full_name, generated_source, is_anonymous, name, num_aliases, object_path, padding, padding_pss, pss, pss_without_padding, section, section_name, size, size_without_padding, source_path, template_name
 
 SymbolGroup (extends Symbol): CountUniqueSymbols, Filter, GroupedBy, GroupedByAliases, GroupedByFullName, GroupedByName, GroupedByPath, GroupedBySectionName, Inverted, IterUniqueSymbols, SetName, Sorted, SortedByAddress, SortedByCount, SortedByName, WhereAddressInRange, WhereFullNameMatches, WhereGeneratedByToolchain, WhereHasAnyAttribution, WhereHasPath, WhereInSection, WhereIsGroup, WhereIsNative, WhereIsPak, WhereIsTemplate, WhereMatches, WhereNameMatches, WhereObjectPathMatches, WherePathMatches, WherePssBiggerThan, WhereSizeBiggerThan, WhereSourceIsGenerated, WhereSourcePathMatches, WhereTemplateNameMatches, index, is_sorted
 
diff --git a/tools/binary_size/libsupersize/testdata/Csv.golden b/tools/binary_size/libsupersize/testdata/Csv.golden
index b4d68c3..3dac1cf2 100644
--- a/tools/binary_size/libsupersize/testdata/Csv.golden
+++ b/tools/binary_size/libsupersize/testdata/Csv.golden
@@ -26,7 +26,7 @@
 ,0x2c17740,789904,0,1,789904.0,R,chrome::mojom::FieldTrialRecorderProxy [vtable]
 ,0x2cd84e0,16,16,1,32.0,R,.Lswitch.table.45
 ,0x2cd84f0,8,0,1,8.0,R,kSystemClassPrefixes
-,0x0,33984171,0,1,33984171.0,o,Overhead: ELF file
+,0x0,0,33984171,1,33984171.0,o,Overhead: ELF file
 ,0x266e600,5,0,2,2.5,r,string literal
 ,0x266e600,5,0,2,2.5,r,string literal
 ,0x266e605,16,0,1,16.0,r,string literal
diff --git a/tools/binary_size/libsupersize/testdata/FullDescription.golden b/tools/binary_size/libsupersize/testdata/FullDescription.golden
index e4488a5..0d9c3427 100644
--- a/tools/binary_size/libsupersize/testdata/FullDescription.golden
+++ b/tools/binary_size/libsupersize/testdata/FullDescription.golden
@@ -67,7 +67,7 @@
 * Contains 0 aliases
 * 0 symbols have shared ownership
 Section .other: has 100.0% of 33984171 bytes accounted for from 1 symbols. 0 bytes are unaccounted for.
-* Padding accounts for 0 bytes (0.0%)
+* Padding accounts for 33984171 bytes (100.0%)
 * Contains 0 aliases
 * 0 symbols have shared ownership
 
@@ -127,7 +127,7 @@
 14)   1957016 (2.5%)  R@0x2cd84f0  pss=8  padding=0  num_aliases=1
              source_path= 	object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o
              flags={anon}  name=kSystemClassPrefixes
-15)  35941187 (46.2%) o@0x0        pss=33984171  padding=0  num_aliases=1
+15)  35941187 (46.2%) o@0x0        pss=33984171  padding=33984171  num_aliases=1
              source_path= 	object_path=
              flags={}  name=Overhead: ELF file
 16)  35941189 (46.2%) r@0x266e600  pss=2.5 (size=5)  padding=0  num_aliases=2
@@ -309,7 +309,7 @@
 14)   1957016 (2.5%)  R@0x2cd84f0  pss=8  padding=0  num_aliases=1
              source_path= 	object_path=third_party/gvr-android-sdk/libgvr_shim_static_arm.a/libport_android_jni.a_jni_utils.o
              flags={anon}  name=kSystemClassPrefixes
-15)  35941187 (46.2%) o@0x0        pss=33984171  padding=0  num_aliases=1
+15)  35941187 (46.2%) o@0x0        pss=33984171  padding=33984171  num_aliases=1
              source_path= 	object_path=
              flags={}  name=Overhead: ELF file
 16)  35941189 (46.2%) r@0x266e600  pss=2.5 (size=5)  padding=0  num_aliases=2
diff --git a/tools/chrome_proxy/webdriver/client_config.py b/tools/chrome_proxy/webdriver/client_config.py
index 79cdbe2d..cff3eb8 100644
--- a/tools/chrome_proxy/webdriver/client_config.py
+++ b/tools/chrome_proxy/webdriver/client_config.py
@@ -6,6 +6,7 @@
 from common import TestDriver
 from common import IntegrationTest
 from decorators import ChromeVersionEqualOrAfterM
+from decorators import SkipIfForcedBrowserArg
 import json
 
 
@@ -31,6 +32,7 @@
         self.assertEqual(200, response.status)
 
   # Ensure Chrome uses a direct connection when no valid client config is given.
+  @SkipIfForcedBrowserArg('data-reduction-proxy-config-url')
   def testNoClientConfigUseDirect(self):
     with TestDriver() as t:
       t.AddChromeArg('--enable-spdy-proxy-auth')
diff --git a/tools/chrome_proxy/webdriver/decorator_smoke.py b/tools/chrome_proxy/webdriver/decorator_smoke.py
index 04979ff5..acc7fbf9 100644
--- a/tools/chrome_proxy/webdriver/decorator_smoke.py
+++ b/tools/chrome_proxy/webdriver/decorator_smoke.py
@@ -2,15 +2,21 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import sys
+
 from decorators import AndroidOnly
 from decorators import ChromeVersionEqualOrAfterM
 from decorators import ChromeVersionBeforeM
+from decorators import SkipIfForcedBrowserArg
 from common import ParseFlags
 from common import IntegrationTest
 
 
 class DecoratorSmokeTest(IntegrationTest):
 
+  def setUp(self):
+    sys.argv.append('--browser_arg=test')
+
   def AndroidOnlyFunction(self):
     # This function should never be called.
     self.fail()
@@ -31,5 +37,9 @@
     self.fail('This function should not be called when the Chrome Milestone is '
       'less than 999999999')
 
+  @SkipIfForcedBrowserArg('test')
+  def testSkipBrowserArg(self):
+    self.fail('This function should not be called')
+
 if __name__ == '__main__':
   IntegrationTest.RunAllTests()
diff --git a/tools/chrome_proxy/webdriver/decorators.py b/tools/chrome_proxy/webdriver/decorators.py
index e3e716b..03083f04 100644
--- a/tools/chrome_proxy/webdriver/decorators.py
+++ b/tools/chrome_proxy/webdriver/decorators.py
@@ -125,3 +125,12 @@
     else:
       func(*args, **kwargs)
   return wrapper
+
+def SkipIfForcedBrowserArg(arg):
+  def puesdo_wrapper(func):
+    def wrapper(*args, **kwargs):
+      if ParseFlags().browser_args and arg in ParseFlags().browser_args:
+        args[0].skipTest(
+          'Test skipped because "%s" was given on the command line' % arg)
+    return wrapper
+  return puesdo_wrapper
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index de99374..be302c4 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -5025,6 +5025,14 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="FileBrowser.SelectSearch">
+  <owner>sashab@chromium.org</owner>
+  <description>
+    Chrome OS Files App: When the user focused the search field at the top of
+    the file manager app.
+  </description>
+</action>
+
 <action name="FileBrowser.SuggestApps.ShowDialog">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index fd641ed..5074fd5 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -4208,6 +4208,11 @@
   <int value="1" label="Unresponsive"/>
 </enum>
 
+<enum name="BooleanUpload">
+  <int value="0" label="Download"/>
+  <int value="1" label="Upload"/>
+</enum>
+
 <enum name="BooleanUsage">
   <int value="0" label="Not Used"/>
   <int value="1" label="Used"/>
@@ -7223,6 +7228,7 @@
 <enum name="CookieLoadProblem">
   <int value="0" label="Entry decryption failed"/>
   <int value="1" label="Entry decryption taking over a minute"/>
+  <int value="2" label="Cookie canonical form check failed"/>
 </enum>
 
 <enum name="CookieOrCacheDeletion">
@@ -14820,6 +14826,7 @@
   <int value="1227" label="VIRTUALKEYBOARDPRIVATE_SETCONTAINERBEHAVIOR"/>
   <int value="1228" label="QUICKUNLOCKPRIVATE_GETAUTHTOKEN"/>
   <int value="1229" label="QUICKUNLOCKPRIVATE_SETLOCKSCREENENABLED"/>
+  <int value="1230" label="LANGUAGESETTINGSPRIVATE_RETRYDOWNLOADDICTIONARY"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -17980,6 +17987,7 @@
   <int value="2395" label="KeyboardApiUnlock"/>
   <int value="2396" label="PPAPIURLRequestStreamToFile"/>
   <int value="2397" label="PaymentHandler"/>
+  <int value="2398" label="PaymentRequestShowWithoutGesture"/>
 </enum>
 
 <enum name="FeedbackSource">
@@ -18856,6 +18864,12 @@
   <int value="6" label="Error"/>
 </enum>
 
+<enum name="FileManagerListType">
+  <int value="0" label="Uninitialized"/>
+  <int value="1" label="List view (detail)"/>
+  <int value="2" label="Grid view (thumbnail)"/>
+</enum>
+
 <enum name="FileManagerMenuCommands">
   <int value="0" label="Help"/>
   <int value="1" label="Help (In Google Drive)"/>
@@ -26196,7 +26210,6 @@
   <int value="-886912558" label="ChromeHomePromo:enabled"/>
   <int value="-885601782" label="enable-contextual-search"/>
   <int value="-884864731" label="WebPaymentsSingleAppUiSkip:enabled"/>
-  <int value="-881854123" label="enable-heap-profiling"/>
   <int value="-881447505" label="ash-disable-shelf-model-synchronization"/>
   <int value="-881054479" label="WebAssemblyStreaming:disabled"/>
   <int value="-879055117" label="ClipboardContentSetting:enabled"/>
@@ -26521,6 +26534,7 @@
   <int value="-88273414" label="ContentSuggestionsShowSummary:enabled"/>
   <int value="-86788587" label="allow-autofill-sync-credential"/>
   <int value="-80353187" label="disable-display-color-calibration"/>
+  <int value="-79327236" label="ModeSpecificPowerButton:enabled"/>
   <int value="-78035185" label="custom_summary"/>
   <int value="-77872983" label="BookmarkAppsMac:disabled"/>
   <int value="-76631048" label="disable-offline-auto-reload-visible-only"/>
@@ -26540,6 +26554,7 @@
       label="ContentSuggestionsThumbnailDominantColor:enabled"/>
   <int value="-50021298" label="ash-adjustable-large-cursor"/>
   <int value="-48920737" label="enable-smooth-scrolling"/>
+  <int value="-45849989" label="ModeSpecificPowerButton:disabled"/>
   <int value="-45532639" label="enable-default-media-session"/>
   <int value="-45074716" label="SystemDownloadManager:disabled"/>
   <int value="-45067971" label="NewPrintPreview:disabled"/>
@@ -41570,6 +41585,41 @@
   <int value="2" label="Completed Later"/>
 </enum>
 
+<enum name="SrtpErrorCode">
+  <summary>
+    Error codes reported by libsrtp (defined in
+    third_party/libsrtp/include/srtp.h).
+  </summary>
+  <int value="0" label="Nothing to report"/>
+  <int value="1" label="Unspecified failure"/>
+  <int value="2" label="Unsupported parameter"/>
+  <int value="3" label="Couldn't allocate memory"/>
+  <int value="4" label="Couldn't deallocate properly"/>
+  <int value="5" label="Couldn't initialize"/>
+  <int value="6" label="Can't process as much data as requested"/>
+  <int value="7" label="Authentication failure"/>
+  <int value="8" label="Cipher failure"/>
+  <int value="9" label="Replay check failed (bad index)"/>
+  <int value="10" label="Replay check failed (index too old)"/>
+  <int value="11" label="Algorithm failed test routine"/>
+  <int value="12" label="Unsupported operation"/>
+  <int value="13" label="No appropriate context found"/>
+  <int value="14" label="Unable to perform desired validation"/>
+  <int value="15" label="Can't use key any more"/>
+  <int value="16" label="Error in use of socket"/>
+  <int value="17" label="Error in use POSIX signals"/>
+  <int value="18" label="Nonce check failed"/>
+  <int value="19" label="Couldn't read data"/>
+  <int value="20" label="Couldn't write data"/>
+  <int value="21" label="Error parsing data"/>
+  <int value="22" label="Error encoding data"/>
+  <int value="23" label="Error while using semaphores"/>
+  <int value="24" label="Error while using pfkey"/>
+  <int value="25" label="Error MKI present in packet is invalid"/>
+  <int value="26" label="Packet index is too old to consider"/>
+  <int value="27" label="Packet index advanced, reset needed"/>
+</enum>
+
 <enum name="SRTPromptUsage">
   <obsolete>
     Deprecated as of 2017-11-01. Replaced with
@@ -44401,6 +44451,16 @@
   <int value="5" label="Critical"/>
 </enum>
 
+<enum name="UploadAcceptedCardOrigin">
+  <int value="0" label="Upload was accepted for existing local card"/>
+  <int value="1" label="Upload was accepted for newly-seen card"/>
+</enum>
+
+<enum name="UploadOfferedCardOrigin">
+  <int value="0" label="Upload was offered for existing local card"/>
+  <int value="1" label="Upload was offered for newly-seen card"/>
+</enum>
+
 <enum name="URLRequestAnnotationType">
 <!-- Not updated since 3/4/2018 as histogram is obsolete.-->
 
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 7403fa6..5992cef 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -6432,6 +6432,24 @@
   </summary>
 </histogram>
 
+<histogram name="Autofill.UploadAcceptedCardOrigin"
+    enum="UploadAcceptedCardOrigin">
+  <owner>jsaul@google.com</owner>
+  <summary>
+    Measures if a card for which upload was accepted is already stored as a
+    local card on the device or if it has not yet been seen.
+  </summary>
+</histogram>
+
+<histogram name="Autofill.UploadOfferedCardOrigin"
+    enum="UploadOfferedCardOrigin">
+  <owner>jsaul@google.com</owner>
+  <summary>
+    Measures if a card for which upload was offered is already stored as a local
+    on the device or if it has not yet been seen.
+  </summary>
+</histogram>
+
 <histogram name="Autofill.UserHappiness" enum="AutofillUserHappiness">
   <owner>isherman@chromium.org</owner>
   <summary>
@@ -18815,6 +18833,16 @@
   <summary>The completion type for downloads in download service.</summary>
 </histogram>
 
+<histogram name="Download.Service.OnUploadDataReceived.PauseReason"
+    enum="Download.Service.PauseReason">
+  <owner>shaktisahu@chromium.org</owner>
+  <summary>
+    The reason for suspending a download right after the upload data is received
+    from the client. Every pause will result in two entries in the histogram:
+    ANY and a more specific reason.
+  </summary>
+</histogram>
+
 <histogram name="Download.Service.PauseReason"
     enum="Download.Service.PauseReason">
   <owner>shaktisahu@chromium.org</owner>
@@ -18899,6 +18927,28 @@
   </summary>
 </histogram>
 
+<histogram name="Download.Service.Upload.EntryNotFound" enum="BooleanHit">
+  <owner>shaktisahu@chromium.org</owner>
+  <summary>
+    Records if the entry has been deleted for some reason while we are waiting
+    for the client to respond with the upload data.
+  </summary>
+</histogram>
+
+<histogram base="true" name="Download.Service.Upload.HasUploadData"
+    enum="BooleanUpload">
+<!-- Name completed by histogram_suffixes
+     name="Download.Service.Client" -->
+
+  <owner>shaktisahu@chromium.org</owner>
+  <summary>
+    The download service uses a single code path for both downloading and
+    uploading data.  This metric records whether a given request includes upload
+    data. It is recorded upon  starting a download or upload, immediately after
+    the client indicates whether the request includes upload data.
+  </summary>
+</histogram>
+
 <histogram name="Download.Shelf.DragEvent" enum="Download.Shelf.DragEvent">
   <owner>sdy@chromium.org</owner>
   <summary>
@@ -27052,6 +27102,14 @@
   </summary>
 </histogram>
 
+<histogram name="FileBrowser.ToggleFileListType" enum="FileManagerListType">
+  <owner>sashab@chromium.org</owner>
+  <summary>
+    Chrome OS Files App: Recorded when the Grid View/List View toggle menu icon
+    is selected.
+  </summary>
+</histogram>
+
 <histogram name="FileBrowser.ViewingFileType" enum="ViewFileType">
   <owner>joshwoodward@google.com</owner>
   <summary>
@@ -47586,6 +47644,16 @@
   </summary>
 </histogram>
 
+<histogram name="Net.QuicSession.ConnectionMigrationProbeSuccess"
+    enum="BooleanSuccess">
+  <owner>fayang@chromium.org</owner>
+  <summary>
+    The result of connectivity probing according to different migration causes.
+    Recorded for every connectivity probing when attempting connection
+    migrations.
+  </summary>
+</histogram>
+
 <histogram name="Net.QuicSession.ConnectionTypeFromPeer" enum="AddressFamily">
   <owner>rch@chromium.org</owner>
   <summary>
@@ -102317,6 +102385,15 @@
   </summary>
 </histogram>
 
+<histogram name="WebRTC.PeerConnection.SrtcpUnprotectError"
+    enum="SrtpErrorCode">
+  <owner>steveanton@chromium.org</owner>
+  <summary>
+    What error code is reported by libsrtp when failing to unprotect an incoming
+    SRTCP (secured media control) packet.
+  </summary>
+</histogram>
+
 <histogram name="WebRTC.PeerConnection.SrtpCryptoSuite"
     enum="DTLS_SRTPCryptoSuite">
   <owner>guoweis@chromium.org</owner>
@@ -102326,6 +102403,14 @@
   </summary>
 </histogram>
 
+<histogram name="WebRTC.PeerConnection.SrtpUnprotectError" enum="SrtpErrorCode">
+  <owner>steveanton@chromium.org</owner>
+  <summary>
+    What error code is reported by libsrtp when failing to unprotect an incoming
+    SRTP (secured media) packet.
+  </summary>
+</histogram>
+
 <histogram name="WebRTC.PeerConnection.SslCipherSuite" enum="SSLCipherSuite">
   <owner>guoweis@chromium.org</owner>
   <summary>
@@ -105776,6 +105861,7 @@
   <suffix name="OnMigrateBackToDefaultNetwork"/>
   <suffix name="OnPathDegrading"/>
   <affected-histogram name="Net.QuicSession.ConnectionMigration"/>
+  <affected-histogram name="Net.QuicSession.ConnectionMigrationProbeSuccess"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="ConnectivityDiagnostics" separator=".">
@@ -107054,6 +107140,7 @@
   <affected-histogram name="Download.Service.Request.ClientAction"/>
   <affected-histogram name="Download.Service.Request.StartResponse"/>
   <affected-histogram name="Download.Service.Request.StartResult"/>
+  <affected-histogram name="Download.Service.Upload.HasUploadData"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="Download.Service.CompletionType" separator=".">
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index 3122719..9218d8f 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -17,6 +17,7 @@
 blink_perf.shadow_dom,hayato@chromium.org,
 blink_perf.svg,"kouhei@chromium.org, fs@opera.com",
 cc_perftests,enne@chromium.org,
+components_perftests,csharrison@chromium.org,
 dromaeo,"jbroman@chromium.org, yukishiino@chromium.org, haraken@chromium.org",
 dummy_benchmark.histogram_benchmark_1,"eakuefner@chromium.org, simonhatch@chromium.org",
 dummy_benchmark.noisy_benchmark_1,nednguyen@google.com,
@@ -102,7 +103,6 @@
 v8.browsing_desktop-future,"mythria@chromium.org, ulan@chromium.org",
 v8.browsing_mobile,"mythria@chromium.org, ulan@chromium.org",
 v8.browsing_mobile-future,"mythria@chromium.org, ulan@chromium.org",
-v8.detached_context_age_in_gc,ulan@chromium.org,
 v8.runtime_stats.top_25,cbruni@chromium.org,
 validating_command_buffer_perftests,"piman@chromium.org, chrome-gpu-perf-owners@chromium.org",Internals>GPU
 views_perftests,tapted@chromium.org,Internals>Views
diff --git a/tools/perf/benchmarks/memory.py b/tools/perf/benchmarks/memory.py
index 5a1e2a84..7870c80 100644
--- a/tools/perf/benchmarks/memory.py
+++ b/tools/perf/benchmarks/memory.py
@@ -89,14 +89,6 @@
     return page_sets.TrivialSitesStorySet(wait_in_seconds=0,
                                           measure_memory=True)
 
-  def SetExtraBrowserOptions(self, options):
-    super(MemoryBenchmarkTrivialSitesDesktop, self).SetExtraBrowserOptions(
-          options)
-    # Heap profiling is disabled because of crbug.com/757847.
-    #options.AppendExtraBrowserArgs([
-    #  '--enable-heap-profiling=native',
-    #])
-
   @classmethod
   def Name(cls):
     return 'memory.desktop'
diff --git a/tools/perf/benchmarks/system_health.py b/tools/perf/benchmarks/system_health.py
index 62739ec1..ab763da 100644
--- a/tools/perf/benchmarks/system_health.py
+++ b/tools/perf/benchmarks/system_health.py
@@ -126,14 +126,6 @@
   def Name(cls):
     return 'system_health.memory_desktop'
 
-  def SetExtraBrowserOptions(self, options):
-    super(DesktopMemorySystemHealth, self).SetExtraBrowserOptions(
-          options)
-    # Heap profiling is disabled because of crbug.com/757847.
-    #options.AppendExtraBrowserArgs([
-    #  '--enable-heap-profiling=native',
-    #])
-
 
 @benchmark.Owner(emails=['perezju@chromium.org'])
 class MobileMemorySystemHealth(_MemorySystemHealthBenchmark):
diff --git a/tools/perf/benchmarks/v8.py b/tools/perf/benchmarks/v8.py
index f4e807d0..787fa886 100644
--- a/tools/perf/benchmarks/v8.py
+++ b/tools/perf/benchmarks/v8.py
@@ -4,7 +4,6 @@
 from core import perf_benchmark
 
 
-from measurements import v8_detached_context_age_in_gc
 import page_sets
 
 from telemetry import benchmark
@@ -12,19 +11,6 @@
 from telemetry.web_perf import timeline_based_measurement
 
 
-@benchmark.Owner(emails=['ulan@chromium.org'])
-class V8DetachedContextAgeInGC(perf_benchmark.PerfBenchmark):
-  """Measures the number of GCs needed to collect a detached context.
-
-  http://www.chromium.org/developers/design-documents/rendering-benchmarks"""
-  test = v8_detached_context_age_in_gc.V8DetachedContextAgeInGC
-  page_set = page_sets.PageReloadCasesPageSet
-
-  @classmethod
-  def Name(cls):
-    return 'v8.detached_context_age_in_gc'
-
-
 class _Top25RuntimeStats(perf_benchmark.PerfBenchmark):
   def SetExtraBrowserOptions(self, options):
     options.AppendExtraBrowserArgs(
diff --git a/tools/perf/contrib/heap_profiling/heap_profiling.py b/tools/perf/contrib/heap_profiling/heap_profiling.py
index c60f4c5..8c95ac8b 100644
--- a/tools/perf/contrib/heap_profiling/heap_profiling.py
+++ b/tools/perf/contrib/heap_profiling/heap_profiling.py
@@ -63,9 +63,12 @@
     super(_HeapProfilingBenchmark, self).SetExtraBrowserOptions(options)
     args = []
     if self.PROFILING_MODE == 'pseudo':
-      args += ['--enable-heap-profiling']
+      args += [
+          '--memlog=all', '--memlog-stack-mode=pseudo', '--memlog-sampling']
     elif self.PROFILING_MODE == 'native':
-      args += ['--enable-heap-profiling=native']
+      args += [
+          '--memlog=all', '--memlog-stack-mode=native-with-thread-names',
+          '--memlog-sampling']
     options.AppendExtraBrowserArgs(args)
 
 
diff --git a/tools/perf/contrib/memory_extras/memory_extras.py b/tools/perf/contrib/memory_extras/memory_extras.py
index f46092f..42f44bf 100644
--- a/tools/perf/contrib/memory_extras/memory_extras.py
+++ b/tools/perf/contrib/memory_extras/memory_extras.py
@@ -82,7 +82,9 @@
   def SetExtraBrowserOptions(self, options):
     super(LongRunningMemoryBenchmarkSitesDesktop, self).SetExtraBrowserOptions(
         options)
-    options.AppendExtraBrowserArgs(['--enable-heap-profiling=native'])
+    options.AppendExtraBrowserArgs([
+        '--memlog=all', '--memlog-sampling',
+        '--memlog-stack-mode=native-with-thread-names'])
     # Disable taking screenshot on failing pages.
     options.take_screenshot_for_failed_page = False
 
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index d0c2b5bf..36c99d6 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -136,6 +136,7 @@
          ('gpu_perftests', 'build73-b1--device2'),
          #  ('cc_perftests', 'build73-b1--device2'),  # crbug.com/721757
          ('media_perftests', 'build74-b1--device7'),
+         ('components_perftests', 'build74-b1--device1'),
        ],
        'perf_tests_with_args': [
          ('angle_perftests', 'build73-b1--device4', ['--shard-timeout=300'],
@@ -164,6 +165,7 @@
          ('tracing_perftests', 'build13-b1--device2'),
          ('gpu_perftests', 'build13-b1--device2'),
          ('cc_perftests', 'build13-b1--device2'),
+         ('components_perftests', 'build48-b1--device5'),
         ]
       }
     ])
@@ -316,7 +318,8 @@
           ],
        'perf_tests': [
          ('media_perftests', 'build189-a9'),
-         ('views_perftests', 'build190-a9')]
+         ('views_perftests', 'build190-a9'),
+         ('components_perftests', 'build191-a9')]
       }
     ])
   waterfall = add_tester(
@@ -352,7 +355,8 @@
          ('load_library_perf_tests', 'build187-m1'),
          # crbug.com/735679
          # ('performance_browser_tests', 'build187-m1'),
-         ('media_perftests', 'build188-m1')]
+         ('media_perftests', 'build188-m1'),
+         ('components_perftests', 'build189-m1')]
       }
     ])
   waterfall = add_tester(
@@ -906,7 +910,9 @@
     'performance_browser_tests': BenchmarkMetadata(
         'miu@chromium.org', None, False),
     'views_perftests': BenchmarkMetadata(
-        'tapted@chromium.org', 'Internals>Views', False)
+        'tapted@chromium.org', 'Internals>Views', False),
+    'components_perftests': BenchmarkMetadata(
+        'csharrison@chromium.org', None, False)
 }
 
 
diff --git a/tools/perf/measurements/v8_detached_context_age_in_gc.py b/tools/perf/measurements/v8_detached_context_age_in_gc.py
deleted file mode 100644
index 1fc443d1..0000000
--- a/tools/perf/measurements/v8_detached_context_age_in_gc.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import json
-
-from telemetry.page import legacy_page_test
-from telemetry.value import histogram_util
-from telemetry.value import scalar
-
-_NAME = 'V8.DetachedContextAgeInGC'
-_UNITS = 'garbage_collections'
-_DISPLAY_NAME = 'V8_DetachedContextAgeInGC'
-_TYPE = histogram_util.RENDERER_HISTOGRAM
-_DESCRIPTION = 'Number of GCs needed to collect detached context'
-
-
-def _GetMaxDetachedContextAge(tab, data_start):
-  data = histogram_util.GetHistogram(_TYPE, _NAME, tab)
-  delta = histogram_util.SubtractHistogram(data, data_start)
-  if not 'buckets' in delta:
-    return
-  buckets = json.loads(delta)['buckets']
-  if buckets:
-    return max(x.get('high', x['low']) for x in buckets)
-
-
-class V8DetachedContextAgeInGC(legacy_page_test.LegacyPageTest):
-
-  def __init__(self):
-    super(V8DetachedContextAgeInGC, self).__init__()
-    self._data_start = None
-
-  def CustomizeBrowserOptions(self, options):
-    options.AppendExtraBrowserArgs(['--enable-stats-collection-bindings'])
-
-  def DidNavigateToPage(self, page, tab):
-    del page  # unused
-    self._data_start = histogram_util.GetHistogram(_TYPE, _NAME, tab)
-
-  def ValidateAndMeasurePage(self, page, tab, results):
-    del page  # unused
-    # Trigger GC to get histogram data.
-    # Seven GCs should be enough to collect any detached context.
-    # If a detached context survives more GCs then there is a leak.
-    MAX_AGE = 8
-    for _ in xrange(MAX_AGE):
-      tab.CollectGarbage()
-    value = _GetMaxDetachedContextAge(tab, self._data_start)
-    if value is None:
-      results.Skip('No detached contexts')
-    else:
-      results.AddValue(scalar.ScalarValue(
-          results.current_page, _DISPLAY_NAME, _UNITS, value,
-          description=_DESCRIPTION))
diff --git a/tools/perf/measurements/v8_detached_context_age_in_gc_unittest.py b/tools/perf/measurements/v8_detached_context_age_in_gc_unittest.py
deleted file mode 100644
index 63f3168..0000000
--- a/tools/perf/measurements/v8_detached_context_age_in_gc_unittest.py
+++ /dev/null
@@ -1,105 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-from telemetry.internal.results import page_test_results
-from telemetry.page import page as page_module
-from telemetry.testing import options_for_unittests
-from telemetry.testing import page_test_test_case
-from telemetry.util import wpr_modes
-
-from measurements import v8_detached_context_age_in_gc
-
-
-class FakePage(page_module.Page):
-
-  def __init__(self, url):
-    super(FakePage, self).__init__(url=url, name=url)
-
-  @property
-  def is_file(self):
-    return self._url.startswith('file://')
-
-
-class FakeTab(object):
-
-  def __init__(self, histograms):
-    self.histograms = histograms
-    self.current_histogram_index = 0
-
-  def EvaluateJavaScript(self, script, **kwargs):
-    histogram_name = 'V8.DetachedContextAgeInGC'
-    if kwargs.get('name') == histogram_name or histogram_name in script:
-      self.current_histogram_index += 1
-      return self.histograms[self.current_histogram_index - 1]
-    return '{}'
-
-  def CollectGarbage(self):
-    pass
-
-
-def _MeasureFakePage(histograms):
-  results = page_test_results.PageTestResults()
-  page = FakePage('file://blank.html')
-  tab = FakeTab(histograms)
-  metric = v8_detached_context_age_in_gc.V8DetachedContextAgeInGC()
-  results.WillRunPage(page)
-  metric.DidNavigateToPage(page, tab)
-  metric.ValidateAndMeasurePage(page, tab, results)
-  results.DidRunPage(page)
-  return results
-
-
-def _ActualValues(results):
-  return dict((v.name, v) for v in results.all_page_specific_values)
-
-
-class SimplePage(page_module.Page):
-
-  def __init__(self, page_set):
-    super(SimplePage, self).__init__(
-        'file://host.html', page_set, page_set.base_dir, name='host.html')
-
-  def RunPageInteractions(self, action_runner):
-    # Reload the page to detach the previous context.
-    action_runner.ReloadPage()
-
-
-class V8DetachedContextAgeInGCTests(page_test_test_case.PageTestTestCase):
-
-  def setUp(self):
-    self._options = options_for_unittests.GetCopy()
-    self._options.browser_options.wpr_mode = wpr_modes.WPR_OFF
-
-  def testWithNoData(self):
-    histograms = [
-        """{"count": 0, "buckets": []}""",
-        """{"count": 0, "buckets": []}"""
-    ]
-    results = _MeasureFakePage(histograms)
-    actual = _ActualValues(results)
-    self.assertTrue('skip' in actual)
-    self.assertFalse('V8_DetachedContextAgeInGC' in actual)
-
-  def testWithData(self):
-    histograms = [
-        """{"count": 3, "buckets": [{"low": 1, "high": 2, "count": 1},
-                                    {"low": 2, "high": 3, "count": 2}]}""",
-        """{"count": 4, "buckets": [{"low": 1, "high": 2, "count": 2},
-                                    {"low": 2, "high": 3, "count": 2}]}""",
-    ]
-    results = _MeasureFakePage(histograms)
-    actual = _ActualValues(results)['V8_DetachedContextAgeInGC']
-    self.assertEqual(2, actual.value)
-    self.assertEqual('garbage_collections', actual.units)
-
-  def testWithSimplePage(self):
-    page_set = self.CreateEmptyPageSet()
-    page = SimplePage(page_set)
-    page_set.AddStory(page)
-    metric = v8_detached_context_age_in_gc.V8DetachedContextAgeInGC()
-    results = self.RunMeasurement(metric, page_set, options=self._options)
-    self.assertFalse(results.had_failures)
-    actual = _ActualValues(results)['V8_DetachedContextAgeInGC']
-    self.assertLessEqual(0, actual.value)
-    self.assertEqual('garbage_collections', actual.units)
diff --git a/tools/perf/page_sets/page_reload_cases.py b/tools/perf/page_sets/page_reload_cases.py
deleted file mode 100644
index 9851401..0000000
--- a/tools/perf/page_sets/page_reload_cases.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-from telemetry.page import shared_page_state
-from telemetry import story
-
-from page_sets import top_pages
-
-
-def _Reload(action_runner):
-  # Numbers below are chosen arbitrarily. For the V8DetachedContextAgeInGC
-  # the number of reloads should be high enough so that V8 could do few
-  # incremental GCs.
-  NUMBER_OF_RELOADS = 7
-  WAIT_TIME = 2
-  for _ in xrange(NUMBER_OF_RELOADS):
-    action_runner.ReloadPage()
-    action_runner.Wait(WAIT_TIME)
-
-
-def _CreatePageClassWithReload(page_cls):
-  class DerivedSmoothPage(page_cls):  # pylint: disable=no-init
-
-    def RunPageInteractions(self, action_runner):
-      _Reload(action_runner)
-  return DerivedSmoothPage
-
-
-class PageReloadCasesPageSet(story.StorySet):
-
-  """ Pages for testing GC efficiency on page reload. """
-
-  def __init__(self):
-    super(PageReloadCasesPageSet, self).__init__(
-        archive_data_file='data/top_25.json',
-        cloud_storage_bucket=story.PARTNER_BUCKET)
-
-    shared_desktop_state = shared_page_state.SharedDesktopPageState
-
-    self.AddStory(_CreatePageClassWithReload(
-        top_pages.GoogleWebSearchPage)(self, shared_desktop_state))
-    self.AddStory(_CreatePageClassWithReload(
-        top_pages.GoogleDocPage)(self, shared_desktop_state))
diff --git a/ui/app_list/views/app_list_page.cc b/ui/app_list/views/app_list_page.cc
index 2140fd4..e2fd0627 100644
--- a/ui/app_list/views/app_list_page.cc
+++ b/ui/app_list/views/app_list_page.cc
@@ -34,11 +34,6 @@
   return GetSearchBoxBounds();
 }
 
-gfx::Rect AppListPage::GetPageBoundsDuringDragging(
-    ash::AppListState state) const {
-  return GetPageBoundsForState(state);
-}
-
 views::View* AppListPage::GetSelectedView() const {
   return nullptr;
 }
@@ -51,6 +46,10 @@
   return nullptr;
 }
 
+bool AppListPage::ShouldShowSearchBox() const {
+  return true;
+}
+
 gfx::Rect AppListPage::GetAboveContentsOffscreenBounds(
     const gfx::Size& size) const {
   gfx::Rect rect(size);
diff --git a/ui/app_list/views/app_list_page.h b/ui/app_list/views/app_list_page.h
index 692eeef..e1e408a4 100644
--- a/ui/app_list/views/app_list_page.h
+++ b/ui/app_list/views/app_list_page.h
@@ -16,6 +16,9 @@
 
 class APP_LIST_EXPORT AppListPage : public views::View {
  public:
+  AppListPage();
+  ~AppListPage() override;
+
   // Triggered when the page is about to be shown.
   virtual void OnWillBeShown();
 
@@ -43,9 +46,6 @@
   // Returns where this page should move to when the given state is active.
   virtual gfx::Rect GetPageBoundsForState(ash::AppListState state) const = 0;
 
-  // Returns the bounds of the page during dragging.
-  virtual gfx::Rect GetPageBoundsDuringDragging(ash::AppListState state) const;
-
   const ContentsView* contents_view() const { return contents_view_; }
   void set_contents_view(ContentsView* contents_view) {
     contents_view_ = contents_view;
@@ -60,9 +60,8 @@
   // Returns the last focusable view in this page.
   virtual views::View* GetLastFocusableView();
 
- protected:
-  AppListPage();
-  ~AppListPage() override;
+  // Returns true if the search box should be shown in this page.
+  virtual bool ShouldShowSearchBox() const;
 
   // Returns the area above the contents view, given the desired size of this
   // page, in the contents view's coordinate space.
diff --git a/ui/app_list/views/apps_container_view.cc b/ui/app_list/views/apps_container_view.cc
index 4375f474..eb3ae813 100644
--- a/ui/app_list/views/apps_container_view.cc
+++ b/ui/app_list/views/apps_container_view.cc
@@ -17,6 +17,7 @@
 #include "ui/app_list/views/apps_grid_view.h"
 #include "ui/app_list/views/contents_view.h"
 #include "ui/app_list/views/folder_background_view.h"
+#include "ui/app_list/views/horizontal_page_container.h"
 #include "ui/app_list/views/page_switcher.h"
 #include "ui/app_list/views/search_box_view.h"
 #include "ui/app_list/views/suggestions_container_view.h"
@@ -28,6 +29,15 @@
 
 namespace app_list {
 
+// Initial search box top padding in shelf mode.
+constexpr int kSearchBoxInitalTopPadding = 12;
+
+// Top padding of search box in peeking state.
+constexpr int kSearchBoxPeekingTopPadding = 24;
+
+// Minimum top padding of search box in fullscreen state.
+constexpr int kSearchBoxMinimumTopPadding = 24;
+
 AppsContainerView::AppsContainerView(ContentsView* contents_view,
                                      AppListModel* model)
     : contents_view_(contents_view) {
@@ -210,6 +220,142 @@
       this, GetWidget(), false /* reverse */, false /* dont_loop */);
 }
 
+gfx::Rect AppsContainerView::GetPageBoundsForState(
+    ash::AppListState state) const {
+  if (contents_view_->app_list_view()->is_in_drag())
+    return GetPageBoundsDuringDragging(state);
+
+  gfx::Rect bounds = parent()->GetContentsBounds();
+  bounds.ClampToCenteredSize(GetPreferredSize());
+
+  // AppsContainerView page is shown in both STATE_START and STATE_APPS.
+  if (state == ash::AppListState::kStateApps ||
+      state == ash::AppListState::kStateStart) {
+    // The bottom left point relative to |contents_view_|.
+    gfx::Point bottom_left = GetSearchBoxBoundsForState(state).bottom_left();
+    if (state == ash::AppListState::kStateStart) {
+      bottom_left.Offset(
+          0, kSearchBoxPeekingBottomPadding - kSearchBoxBottomPadding);
+    }
+    ConvertPointToTarget(contents_view_, parent(), &bottom_left);
+    bounds.set_y(bottom_left.y());
+  }
+
+  return bounds;
+}
+
+gfx::Rect AppsContainerView::GetSearchBoxBoundsForState(
+    ash::AppListState state) const {
+  gfx::Rect search_box_bounds(contents_view_->GetDefaultSearchBoxBounds());
+  bool is_in_drag = contents_view_->app_list_view()->is_in_drag();
+  if (is_in_drag) {
+    search_box_bounds.set_y(GetSearchBoxTopPaddingDuringDragging());
+  } else {
+    if (state == ash::AppListState::kStateStart)
+      search_box_bounds.set_y(kSearchBoxPeekingTopPadding);
+    else
+      search_box_bounds.set_y(GetSearchBoxFinalTopPadding());
+  }
+
+  return search_box_bounds;
+}
+
+int AppsContainerView::GetSearchBoxFinalTopPadding() const {
+  gfx::Rect search_box_bounds(contents_view_->GetDefaultSearchBoxBounds());
+  const int total_height =
+      GetPreferredSize().height() + search_box_bounds.height();
+
+  // Makes search box and content vertically centered in contents_view.
+  int y = std::max(search_box_bounds.y(),
+                   (contents_view_->GetDisplayHeight() - total_height) / 2);
+
+  // Top padding of the searchbox should not be smaller than
+  // |kSearchBoxMinimumTopPadding|
+  return std::max(y, kSearchBoxMinimumTopPadding);
+}
+
+int AppsContainerView::GetSearchBoxTopPaddingDuringDragging() const {
+  float searchbox_final_y = GetSearchBoxFinalTopPadding();
+  float peeking_to_fullscreen_height =
+      contents_view_->GetDisplayHeight() - kPeekingAppListHeight;
+  float drag_amount = std::max(
+      0, contents_view_->app_list_view()->GetScreenBottom() - kShelfSize -
+             contents_view_->app_list_view()->app_list_y_position_in_screen());
+
+  if (drag_amount <= (kPeekingAppListHeight - kShelfSize)) {
+    // App list is dragged from collapsed to peeking, which moved up at most
+    // |kPeekingAppListHeight - kShelfSize| (272px). The top padding of search
+    // box changes from |kSearchBoxInitalTopPadding| to
+    // |kSearchBoxPeekingTopPadding|,
+    return std::ceil(
+        (kSearchBoxPeekingTopPadding - kSearchBoxInitalTopPadding) +
+        ((kSearchBoxPeekingTopPadding - kSearchBoxInitalTopPadding) *
+         drag_amount) /
+            (kPeekingAppListHeight - kShelfSize));
+  } else {
+    // App list is dragged from peeking to fullscreen, which moved up at most
+    // |peeking_to_fullscreen_height|. The top padding of search box changes
+    // from |kSearchBoxPeekingTopPadding| to |searchbox_final_y|.
+    int y = (kSearchBoxPeekingTopPadding +
+             std::ceil((searchbox_final_y - kSearchBoxPeekingTopPadding) *
+                       (drag_amount - (kPeekingAppListHeight - kShelfSize)) /
+                       peeking_to_fullscreen_height));
+    y = std::max(kSearchBoxPeekingTopPadding,
+                 std::min<int>(searchbox_final_y, y));
+    return y;
+  }
+}
+
+gfx::Rect AppsContainerView::GetPageBoundsDuringDragging(
+    ash::AppListState state) const {
+  float app_list_y_position_in_screen =
+      contents_view_->app_list_view()->app_list_y_position_in_screen();
+  float drag_amount =
+      std::max(0.f, contents_view_->app_list_view()->GetScreenBottom() -
+                        kShelfSize - app_list_y_position_in_screen);
+
+  float y = 0;
+  float peeking_final_y =
+      kSearchBoxPeekingTopPadding + search_box::kSearchBoxPreferredHeight +
+      kSearchBoxPeekingBottomPadding - kSearchBoxBottomPadding;
+  if (drag_amount <= (kPeekingAppListHeight - kShelfSize)) {
+    // App list is dragged from collapsed to peeking, which moved up at most
+    // |kPeekingAppListHeight - kShelfSize| (272px). The top padding of apps
+    // container view changes from |-kSearchBoxFullscreenBottomPadding| to
+    // |kSearchBoxPeekingTopPadding + kSearchBoxPreferredHeight +
+    // kSearchBoxPeekingBottomPadding - kSearchBoxFullscreenBottomPadding|.
+    y = std::ceil(((peeking_final_y + kSearchBoxBottomPadding) * drag_amount) /
+                      (kPeekingAppListHeight - kShelfSize) -
+                  kSearchBoxBottomPadding);
+  } else {
+    // App list is dragged from peeking to fullscreen, which moved up at most
+    // |peeking_to_fullscreen_height|. The top padding of apps container view
+    // changes from |peeking_final_y| to |final_y|.
+    float final_y =
+        GetSearchBoxFinalTopPadding() + search_box::kSearchBoxPreferredHeight;
+    float peeking_to_fullscreen_height =
+        contents_view_->GetDisplayHeight() - kPeekingAppListHeight;
+    y = std::ceil((final_y - peeking_final_y) *
+                      (drag_amount - (kPeekingAppListHeight - kShelfSize)) /
+                      peeking_to_fullscreen_height +
+                  peeking_final_y);
+    y = std::max(std::min(final_y, y), peeking_final_y);
+  }
+
+  gfx::Rect bounds = parent()->GetContentsBounds();
+  bounds.ClampToCenteredSize(GetPreferredSize());
+
+  // AppsContainerView page is shown in both STATE_START and STATE_APPS.
+  if (state == ash::AppListState::kStateApps ||
+      state == ash::AppListState::kStateStart) {
+    gfx::Point point(0, y);
+    ConvertPointToTarget(contents_view_, parent(), &point);
+    bounds.set_y(point.y());
+  }
+
+  return bounds;
+}
+
 void AppsContainerView::SetShowState(ShowState show_state,
                                      bool show_apps_with_animation) {
   if (show_state_ == show_state)
diff --git a/ui/app_list/views/apps_container_view.h b/ui/app_list/views/apps_container_view.h
index a101a511cb..ce7efa4 100644
--- a/ui/app_list/views/apps_container_view.h
+++ b/ui/app_list/views/apps_container_view.h
@@ -77,6 +77,11 @@
   // HorizontalPage overrides:
   void OnWillBeHidden() override;
   views::View* GetFirstFocusableView() override;
+  gfx::Rect GetPageBoundsForState(ash::AppListState state) const override;
+
+  // Returns the expected search box bounds in the screen when the given state
+  // is active.
+  gfx::Rect GetSearchBoxBoundsForState(ash::AppListState state) const;
 
   AppsGridView* apps_grid_view() { return apps_grid_view_; }
   FolderBackgroundView* folder_background_view() {
@@ -94,6 +99,15 @@
 
   void SetShowState(ShowState show_state, bool show_apps_with_animation);
 
+  // Gets the final top padding of search box.
+  int GetSearchBoxFinalTopPadding() const;
+
+  // Gets the top padding of search box during dragging.
+  int GetSearchBoxTopPaddingDuringDragging() const;
+
+  // Returns the bounds of the page in the parent view during dragging.
+  gfx::Rect GetPageBoundsDuringDragging(ash::AppListState state) const;
+
   ContentsView* contents_view_;  // Not owned.
 
   // The views below are owned by views hierarchy.
diff --git a/ui/app_list/views/assistant_container_view.cc b/ui/app_list/views/assistant_container_view.cc
index 7cb6d3f..68069e42 100644
--- a/ui/app_list/views/assistant_container_view.cc
+++ b/ui/app_list/views/assistant_container_view.cc
@@ -17,7 +17,11 @@
     return gfx::Size();
   }
   return gfx::Size(contents_view_->GetDisplayWidth(),
-                   kHorizontalPagePreferredHeight);
+                   contents_view_->GetDisplayHeight());
+}
+
+bool AssistantContainerView::ShouldShowSearchBox() const {
+  return false;
 }
 
 }  // namespace app_list
diff --git a/ui/app_list/views/assistant_container_view.h b/ui/app_list/views/assistant_container_view.h
index 31b144ea6..cfcc455 100644
--- a/ui/app_list/views/assistant_container_view.h
+++ b/ui/app_list/views/assistant_container_view.h
@@ -20,6 +20,9 @@
   // Overridden from views::View.
   gfx::Size CalculatePreferredSize() const override;
 
+  // Overridden from HorizontalPage.
+  bool ShouldShowSearchBox() const override;
+
  private:
   ContentsView* const contents_view_;  // Not owned.
 
diff --git a/ui/app_list/views/contents_view.cc b/ui/app_list/views/contents_view.cc
index ad344da..d286f8a 100644
--- a/ui/app_list/views/contents_view.cc
+++ b/ui/app_list/views/contents_view.cc
@@ -252,6 +252,8 @@
 
   // Update app list pages.
   for (AppListPage* page : app_list_pages_) {
+    page->OnAnimationUpdated(progress, current_state, target_state);
+
     gfx::Rect to_rect = page->GetPageBoundsForState(target_state);
     gfx::Rect from_rect = page->GetPageBoundsForState(current_state);
     if (from_rect == to_rect)
@@ -262,7 +264,6 @@
         gfx::Tween::RectValueBetween(progress, from_rect, to_rect));
 
     page->SetBoundsRect(bounds);
-    page->OnAnimationUpdated(progress, current_state, target_state);
   }
 
   // Update the search box.
@@ -345,10 +346,7 @@
 }
 
 gfx::Rect ContentsView::GetDefaultContentsBounds() const {
-  const gfx::Size contents_size(GetDefaultContentsSize());
-  gfx::Point origin(0, GetDefaultSearchBoxBounds().bottom());
-  origin.Offset((bounds().width() - contents_size.width()) / 2, 0);
-  return gfx::Rect(origin, contents_size);
+  return GetContentsBounds();
 }
 
 gfx::Size ContentsView::GetMaximumContentsSize() const {
@@ -394,13 +392,7 @@
 }
 
 gfx::Size ContentsView::CalculatePreferredSize() const {
-  gfx::Rect search_box_bounds = GetDefaultSearchBoxBounds();
-  gfx::Rect default_contents_bounds = GetDefaultContentsBounds();
-  gfx::Vector2d bottom_right =
-      search_box_bounds.bottom_right().OffsetFromOrigin();
-  bottom_right.SetToMax(
-      default_contents_bounds.bottom_right().OffsetFromOrigin());
-  return gfx::Size(bottom_right.x(), GetDisplayHeight());
+  return gfx::Size(GetDisplayWidth(), GetDisplayHeight());
 }
 
 void ContentsView::Layout() {
@@ -411,10 +403,11 @@
     return;
 
   for (AppListPage* page : app_list_pages_) {
-    if (app_list_view_ && app_list_view_->is_in_drag())
-      page->SetBoundsRect(page->GetPageBoundsDuringDragging(GetActiveState()));
-    else
-      page->SetBoundsRect(page->GetPageBoundsForState(GetActiveState()));
+    // Ensures re-layout happens even when the page bounds does not change. So
+    // that |horizontal_page_container_| can layout its children in response to
+    // user dragging.
+    page->InvalidateLayout();
+    page->SetBoundsRect(page->GetPageBoundsForState(GetActiveState()));
   }
 
   // The search box is contained in a widget so set the bounds of the widget
diff --git a/ui/app_list/views/contents_view.h b/ui/app_list/views/contents_view.h
index ea3fa1a..3328e60f 100644
--- a/ui/app_list/views/contents_view.h
+++ b/ui/app_list/views/contents_view.h
@@ -110,6 +110,9 @@
   SearchResultListView* search_result_list_view_for_test() const {
     return search_result_list_view_;
   }
+  HorizontalPageContainer* horizontal_page_container() const {
+    return horizontal_page_container_;
+  }
   AppListPage* GetPageView(int index) const;
 
   SearchBoxView* GetSearchBoxView() const;
diff --git a/ui/app_list/views/horizontal_page.cc b/ui/app_list/views/horizontal_page.cc
index 16de048..eb89e88 100644
--- a/ui/app_list/views/horizontal_page.cc
+++ b/ui/app_list/views/horizontal_page.cc
@@ -20,6 +20,14 @@
       this, GetWidget(), true /* reverse */, false /* dont_loop */);
 }
 
+gfx::Rect HorizontalPage::GetPageBoundsForState(ash::AppListState state) const {
+  return gfx::Rect(GetPreferredSize());
+}
+
+bool HorizontalPage::ShouldShowSearchBox() const {
+  return true;
+}
+
 HorizontalPage::HorizontalPage() = default;
 
 HorizontalPage::~HorizontalPage() = default;
diff --git a/ui/app_list/views/horizontal_page.h b/ui/app_list/views/horizontal_page.h
index c107179..173320a 100644
--- a/ui/app_list/views/horizontal_page.h
+++ b/ui/app_list/views/horizontal_page.h
@@ -24,6 +24,12 @@
   virtual views::View* GetFirstFocusableView();
   virtual views::View* GetLastFocusableView();
 
+  // Returns the page bounds in the parent view when the given state is active.
+  virtual gfx::Rect GetPageBoundsForState(ash::AppListState state) const;
+
+  // Returns true if the search box should be shown in this page.
+  virtual bool ShouldShowSearchBox() const;
+
  protected:
   HorizontalPage();
   ~HorizontalPage() override;
diff --git a/ui/app_list/views/horizontal_page_container.cc b/ui/app_list/views/horizontal_page_container.cc
index 706cf303..76e118f 100644
--- a/ui/app_list/views/horizontal_page_container.cc
+++ b/ui/app_list/views/horizontal_page_container.cc
@@ -12,20 +12,13 @@
 #include "ui/app_list/views/apps_container_view.h"
 #include "ui/app_list/views/assistant_container_view.h"
 #include "ui/app_list/views/contents_view.h"
+#include "ui/app_list/views/search_box_view.h"
+#include "ui/app_list/views/search_result_page_view.h"
 #include "ui/chromeos/search_box/search_box_constants.h"
 #include "ui/views/controls/label.h"
 
 namespace app_list {
 
-// Initial search box top padding in shelf mode.
-constexpr int kSearchBoxInitalTopPadding = 12;
-
-// Top padding of search box in peeking state.
-constexpr int kSearchBoxPeekingTopPadding = 24;
-
-// Minimum top padding of search box in fullscreen state.
-constexpr int kSearchBoxMinimumTopPadding = 24;
-
 HorizontalPageContainer::HorizontalPageContainer(ContentsView* contents_view,
                                                  AppListModel* model)
     : contents_view_(contents_view) {
@@ -54,20 +47,23 @@
 }
 
 gfx::Size HorizontalPageContainer::CalculatePreferredSize() const {
-  gfx::Size size;
-  for (auto* page : horizontal_pages_) {
-    size.SetToMax(page->GetPreferredSize());
-  }
-  return size;
+  if (!GetWidget())
+    return gfx::Size();
+
+  return gfx::Size(contents_view_->GetDisplayWidth(),
+                   contents_view_->GetDisplayHeight());
 }
 
 void HorizontalPageContainer::Layout() {
-  gfx::Rect content_bounds(GetContentsBounds());
+  if (!GetWidget())
+    return;
+
   for (size_t i = 0; i < horizontal_pages_.size(); ++i) {
-    gfx::Rect page_bounds(content_bounds);
-    page_bounds.ClampToCenteredSize(horizontal_pages_[i]->GetPreferredSize());
+    HorizontalPage* page = horizontal_pages_[i];
+    gfx::Rect page_bounds(
+        page->GetPageBoundsForState(contents_view_->GetActiveState()));
     page_bounds.Offset(GetOffsetForPageIndex(i));
-    horizontal_pages_[i]->SetBoundsRect(page_bounds);
+    page->SetBoundsRect(page_bounds);
   }
 }
 
@@ -80,26 +76,31 @@
   GetSelectedPage()->OnWillBeHidden();
 }
 
+void HorizontalPageContainer::OnAnimationUpdated(double progress,
+                                                 ash::AppListState from_state,
+                                                 ash::AppListState to_state) {
+  for (size_t i = 0; i < horizontal_pages_.size(); ++i) {
+    HorizontalPage* page = horizontal_pages_[i];
+    gfx::Rect to_rect = page->GetPageBoundsForState(to_state);
+    gfx::Rect from_rect = page->GetPageBoundsForState(from_state);
+
+    // Animate linearly (the PaginationModel handles easing).
+    gfx::Rect bounds(
+        gfx::Tween::RectValueBetween(progress, from_rect, to_rect));
+    bounds.Offset(GetOffsetForPageIndex(i));
+    page->SetBoundsRect(bounds);
+  }
+}
+
 gfx::Rect HorizontalPageContainer::GetSearchBoxBounds() const {
   return GetSearchBoxBoundsForState(contents_view_->GetActiveState());
 }
 
 gfx::Rect HorizontalPageContainer::GetSearchBoxBoundsForState(
     ash::AppListState state) const {
-  gfx::Rect search_box_bounds(contents_view_->GetDefaultSearchBoxBounds());
-  bool is_in_drag = false;
-  if (contents_view_->app_list_view())
-    is_in_drag = contents_view_->app_list_view()->is_in_drag();
-  if (is_in_drag) {
-    search_box_bounds.set_y(GetSearchBoxTopPaddingDuringDragging());
-  } else {
-    if (state == ash::AppListState::kStateStart)
-      search_box_bounds.set_y(kSearchBoxPeekingTopPadding);
-    else
-      search_box_bounds.set_y(GetSearchBoxFinalTopPadding());
-  }
-
-  return search_box_bounds;
+  // The search box bounds are decided by AppsContainerView and are not changed
+  // during horizontal page switching.
+  return apps_container_view_->GetSearchBoxBoundsForState(state);
 }
 
 gfx::Rect HorizontalPageContainer::GetPageBoundsForState(
@@ -109,61 +110,12 @@
   // Both STATE_START and STATE_APPS are AppsContainerView page.
   if (state == ash::AppListState::kStateApps ||
       state == ash::AppListState::kStateStart) {
-    int y = GetSearchBoxBoundsForState(state).bottom();
-    if (state == ash::AppListState::kStateStart)
-      y -= (kSearchBoxBottomPadding - kSearchBoxPeekingBottomPadding);
-    onscreen_bounds.set_y(y);
     return onscreen_bounds;
   }
 
   return GetBelowContentsOffscreenBounds(onscreen_bounds.size());
 }
 
-gfx::Rect HorizontalPageContainer::GetPageBoundsDuringDragging(
-    ash::AppListState state) const {
-  float app_list_y_position_in_screen =
-      contents_view_->app_list_view()->app_list_y_position_in_screen();
-  float drag_amount =
-      std::max(0.f, contents_view_->app_list_view()->GetScreenBottom() -
-                        kShelfSize - app_list_y_position_in_screen);
-
-  float y = 0;
-  float peeking_final_y =
-      kSearchBoxPeekingTopPadding + search_box::kSearchBoxPreferredHeight +
-      kSearchBoxPeekingBottomPadding - kSearchBoxBottomPadding;
-  if (drag_amount <= (kPeekingAppListHeight - kShelfSize)) {
-    // App list is dragged from collapsed to peeking, which moved up at most
-    // |kPeekingAppListHeight - kShelfSize| (272px). The top padding of apps
-    // container view changes from |-kSearchBoxFullscreenBottomPadding| to
-    // |kSearchBoxPeekingTopPadding + kSearchBoxPreferredHeight +
-    // kSearchBoxPeekingBottomPadding - kSearchBoxFullscreenBottomPadding|.
-    y = std::ceil(((peeking_final_y + kSearchBoxBottomPadding) * drag_amount) /
-                      (kPeekingAppListHeight - kShelfSize) -
-                  kSearchBoxBottomPadding);
-  } else {
-    // App list is dragged from peeking to fullscreen, which moved up at most
-    // |peeking_to_fullscreen_height|. The top padding of apps container view
-    // changes from |peeking_final_y| to |final_y|.
-    float final_y =
-        GetSearchBoxFinalTopPadding() + search_box::kSearchBoxPreferredHeight;
-    float peeking_to_fullscreen_height =
-        contents_view_->GetDisplayHeight() - kPeekingAppListHeight;
-    y = std::ceil((final_y - peeking_final_y) *
-                      (drag_amount - (kPeekingAppListHeight - kShelfSize)) /
-                      peeking_to_fullscreen_height +
-                  peeking_final_y);
-    y = std::max(std::min(final_y, y), peeking_final_y);
-  }
-
-  gfx::Rect onscreen_bounds = GetPageBoundsForState(state);
-  // Both STATE_START and STATE_APPS are AppsContainerView page.
-  if (state == ash::AppListState::kStateApps ||
-      state == ash::AppListState::kStateStart)
-    onscreen_bounds.set_y(y);
-
-  return onscreen_bounds;
-}
-
 views::View* HorizontalPageContainer::GetFirstFocusableView() {
   return GetSelectedPage()->GetFirstFocusableView();
 }
@@ -172,6 +124,10 @@
   return GetSelectedPage()->GetLastFocusableView();
 }
 
+bool HorizontalPageContainer::ShouldShowSearchBox() const {
+  return GetSelectedPage()->ShouldShowSearchBox();
+}
+
 void HorizontalPageContainer::TotalPagesChanged() {}
 
 void HorizontalPageContainer::SelectedPageChanged(int old_selected,
@@ -185,6 +141,25 @@
 
 void HorizontalPageContainer::TransitionChanged() {
   Layout();
+
+  // Transition the search box opacity.
+  const int current_page = pagination_model_.selected_page();
+  DCHECK(pagination_model_.is_valid_page(current_page));
+  const PaginationModel::Transition& transition =
+      pagination_model_.transition();
+  const bool is_valid = pagination_model_.is_valid_page(transition.target_page);
+  float search_box_opacity =
+      GetSelectedPage()->ShouldShowSearchBox() ? 1.0f : 0.0f;
+  if (is_valid) {
+    float target_search_box_opacity =
+        horizontal_pages_[transition.target_page]->ShouldShowSearchBox() ? 1.0f
+                                                                         : 0.0f;
+    search_box_opacity = gfx::Tween::FloatValueBetween(
+        transition.progress, search_box_opacity, target_search_box_opacity);
+  }
+  contents_view_->GetSearchBoxView()->layer()->SetOpacity(search_box_opacity);
+  contents_view_->search_results_page_view()->layer()->SetOpacity(
+      search_box_opacity);
 }
 
 void HorizontalPageContainer::TransitionEnded() {
@@ -206,6 +181,11 @@
 }
 
 HorizontalPage* HorizontalPageContainer::GetSelectedPage() {
+  return const_cast<HorizontalPage*>(
+      const_cast<const HorizontalPageContainer*>(this)->GetSelectedPage());
+}
+
+const HorizontalPage* HorizontalPageContainer::GetSelectedPage() const {
   const int current_page = pagination_model_.selected_page();
   DCHECK(pagination_model_.is_valid_page(current_page));
   return horizontal_pages_[current_page];
@@ -233,50 +213,4 @@
   return gfx::Vector2d(x_offset, 0);
 }
 
-int HorizontalPageContainer::GetSearchBoxFinalTopPadding() const {
-  gfx::Rect search_box_bounds(contents_view_->GetDefaultSearchBoxBounds());
-  const int total_height =
-      GetDefaultContentsBounds().bottom() - search_box_bounds.y();
-
-  // Makes search box and content vertically centered in contents_view.
-  int y = std::max(search_box_bounds.y(),
-                   (contents_view_->GetDisplayHeight() - total_height) / 2);
-
-  // Top padding of the searchbox should not be smaller than
-  // |kSearchBoxMinimumTopPadding|
-  return std::max(y, kSearchBoxMinimumTopPadding);
-}
-
-int HorizontalPageContainer::GetSearchBoxTopPaddingDuringDragging() const {
-  float searchbox_final_y = GetSearchBoxFinalTopPadding();
-  float peeking_to_fullscreen_height =
-      contents_view_->GetDisplayHeight() - kPeekingAppListHeight;
-  float drag_amount = std::max(
-      0, contents_view_->app_list_view()->GetScreenBottom() - kShelfSize -
-             contents_view_->app_list_view()->app_list_y_position_in_screen());
-
-  if (drag_amount <= (kPeekingAppListHeight - kShelfSize)) {
-    // App list is dragged from collapsed to peeking, which moved up at most
-    // |kPeekingAppListHeight - kShelfSize| (272px). The top padding of search
-    // box changes from |kSearchBoxInitalTopPadding| to
-    // |kSearchBoxPeekingTopPadding|,
-    return std::ceil(
-        (kSearchBoxPeekingTopPadding - kSearchBoxInitalTopPadding) +
-        ((kSearchBoxPeekingTopPadding - kSearchBoxInitalTopPadding) *
-         drag_amount) /
-            (kPeekingAppListHeight - kShelfSize));
-  } else {
-    // App list is dragged from peeking to fullscreen, which moved up at most
-    // |peeking_to_fullscreen_height|. The top padding of search box changes
-    // from |kSearchBoxPeekingTopPadding| to |searchbox_final_y|.
-    int y = (kSearchBoxPeekingTopPadding +
-             std::ceil((searchbox_final_y - kSearchBoxPeekingTopPadding) *
-                       (drag_amount - (kPeekingAppListHeight - kShelfSize)) /
-                       peeking_to_fullscreen_height));
-    y = std::max(kSearchBoxPeekingTopPadding,
-                 std::min<int>(searchbox_final_y, y));
-    return y;
-  }
-}
-
 }  // namespace app_list
diff --git a/ui/app_list/views/horizontal_page_container.h b/ui/app_list/views/horizontal_page_container.h
index b25bf03..1a716c50 100644
--- a/ui/app_list/views/horizontal_page_container.h
+++ b/ui/app_list/views/horizontal_page_container.h
@@ -33,12 +33,15 @@
 
   // AppListPage overrides:
   void OnWillBeHidden() override;
+  void OnAnimationUpdated(double progress,
+                          ash::AppListState from_state,
+                          ash::AppListState to_state) override;
   gfx::Rect GetSearchBoxBounds() const override;
   gfx::Rect GetSearchBoxBoundsForState(ash::AppListState state) const override;
   gfx::Rect GetPageBoundsForState(ash::AppListState state) const override;
-  gfx::Rect GetPageBoundsDuringDragging(ash::AppListState state) const override;
   views::View* GetFirstFocusableView() override;
   views::View* GetLastFocusableView() override;
+  bool ShouldShowSearchBox() const override;
 
   AppsContainerView* apps_container_view() { return apps_container_view_; }
 
@@ -59,16 +62,11 @@
 
   // Gets the currently selected horizontal page.
   HorizontalPage* GetSelectedPage();
+  const HorizontalPage* GetSelectedPage() const;
 
   // Gets the offset for the horizontal page with specified index.
   gfx::Vector2d GetOffsetForPageIndex(int index) const;
 
-  // Gets the final top padding of search box.
-  int GetSearchBoxFinalTopPadding() const;
-
-  // Gets the top padding of search box during dragging.
-  int GetSearchBoxTopPaddingDuringDragging() const;
-
   // Manages the pagination for the horizontal pages.
   PaginationModel pagination_model_;
 
diff --git a/ui/app_list/views/search_box_view.cc b/ui/app_list/views/search_box_view.cc
index 7192242..87ae87e 100644
--- a/ui/app_list/views/search_box_view.cc
+++ b/ui/app_list/views/search_box_view.cc
@@ -200,6 +200,10 @@
   // changes from |kOpacityStartFraction| to |kOpaticyEndFraction|, the opacity
   // of searchbox changes from 0.f to 1.0f.
   ContentsView* contents = static_cast<ContentsView*>(contents_view());
+  if (!contents->GetPageView(contents->GetActivePageIndex())
+           ->ShouldShowSearchBox()) {
+    return;
+  }
   int app_list_y_position_in_screen =
       contents->app_list_view()->app_list_y_position_in_screen();
   float fraction =
@@ -315,6 +319,10 @@
   NotifyQueryChanged();
 }
 
+void SearchBoxView::SearchEngineChanged() {
+  UpdateSearchIcon();
+}
+
 void SearchBoxView::OnWallpaperProminentColorsReceived(
     const std::vector<SkColor>& prominent_colors) {
   if (prominent_colors.empty())
diff --git a/ui/app_list/views/search_box_view.h b/ui/app_list/views/search_box_view.h
index 8a933e4..51aee46d 100644
--- a/ui/app_list/views/search_box_view.h
+++ b/ui/app_list/views/search_box_view.h
@@ -95,6 +95,7 @@
   void HintTextChanged() override;
   void SelectionModelChanged() override;
   void Update() override;
+  void SearchEngineChanged() override;
 
   // Overridden from AppListViewDelegateObserver:
   void OnWallpaperColorsChanged() override;
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 9a5f703b..b1fd22a 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -312,15 +312,8 @@
     "win/message_box_win.h",
     "win/mouse_wheel_util.cc",
     "win/mouse_wheel_util.h",
-    "win/on_screen_keyboard_display_manager_stub.cc",
-    "win/on_screen_keyboard_display_manager_stub.h",
-    "win/on_screen_keyboard_display_manager_tab_tip.cc",
-    "win/on_screen_keyboard_display_manager_tab_tip.h",
     "win/open_file_name_win.cc",
     "win/open_file_name_win.h",
-    "win/osk_display_manager.cc",
-    "win/osk_display_manager.h",
-    "win/osk_display_observer.h",
     "win/scoped_ole_initializer.cc",
     "win/scoped_ole_initializer.h",
     "win/shell.cc",
@@ -886,6 +879,7 @@
       "ime/input_method_base_unittest.cc",
       "ime/input_method_chromeos_unittest.cc",
       "ime/win/imm32_manager_unittest.cc",
+      "ime/win/on_screen_keyboard_display_manager_unittest.cc",
       "ime/win/tsf_input_scope_unittest.cc",
       "ime/win/tsf_text_store_unittest.cc",
     ]
@@ -940,7 +934,6 @@
       "win/direct_manipulation_unittest.cc",
       "win/hwnd_subclass_unittest.cc",
       "win/open_file_name_win_unittest.cc",
-      "win/osk_display_manager_unittest.cc",
     ]
 
     ldflags = [
diff --git a/ui/base/ime/BUILD.gn b/ui/base/ime/BUILD.gn
index 95dc069..b288b050 100644
--- a/ui/base/ime/BUILD.gn
+++ b/ui/base/ime/BUILD.gn
@@ -96,6 +96,13 @@
     "ui_base_ime_export.h",
     "win/imm32_manager.cc",
     "win/imm32_manager.h",
+    "win/on_screen_keyboard_display_manager_stub.cc",
+    "win/on_screen_keyboard_display_manager_stub.h",
+    "win/on_screen_keyboard_display_manager_tab_tip.cc",
+    "win/on_screen_keyboard_display_manager_tab_tip.h",
+    "win/osk_display_manager.cc",
+    "win/osk_display_manager.h",
+    "win/osk_display_observer.h",
     "win/tsf_bridge.cc",
     "win/tsf_bridge.h",
     "win/tsf_event_router.cc",
@@ -118,6 +125,7 @@
     "//net",
     "//third_party/icu",
     "//ui/base",
+    "//ui/base/ime/chromeos/public/interfaces",
     "//ui/display",
     "//ui/events",
     "//ui/gfx",
diff --git a/ui/base/ime/chromeos/input_method_manager.h b/ui/base/ime/chromeos/input_method_manager.h
index 809e5da..80e7052 100644
--- a/ui/base/ime/chromeos/input_method_manager.h
+++ b/ui/base/ime/chromeos/input_method_manager.h
@@ -14,6 +14,7 @@
 
 #include "base/memory/ref_counted.h"
 #include "ui/base/ime/chromeos/input_method_descriptor.h"
+#include "ui/base/ime/chromeos/public/interfaces/ime_keyset.mojom.h"
 #include "ui/base/ime/ui_base_ime_export.h"
 
 class Profile;
@@ -327,10 +328,9 @@
   // is different from previous.
   virtual void MaybeNotifyImeMenuActivationChanged() = 0;
 
-  // Overrides the keyboard url ref (stuff following '#' to the end of the
-  // string) with the given keyset (emoji, hwt or voice). If |keyset| is empty,
-  // it indicates that we should override the url back with the keyboard keyset.
-  virtual void OverrideKeyboardUrlRef(const std::string& keyset) = 0;
+  // Overrides active keyset with the given keyset if the active IME supports
+  // the given keyset.
+  virtual void OverrideKeyboardKeyset(mojom::ImeKeyset keyset) = 0;
 
   // Enables or disables some advanced features, e.g. handwiring, voices input.
   virtual void SetImeMenuFeatureEnabled(ImeMenuFeature feature,
diff --git a/ui/base/ime/chromeos/mock_input_method_manager.cc b/ui/base/ime/chromeos/mock_input_method_manager.cc
index ad80d23..c73583a 100644
--- a/ui/base/ime/chromeos/mock_input_method_manager.cc
+++ b/ui/base/ime/chromeos/mock_input_method_manager.cc
@@ -198,8 +198,7 @@
 
 void MockInputMethodManager::MaybeNotifyImeMenuActivationChanged() {}
 
-void MockInputMethodManager::OverrideKeyboardUrlRef(const std::string& keyset) {
-}
+void MockInputMethodManager::OverrideKeyboardKeyset(mojom::ImeKeyset keyset) {}
 
 void MockInputMethodManager::SetImeMenuFeatureEnabled(ImeMenuFeature feature,
                                                       bool enabled) {
diff --git a/ui/base/ime/chromeos/mock_input_method_manager.h b/ui/base/ime/chromeos/mock_input_method_manager.h
index 615d1a1..ddd8f42 100644
--- a/ui/base/ime/chromeos/mock_input_method_manager.h
+++ b/ui/base/ime/chromeos/mock_input_method_manager.h
@@ -109,7 +109,7 @@
       const std::string& engine_id,
       const std::vector<InputMethodManager::MenuItem>& items) override;
   void MaybeNotifyImeMenuActivationChanged() override;
-  void OverrideKeyboardUrlRef(const std::string& keyset) override;
+  void OverrideKeyboardKeyset(mojom::ImeKeyset keyset) override;
   void SetImeMenuFeatureEnabled(ImeMenuFeature feature, bool enabled) override;
   bool GetImeMenuFeatureEnabled(ImeMenuFeature feature) const override;
   void NotifyObserversImeExtraInputStateChange() override;
diff --git a/ui/base/ime/chromeos/public/interfaces/BUILD.gn b/ui/base/ime/chromeos/public/interfaces/BUILD.gn
new file mode 100644
index 0000000..625cdd4c
--- /dev/null
+++ b/ui/base/ime/chromeos/public/interfaces/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+  sources = [
+    "ime_keyset.mojom",
+  ]
+}
diff --git a/ui/base/ime/chromeos/public/interfaces/OWNERS b/ui/base/ime/chromeos/public/interfaces/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/ui/base/ime/chromeos/public/interfaces/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/ui/base/ime/chromeos/public/interfaces/ime_keyset.mojom b/ui/base/ime/chromeos/public/interfaces/ime_keyset.mojom
new file mode 100644
index 0000000..7a9d629
--- /dev/null
+++ b/ui/base/ime/chromeos/public/interfaces/ime_keyset.mojom
@@ -0,0 +1,15 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module chromeos.input_method.mojom;
+
+// Used by the virtual keyboard to represent different key layouts for
+// different purposes. 'kNone' represents the default key layout.
+// Used in UMA, so this enum should not be reordered.
+enum ImeKeyset {
+  kNone = 0,
+  kEmoji = 1,
+  kHandwriting = 2,
+  kVoice = 3,
+};
diff --git a/ui/base/win/on_screen_keyboard_display_manager_stub.cc b/ui/base/ime/win/on_screen_keyboard_display_manager_stub.cc
similarity index 91%
rename from ui/base/win/on_screen_keyboard_display_manager_stub.cc
rename to ui/base/ime/win/on_screen_keyboard_display_manager_stub.cc
index 6476524..ef3d5a8 100644
--- a/ui/base/win/on_screen_keyboard_display_manager_stub.cc
+++ b/ui/base/ime/win/on_screen_keyboard_display_manager_stub.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 "ui/base/win/on_screen_keyboard_display_manager_stub.h"
+#include "ui/base/ime/win/on_screen_keyboard_display_manager_stub.h"
 
 namespace ui {
 
diff --git a/ui/base/win/on_screen_keyboard_display_manager_stub.h b/ui/base/ime/win/on_screen_keyboard_display_manager_stub.h
similarity index 69%
rename from ui/base/win/on_screen_keyboard_display_manager_stub.h
rename to ui/base/ime/win/on_screen_keyboard_display_manager_stub.h
index 9fb9e25..184f806 100644
--- a/ui/base/win/on_screen_keyboard_display_manager_stub.h
+++ b/ui/base/ime/win/on_screen_keyboard_display_manager_stub.h
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_BASE_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_STUB_H_
-#define UI_BASE_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_STUB_H_
+#ifndef UI_BASE_IME_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_STUB_H_
+#define UI_BASE_IME_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_STUB_H_
 
-#include "ui/base/ui_base_export.h"
-#include "ui/base/win/osk_display_manager.h"
+#include "ui/base/ime/ui_base_ime_export.h"
+#include "ui/base/ime/win/osk_display_manager.h"
 
 namespace ui {
 
 // This class provides a stub OnScreenDisplayManager.
 // Used for < Win8 OS versions.
-class UI_BASE_EXPORT OnScreenKeyboardDisplayManagerStub
+class UI_BASE_IME_EXPORT OnScreenKeyboardDisplayManagerStub
     : public OnScreenKeyboardDisplayManager {
  public:
   ~OnScreenKeyboardDisplayManagerStub() override;
@@ -32,4 +32,4 @@
 
 }  // namespace ui
 
-#endif  // UI_BASE_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_STUB_H_
+#endif  // UI_BASE_IME_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_STUB_H_
diff --git a/ui/base/win/on_screen_keyboard_display_manager_tab_tip.cc b/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.cc
similarity index 98%
rename from ui/base/win/on_screen_keyboard_display_manager_tab_tip.cc
rename to ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.cc
index e4c4a6d0..f1fcd63e 100644
--- a/ui/base/win/on_screen_keyboard_display_manager_tab_tip.cc
+++ b/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.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 "ui/base/win/on_screen_keyboard_display_manager_tab_tip.h"
+#include "ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h"
 
 #include <windows.h>
 
@@ -20,8 +20,8 @@
 #include "base/win/scoped_co_mem.h"
 #include "base/win/win_util.h"
 #include "base/win/windows_version.h"
+#include "ui/base/ime/win/osk_display_observer.h"
 #include "ui/base/win/hidden_window.h"
-#include "ui/base/win/osk_display_observer.h"
 #include "ui/display/win/screen_win.h"
 #include "ui/gfx/geometry/dip_util.h"
 
diff --git a/ui/base/win/on_screen_keyboard_display_manager_tab_tip.h b/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h
similarity index 79%
rename from ui/base/win/on_screen_keyboard_display_manager_tab_tip.h
rename to ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h
index d2428e6..e63bf81 100644
--- a/ui/base/win/on_screen_keyboard_display_manager_tab_tip.h
+++ b/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_BASE_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_TAP_TIP_H_
-#define UI_BASE_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_TAP_TIP_H_
+#ifndef UI_BASE_IME_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_TAB_TIP_H_
+#define UI_BASE_IME_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_TAB_TIP_H_
 
 #include "base/gtest_prod_util.h"
 #include "base/strings/string16.h"
-#include "ui/base/ui_base_export.h"
-#include "ui/base/win/osk_display_manager.h"
+#include "ui/base/ime/ui_base_ime_export.h"
+#include "ui/base/ime/win/osk_display_manager.h"
 
 namespace ui {
 
@@ -17,7 +17,7 @@
 // This class provides an implementation of the OnScreenKeyboardDisplayManager
 // that uses heuristics and the TabTip.exe to display the on screen keyboard.
 // Used on Windows > 7 and Windows < 10.0.10240.0
-class UI_BASE_EXPORT OnScreenKeyboardDisplayManagerTabTip
+class UI_BASE_IME_EXPORT OnScreenKeyboardDisplayManagerTabTip
     : public OnScreenKeyboardDisplayManager {
  public:
   ~OnScreenKeyboardDisplayManagerTabTip() override;
@@ -48,4 +48,4 @@
 
 }  // namespace ui
 
-#endif  // UI_BASE_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_TAP_TIP_H_
+#endif  // UI_BASE_IME_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_TAB_TIP_H_
diff --git a/ui/base/win/osk_display_manager_unittest.cc b/ui/base/ime/win/on_screen_keyboard_display_manager_unittest.cc
similarity index 95%
rename from ui/base/win/osk_display_manager_unittest.cc
rename to ui/base/ime/win/on_screen_keyboard_display_manager_unittest.cc
index 2beda8b..16d785f0 100644
--- a/ui/base/win/osk_display_manager_unittest.cc
+++ b/ui/base/ime/win/on_screen_keyboard_display_manager_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/base/win/on_screen_keyboard_display_manager_tab_tip.h"
+#include "ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h"
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
diff --git a/ui/base/win/osk_display_manager.cc b/ui/base/ime/win/osk_display_manager.cc
similarity index 82%
rename from ui/base/win/osk_display_manager.cc
rename to ui/base/ime/win/osk_display_manager.cc
index 28456462..e7b441c 100644
--- a/ui/base/win/osk_display_manager.cc
+++ b/ui/base/ime/win/osk_display_manager.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/base/win/osk_display_manager.h"
+#include "ui/base/ime/win/osk_display_manager.h"
 
 #include "base/debug/leak_annotations.h"
 #include "base/win/windows_version.h"
-#include "ui/base/win/on_screen_keyboard_display_manager_stub.h"
-#include "ui/base/win/on_screen_keyboard_display_manager_tab_tip.h"
+#include "ui/base/ime/win/on_screen_keyboard_display_manager_stub.h"
+#include "ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h"
 
 namespace ui {
 
diff --git a/ui/base/win/osk_display_manager.h b/ui/base/ime/win/osk_display_manager.h
similarity index 84%
rename from ui/base/win/osk_display_manager.h
rename to ui/base/ime/win/osk_display_manager.h
index 628fbb3..4895586 100644
--- a/ui/base/win/osk_display_manager.h
+++ b/ui/base/ime/win/osk_display_manager.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_BASE_WIN_OSK_DISPLAY_MANAGER_H_
-#define UI_BASE_WIN_OSK_DISPLAY_MANAGER_H_
+#ifndef UI_BASE_IME_WIN_OSK_DISPLAY_MANAGER_H_
+#define UI_BASE_IME_WIN_OSK_DISPLAY_MANAGER_H_
 
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/strings/string16.h"
-#include "ui/base/ui_base_export.h"
+#include "ui/base/ime/ui_base_ime_export.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace ui {
@@ -18,7 +18,7 @@
 // This class provides functionality to display the on screen keyboard on
 // Windows 8+. It optionally notifies observers that the OSK is displayed,
 // hidden, etc.
-class UI_BASE_EXPORT OnScreenKeyboardDisplayManager {
+class UI_BASE_IME_EXPORT OnScreenKeyboardDisplayManager {
  public:
   static OnScreenKeyboardDisplayManager* GetInstance();
 
@@ -44,4 +44,4 @@
 
 }  // namespace ui
 
-#endif  // UI_BASE_WIN_OSK_DISPLAY_MANAGER_H_
+#endif  // UI_BASE_IME_WIN_OSK_DISPLAY_MANAGER_H_
diff --git a/ui/base/win/osk_display_observer.h b/ui/base/ime/win/osk_display_observer.h
similarity index 74%
rename from ui/base/win/osk_display_observer.h
rename to ui/base/ime/win/osk_display_observer.h
index 16d341a..8c83f56 100644
--- a/ui/base/win/osk_display_observer.h
+++ b/ui/base/ime/win/osk_display_observer.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_BASE_WIN_OSK_OBSERVER_H_
-#define UI_BASE_WIN_OSK_OBSERVER_H_
+#ifndef UI_BASE_IME_WIN_OSK_DISPLAY_OBSERVER_H_
+#define UI_BASE_IME_WIN_OSK_DISPLAY_OBSERVER_H_
 
 namespace gfx {
 class Rect;
@@ -13,7 +13,7 @@
 
 // Implemented by classes who wish to get notified about the on screen keyboard
 // becoming visible/hidden.
-class UI_BASE_EXPORT OnScreenKeyboardObserver {
+class UI_BASE_IME_EXPORT OnScreenKeyboardObserver {
  public:
   virtual ~OnScreenKeyboardObserver() {}
 
@@ -24,4 +24,4 @@
 
 }  // namespace ui
 
-#endif  // UI_BASE_WIN_OSK_OBSERVER_H_
+#endif  // UI_BASE_IME_WIN_OSK_DISPLAY_OBSERVER_H_
diff --git a/ui/base/user_activity/user_activity_detector.cc b/ui/base/user_activity/user_activity_detector.cc
index c45121d..63a31ec 100644
--- a/ui/base/user_activity/user_activity_detector.cc
+++ b/ui/base/user_activity/user_activity_detector.cc
@@ -47,13 +47,13 @@
 // and we'll ignore legitimate activity.
 const int UserActivityDetector::kDisplayPowerChangeIgnoreMouseMs = 1000;
 
-UserActivityDetector::UserActivityDetector() {
+UserActivityDetector::UserActivityDetector(bool observe_event_source) {
   CHECK(!g_instance);
   g_instance = this;
 
   PlatformEventSource* platform_event_source =
       PlatformEventSource::GetInstance();
-  if (platform_event_source)
+  if (platform_event_source && observe_event_source)
     platform_event_source->AddPlatformEventObserver(this);
 }
 
diff --git a/ui/base/user_activity/user_activity_detector.h b/ui/base/user_activity/user_activity_detector.h
index c12fbd4..867a8d3 100644
--- a/ui/base/user_activity/user_activity_detector.h
+++ b/ui/base/user_activity/user_activity_detector.h
@@ -27,7 +27,10 @@
   // is received that displays' power states are being changed.
   static const int kDisplayPowerChangeIgnoreMouseMs;
 
-  UserActivityDetector();
+  // If |observe_event_source| is true, this detector observes platform events
+  // directly from the PlatformEventSource. Otherwise, this instance relies on
+  // reports of events through |HandleExternalUserActivity|.
+  explicit UserActivityDetector(bool observe_event_source = true);
   ~UserActivityDetector() override;
 
   // Returns the UserActivityDetector instance if one was created.
diff --git a/ui/base/win/direct_manipulation.cc b/ui/base/win/direct_manipulation.cc
index 98b6ca5f..97b7043 100644
--- a/ui/base/win/direct_manipulation.cc
+++ b/ui/base/win/direct_manipulation.cc
@@ -11,6 +11,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/win/windows_version.h"
 #include "ui/base/ui_base_features.h"
+#include "ui/display/win/screen_win.h"
 
 namespace ui {
 namespace win {
@@ -150,6 +151,10 @@
 bool DirectManipulationHelper::OnPointerHitTest(
     WPARAM w_param,
     WindowEventTarget* event_target) {
+  // Update the device scale factor.
+  event_handler_->SetDeviceScaleFactor(
+      display::win::ScreenWin::GetScaleFactorForHWND(window_));
+
   // Only DM_POINTERHITTEST can be the first message of input sequence of
   // touchpad input.
   // TODO(chaopeng) Check if Windows API changes:
@@ -193,6 +198,10 @@
   return need_poll_events_;
 }
 
+void DirectManipulationHelper::SetDeviceScaleFactorForTesting(float factor) {
+  event_handler_->SetDeviceScaleFactor(factor);
+}
+
 // DirectManipulationHandler
 DirectManipulationHandler::DirectManipulationHandler() {
   NOTREACHED();
@@ -294,8 +303,8 @@
     return hr;
 
   float scale = xform[0];
-  int x_offset = xform[4];
-  int y_offset = xform[5];
+  int x_offset = xform[4] / device_scale_factor_;
+  int y_offset = xform[5] / device_scale_factor_;
 
   // Ignore if Windows pass scale=0 to us.
   if (scale == 0.0f) {
@@ -352,5 +361,10 @@
   event_target_ = event_target;
 }
 
+void DirectManipulationHandler::SetDeviceScaleFactor(
+    float device_scale_factor) {
+  device_scale_factor_ = device_scale_factor;
+}
+
 }  // namespace win
 }  // namespace ui
diff --git a/ui/base/win/direct_manipulation.h b/ui/base/win/direct_manipulation.h
index 91371a8f2..6faf8417 100644
--- a/ui/base/win/direct_manipulation.h
+++ b/ui/base/win/direct_manipulation.h
@@ -46,6 +46,8 @@
   // hierarchy changed.
   void SetWindowEventTarget(WindowEventTarget* event_target);
 
+  void SetDeviceScaleFactor(float device_scale_factor);
+
  private:
   friend DirectManipulationUnitTest;
 
@@ -74,6 +76,7 @@
   int last_x_offset_ = 0;
   int last_y_offset_ = 0;
   bool first_ready_ = false;
+  float device_scale_factor_ = 1.0f;
 
   // Current recognized gesture from Direct Manipulation.
   Gesture gesture_state_ = Gesture::kNone;
@@ -133,6 +136,8 @@
   // the passed in |window|. Return false if initialize failed.
   bool Initialize();
 
+  void SetDeviceScaleFactorForTesting(float factor);
+
   Microsoft::WRL::ComPtr<IDirectManipulationManager> manager_;
   Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> update_manager_;
   Microsoft::WRL::ComPtr<IDirectManipulationViewport> viewport_;
diff --git a/ui/base/win/direct_manipulation_unittest.cc b/ui/base/win/direct_manipulation_unittest.cc
index 8e6d9e0..6a245cec 100644
--- a/ui/base/win/direct_manipulation_unittest.cc
+++ b/ui/base/win/direct_manipulation_unittest.cc
@@ -376,6 +376,10 @@
     return direct_manipulation_helper_->need_poll_events_;
   }
 
+  void SetDeviceScaleFactor(float factor) {
+    direct_manipulation_helper_->SetDeviceScaleFactorForTesting(factor);
+  }
+
  private:
   std::unique_ptr<DirectManipulationHelper> direct_manipulation_helper_;
   Microsoft::WRL::ComPtr<MockDirectManipulationViewport> viewport_;
@@ -550,6 +554,18 @@
   EXPECT_FALSE(NeedAnimation());
 }
 
+TEST_F(DirectManipulationUnitTest, HiDPIScroll) {
+  if (!GetDirectManipulationHelper())
+    return;
+
+  SetDeviceScaleFactor(10.0);
+  ContentUpdated(1.0f, 50, 0);
+  std::vector<Event> events = GetEvents();
+  EXPECT_EQ(1u, events.size());
+  EXPECT_EQ(Gesture::kScroll, events[0].gesture_);
+  EXPECT_EQ(5, events[0].scroll_x_);
+}
+
 }  //  namespace win
 
 }  //  namespace ui
\ No newline at end of file
diff --git a/ui/display/mac/screen_mac.mm b/ui/display/mac/screen_mac.mm
index 4850c79..be4f343b 100644
--- a/ui/display/mac/screen_mac.mm
+++ b/ui/display/mac/screen_mac.mm
@@ -206,7 +206,7 @@
   Display GetDisplayNearestView(gfx::NativeView view) const override {
     NSWindow* window = [view window];
     if (!window)
-      window = [NSApp keyWindow];
+      return GetPrimaryDisplay();
     return GetDisplayNearestWindow(window);
   }
 
diff --git a/ui/events/test/event_generator.cc b/ui/events/test/event_generator.cc
index 5f97117..b617bb1 100644
--- a/ui/events/test/event_generator.cc
+++ b/ui/events/test/event_generator.cc
@@ -37,18 +37,21 @@
 namespace ui {
 namespace test {
 
+// A TickClock that advances time by one millisecond on each call to NowTicks().
 class TestTickClock : public base::TickClock {
  public:
-  // Starts off with a clock set to TimeTicks().
-  TestTickClock() {}
+  TestTickClock() = default;
 
-  base::TimeTicks NowTicks() override {
-    return base::TimeTicks() +
-           base::TimeDelta::FromMicroseconds(ticks_++ * 1000);
+  // Unconditionally returns a tick count that is 1ms later than the previous
+  // call, starting at 1ms.
+  base::TimeTicks NowTicks() const override {
+    static constexpr base::TimeDelta kOneMillisecond =
+        base::TimeDelta::FromMilliseconds(1);
+    return ticks_ += kOneMillisecond;
   }
 
  private:
-  int64_t ticks_ = 1;
+  mutable base::TimeTicks ticks_;
 
   DISALLOW_COPY_AND_ASSIGN(TestTickClock);
 };
diff --git a/ui/file_manager/externs/background/file_operation_manager.js b/ui/file_manager/externs/background/file_operation_manager.js
index f96b5d7..c0e1254 100644
--- a/ui/file_manager/externs/background/file_operation_manager.js
+++ b/ui/file_manager/externs/background/file_operation_manager.js
@@ -77,11 +77,11 @@
 /**
  * Creates a zip file for the selection of files.
  *
- * @param {!DirectoryEntry} dirEntry The directory containing the selection.
  * @param {!Array<!Entry>} selectionEntries The selected entries.
+ * @param {!DirectoryEntry} dirEntry The directory containing the selection.
  */
 FileOperationManager.prototype.zipSelection = function(
-    dirEntry, selectionEntries) {};
+    selectionEntries, dirEntry) {};
 
 /**
  * Generates new task ID.
diff --git a/ui/file_manager/file_manager/background/js/file_operation_manager.js b/ui/file_manager/file_manager/background/js/file_operation_manager.js
index f73e384..7317b09 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_manager.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_manager.js
@@ -467,11 +467,11 @@
 /**
  * Creates a zip file for the selection of files.
  *
- * @param {!DirectoryEntry} dirEntry The directory containing the selection.
  * @param {!Array<!Entry>} selectionEntries The selected entries.
+ * @param {!DirectoryEntry} dirEntry The directory containing the selection.
  */
 FileOperationManager.prototype.zipSelection = function(
-    dirEntry, selectionEntries) {
+    selectionEntries, dirEntry) {
   var zipTask = new fileOperationUtil.ZipTask(
       this.generateTaskId(), selectionEntries, dirEntry, dirEntry);
   this.eventRouter_.sendProgressEvent(
diff --git a/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.js b/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.js
index afcec8e7..a46df04 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_manager_unittest.js
@@ -152,7 +152,7 @@
  */
 FakeVolumeManager.prototype.getVolumeInfo = function(entry) {
   return { volumeId: entry.filesystem.name };
-}
+};
 
 /**
  * Returns file system of the url.
@@ -827,13 +827,13 @@
   });
   window.webkitResolveLocalFileSystemURL =
       resolveTestFileSystemURL.bind(null, fileSystem);
-  chrome.fileManagerPrivate.zipSelection =
-      function(parent, sources, newName, success, error) {
-        var newPath = joinPath('/', newName);
-        var newEntry = new MockFileEntry(fileSystem, newPath, {size: 10});
-        fileSystem.entries[newPath] = newEntry;
-        success(newEntry);
-      };
+  chrome.fileManagerPrivate.zipSelection = function(
+      sources, parent, newName, success, error) {
+    var newPath = joinPath('/', newName);
+    var newEntry = new MockFileEntry(fileSystem, newPath, {size: 10});
+    fileSystem.entries[newPath] = newEntry;
+    success(newEntry);
+  };
 
   volumeManager = new FakeVolumeManager();
   fileOperationManager = new FileOperationManager();
@@ -862,6 +862,5 @@
   }), callback);
 
   fileOperationManager.zipSelection(
-      fileSystem.entries['/'],
-      [fileSystem.entries['/test.txt']]);
+      [fileSystem.entries['/test.txt']], fileSystem.entries['/']);
 }
diff --git a/ui/file_manager/file_manager/background/js/file_operation_util.js b/ui/file_manager/file_manager/background/js/file_operation_util.js
index 40b4d83f..bc7387b 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_util.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_util.js
@@ -486,9 +486,7 @@
 fileOperationUtil.zipSelection = function(
     sources, parent, newName, successCallback, errorCallback) {
   chrome.fileManagerPrivate.zipSelection(
-      parent,
-      sources,
-      newName, function(success) {
+      sources, parent, newName, function(success) {
         if (!success) {
           // Failed to create a zip archive.
           errorCallback(
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index 66febd16..73a30c5 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -1434,7 +1434,7 @@
     } else {
       var selectionEntries = fileManager.getSelection().entries;
       fileManager.fileOperationManager.zipSelection(
-          /** @type {!DirectoryEntry} */ (dirEntry), selectionEntries);
+          selectionEntries, /** @type {!DirectoryEntry} */ (dirEntry));
     }
   },
   /**
diff --git a/ui/file_manager/file_manager/foreground/js/main_window_component.js b/ui/file_manager/file_manager/foreground/js/main_window_component.js
index 5e67b858..dbf7053c 100644
--- a/ui/file_manager/file_manager/foreground/js/main_window_component.js
+++ b/ui/file_manager/file_manager/foreground/js/main_window_component.js
@@ -296,11 +296,12 @@
       this.ui_.listContainer.currentListType === ListContainer.ListType.DETAIL ?
       ListContainer.ListType.THUMBNAIL :
       ListContainer.ListType.DETAIL;
-
   this.ui_.setCurrentListType(listType);
   this.appStateController_.saveViewOptions();
 
   this.ui_.listContainer.focus();
+  metrics.recordEnum(
+      'ToggleFileListType', listType, ListContainer.ListTypesForUMA);
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/ui/compiled_resources2.gyp b/ui/file_manager/file_manager/foreground/js/ui/compiled_resources2.gyp
index 085759b..645b490 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/compiled_resources2.gyp
+++ b/ui/file_manager/file_manager/foreground/js/ui/compiled_resources2.gyp
@@ -310,6 +310,7 @@
       'dependencies': [
         '../../../../externs/compiled_resources2.gyp:search_item',
         '../../../common/js/compiled_resources2.gyp:file_type',
+        '../../../common/js/compiled_resources2.gyp:metrics',
         '../../../common/js/compiled_resources2.gyp:util',
         '../../elements/compiled_resources2.gyp:files_toggle_ripple',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
diff --git a/ui/file_manager/file_manager/foreground/js/ui/list_container.js b/ui/file_manager/file_manager/foreground/js/ui/list_container.js
index f976566..681841f6 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/list_container.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/list_container.js
@@ -149,6 +149,25 @@
   THUMBNAIL: 'thumb'
 };
 
+/**
+ * Keep the order of this in sync with FileManagerListType in
+ * tools/metrics/histograms/enums.xml.
+ * The array indices will be recorded in UMA as enum values. The index for each
+ * root type should never be renumbered nor reused in this array.
+ *
+ * @type {Array<ListContainer.ListType>}
+ * @const
+ */
+ListContainer.ListTypesForUMA = Object.freeze([
+  ListContainer.ListType.UNINITIALIZED,
+  ListContainer.ListType.DETAIL,
+  ListContainer.ListType.THUMBNAIL,
+]);
+console.assert(
+    Object.keys(ListContainer.ListType).length ===
+        ListContainer.ListTypesForUMA.length,
+    'Members in ListTypesForUMA do not match those in ListType.');
+
 ListContainer.prototype = /** @struct */ {
   /**
    * @return {!FileTable|!FileGrid}
diff --git a/ui/file_manager/file_manager/foreground/js/ui/search_box.js b/ui/file_manager/file_manager/foreground/js/ui/search_box.js
index 07e39b8..60328bb 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/search_box.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/search_box.js
@@ -189,7 +189,7 @@
 SearchBox.prototype.setHidden = function(hidden) {
   this.element.hidden = hidden;
   this.searchButton.hidden = hidden;
-}
+};
 
 /**
  * @private
@@ -208,6 +208,7 @@
   this.autocompleteList.attachToInput(this.inputElement);
   this.updateStyles_();
   this.searchButtonToggleRipple_.activated = true;
+  metrics.recordUserAction('SelectSearch');
 };
 
 /**
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
index f49d7cb..134c40e 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
@@ -137,9 +137,9 @@
 
   // Returns true if |plane| can support |overlay| and compatible with
   // |crtc_index|.
-  bool IsCompatible(HardwareDisplayPlane* plane,
-                    const OverlayPlane& overlay,
-                    uint32_t crtc_index) const;
+  virtual bool IsCompatible(HardwareDisplayPlane* plane,
+                            const OverlayPlane& overlay,
+                            uint32_t crtc_index) const;
 
   void ResetCurrentPlaneList(HardwareDisplayPlaneList* plane_list) const;
 
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
index 3c6b9c4..a87020d2 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc
@@ -173,4 +173,17 @@
   return true;
 }
 
+bool HardwareDisplayPlaneManagerLegacy::IsCompatible(
+    HardwareDisplayPlane* plane,
+    const OverlayPlane& overlay,
+    uint32_t crtc_index) const {
+  if (!plane->CanUseForCrtc(crtc_index))
+    return false;
+
+  // When using legacy kms we always scanout only one plane (the primary),
+  // and we always use the opaque fb. Refer to SetPlaneData above.
+  const uint32_t format = overlay.buffer->GetOpaqueFramebufferPixelFormat();
+  return plane->IsSupportedFormat(format);
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h
index 3f492ca..db1d78a 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h
@@ -35,6 +35,9 @@
                     uint32_t crtc_id,
                     const gfx::Rect& src_rect,
                     CrtcController* crtc) override;
+  bool IsCompatible(HardwareDisplayPlane* plane,
+                    const OverlayPlane& overlay,
+                    uint32_t crtc_index) const override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(HardwareDisplayPlaneManagerLegacy);
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index 3e0b41a4..03cc3b5 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -435,6 +435,9 @@
       <message name="IDS_APP_ACCNAME_RESTORE" desc="The accessible name for the Restore button.">
         Restore
       </message>
+      <message name="IDS_APP_ACCNAME_MENU" desc="The accessible name for the Menu button.">
+        Menu
+      </message>
 
       <!-- Scroll Bar Context Menu Labels -->
       <message name="IDS_APP_SCROLLBAR_CXMENU_SCROLLHERE" desc="The label for the 'Scroll Here' item">
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index c2b2ee7c..f12108ad 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -58,7 +58,7 @@
 
 #if defined(OS_WIN)
 #include "base/win/win_util.h"
-#include "ui/base/win/osk_display_manager.h"
+#include "ui/base/ime/win/osk_display_manager.h"
 #endif
 
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.html b/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.html
index 4666209..96310bc 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.html
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.html
@@ -36,29 +36,31 @@
         <button id="dots" title="[[i18n('moreActions')]]" on-tap="onDotsTap_">
         </button>
       </paper-icon-button-light>
-      <template is="cr-lazy-render" id="menu">
-        <cr-action-menu>
-          <button slot="item" class="dropdown-item" id="view"
-              on-tap="onViewTap_">
-            [[i18n('certificateManagerView')]]
-          </button>
-          <button slot="item" class="dropdown-item" id="edit"
-              hidden$="[[!canEdit_(certificateType, model)]]"
-              on-tap="onEditTap_">
-            [[i18n('edit')]]
-          </button>
-          <button slot="item" class="dropdown-item" id="export"
-              hidden$="[[!canExport_(certificateType, model)]]"
-              on-tap="onExportTap_">
-            [[i18n('certificateManagerExport')]]
-          </button>
-          <button slot="item" class="dropdown-item" id="delete"
-              hidden$="[[!canDelete_(model)]]"
-              on-tap="onDeleteTap_">
-            [[i18n('certificateManagerDelete')]]
-          </button>
-        </cr-action-menu>
-      </template>
+      <cr-lazy-render id="menu">
+        <template>
+          <cr-action-menu>
+            <button slot="item" class="dropdown-item" id="view"
+                on-tap="onViewTap_">
+              [[i18n('certificateManagerView')]]
+            </button>
+            <button slot="item" class="dropdown-item" id="edit"
+                hidden$="[[!canEdit_(certificateType, model)]]"
+                on-tap="onEditTap_">
+              [[i18n('edit')]]
+            </button>
+            <button slot="item" class="dropdown-item" id="export"
+                hidden$="[[!canExport_(certificateType, model)]]"
+                on-tap="onExportTap_">
+              [[i18n('certificateManagerExport')]]
+            </button>
+            <button slot="item" class="dropdown-item" id="delete"
+                hidden$="[[!canDelete_(model)]]"
+                on-tap="onDeleteTap_">
+              [[i18n('certificateManagerDelete')]]
+            </button>
+          </cr-action-menu>
+        </template>
+      </cr-lazy-render>
     <div>
   </template>
   <script src="certificate_subentry.js"></script>
diff --git a/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.html b/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.html
index 9dffa4c..be956c7 100644
--- a/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.html
+++ b/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.html
@@ -1,3 +1,8 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<script src="cr_lazy_render.js"></script>
+<dom-module id="cr-lazy-render">
+  <template>
+    <slot></slot>
+  </template>
+  <script src="cr_lazy_render.js"></script>
+</dom-module>
diff --git a/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.js b/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.js
index 8fac4f4..48fd9ac 100644
--- a/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.js
+++ b/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.js
@@ -7,16 +7,20 @@
  * cr-lazy-render is a simple variant of dom-if designed for lazy rendering
  * of elements that are accessed imperatively.
  * Usage:
- *   <template is="cr-lazy-render" id="menu">
- *     <heavy-menu></heavy-menu>
- *   </template>
+ *   <cr-lazy-render id="menu">
+ *     <template>
+ *       <heavy-menu></heavy-menu>
+ *     </template>
+ *   </cr-lazy-render>
  *
  *   this.$.menu.get().show();
+ *
+ * TODO(calamity): Use Polymer.Templatize instead of inheriting the
+ * Polymer.Templatizer behavior once Polymer 2.0 is available.
  */
 
 Polymer({
   is: 'cr-lazy-render',
-  extends: 'template',
 
   behaviors: [Polymer.Templatizer],
 
@@ -43,8 +47,9 @@
 
   /** @private */
   render_: function() {
-    if (!this.ctor)
-      this.templatize(this);
+    var template = this.getContentChildren()[0];
+    if (!template.ctor)
+      this.templatize(template);
     var parentNode = this.parentNode;
     if (parentNode && !this.child_) {
       var instance = this.stamp({});
diff --git a/ui/webui/resources/polymer_resources.grdp b/ui/webui/resources/polymer_resources.grdp
index 6f4a7f9..46f529a 100644
--- a/ui/webui/resources/polymer_resources.grdp
+++ b/ui/webui/resources/polymer_resources.grdp
@@ -784,22 +784,10 @@
              file="../../../third_party/polymer/v1_0/components-chromium/paper-styles/default-theme.html"
              type="chrome_html"
              compress="gzip" />
-  <structure name="IDR_POLYMER_1_0_PAPER_STYLES_ELEMENT_STYLES_PAPER_ITEM_STYLES_HTML"
-             file="../../../third_party/polymer/v1_0/components-chromium/paper-styles/element-styles/paper-item-styles.html"
-             type="chrome_html"
-             compress="gzip" />
   <structure name="IDR_POLYMER_1_0_PAPER_STYLES_ELEMENT_STYLES_PAPER_MATERIAL_STYLES_HTML"
              file="../../../third_party/polymer/v1_0/components-chromium/paper-styles/element-styles/paper-material-styles.html"
              type="chrome_html"
              compress="gzip" />
-  <structure name="IDR_POLYMER_1_0_PAPER_STYLES_PAPER_STYLES_CLASSES_HTML"
-             file="../../../third_party/polymer/v1_0/components-chromium/paper-styles/paper-styles-classes.html"
-             type="chrome_html"
-             compress="gzip" />
-  <structure name="IDR_POLYMER_1_0_PAPER_STYLES_PAPER_STYLES_HTML"
-             file="../../../third_party/polymer/v1_0/components-chromium/paper-styles/paper-styles.html"
-             type="chrome_html"
-             compress="gzip" />
   <structure name="IDR_POLYMER_1_0_PAPER_STYLES_SHADOW_HTML"
              file="../../../third_party/polymer/v1_0/components-chromium/paper-styles/shadow.html"
              type="chrome_html"