diff --git a/DEPS b/DEPS
index 1b30258..eadf524 100644
--- a/DEPS
+++ b/DEPS
@@ -121,7 +121,7 @@
   # 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': 'd4c7458f64270d605c04aa24e2171209c00a2d32',
+  'skia_revision': '5e710e13a55bfd42389427c29e65dfd078e5d4e8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -133,11 +133,11 @@
   # 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': 'e4109f27136d721659e7b545aaead2815fef2918',
+  'angle_revision': '9db8df4c3e6e8ae7ec5af3515dbeb3969a3461aa',
   # 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.
-  'buildtools_revision': '7d88270de197ebe8b439ab5eb57a4a2a0bb810e0',
+  'buildtools_revision': '0e1cbc4eab6861b0c84bf2ed9a3c4b7aa2063819',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -679,7 +679,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '599dad1eadafee83bd2380af69d2a0d290d854c0',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '1457e48474e72375f9fed300a1a826d0b5f02614',
       'condition': 'checkout_linux',
   },
 
@@ -704,7 +704,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'f3d4ab3430828d2be748ba2f164b04cc115ba38c',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c0641b8e930496500616b6f7253da869fc4a343e',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1230,7 +1230,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@aae753f5a641dd15216b9fc9f3e63d7828c264c5',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@fccd9e7db52d5e771aaa6cd97da79264dbee622c',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_permission_manager.cc b/android_webview/browser/aw_permission_manager.cc
index 3a101d39..df85ddee4 100644
--- a/android_webview/browser/aw_permission_manager.cc
+++ b/android_webview/browser/aw_permission_manager.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <utility>
 
 #include "android_webview/browser/aw_browser_permission_request_delegate.h"
@@ -142,7 +143,7 @@
     return requesting + "," + embedding;
   }
 
-  using StatusMap = base::hash_map<std::string, PermissionStatus>;
+  using StatusMap = std::unordered_map<std::string, PermissionStatus>;
   StatusMap pmi_result_cache_;
 
   DISALLOW_COPY_AND_ASSIGN(LastRequestResultCache);
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index ca342ff..673d6c9 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -656,6 +656,12 @@
     "sticky_keys/sticky_keys_state.h",
     "system/accessibility/accessibility_feature_pod_controller.cc",
     "system/accessibility/accessibility_feature_pod_controller.h",
+    "system/accessibility/autoclick_tray.cc",
+    "system/accessibility/autoclick_tray.h",
+    "system/accessibility/autoclick_tray_action_list_view.cc",
+    "system/accessibility/autoclick_tray_action_list_view.h",
+    "system/accessibility/autoclick_tray_action_view.cc",
+    "system/accessibility/autoclick_tray_action_view.h",
     "system/accessibility/dictation_button_tray.cc",
     "system/accessibility/dictation_button_tray.h",
     "system/accessibility/select_to_speak_tray.cc",
@@ -1736,6 +1742,7 @@
     "sticky_keys/sticky_keys_overlay_unittest.cc",
     "sticky_keys/sticky_keys_unittest.cc",
     "system/accessibility/accessibility_feature_pod_controller_unittest.cc",
+    "system/accessibility/autoclick_tray_unittest.cc",
     "system/accessibility/dictation_button_tray_unittest.cc",
     "system/accessibility/select_to_speak_tray_unittest.cc",
     "system/accessibility/tray_accessibility_unittest.cc",
diff --git a/ash/accessibility/accessibility_controller.cc b/ash/accessibility/accessibility_controller.cc
index ddd0dd4..78e45d2 100644
--- a/ash/accessibility/accessibility_controller.cc
+++ b/ash/accessibility/accessibility_controller.cc
@@ -934,11 +934,8 @@
 }
 
 void AccessibilityController::UpdateAutoclickEventTypeFromPref() {
-  DCHECK(active_user_prefs_);
-  mojom::AutoclickEventType event_type = static_cast<mojom::AutoclickEventType>(
-      active_user_prefs_->GetInteger(prefs::kAccessibilityAutoclickEventType));
-
-  Shell::Get()->autoclick_controller()->SetAutoclickEventType(event_type);
+  Shell::Get()->autoclick_controller()->SetAutoclickEventType(
+      GetAutoclickEventType());
 }
 
 void AccessibilityController::SetAutoclickEventType(
@@ -948,6 +945,13 @@
   active_user_prefs_->SetInteger(prefs::kAccessibilityAutoclickEventType,
                                  static_cast<int>(event_type));
   active_user_prefs_->CommitPendingWrite();
+  Shell::Get()->autoclick_controller()->SetAutoclickEventType(event_type);
+}
+
+mojom::AutoclickEventType AccessibilityController::GetAutoclickEventType() {
+  DCHECK(active_user_prefs_);
+  return static_cast<mojom::AutoclickEventType>(
+      active_user_prefs_->GetInteger(prefs::kAccessibilityAutoclickEventType));
 }
 
 void AccessibilityController::UpdateAutoclickRevertToLeftClickFromPref() {
diff --git a/ash/accessibility/accessibility_controller.h b/ash/accessibility/accessibility_controller.h
index 37d01415..4863985 100644
--- a/ash/accessibility/accessibility_controller.h
+++ b/ash/accessibility/accessibility_controller.h
@@ -72,6 +72,7 @@
   void SetAutoclickEnabled(bool enabled);
   bool autoclick_enabled() const { return autoclick_enabled_; }
   void SetAutoclickEventType(mojom::AutoclickEventType event_type);
+  mojom::AutoclickEventType GetAutoclickEventType();
 
   void SetCaretHighlightEnabled(bool enabled);
   bool caret_highlight_enabled() const { return caret_highlight_enabled_; }
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index a877e3396..a3bf5e0 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -470,9 +470,27 @@
       <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LARGE_CURSOR" desc="The label used in the accessibility menu of the system tray to toggle on/off large mouse cursor feature.">
         Large mouse cursor
       </message>
-      <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_AUTOCLICK" desc="The label used in the accessibility menu of the system tray to toggle on/off automatic mouse clicks.">
+      <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_AUTOCLICK" desc="The label and title used in the accessibility menu of the system tray to toggle on/off automatic mouse clicks.">
         Automatic clicks
       </message>
+      <message name="IDS_ASH_STATUS_TRAY_AUTOCLICK_SETTINGS" desc="The settings button for the automatic clicks menu">
+        Settings
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_LEFT_CLICK" desc="The menu option for the automatic clicks menu that results in the next automatic click being a left-click.">
+        Left click
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_RIGHT_CLICK" desc="The menu option for the automatic clicks menu that results in the next automatic click being a right-click.">
+        Right click
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_DOUBLE_CLICK" desc="The menu option for the automatic clicks menu that results in the next automatic click being a double-click.">
+        Double click
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_DRAG_AND_DROP" desc="The menu option for the automatic clicks menu that results in the next automatic click being a drag-and-drop.">
+        Drag and drop
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_NO_ACTION" desc="The menu option for the automatic clicks menu that results in pausing the automatic clicks feature without disabling it.">
+        No action (pause)
+      </message>
       <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD" desc="The label used in the accessibility menu of the system tray to toggle on/off the onscreen keyboard.">
         On-screen keyboard
       </message>
@@ -1322,10 +1340,12 @@
       <message name="IDS_ASH_IME_MENU_ACCESSIBLE_NAME" desc="The accessible text for opt-in IME menu icon in status tray.">
         IME menu button
       </message>
-
       <message name="IDS_ASH_SELECT_TO_SPEAK_TRAY_ACCESSIBLE_NAME" desc="The accessible text for the select-to-speak menu icon in the status tray.">
         Select-to-Speak button
       </message>
+      <message name="IDS_ASH_AUTOCLICK_TRAY_ACCESSIBLE_NAME" desc="The accessible text for the automatic clicks menu icon in the status tray.">
+        Automatic clicks button
+      </message>
 
       <!-- Deprecated Accelerators Messages -->
       <!-- Shortcut keys MUST be connected with pluses (e.g. Ctrl+Shift+L). -->
diff --git a/ash/ash_strings_grd/IDS_ASH_AUTOCLICK_TRAY_ACCESSIBLE_NAME.png.sha1 b/ash/ash_strings_grd/IDS_ASH_AUTOCLICK_TRAY_ACCESSIBLE_NAME.png.sha1
new file mode 100644
index 0000000..438db68
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_AUTOCLICK_TRAY_ACCESSIBLE_NAME.png.sha1
@@ -0,0 +1 @@
+502c045d66c396293cd0150f8abe61c2aeb2e1e4
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_ACCESSIBILITY_AUTOCLICK.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_ACCESSIBILITY_AUTOCLICK.png.sha1
new file mode 100644
index 0000000..438db68
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_ACCESSIBILITY_AUTOCLICK.png.sha1
@@ -0,0 +1 @@
+502c045d66c396293cd0150f8abe61c2aeb2e1e4
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_DOUBLE_CLICK.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_DOUBLE_CLICK.png.sha1
new file mode 100644
index 0000000..438db68
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_DOUBLE_CLICK.png.sha1
@@ -0,0 +1 @@
+502c045d66c396293cd0150f8abe61c2aeb2e1e4
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_DRAG_AND_DROP.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_DRAG_AND_DROP.png.sha1
new file mode 100644
index 0000000..438db68
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_DRAG_AND_DROP.png.sha1
@@ -0,0 +1 @@
+502c045d66c396293cd0150f8abe61c2aeb2e1e4
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_LEFT_CLICK.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_LEFT_CLICK.png.sha1
new file mode 100644
index 0000000..438db68
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_LEFT_CLICK.png.sha1
@@ -0,0 +1 @@
+502c045d66c396293cd0150f8abe61c2aeb2e1e4
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_NO_ACTION.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_NO_ACTION.png.sha1
new file mode 100644
index 0000000..438db68
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_NO_ACTION.png.sha1
@@ -0,0 +1 @@
+502c045d66c396293cd0150f8abe61c2aeb2e1e4
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_RIGHT_CLICK.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_RIGHT_CLICK.png.sha1
new file mode 100644
index 0000000..438db68
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_RIGHT_CLICK.png.sha1
@@ -0,0 +1 @@
+502c045d66c396293cd0150f8abe61c2aeb2e1e4
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_SETTINGS.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_SETTINGS.png.sha1
new file mode 100644
index 0000000..438db68
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_AUTOCLICK_SETTINGS.png.sha1
@@ -0,0 +1 @@
+502c045d66c396293cd0150f8abe61c2aeb2e1e4
\ No newline at end of file
diff --git a/ash/autoclick/autoclick_controller.cc b/ash/autoclick/autoclick_controller.cc
index 64531c6d..f9c079e 100644
--- a/ash/autoclick/autoclick_controller.cc
+++ b/ash/autoclick/autoclick_controller.cc
@@ -120,8 +120,8 @@
     mojom::AutoclickEventType type) {
   if (event_type_ == type)
     return;
-  event_type_ = type;
   CancelAutoclickAction();
+  event_type_ = type;
 }
 
 void AutoclickController::CreateAutoclickRingWidget(
@@ -156,7 +156,10 @@
 }
 
 void AutoclickController::DoAutoclickAction() {
-  RecordUserAction(event_type_);
+  // Set the in-progress event type locally so that if the event type is updated
+  // in the middle of this event being executed it doesn't change execution.
+  mojom::AutoclickEventType in_progress_event_type = event_type_;
+  RecordUserAction(in_progress_event_type);
 
   aura::Window* root_window = wm::GetRootWindowAt(anchor_location_);
   DCHECK(root_window) << "Root window not found while attempting autoclick.";
@@ -166,17 +169,19 @@
   aura::WindowTreeHost* host = root_window->GetHost();
   host->ConvertDIPToPixels(&location_in_pixels);
 
-  bool drag_start = event_type_ == mojom::AutoclickEventType::kDragAndDrop &&
-                    !drag_event_rewriter_->IsEnabled();
+  bool drag_start =
+      in_progress_event_type == mojom::AutoclickEventType::kDragAndDrop &&
+      !drag_event_rewriter_->IsEnabled();
   bool drag_stop = DragInProgress();
 
-  if (event_type_ == mojom::AutoclickEventType::kLeftClick ||
-      event_type_ == mojom::AutoclickEventType::kRightClick ||
-      event_type_ == mojom::AutoclickEventType::kDoubleClick || drag_start ||
-      drag_stop) {
-    int button = event_type_ == mojom::AutoclickEventType::kRightClick
-                     ? ui::EF_RIGHT_MOUSE_BUTTON
-                     : ui::EF_LEFT_MOUSE_BUTTON;
+  if (in_progress_event_type == mojom::AutoclickEventType::kLeftClick ||
+      in_progress_event_type == mojom::AutoclickEventType::kRightClick ||
+      in_progress_event_type == mojom::AutoclickEventType::kDoubleClick ||
+      drag_start || drag_stop) {
+    int button =
+        in_progress_event_type == mojom::AutoclickEventType::kRightClick
+            ? ui::EF_RIGHT_MOUSE_BUTTON
+            : ui::EF_LEFT_MOUSE_BUTTON;
 
     ui::EventDispatchDetails details;
     if (!drag_stop) {
@@ -191,7 +196,7 @@
         return;
       }
       if (details.dispatcher_destroyed) {
-        OnActionCompleted();
+        OnActionCompleted(in_progress_event_type);
         return;
       }
     }
@@ -203,9 +208,9 @@
     details = host->event_sink()->OnEventFromSource(&release_event);
 
     // Now a single click, or half the drag & drop, has been completed.
-    if (event_type_ != mojom::AutoclickEventType::kDoubleClick ||
+    if (in_progress_event_type != mojom::AutoclickEventType::kDoubleClick ||
         details.dispatcher_destroyed) {
-      OnActionCompleted();
+      OnActionCompleted(in_progress_event_type);
       return;
     }
 
@@ -219,11 +224,11 @@
         mouse_event_flags_ | button | ui::EF_IS_DOUBLE_CLICK, button);
     details = host->event_sink()->OnEventFromSource(&double_press_event);
     if (details.dispatcher_destroyed) {
-      OnActionCompleted();
+      OnActionCompleted(in_progress_event_type);
       return;
     }
     details = host->event_sink()->OnEventFromSource(&double_release_event);
-    OnActionCompleted();
+    OnActionCompleted(in_progress_event_type);
   }
 }
 
@@ -252,9 +257,13 @@
   SetTapDownTarget(nullptr);
 }
 
-void AutoclickController::OnActionCompleted() {
+void AutoclickController::OnActionCompleted(
+    mojom::AutoclickEventType completed_event_type) {
+  // No need to change to left click if the setting is not enabled or the
+  // event that just executed already was a left click.
   if (!revert_to_left_click_ ||
-      event_type_ == mojom::AutoclickEventType::kLeftClick)
+      event_type_ == mojom::AutoclickEventType::kLeftClick ||
+      completed_event_type == mojom::AutoclickEventType::kLeftClick)
     return;
   // Change the preference, but set it locally so we do not reset any state when
   // AutoclickController::SetAutoclickEventType is called.
diff --git a/ash/autoclick/autoclick_controller.h b/ash/autoclick/autoclick_controller.h
index f647f0b..d76e4b0f 100644
--- a/ash/autoclick/autoclick_controller.h
+++ b/ash/autoclick/autoclick_controller.h
@@ -74,7 +74,7 @@
   void DoAutoclickAction();
   void StartAutoclickGesture();
   void CancelAutoclickAction();
-  void OnActionCompleted();
+  void OnActionCompleted(mojom::AutoclickEventType event_type);
   void InitClickTimers();
   void UpdateRingWidget(const gfx::Point& mouse_location);
   void RecordUserAction(mojom::AutoclickEventType event_type) const;
diff --git a/ash/system/accessibility/autoclick_tray.cc b/ash/system/accessibility/autoclick_tray.cc
new file mode 100644
index 0000000..bb740b3
--- /dev/null
+++ b/ash/system/accessibility/autoclick_tray.cc
@@ -0,0 +1,221 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/accessibility/autoclick_tray.h"
+
+#include "ash/accessibility/accessibility_controller.h"
+#include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/session/session_controller.h"
+#include "ash/shelf/shelf_constants.h"
+#include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/system/accessibility/autoclick_tray_action_list_view.h"
+#include "ash/system/model/system_tray_model.h"
+#include "ash/system/tray/system_menu_button.h"
+#include "ash/system/tray/tray_constants.h"
+#include "ash/system/tray/tray_container.h"
+#include "ash/system/tray/tray_popup_item_style.h"
+#include "ash/system/tray/tray_popup_utils.h"
+#include "ash/system/tray/tray_utils.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/box_layout.h"
+
+namespace ash {
+
+namespace {
+
+// Used for testing.
+const int kActionsListId = 1;
+
+}  // namespace
+
+// The view that contains Autoclick menu title and settings button.
+// TODO(katie): Refactor to share code with ImeTitleView.
+class AutoclickTitleView : public views::View, public views::ButtonListener {
+ public:
+  explicit AutoclickTitleView(AutoclickTray* autoclick_tray)
+      : autoclick_tray_(autoclick_tray) {
+    const int separator_width = TrayConstants::separator_width();
+    SetBorder(views::CreatePaddedBorder(
+        views::CreateSolidSidedBorder(0, 0, separator_width, 0,
+                                      kMenuSeparatorColor),
+        gfx::Insets(kMenuSeparatorVerticalPadding - separator_width, 0)));
+    auto box_layout =
+        std::make_unique<views::BoxLayout>(views::BoxLayout::kHorizontal);
+    box_layout->set_minimum_cross_axis_size(kTrayPopupItemMinHeight);
+    views::BoxLayout* layout_ptr = SetLayoutManager(std::move(box_layout));
+    auto* title_label = new views::Label(
+        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_AUTOCLICK));
+    title_label->SetBorder(
+        views::CreateEmptyBorder(0, kMenuEdgeEffectivePadding, 1, 0));
+    title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::TITLE,
+                             false /* use_unified_theme */);
+    style.SetupLabel(title_label);
+
+    AddChildView(title_label);
+    layout_ptr->SetFlexForView(title_label, 1);
+
+    settings_button_ = new SystemMenuButton(
+        this, kSystemMenuSettingsIcon, IDS_ASH_STATUS_TRAY_AUTOCLICK_SETTINGS);
+    if (!TrayPopupUtils::CanOpenWebUISettings())
+      settings_button_->SetEnabled(false);
+    AddChildView(settings_button_);
+  }
+
+  // views::ButtonListener:
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override {
+    DCHECK_EQ(sender, settings_button_);
+    autoclick_tray_->OnSettingsPressed();
+  }
+
+  ~AutoclickTitleView() override = default;
+
+ private:
+  AutoclickTray* autoclick_tray_;
+  SystemMenuButton* settings_button_;
+
+  DISALLOW_COPY_AND_ASSIGN(AutoclickTitleView);
+};
+
+AutoclickTray::AutoclickTray(Shelf* shelf) : TrayBackgroundView(shelf) {
+  SetInkDropMode(InkDropMode::ON);
+
+  UpdateIconsForSession();
+  views::ImageView* icon = new views::ImageView();
+  icon->SetImage(tray_image_);
+  const int vertical_padding = (kTrayItemSize - tray_image_.height()) / 2;
+  const int horizontal_padding = (kTrayItemSize - tray_image_.width()) / 2;
+  icon->SetBorder(views::CreateEmptyBorder(
+      gfx::Insets(vertical_padding, horizontal_padding)));
+  icon->set_tooltip_text(
+      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_AUTOCLICK));
+  // This transfers pointer ownership to the tray_container.
+  tray_container()->AddChildView(icon);
+  CheckStatusAndUpdateIcon();
+
+  // Observe the accessibility controller state changes to know when autoclick
+  // state is updated or when it is disabled/enabled.
+  Shell::Get()->accessibility_controller()->AddObserver(this);
+
+  // Observe the session state to know when the session type changes.
+  Shell::Get()->session_controller()->AddObserver(this);
+}
+
+AutoclickTray::~AutoclickTray() {
+  Shell::Get()->session_controller()->RemoveObserver(this);
+  Shell::Get()->accessibility_controller()->RemoveObserver(this);
+  if (bubble_)
+    bubble_->bubble_view()->ResetDelegate();
+}
+
+base::string16 AutoclickTray::GetAccessibleNameForTray() {
+  return l10n_util::GetStringUTF16(IDS_ASH_AUTOCLICK_TRAY_ACCESSIBLE_NAME);
+}
+
+bool AutoclickTray::PerformAction(const ui::Event& event) {
+  if (bubble_) {
+    CloseBubble();
+    SetIsActive(false);
+  } else {
+    ShowBubble(true /* show by click */);
+    SetIsActive(true);
+  }
+  return true;
+}
+
+void AutoclickTray::HideBubbleWithView(const TrayBubbleView* bubble_view) {
+  if (bubble_->bubble_view() == bubble_view)
+    CloseBubble();
+}
+
+void AutoclickTray::CloseBubble() {
+  bubble_.reset();
+}
+
+void AutoclickTray::ShowBubble(bool show_by_click) {
+  TrayBubbleView::InitParams init_params;
+  init_params.delegate = this;
+  init_params.parent_window = GetBubbleWindowContainer();
+  init_params.anchor_view = GetBubbleAnchor();
+  init_params.anchor_alignment = GetAnchorAlignment();
+  init_params.min_width = kTrayMenuWidth;
+  init_params.max_width = kTrayMenuWidth;
+  init_params.close_on_deactivate = true;
+  init_params.show_by_click = show_by_click;
+
+  TrayBubbleView* bubble_view = new TrayBubbleView(init_params);
+  bubble_view->set_anchor_view_insets(GetBubbleAnchorInsets());
+
+  bubble_view->AddChildView(new AutoclickTitleView(this));
+
+  AutoclickTrayActionListView* actions_list = new AutoclickTrayActionListView(
+      this, Shell::Get()->accessibility_controller()->GetAutoclickEventType());
+  actions_list->set_id(kActionsListId);
+  bubble_view->AddChildView(actions_list);
+
+  bubble_ = std::make_unique<TrayBubbleWrapper>(this, bubble_view,
+                                                true /* is_persistent */);
+}
+
+TrayBubbleView* AutoclickTray::GetBubbleView() {
+  return bubble_ ? bubble_->bubble_view() : nullptr;
+}
+
+base::string16 AutoclickTray::GetAccessibleNameForBubble() {
+  return l10n_util::GetStringUTF16(IDS_ASH_AUTOCLICK_TRAY_ACCESSIBLE_NAME);
+}
+
+bool AutoclickTray::ShouldEnableExtraKeyboardAccessibility() {
+  return Shell::Get()->accessibility_controller()->spoken_feedback_enabled();
+}
+
+void AutoclickTray::HideBubble(const TrayBubbleView* bubble_view) {
+  HideBubbleWithView(bubble_view);
+}
+
+void AutoclickTray::OnAccessibilityStatusChanged() {
+  CheckStatusAndUpdateIcon();
+}
+
+void AutoclickTray::OnSessionStateChanged(session_manager::SessionState state) {
+  UpdateIconsForSession();
+  CheckStatusAndUpdateIcon();
+}
+
+void AutoclickTray::OnSettingsPressed() {
+  // TODO(katie): Record a user action metic.
+  CloseBubble();
+  SetIsActive(false);
+  // TODO(katie): Try to jump to autoclick's specific settings.
+  Shell::Get()->system_tray_model()->client_ptr()->ShowAccessibilitySettings();
+}
+
+void AutoclickTray::OnEventTypePressed(mojom::AutoclickEventType type) {
+  // When the user selects an autoclick event type option, close the bubble
+  // view and update the autoclick controller's state.
+  // TODO(katie): Record a user action metric.
+  CloseBubble();
+  SetIsActive(false);
+  Shell::Get()->accessibility_controller()->SetAutoclickEventType(type);
+}
+
+void AutoclickTray::UpdateIconsForSession() {
+  session_manager::SessionState session_state =
+      Shell::Get()->session_controller()->GetSessionState();
+  SkColor color = TrayIconColor(session_state);
+
+  // TODO(katie): Use autoclick asset when available.
+  tray_image_ = gfx::CreateVectorIcon(kSystemTraySelectToSpeakNewuiIcon, color);
+}
+
+void AutoclickTray::CheckStatusAndUpdateIcon() {
+  SetVisible(Shell::Get()->accessibility_controller()->autoclick_enabled());
+}
+
+}  // namespace ash
diff --git a/ash/system/accessibility/autoclick_tray.h b/ash/system/accessibility/autoclick_tray.h
new file mode 100644
index 0000000..52b64a2
--- /dev/null
+++ b/ash/system/accessibility/autoclick_tray.h
@@ -0,0 +1,77 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_ACCESSIBILITY_AUTOCLICK_TRAY_H_
+#define ASH_SYSTEM_ACCESSIBILITY_AUTOCLICK_TRAY_H_
+
+#include "ash/accessibility/accessibility_observer.h"
+#include "ash/ash_export.h"
+#include "ash/public/interfaces/accessibility_controller.mojom.h"
+#include "ash/session/session_observer.h"
+#include "ash/system/tray/tray_background_view.h"
+#include "ash/system/tray/tray_bubble_view.h"
+#include "ash/system/tray/tray_bubble_wrapper.h"
+#include "base/macros.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace ash {
+
+// A button in the tray that opens a menu which lets users manage their
+// autoclick settings.
+class ASH_EXPORT AutoclickTray : public TrayBackgroundView,
+                                 public AccessibilityObserver,
+                                 public SessionObserver {
+ public:
+  explicit AutoclickTray(Shelf* shelf);
+  ~AutoclickTray() override;
+
+  // TrayBackgroundView:
+  base::string16 GetAccessibleNameForTray() override;
+  void HideBubbleWithView(const TrayBubbleView* bubble_view) override;
+  void ClickedOutsideBubble() override {}
+  bool PerformAction(const ui::Event& event) override;
+  void CloseBubble() override;
+  void ShowBubble(bool show_by_click) override;
+  TrayBubbleView* GetBubbleView() override;
+
+  // TrayBubbleView::Delegate:
+  base::string16 GetAccessibleNameForBubble() override;
+  bool ShouldEnableExtraKeyboardAccessibility() override;
+  void HideBubble(const TrayBubbleView* bubble_view) override;
+
+  // AccessibilityObserver:
+  void OnAccessibilityStatusChanged() override;
+
+  // SessionObserver:
+  void OnSessionStateChanged(session_manager::SessionState state) override;
+
+  // Called when the user wants to open the autoclick section of the chrome
+  // settings. Used when the bubble menu's settings button is tapped.
+  void OnSettingsPressed();
+
+  // Called when the user wants to change the autoclick event type. Used when
+  // an event type in the bubble menu is tapped.
+  void OnEventTypePressed(mojom::AutoclickEventType type);
+
+ private:
+  friend class AutoclickTrayTest;
+
+  // Updates the icons color depending on if the user is logged-in or not.
+  void UpdateIconsForSession();
+
+  // Updates visibility when autoclick is enabled / disabled.
+  void CheckStatusAndUpdateIcon();
+
+  // The image shown in the tray icon.
+  gfx::ImageSkia tray_image_;
+
+  // Bubble view holds additional actions while active.
+  std::unique_ptr<TrayBubbleWrapper> bubble_;
+
+  DISALLOW_COPY_AND_ASSIGN(AutoclickTray);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_ACCESSIBILITY_AUTOCLICK_TRAY_H_
diff --git a/ash/system/accessibility/autoclick_tray_action_list_view.cc b/ash/system/accessibility/autoclick_tray_action_list_view.cc
new file mode 100644
index 0000000..69c5cfd
--- /dev/null
+++ b/ash/system/accessibility/autoclick_tray_action_list_view.cc
@@ -0,0 +1,85 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/accessibility/autoclick_tray_action_list_view.h"
+
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/system/accessibility/autoclick_tray.h"
+#include "ash/system/accessibility/autoclick_tray_action_view.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/views/layout/box_layout.h"
+
+namespace ash {
+
+namespace {
+
+// Used for testing.
+const int kLeftClickViewID = 2;
+const int kRightClickViewID = 3;
+const int kDoubleClickViewID = 4;
+const int kDragAndDropViewID = 5;
+const int kPauseViewID = 6;
+
+}  // namespace
+
+AutoclickTrayActionListView::AutoclickTrayActionListView(
+    AutoclickTray* tray,
+    mojom::AutoclickEventType selected_event_type)
+    : autoclick_tray_(tray), selected_event_type_(selected_event_type) {
+  DCHECK(tray);
+  views::BoxLayout* layout = SetLayoutManager(
+      std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical));
+
+  // The views will be owned by the view hierarchy.
+  views::View* left_click_view = new AutoclickTrayActionView(
+      this, mojom::AutoclickEventType::kLeftClick,
+      l10n_util::GetStringUTF16(
+          IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_LEFT_CLICK),
+      selected_event_type_ == mojom::AutoclickEventType::kLeftClick);
+  left_click_view->set_id(kLeftClickViewID);
+  AddChildView(left_click_view);
+  layout->SetFlexForView(left_click_view, 1);
+
+  views::View* right_click_view = new AutoclickTrayActionView(
+      this, mojom::AutoclickEventType::kRightClick,
+      l10n_util::GetStringUTF16(
+          IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_RIGHT_CLICK),
+      selected_event_type_ == mojom::AutoclickEventType::kRightClick);
+  right_click_view->set_id(kRightClickViewID);
+  AddChildView(right_click_view);
+  layout->SetFlexForView(right_click_view, 1);
+
+  views::View* double_click_view = new AutoclickTrayActionView(
+      this, mojom::AutoclickEventType::kDoubleClick,
+      l10n_util::GetStringUTF16(
+          IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_DOUBLE_CLICK),
+      selected_event_type_ == mojom::AutoclickEventType::kDoubleClick);
+  double_click_view->set_id(kDoubleClickViewID);
+  AddChildView(double_click_view);
+  layout->SetFlexForView(double_click_view, 1);
+
+  views::View* drag_and_drop_view = new AutoclickTrayActionView(
+      this, mojom::AutoclickEventType::kDragAndDrop,
+      l10n_util::GetStringUTF16(
+          IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_DRAG_AND_DROP),
+      selected_event_type_ == mojom::AutoclickEventType::kDragAndDrop);
+  drag_and_drop_view->set_id(kDragAndDropViewID);
+  AddChildView(drag_and_drop_view);
+  layout->SetFlexForView(drag_and_drop_view, 1);
+
+  views::View* pause_view = new AutoclickTrayActionView(
+      this, mojom::AutoclickEventType::kNoAction,
+      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUTOCLICK_OPTION_NO_ACTION),
+      selected_event_type_ == mojom::AutoclickEventType::kNoAction);
+  pause_view->set_id(kPauseViewID);
+  AddChildView(pause_view);
+  layout->SetFlexForView(pause_view, 1);
+}
+
+void AutoclickTrayActionListView::PerformTrayActionViewAction(
+    mojom::AutoclickEventType type) {
+  autoclick_tray_->OnEventTypePressed(type);
+}
+
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/system/accessibility/autoclick_tray_action_list_view.h b/ash/system/accessibility/autoclick_tray_action_list_view.h
new file mode 100644
index 0000000..300e2a0
--- /dev/null
+++ b/ash/system/accessibility/autoclick_tray_action_list_view.h
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_ACCESSIBILITY_AUTOCLICK_TRAY_ACTION_LIST_VIEW_H_
+#define ASH_SYSTEM_ACCESSIBILITY_AUTOCLICK_TRAY_ACTION_LIST_VIEW_H_
+
+#include "ash/public/interfaces/accessibility_controller.mojom.h"
+#include "ash/system/tray/system_menu_button.h"
+#include "base/macros.h"
+
+namespace ash {
+class AutoclickTray;
+
+// View for the list of Autoclick action options.
+class AutoclickTrayActionListView : public views::View {
+ public:
+  AutoclickTrayActionListView(AutoclickTray* tray,
+                              mojom::AutoclickEventType selected_event_type);
+  ~AutoclickTrayActionListView() override = default;
+
+  void PerformTrayActionViewAction(mojom::AutoclickEventType type);
+
+ private:
+  AutoclickTray* autoclick_tray_;
+  mojom::AutoclickEventType selected_event_type_;
+
+  DISALLOW_COPY_AND_ASSIGN(AutoclickTrayActionListView);
+};
+
+}  // namespace ash
+
+#endif  // defined ASH_SYSTEM_ACCESSIBILITY_AUTOCLICK_TRAY_ACTION_LIST_VIEW_H_
\ No newline at end of file
diff --git a/ash/system/accessibility/autoclick_tray_action_view.cc b/ash/system/accessibility/autoclick_tray_action_view.cc
new file mode 100644
index 0000000..455a89ba
--- /dev/null
+++ b/ash/system/accessibility/autoclick_tray_action_view.cc
@@ -0,0 +1,72 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/accessibility/autoclick_tray_action_view.h"
+
+#include "ash/system/accessibility/autoclick_tray_action_list_view.h"
+#include "ash/system/tray/tray_popup_item_style.h"
+#include "ash/system/tray/tray_popup_utils.h"
+#include "ash/system/tray/tri_view.h"
+#include "ui/accessibility/ax_node_data.h"
+#include "ui/gfx/color_palette.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/fill_layout.h"
+
+namespace ash {
+
+AutoclickTrayActionView::AutoclickTrayActionView(
+    AutoclickTrayActionListView* list_view,
+    mojom::AutoclickEventType event_type,
+    const base::string16& label,
+    bool selected)
+    : ActionableView(TrayPopupInkDropStyle::FILL_BOUNDS),
+      list_view_(list_view),
+      event_type_(event_type),
+      selected_(selected) {
+  SetInkDropMode(InkDropMode::ON);
+
+  TriView* tri_view = TrayPopupUtils::CreateDefaultRowView();
+  AddChildView(tri_view);
+  SetLayoutManager(std::make_unique<views::FillLayout>());
+
+  views::ImageView* icon = new views::ImageView();
+  // TODO(katie): Use autoclick assets when available.
+  icon->SetImage(gfx::CreateVectorIcon(kSystemTraySelectToSpeakNewuiIcon,
+                                       gfx::kGoogleGrey700));
+  tri_view->AddView(TriView::Container::START, icon);
+
+  auto* label_view = TrayPopupUtils::CreateDefaultLabel();
+  label_view->SetText(label);
+  TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL,
+                           false /* use unified theme */);
+  style.SetupLabel(label_view);
+
+  label_view->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  tri_view->AddView(TriView::Container::CENTER, label_view);
+
+  if (selected) {
+    // The checked button indicates the autoclick type is selected.
+    views::ImageView* checked_image = TrayPopupUtils::CreateMainImageView();
+    checked_image->SetImage(gfx::CreateVectorIcon(
+        kCheckCircleIcon, kMenuIconSize, gfx::kGoogleGreen700));
+    tri_view->AddView(TriView::Container::END, checked_image);
+  }
+  SetAccessibleName(label_view->text());
+}
+
+bool AutoclickTrayActionView::PerformAction(const ui::Event& event) {
+  list_view_->PerformTrayActionViewAction(event_type_);
+  return true;
+}
+
+void AutoclickTrayActionView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+  ActionableView::GetAccessibleNodeData(node_data);
+  node_data->role = ax::mojom::Role::kCheckBox;
+  node_data->SetCheckedState(selected_ ? ax::mojom::CheckedState::kTrue
+                                       : ax::mojom::CheckedState::kFalse);
+}
+
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/system/accessibility/autoclick_tray_action_view.h b/ash/system/accessibility/autoclick_tray_action_view.h
new file mode 100644
index 0000000..8920ddc
--- /dev/null
+++ b/ash/system/accessibility/autoclick_tray_action_view.h
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_ACCESSIBILITY_AUTOCLICK_TRAY_ACTION_VIEW_H_
+#define ASH_SYSTEM_ACCESSIBILITY_AUTOCLICK_TRAY_ACTION_VIEW_H_
+
+#include "ash/public/interfaces/accessibility_controller.mojom.h"
+#include "ash/system/tray/actionable_view.h"
+#include "base/macros.h"
+
+namespace ash {
+class AutoclickTrayActionListView;
+
+// A single action view in the autoclick tray bubble.
+class AutoclickTrayActionView : public ActionableView {
+ public:
+  AutoclickTrayActionView(AutoclickTrayActionListView* list_view,
+                          mojom::AutoclickEventType event_type,
+                          const base::string16& label,
+                          bool selected);
+  ~AutoclickTrayActionView() override = default;
+
+  // ActionableView:
+  bool PerformAction(const ui::Event& event) override;
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+
+ private:
+  AutoclickTrayActionListView* list_view_;
+  mojom::AutoclickEventType event_type_;
+  bool selected_;
+
+  DISALLOW_COPY_AND_ASSIGN(AutoclickTrayActionView);
+};
+
+}  // namespace ash
+
+#endif  // defined ASH_SYSTEM_ACCESSIBILITY_AUTOCLICK_TRAY_ACTION_VIEW_H_
\ No newline at end of file
diff --git a/ash/system/accessibility/autoclick_tray_unittest.cc b/ash/system/accessibility/autoclick_tray_unittest.cc
new file mode 100644
index 0000000..00b984e91
--- /dev/null
+++ b/ash/system/accessibility/autoclick_tray_unittest.cc
@@ -0,0 +1,155 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/accessibility/autoclick_tray.h"
+
+#include "ash/accessibility/accessibility_controller.h"
+#include "ash/accessibility/test_accessibility_controller_client.h"
+#include "ash/shell.h"
+#include "ash/system/accessibility/autoclick_tray_action_list_view.h"
+#include "ash/system/accessibility/autoclick_tray_action_view.h"
+#include "ash/system/status_area_widget.h"
+#include "ash/system/status_area_widget_test_helper.h"
+#include "ash/test/ash_test_base.h"
+#include "base/command_line.h"
+#include "base/strings/utf_string_conversions.h"
+#include "ui/accessibility/accessibility_switches.h"
+#include "ui/accessibility/ax_node_data.h"
+#include "ui/base/ime/ime_bridge.h"
+#include "ui/base/ime/text_input_flags.h"
+#include "ui/events/event.h"
+#include "ui/views/controls/label.h"
+
+namespace ash {
+
+namespace {
+
+const int kActionsListId = 1;
+const int kLeftClickViewID = 2;
+const int kRightClickViewID = 3;
+const int kDoubleClickViewID = 4;
+const int kDragAndDropViewID = 5;
+const int kPauseViewID = 6;
+
+AutoclickTray* GetTray() {
+  return StatusAreaWidgetTestHelper::GetStatusAreaWidget()->autoclick_tray();
+}
+
+ui::GestureEvent CreateTapEvent() {
+  return ui::GestureEvent(0, 0, 0, base::TimeTicks(),
+                          ui::GestureEventDetails(ui::ET_GESTURE_TAP));
+}
+
+}  // namespace
+
+class AutoclickTrayTest : public AshTestBase {
+ public:
+  AutoclickTrayTest() = default;
+  ~AutoclickTrayTest() override = default;
+
+  // testing::Test:
+  void SetUp() override {
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        switches::kEnableExperimentalAccessibilityAutoclick);
+    AshTestBase::SetUp();
+    Shell::Get()->accessibility_controller()->SetAutoclickEnabled(true);
+  }
+
+ protected:
+  // Returns true if the Autoclick tray is visible.
+  bool IsVisible() { return GetTray()->visible(); }
+
+  // Returns true if the background color of the tray is active.
+  bool IsTrayBackgroundActive() { return GetTray()->is_active(); }
+
+  // Gets the current tray image icon.
+  gfx::ImageSkia* GetImageIcon() { return &GetTray()->tray_image_; }
+
+  TrayBubbleWrapper* GetBubble() { return GetTray()->bubble_.get(); }
+
+  AutoclickTrayActionView* GetTrayActionView(int view_id) {
+    TrayBubbleWrapper* bubble = GetBubble();
+    if (!bubble)
+      return nullptr;
+    AutoclickTrayActionListView* actions_list =
+        static_cast<AutoclickTrayActionListView*>(
+            bubble->bubble_view()->GetViewByID(kActionsListId));
+    if (!actions_list)
+      return nullptr;
+    return static_cast<AutoclickTrayActionView*>(
+        actions_list->GetViewByID(view_id));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AutoclickTrayTest);
+};
+
+// Ensures that creation doesn't cause any crashes and adds the image icon.
+// Also checks that the tray is visible.
+TEST_F(AutoclickTrayTest, BasicConstruction) {
+  EXPECT_TRUE(GetImageIcon());
+  EXPECT_TRUE(IsVisible());
+}
+
+// Tests the icon disappears when autoclick is disabled and re-appears
+// when it is enabled.
+TEST_F(AutoclickTrayTest, ShowsAndHidesWithAutoclickEnabled) {
+  Shell::Get()->accessibility_controller()->SetAutoclickEnabled(false);
+  EXPECT_FALSE(IsVisible());
+  Shell::Get()->accessibility_controller()->SetAutoclickEnabled(true);
+  EXPECT_TRUE(IsVisible());
+}
+
+// Test that clicking the button opens the bubble.
+TEST_F(AutoclickTrayTest, ButtonOpensBubble) {
+  EXPECT_FALSE(IsTrayBackgroundActive());
+  EXPECT_FALSE(GetBubble());
+
+  GetTray()->PerformAction(CreateTapEvent());
+  EXPECT_TRUE(IsTrayBackgroundActive());
+  EXPECT_TRUE(GetBubble());
+
+  GetTray()->PerformAction(CreateTapEvent());
+  EXPECT_FALSE(IsTrayBackgroundActive());
+  EXPECT_FALSE(GetBubble());
+}
+
+TEST_F(AutoclickTrayTest, CanSelectAutoclickTypeFromBubble) {
+  AccessibilityController* controller =
+      Shell::Get()->accessibility_controller();
+  // Set to a different event type than the first event in kTestCases.
+  controller->SetAutoclickEventType(mojom::AutoclickEventType::kRightClick);
+
+  const struct {
+    int view_id;
+    mojom::AutoclickEventType expected_event_type;
+  } kTestCases[] = {
+      {kLeftClickViewID, mojom::AutoclickEventType::kLeftClick},
+      {kRightClickViewID, mojom::AutoclickEventType::kRightClick},
+      {kDoubleClickViewID, mojom::AutoclickEventType::kDoubleClick},
+      {kDragAndDropViewID, mojom::AutoclickEventType::kDragAndDrop},
+      {kPauseViewID, mojom::AutoclickEventType::kNoAction},
+  };
+
+  for (const auto& test : kTestCases) {
+    // Open the menu.
+    GetTray()->PerformAction(CreateTapEvent());
+
+    // Find the autoclick action button for this test case.
+    AutoclickTrayActionView* view = GetTrayActionView(test.view_id);
+    ASSERT_TRUE(view) << "No view for id " << test.view_id;
+
+    // Tap the left-click action button.
+    ui::GestureEvent event = CreateTapEvent();
+    view->OnGestureEvent(&event);
+
+    // Bubble is hidden.
+    EXPECT_FALSE(GetBubble());
+
+    // Pref change happened.
+    EXPECT_EQ(test.expected_event_type, controller->GetAutoclickEventType());
+  }
+}
+
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index 482a33c..e25cb6d2f 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -7,6 +7,7 @@
 #include "ash/session/session_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
+#include "ash/system/accessibility/autoclick_tray.h"
 #include "ash/system/accessibility/dictation_button_tray.h"
 #include "ash/system/accessibility/select_to_speak_tray.h"
 #include "ash/system/flag_warning/flag_warning_tray.h"
@@ -19,6 +20,7 @@
 #include "ash/system/virtual_keyboard/virtual_keyboard_tray.h"
 #include "base/command_line.h"
 #include "base/i18n/time_formatting.h"
+#include "ui/accessibility/accessibility_switches.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/display/display.h"
 #include "ui/native_theme/native_theme_dark_aura.h"
@@ -62,6 +64,12 @@
   select_to_speak_tray_ = std::make_unique<SelectToSpeakTray>(shelf_);
   status_area_widget_delegate_->AddChildView(select_to_speak_tray_.get());
 
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableExperimentalAccessibilityAutoclick)) {
+    autoclick_tray_ = std::make_unique<AutoclickTray>(shelf_);
+    status_area_widget_delegate_->AddChildView(autoclick_tray_.get());
+  }
+
   dictation_button_tray_ = std::make_unique<DictationButtonTray>(shelf_);
   status_area_widget_delegate_->AddChildView(dictation_button_tray_.get());
 
@@ -83,6 +91,8 @@
   virtual_keyboard_tray_->Initialize();
   ime_menu_tray_->Initialize();
   select_to_speak_tray_->Initialize();
+  if (autoclick_tray_)
+    autoclick_tray_->Initialize();
   if (dictation_button_tray_)
     dictation_button_tray_->Initialize();
   overview_button_tray_->Initialize();
@@ -98,6 +108,7 @@
   unified_system_tray_.reset();
   ime_menu_tray_.reset();
   select_to_speak_tray_.reset();
+  autoclick_tray_.reset();
   dictation_button_tray_.reset();
   virtual_keyboard_tray_.reset();
   palette_tray_.reset();
diff --git a/ash/system/status_area_widget.h b/ash/system/status_area_widget.h
index 212c242..5cecd3e 100644
--- a/ash/system/status_area_widget.h
+++ b/ash/system/status_area_widget.h
@@ -17,6 +17,7 @@
 }
 
 namespace ash {
+class AutoclickTray;
 class FlagWarningTray;
 class ImeMenuTray;
 class LogoutButtonTray;
@@ -79,6 +80,7 @@
   SelectToSpeakTray* select_to_speak_tray() {
     return select_to_speak_tray_.get();
   }
+  AutoclickTray* autoclick_tray() { return autoclick_tray_.get(); }
 
   Shelf* shelf() { return shelf_; }
 
@@ -131,6 +133,7 @@
   std::unique_ptr<VirtualKeyboardTray> virtual_keyboard_tray_;
   std::unique_ptr<ImeMenuTray> ime_menu_tray_;
   std::unique_ptr<SelectToSpeakTray> select_to_speak_tray_;
+  std::unique_ptr<AutoclickTray> autoclick_tray_;
   std::unique_ptr<FlagWarningTray> flag_warning_tray_;
 
   LoginStatus login_status_ = LoginStatus::NOT_LOGGED_IN;
diff --git a/build/config/c++/c++.gni b/build/config/c++/c++.gni
index 1b94aaa8..ed179a2 100644
--- a/build/config/c++/c++.gni
+++ b/build/config/c++/c++.gni
@@ -11,7 +11,7 @@
   # Don't check in changes that set this to false for more platforms; doing so
   # is not supported.
   use_custom_libcxx =
-      is_msan || is_fuchsia ||
+      is_msan || is_fuchsia || is_android ||
       (is_linux &&
        (!is_chromeos || default_toolchain != "//build/toolchain/cros:target"))
 
diff --git a/build/config/mac/mac_sdk_overrides.gni b/build/config/mac/mac_sdk_overrides.gni
index b05bc6a..3632678 100644
--- a/build/config/mac/mac_sdk_overrides.gni
+++ b/build/config/mac/mac_sdk_overrides.gni
@@ -9,8 +9,14 @@
 declare_args() {
   # Minimum supported version of the Mac SDK.
   if (_sdk_min_from_env == "") {
-    mac_sdk_min = "10.13"
+    mac_sdk_min = "10.12"
   } else {
     mac_sdk_min = _sdk_min_from_env
   }
-}
\ No newline at end of file
+}
+
+# Always assert that mac_sdk_min is used on non-macOS platforms to prevent
+# unused args warnings.
+if (!is_mac) {
+  assert(mac_sdk_min == "10.12" || true)
+}
diff --git a/build/mac_toolchain.py b/build/mac_toolchain.py
index 9d393c67..9f9d274 100755
--- a/build/mac_toolchain.py
+++ b/build/mac_toolchain.py
@@ -25,12 +25,18 @@
 
 # This can be changed after running:
 #    mac_toolchain upload -xcode-path path/to/Xcode.app
-MAC_TOOLCHAIN_VERSION = '9E145'
+MAC_TOOLCHAIN_VERSION = '8E2002'
 
 # The toolchain will not be downloaded if the minimum OS version is not met.
-# 17 is the major version number for macOS 10.13.
-# 9E145 (Xcode 9.3) only runs on 10.13.2 and newer.
-MAC_MINIMUM_OS_VERSION = 17
+# 16 is the major version number for macOS 10.12.
+MAC_MINIMUM_OS_VERSION = 16
+
+# The toolchain will not be downloaded if the maximum OS version is exceeded.
+# 17 is the major version number for macOS 10.13. Xcode 8 does not run on macOS
+# 10.14.
+# TODO(https://crbug.com/780980): Once we build with 10.13 SDK, Xcode 9, we
+# should be able to remove this upper bound.
+MAC_MAXIMUM_OS_VERSION = 17
 
 MAC_TOOLCHAIN_INSTALLER = 'mac_toolchain'
 
@@ -48,7 +54,8 @@
 
 def PlatformMeetsHermeticXcodeRequirements():
   major_version = int(platform.release().split('.')[0])
-  return major_version >= MAC_MINIMUM_OS_VERSION
+  return (major_version >= MAC_MINIMUM_OS_VERSION and
+          major_version <= MAC_MAXIMUM_OS_VERSION)
 
 
 def _UseHermeticToolchain():
diff --git a/cc/animation/scroll_offset_animation_curve.cc b/cc/animation/scroll_offset_animation_curve.cc
index 39d75dc2..fe61dc63 100644
--- a/cc/animation/scroll_offset_animation_curve.cc
+++ b/cc/animation/scroll_offset_animation_curve.cc
@@ -18,6 +18,9 @@
 const double kConstantDuration = 9.0;
 const double kDurationDivisor = 60.0;
 
+// 3 seconds limit for long-distance programmatic scrolls
+const double kDeltaBasedMaxDuration = 180.0;
+
 const double kInverseDeltaRampStartPx = 120.0;
 const double kInverseDeltaRampEndPx = 480.0;
 const double kInverseDeltaMinDuration = 6.0;
@@ -83,7 +86,8 @@
       duration = kConstantDuration;
       break;
     case DurationBehavior::DELTA_BASED:
-      duration = std::sqrt(std::abs(MaximumDimension(delta)));
+      duration = std::min(double(std::sqrt(std::abs(MaximumDimension(delta)))),
+                          kDeltaBasedMaxDuration);
       break;
     case DurationBehavior::INVERSE_DELTA:
       duration = std::min(
diff --git a/cc/layers/painted_scrollbar_layer.cc b/cc/layers/painted_scrollbar_layer.cc
index 541c024e..214a33b 100644
--- a/cc/layers/painted_scrollbar_layer.cc
+++ b/cc/layers/painted_scrollbar_layer.cc
@@ -4,24 +4,12 @@
 
 #include "cc/layers/painted_scrollbar_layer.h"
 
-#include <algorithm>
-
 #include "base/auto_reset.h"
-#include "cc/base/math_util.h"
-#include "cc/input/main_thread_scrolling_reason.h"
 #include "cc/layers/painted_scrollbar_layer_impl.h"
-#include "cc/paint/paint_flags.h"
 #include "cc/paint/skia_paint_canvas.h"
-#include "cc/resources/ui_resource_bitmap.h"
 #include "cc/trees/draw_property_utils.h"
 #include "cc/trees/layer_tree_host.h"
-#include "cc/trees/layer_tree_impl.h"
-#include "skia/ext/platform_canvas.h"
 #include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkSize.h"
-#include "ui/gfx/geometry/size_conversions.h"
-#include "ui/gfx/skia_util.h"
 
 namespace {
 static constexpr int kMaxScrollbarDimension = 8192;
@@ -54,10 +42,6 @@
       is_overlay_(scrollbar_->IsOverlay()),
       has_thumb_(scrollbar_->HasThumb()),
       thumb_opacity_(scrollbar_->ThumbOpacity()) {
-  if (!scrollbar_->IsOverlay()) {
-    AddMainThreadScrollingReasons(
-        MainThreadScrollingReason::kScrollbarScrolling);
-  }
   SetIsScrollbar(true);
 }
 
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc
index 4425f722..c46e3ee 100644
--- a/cc/layers/scrollbar_layer_unittest.cc
+++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -252,49 +252,6 @@
   }
 }
 
-TEST_F(ScrollbarLayerTest, ShouldScrollNonOverlayOnMainThread) {
-  // Create and attach a non-overlay scrollbar.
-  std::unique_ptr<Scrollbar> scrollbar(new FakeScrollbar);
-  LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
-      layer_tree_host_.get(), std::move(scrollbar), false, false, 0, 0);
-  PaintedScrollbarLayerImpl* scrollbar_layer_impl =
-      static_cast<PaintedScrollbarLayerImpl*>(
-          layer_impl_tree_root->layer_tree_impl()->LayerById(
-              scrollbar_layer_id_));
-  ScrollTree& scroll_tree =
-      layer_impl_tree_root->layer_tree_impl()->property_trees()->scroll_tree;
-  ScrollNode* scroll_node =
-      scroll_tree.Node(scrollbar_layer_impl->scroll_tree_index());
-
-  // When the scrollbar is not an overlay scrollbar, the scroll should be
-  // responded to on the main thread as the compositor does not yet implement
-  // scrollbar scrolling.
-  InputHandler::ScrollStatus status = layer_tree_host_->host_impl()->TryScroll(
-      gfx::PointF(), InputHandler::TOUCHSCREEN, scroll_tree, scroll_node);
-  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
-  EXPECT_EQ(MainThreadScrollingReason::kScrollbarScrolling,
-            status.main_thread_scrolling_reasons);
-
-  // Create and attach an overlay scrollbar.
-  scrollbar.reset(new FakeScrollbar(false, false, true));
-
-  layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
-      layer_tree_host_.get(), std::move(scrollbar), false, false, 0, 0);
-  scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(
-      layer_impl_tree_root->layer_tree_impl()->LayerById(scrollbar_layer_id_));
-  scroll_tree =
-      layer_impl_tree_root->layer_tree_impl()->property_trees()->scroll_tree;
-  scroll_node = scroll_tree.Node(scrollbar_layer_impl->scroll_tree_index());
-
-  // The user shouldn't be able to drag an overlay scrollbar and the scroll
-  // may be handled in the compositor.
-  status = layer_tree_host_->host_impl()->TryScroll(
-      gfx::PointF(), InputHandler::TOUCHSCREEN, scroll_tree, scroll_node);
-  EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
-  EXPECT_EQ(MainThreadScrollingReason::kNotScrollable,
-            status.main_thread_scrolling_reasons);
-}
-
 class FakeNinePatchScrollbar : public FakeScrollbar {
  public:
   bool UsesNinePatchThumbResource() const override { return true; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java
index e0c7821..5e6a59c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java
@@ -34,17 +34,22 @@
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.library_loader.ProcessInitException;
 import org.chromium.chrome.browser.ChromeApplication;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.download.DownloadNotificationUmaHelper.UmaDownloadResumption;
 import org.chromium.chrome.browser.download.items.OfflineContentAggregatorNotificationBridgeUiFactory;
 import org.chromium.chrome.browser.init.BrowserParts;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.init.EmptyBrowserParts;
+import org.chromium.chrome.browser.init.ServiceManagerStartupUtils;
 import org.chromium.chrome.browser.util.IntentUtils;
 import org.chromium.components.offline_items_collection.ContentId;
 import org.chromium.components.offline_items_collection.LegacyHelpers;
 import org.chromium.components.offline_items_collection.PendingState;
 import org.chromium.content_public.browser.BrowserStartupController;
 
+import java.util.HashSet;
+import java.util.Set;
+
 /**
  * Class that spins up native when an interaction with a notification happens and passes the
  * relevant information on to native.
@@ -197,7 +202,10 @@
             @Override
             public boolean startServiceManagerOnly() {
                 if (!LegacyHelpers.isLegacyDownload(id)) return false;
-                return DownloadUtils.shouldStartServiceManagerOnly()
+                Set<String> features = new HashSet<String>();
+                features.add(ChromeFeatureList.SERVICE_MANAGER_FOR_DOWNLOAD);
+                features.add(ChromeFeatureList.NETWORK_SERVICE);
+                return ServiceManagerStartupUtils.canStartServiceManager(features)
                         && !ACTION_DOWNLOAD_OPEN.equals(intent.getAction());
             }
         };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
index e3e5d66..265beeb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
@@ -42,7 +42,6 @@
 import org.chromium.chrome.browser.download.ui.DownloadHistoryItemWrapper.OfflineItemWrapper;
 import org.chromium.chrome.browser.feature_engagement.ScreenshotTabObserver;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
-import org.chromium.chrome.browser.init.ServiceManagerStartupUtils;
 import org.chromium.chrome.browser.media.MediaViewerUtils;
 import org.chromium.chrome.browser.offlinepages.DownloadUiActionFlags;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
@@ -82,11 +81,9 @@
 import java.util.Calendar;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -1002,14 +999,6 @@
                 && entry.isAutoResumable;
     }
 
-    /** @return Whether we should start service manager only, based off the features enabled. */
-    public static boolean shouldStartServiceManagerOnly() {
-        Set<String> features = new HashSet<String>();
-        features.add(ChromeFeatureList.SERVICE_MANAGER_FOR_DOWNLOAD);
-        features.add(ChromeFeatureList.NETWORK_SERVICE);
-        return ServiceManagerStartupUtils.canStartServiceManager(features);
-    }
-
     /**
      * Format the number of bytes into KB, or MB, or GB and return the corresponding string
      * resource. Uses default download-related set of strings.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadBackgroundTask.java b/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadBackgroundTask.java
index a21f939..80a55b62e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadBackgroundTask.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadBackgroundTask.java
@@ -10,7 +10,6 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.chrome.browser.background_task_scheduler.NativeBackgroundTask;
 import org.chromium.chrome.browser.background_task_scheduler.NativeBackgroundTask.StartBeforeNativeResult;
-import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.background_task_scheduler.TaskParameters;
 import org.chromium.components.download.DownloadTaskType;
@@ -23,8 +22,6 @@
  * Entry point for the download service to perform desired action when the task is fired by the
  * scheduler. The scheduled task is executed for the regular profile and also for incognito profile
  * if an incognito profile exists.
- * TODO(shaktisahu): Since we probably don't need to run tasks for incognito profile, cleanup this
- * class and remove any reference to profiles.
  */
 @JNINamespace("download::android")
 public class DownloadBackgroundTask extends NativeBackgroundTask {
@@ -40,9 +37,6 @@
     // Keeps track of in progress tasks which haven't invoked their {@link TaskFinishedCallback}s.
     private Map<Integer, PendingTaskCounter> mPendingTaskCounters = new HashMap<>();
 
-    @DownloadTaskType
-    private int mCurrentTaskType;
-
     @Override
     protected @StartBeforeNativeResult int onStartTaskBeforeNativeLoaded(
             Context context, TaskParameters taskParameters, TaskFinishedCallback callback) {
@@ -65,42 +59,34 @@
         // In case of future upgrades, we would need to build an intent for the old version and
         // validate that this code still works. This would require decoupling this immediate class
         // from native as well.
-        mCurrentTaskType = taskParameters.getExtras().getInt(DownloadTaskScheduler.EXTRA_TASK_TYPE);
+        @DownloadTaskType
+        final int taskType =
+                taskParameters.getExtras().getInt(DownloadTaskScheduler.EXTRA_TASK_TYPE);
 
         Callback<Boolean> wrappedCallback = new Callback<Boolean>() {
             @Override
             public void onResult(Boolean needsReschedule) {
-                if (mPendingTaskCounters.get(mCurrentTaskType) == null) return;
+                if (mPendingTaskCounters.get(taskType) == null) return;
 
                 boolean noPendingCallbacks =
-                        decrementPendingCallbackCount(mCurrentTaskType, needsReschedule);
+                        decrementPendingCallbackCount(taskType, needsReschedule);
                 if (noPendingCallbacks) {
-                    callback.taskFinished(
-                            mPendingTaskCounters.get(mCurrentTaskType).needsReschedule);
-                    mPendingTaskCounters.remove(mCurrentTaskType);
+                    callback.taskFinished(mPendingTaskCounters.get(taskType).needsReschedule);
+                    mPendingTaskCounters.remove(taskType);
                 }
             }
         };
 
-        Profile profile = supportsServiceManagerOnly()
-                ? null
-                : Profile.getLastUsedProfile().getOriginalProfile();
-        incrementPendingCallbackCount(mCurrentTaskType);
-        nativeStartBackgroundTask(profile, mCurrentTaskType, wrappedCallback);
+        Profile profile = Profile.getLastUsedProfile().getOriginalProfile();
+        incrementPendingCallbackCount(taskType);
+        nativeStartBackgroundTask(profile, taskType, wrappedCallback);
 
-        if (profile != null && profile.hasOffTheRecordProfile()) {
-            incrementPendingCallbackCount(mCurrentTaskType);
-            nativeStartBackgroundTask(
-                    profile.getOffTheRecordProfile(), mCurrentTaskType, wrappedCallback);
+        if (profile.hasOffTheRecordProfile()) {
+            incrementPendingCallbackCount(taskType);
+            nativeStartBackgroundTask(profile.getOffTheRecordProfile(), taskType, wrappedCallback);
         }
     }
 
-    @Override
-    protected boolean supportsServiceManagerOnly() {
-        return mCurrentTaskType == DownloadTaskType.DOWNLOAD_AUTO_RESUMPTION_TASK
-                && DownloadUtils.shouldStartServiceManagerOnly();
-    }
-
     private void incrementPendingCallbackCount(@DownloadTaskType int taskType) {
         PendingTaskCounter taskCounter = mPendingTaskCounters.containsKey(taskType)
                 ? mPendingTaskCounters.get(taskType)
@@ -131,12 +117,10 @@
         int taskType = taskParameters.getExtras().getInt(DownloadTaskScheduler.EXTRA_TASK_TYPE);
         mPendingTaskCounters.remove(taskType);
 
-        Profile profile = supportsServiceManagerOnly()
-                ? null
-                : Profile.getLastUsedProfile().getOriginalProfile();
+        Profile profile = Profile.getLastUsedProfile().getOriginalProfile();
         boolean needsReschedule = nativeStopBackgroundTask(profile, taskType);
 
-        if (profile != null && profile.hasOffTheRecordProfile()) {
+        if (profile.hasOffTheRecordProfile()) {
             needsReschedule |= nativeStopBackgroundTask(profile.getOffTheRecordProfile(), taskType);
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadTaskScheduler.java b/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadTaskScheduler.java
index 3b71fa1f..e54d9e3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadTaskScheduler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadTaskScheduler.java
@@ -29,7 +29,6 @@
     public static final String EXTRA_TASK_TYPE = "extra_task_type";
     static final long TWELVE_HOURS_IN_SECONDS = TimeUnit.HOURS.toSeconds(12);
     static final long FIVE_MINUTES_IN_SECONDS = TimeUnit.MINUTES.toSeconds(5);
-    static final long ONE_DAY_IN_SECONDS = TimeUnit.DAYS.toSeconds(1);
 
     @CalledByNative
     private static void scheduleTask(@DownloadTaskType int taskType,
@@ -70,8 +69,6 @@
                 2 * FIVE_MINUTES_IN_SECONDS);
         scheduleTask(DownloadTaskType.CLEANUP_TASK, false, false, 0, TWELVE_HOURS_IN_SECONDS,
                 2 * TWELVE_HOURS_IN_SECONDS);
-        scheduleTask(DownloadTaskType.DOWNLOAD_AUTO_RESUMPTION_TASK, false, false, 0,
-                FIVE_MINUTES_IN_SECONDS, ONE_DAY_IN_SECONDS);
     }
 
     private static int getTaskId(@DownloadTaskType int taskType) {
@@ -80,8 +77,6 @@
                 return TaskIds.DOWNLOAD_SERVICE_JOB_ID;
             case DownloadTaskType.CLEANUP_TASK:
                 return TaskIds.DOWNLOAD_CLEANUP_JOB_ID;
-            case DownloadTaskType.DOWNLOAD_AUTO_RESUMPTION_TASK:
-                return TaskIds.DOWNLOAD_AUTO_RESUMPTION_JOB_ID;
             default:
                 assert false;
                 return -1;
@@ -90,16 +85,9 @@
 
     private static int getRequiredNetworkType(
             @DownloadTaskType int taskType, boolean requiresUnmeteredNetwork) {
-        switch (taskType) {
-            case DownloadTaskType.CLEANUP_TASK:
-                return TaskInfo.NETWORK_TYPE_NONE;
-            case DownloadTaskType.DOWNLOAD_TASK: // intentional fall-through
-            case DownloadTaskType.DOWNLOAD_AUTO_RESUMPTION_TASK:
-                return requiresUnmeteredNetwork ? TaskInfo.NETWORK_TYPE_UNMETERED
-                                                : TaskInfo.NETWORK_TYPE_ANY;
-            default:
-                assert false;
-                return TaskInfo.NETWORK_TYPE_ANY;
-        }
+        if (taskType != DownloadTaskType.DOWNLOAD_TASK) return TaskInfo.NETWORK_TYPE_NONE;
+
+        return requiresUnmeteredNetwork ? TaskInfo.NETWORK_TYPE_UNMETERED
+                                        : TaskInfo.NETWORK_TYPE_ANY;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/BaseSessionController.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/BaseSessionController.java
index 19785f76..74c122f2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/BaseSessionController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/BaseSessionController.java
@@ -9,6 +9,12 @@
 import com.google.android.gms.cast.CastDevice;
 import com.google.android.gms.cast.framework.CastSession;
 import com.google.android.gms.cast.framework.media.RemoteMediaClient;
+import com.google.android.gms.common.api.PendingResult;
+import com.google.android.gms.common.api.Status;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
 
 import org.chromium.base.Log;
 import org.chromium.chrome.browser.media.router.CastSessionUtil;
@@ -20,6 +26,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Random;
 
 /**
  * A base wrapper for {@link CastSession}, extending its functionality for Chrome MediaRouter.
@@ -44,7 +51,9 @@
         void onMetadataUpdated();
     }
 
+    private final Random mRequestIdGenerator = new Random();
     private CastSession mCastSession;
+    private int mLatestMediaSessionId;
     private final CafBaseMediaRouteProvider mProvider;
     private CreateRouteRequestInfo mRouteCreationInfo;
     private final RemoteMediaClient.Callback mRemoteMediaClientCallback;
@@ -130,6 +139,44 @@
         return mCastSession != null && mCastSession.isConnected();
     }
 
+    /**
+     * Safely seek to a position. This is an workaround for an IllegalStateException in
+     * RemoteMediaClient when a seek command times out. The code should be replaced by a normal
+     * seek() call when the Google Play services SDK gets updated.
+     */
+    public PendingResult<Status> safelySeek(long position) {
+        JSONObject json = new JSONObject();
+        try {
+            json.put("requestId", mRequestIdGenerator.nextInt(10000));
+            json.put("mediaSessionId", mLatestMediaSessionId);
+            json.put("type", "SEEK");
+            json.put("currentTime", position / 1000.0);
+        } catch (JSONException e) {
+            // Ignore.
+        }
+        return getSession().sendMessage(CastSessionUtil.MEDIA_NAMESPACE, json.toString());
+    }
+
+    private void updateMediaSessionId(String message) {
+        try {
+            JSONObject json = new JSONObject(message);
+            JSONArray statusArray = json.optJSONArray("status");
+
+            if (statusArray == null || statusArray.length() == 0) {
+                return;
+            }
+
+            JSONObject status = statusArray.optJSONObject(0);
+            if (status == null) {
+                return;
+            }
+
+            mLatestMediaSessionId = status.optInt("mediaSessionId", mLatestMediaSessionId);
+        } catch (JSONException e) {
+            // Ignore.
+        }
+    }
+
     private void updateRemoteMediaClient(String message) {
         if (!isConnected()) return;
 
@@ -180,6 +227,7 @@
                 "Received message from Cast device: namespace=\"" + namespace + "\" message=\""
                         + message + "\"");
         if (CastSessionUtil.MEDIA_NAMESPACE.equals(namespace)) {
+            updateMediaSessionId(message);
             updateRemoteMediaClient(message);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/CafExpandedControllerActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/CafExpandedControllerActivity.java
index 516f8cf..f45de12a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/CafExpandedControllerActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/CafExpandedControllerActivity.java
@@ -71,7 +71,7 @@
         public void seekTo(long pos) {
             if (!mSessionController.isConnected()) return;
 
-            mSessionController.getSession().getRemoteMediaClient().seek(pos);
+            mSessionController.safelySeek(pos);
         }
 
         @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/FlingingControllerAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/FlingingControllerAdapter.java
index a016cab..b568678d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/FlingingControllerAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/FlingingControllerAdapter.java
@@ -7,6 +7,7 @@
 import com.google.android.gms.cast.MediaInfo;
 import com.google.android.gms.cast.MediaStatus;
 import com.google.android.gms.cast.framework.media.RemoteMediaClient;
+import com.google.android.gms.common.api.Result;
 
 import org.chromium.base.Log;
 import org.chromium.chrome.browser.media.router.FlingingController;
@@ -124,8 +125,7 @@
             return;
         }
 
-        mSessionController.getRemoteMediaClient().seek(position).setResultCallback(
-                this ::onMediaCommandResult);
+        mSessionController.safelySeek(position).setResultCallback(this::onMediaCommandResult);
         mStreamPositionExtrapolator.onSeek(position);
     }
 
@@ -158,7 +158,7 @@
         }
     }
 
-    private void onMediaCommandResult(RemoteMediaClient.MediaChannelResult result) {
+    private void onMediaCommandResult(Result result) {
         // When multiple API calls are made in quick succession, "Results have already been set"
         // IllegalStateExceptions might be thrown from GMS code. We prefer to catch the exception
         // and noop it, than to crash. This might lead to some API calls never getting their
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
index d866faba..af733a4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
@@ -7,8 +7,6 @@
 import static org.chromium.chrome.browser.toolbar.top.ToolbarPhone.URL_FOCUS_CHANGE_ANIMATION_DURATION_MS;
 
 import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.TransitionDrawable;
 import android.support.annotation.ColorRes;
@@ -36,6 +34,8 @@
     private View mLocationBarButtonContainer;
 
     private boolean mAnimationsEnabled;
+    private boolean mAnimatingStatusIconShow;
+    private boolean mAnimatingStatusIconHide;
 
     private @DrawableRes int mIconRes;
     private @ColorRes int mIconTintRes;
@@ -62,38 +62,86 @@
      */
     private void animateStatusIcon() {
         Drawable targetIcon = null;
-
-        // Ensure no animations are pending.
-        mIconView.animate().cancel();
+        boolean wantIconHidden = false;
 
         if (mIconRes != 0 && mIconTintRes != 0) {
             targetIcon =
                     TintedDrawable.constructTintedDrawable(getContext(), mIconRes, mIconTintRes);
-            mIconView.setVisibility(View.VISIBLE);
         } else if (mIconRes != 0) {
             targetIcon = ApiCompatibilityUtils.getDrawable(getContext().getResources(), mIconRes);
-            mIconView.setVisibility(View.VISIBLE);
         } else {
-            targetIcon = new ColorDrawable(Color.TRANSPARENT);
-            mIconView.animate()
-                    .setStartDelay(URL_FOCUS_CHANGE_ANIMATION_DURATION_MS)
-                    .withEndAction(new Runnable() {
-                        @Override
-                        public void run() {
-                            mIconView.setVisibility(View.GONE);
-                        }
-                    }).start();
+            // Do not specify any icon here and do not replace existing icon, either.
+            // TransitionDrawable uses different timing mechanism than Animations, and that may,
+            // depending on animation scale factor, produce a visible glitch.
+            targetIcon = null;
+            wantIconHidden = true;
         }
 
-        TransitionDrawable newImage =
-                new TransitionDrawable(new Drawable[] {mIconView.getDrawable(), targetIcon});
+        // Ensure proper handling of animations.
+        // Possible variants:
+        // 1. Is: shown,           want: hidden  => animate hiding,
+        // 2. Is: shown,           want: shown   => crossfade w/TransitionDrawable,
+        // 3. Is: animating(show), want: hidden  => cancel animation; animate hiding,
+        // 4. Is: animating(show), want: shown   => crossfade (carry on showing),
+        // 5. Is: animating(hide), want: hidden  => no op,
+        // 6. Is: animating(hide), want: shown   => cancel animation; animate showing; crossfade,
+        // 7. Is: hidden,          want: hidden  => no op,
+        // 8. Is: hidden,          want: shown   => animate showing.
+        //
+        // This gives 3 actions:
+        // - Animate showing, if hidden or previously hiding (6 + 8); cancel previous animation (6)
+        // - Animate hiding, if shown or previously showing (1 + 3); cancel previous animation (3)
+        // - crossfade w/TransitionDrawable, if visible (2, 4, 6), otherwise use image directly.
+        // All other options (5, 7) are no-op.
+        //
+        // Note: this will be compacted once we start using LayoutTransition with StatusView.
 
-        mIconView.setImageDrawable(newImage);
+        boolean isIconHidden = mIconView.getVisibility() == View.GONE;
 
-        // Note: crossfade controls blending, not animation.
-        newImage.setCrossFadeEnabled(true);
-        // TODO(ender): Consider simply leaving animations on. It looks so nice!
-        newImage.startTransition(mAnimationsEnabled ? URL_FOCUS_CHANGE_ANIMATION_DURATION_MS : 0);
+        if (!wantIconHidden && (isIconHidden || mAnimatingStatusIconHide)) {
+            // Action 1: animate showing, if icon was either hidden or hiding.
+            if (mAnimatingStatusIconHide) mIconView.animate().cancel();
+            mAnimatingStatusIconHide = false;
+
+            mAnimatingStatusIconShow = true;
+            mIconView.setVisibility(View.VISIBLE);
+            mIconView.animate()
+                    .alpha(1.0f)
+                    .setDuration(URL_FOCUS_CHANGE_ANIMATION_DURATION_MS)
+                    .withEndAction(() -> { mAnimatingStatusIconShow = false; })
+                    .start();
+        } else if (wantIconHidden && (!isIconHidden || mAnimatingStatusIconShow)) {
+            // Action 2: animate hiding, if icon was either shown or showing.
+            if (mAnimatingStatusIconShow) mIconView.animate().cancel();
+            mAnimatingStatusIconShow = false;
+
+            mAnimatingStatusIconHide = true;
+            mIconView.animate()
+                    .setDuration(URL_FOCUS_CHANGE_ANIMATION_DURATION_MS)
+                    .alpha(0.0f)
+                    .withEndAction(() -> {
+                        mIconView.setVisibility(View.GONE);
+                        mAnimatingStatusIconHide = false;
+                    })
+                    .start();
+        }
+
+        // Action 3: Specify icon content. Use TransitionDrawable whenever object is visible.
+        if (targetIcon != null) {
+            if (!isIconHidden) {
+                TransitionDrawable newImage = new TransitionDrawable(
+                        new Drawable[] {mIconView.getDrawable(), targetIcon});
+
+                mIconView.setImageDrawable(newImage);
+
+                // Note: crossfade controls blending, not animation.
+                newImage.setCrossFadeEnabled(true);
+                newImage.startTransition(
+                        mAnimationsEnabled ? URL_FOCUS_CHANGE_ANIMATION_DURATION_MS : 0);
+            } else {
+                mIconView.setImageDrawable(targetIcon);
+            }
+        }
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index 0ba9b488..f2fb0d5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -639,7 +639,8 @@
             // We load the URL from the tab rather than directly from the ContentView so the tab has
             // a chance of using a prerenderer page is any.
             int loadType = nativeLoadUrl(mNativeTabAndroid, params.getUrl(),
-                    params.getVerbatimHeaders(), params.getPostData(), params.getTransitionType(),
+                    params.getInitiatorOrigin(), params.getVerbatimHeaders(), params.getPostData(),
+                    params.getTransitionType(),
                     params.getReferrer() != null ? params.getReferrer().getUrl() : null,
                     // Policy will be ignored for null referrer url, 0 is just a placeholder.
                     // TODO(ppi): Should we pass Referrer jobject and add JNI methods to read it
@@ -2580,8 +2581,9 @@
     }
 
     @CalledByNative
-    protected void openNewTab(String url, String extraHeaders, ResourceRequestBody postData,
-            int disposition, boolean hasParent, boolean isRendererInitiated) {
+    protected void openNewTab(String url, String initiatorOrigin, String extraHeaders,
+            ResourceRequestBody postData, int disposition, boolean hasParent,
+            boolean isRendererInitiated) {
         if (isClosing()) return;
 
         boolean incognito = isIncognito();
@@ -2611,6 +2613,7 @@
         if (shouldIgnoreNewTab(url, incognito) || getTabModelSelector() == null) return;
 
         LoadUrlParams loadUrlParams = new LoadUrlParams(url);
+        loadUrlParams.setInitiatorOrigin(initiatorOrigin);
         loadUrlParams.setVerbatimHeaders(extraHeaders);
         loadUrlParams.setPostData(postData);
         loadUrlParams.setIsRendererInitiated(isRendererInitiated);
@@ -2990,10 +2993,10 @@
     private native void nativeOnPhysicalBackingSizeChanged(
             long nativeTabAndroid, WebContents webContents, int width, int height);
     private native Profile nativeGetProfileAndroid(long nativeTabAndroid);
-    private native int nativeLoadUrl(long nativeTabAndroid, String url, String extraHeaders,
-            ResourceRequestBody postData, int transition, String referrerUrl, int referrerPolicy,
-            boolean isRendererInitiated, boolean shoulReplaceCurrentEntry, boolean hasUserGesture,
-            boolean shouldClearHistoryList, long inputStartTimestamp);
+    private native int nativeLoadUrl(long nativeTabAndroid, String url, String initiatorOrigin,
+            String extraHeaders, ResourceRequestBody postData, int transition, String referrerUrl,
+            int referrerPolicy, boolean isRendererInitiated, boolean shoulReplaceCurrentEntry,
+            boolean hasUserGesture, boolean shouldClearHistoryList, long inputStartTimestamp);
     private native void nativeSetActiveNavigationEntryTitleForUrl(long nativeTabAndroid, String url,
             String title);
     private native boolean nativePrint(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java
index 739bbb6c..12402c2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java
@@ -307,7 +307,7 @@
     @Override
     public void openNewTab(String url, String extraHeaders, ResourceRequestBody postData,
             int disposition, boolean isRendererInitiated) {
-        mTab.openNewTab(url, extraHeaders, postData, disposition, true, isRendererInitiated);
+        mTab.openNewTab(url, null, extraHeaders, postData, disposition, true, isRendererInitiated);
     }
 
     private Pair<WebContents, String> mWebContentsUrlMapping;
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 374baf3c..b1d0bed2 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-73.0.3660.0_rc-r1.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-73.0.3661.0_rc-r1.afdo.bz2
\ No newline at end of file
diff --git a/chrome/app/md_extensions_strings.grdp b/chrome/app/md_extensions_strings.grdp
index beb02430..7f83f24a 100644
--- a/chrome/app/md_extensions_strings.grdp
+++ b/chrome/app/md_extensions_strings.grdp
@@ -79,6 +79,9 @@
   <message name="IDS_MD_EXTENSIONS_ERROR_CONTEXT_UNKNOWN" desc="The text displayed to the user if an error's context url is unknown.">
     Unknown
   </message>
+  <message name="IDS_MD_EXTENSIONS_CLEAR_ACTIVITIES" desc="The label for the button that clears the activity log for the current extension.">
+    Clear activities
+  </message>
   <message name="IDS_MD_EXTENSIONS_ERROR_CLEAR_ALL" desc="The label for the button that clears all the errors.">
     Clear all
   </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 53011cca..6b1f1ab 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -25,7 +25,6 @@
 import("//rlz/buildflags/buildflags.gni")
 import("//sandbox/features.gni")
 import("//third_party/protobuf/proto_library.gni")
-import("//third_party/webrtc/webrtc.gni")
 import("//third_party/widevine/cdm/widevine.gni")
 import("//ui/base/ui_features.gni")
 
@@ -3465,11 +3464,6 @@
         "media/webrtc/window_icon_util_ozone.cc",
       ]
     }
-
-    if (rtc_use_pipewire) {
-      configs +=
-          [ "//third_party/webrtc/modules/desktop_capture:pipewire_config" ]
-    }
   }
 
   if (is_posix || is_fuchsia) {
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 7d5a4f8..60fb452 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1261,12 +1261,6 @@
      flag_descriptions::kWebrtcHwVP8EncodingName,
      flag_descriptions::kWebrtcHwVP8EncodingDescription, kOsAndroid | kOsCrOS,
      FEATURE_VALUE_TYPE(features::kWebRtcHWVP8Encoding)},
-#if defined(WEBRTC_USE_PIPEWIRE)
-    {"enable-webrtc-pipewire-capturer",
-     flag_descriptions::kWebrtcPipeWireCapturerName,
-     flag_descriptions::kWebrtcPipeWireCapturerDescription, kOsLinux,
-     FEATURE_VALUE_TYPE(features::kWebRtcPipeWireCapturer)},
-#endif  // defined(WEBRTC_USE_PIPEWIRE)
 #if !defined(OS_ANDROID)
     {"enable-webrtc-remote-event-log",
      flag_descriptions::kWebRtcRemoteEventLogName,
diff --git a/chrome/browser/android/download/download_controller.cc b/chrome/browser/android/download/download_controller.cc
index f569dcd..166981e1 100644
--- a/chrome/browser/android/download/download_controller.cc
+++ b/chrome/browser/android/download/download_controller.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/android/chrome_feature_list.h"
 #include "chrome/browser/android/download/dangerous_download_infobar_delegate.h"
 #include "chrome/browser/android/download/download_manager_service.h"
-#include "chrome/browser/android/download/download_utils.h"
 #include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/download/download_stats.h"
 #include "chrome/browser/infobars/infobar_service.h"
@@ -29,7 +28,6 @@
 #include "chrome/browser/ui/android/view_android_helper.h"
 #include "chrome/browser/vr/vr_tab_helper.h"
 #include "chrome/grit/chromium_strings.h"
-#include "components/download/public/common/auto_resumption_handler.h"
 #include "components/download/public/common/download_url_parameters.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -61,6 +59,11 @@
 // Guards download_controller_
 base::LazyInstance<base::Lock>::DestructorAtExit g_download_controller_lock_;
 
+// If received bytes is more than the size limit and resumption will restart
+// from the beginning, throttle it.
+int kDefaultAutoResumptionSizeLimit = 10 * 1024 * 1024;  // 10 MB
+const char kAutoResumptionSizeLimitParamName[] = "AutoResumptionSizeLimit";
+
 void CreateContextMenuDownload(
     const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
     const content::ContextMenuParams& params,
@@ -113,6 +116,16 @@
   dlm->DownloadUrl(std::move(dl_params));
 }
 
+int GetAutoResumptionSizeLimit() {
+  std::string value = base::GetFieldTrialParamValueByFeature(
+      chrome::android::kDownloadAutoResumptionThrottling,
+      kAutoResumptionSizeLimitParamName);
+  int size_limit;
+  return base::StringToInt(value, &size_limit)
+             ? size_limit
+             : kDefaultAutoResumptionSizeLimit;
+}
+
 // Helper class for retrieving a DownloadManager.
 class DownloadManagerGetter : public DownloadManager::Observer {
  public:
@@ -382,8 +395,6 @@
   download_item->RemoveObserver(this);
   download_item->AddObserver(this);
 
-  download::AutoResumptionHandler::Get()->OnDownloadStarted(download_item);
-
   OnDownloadUpdated(download_item);
 }
 
@@ -469,7 +480,7 @@
   if (!download_item->GetURL().SchemeIsHTTPOrHTTPS())
     return false;
 
-  static int size_limit = DownloadUtils::GetAutoResumptionSizeLimit();
+  static int size_limit = GetAutoResumptionSizeLimit();
   bool exceeds_size_limit = download_item->GetReceivedBytes() > size_limit;
   std::string etag = download_item->GetETag();
   std::string last_modified = download_item->GetLastModifiedTime();
diff --git a/chrome/browser/android/download/download_manager_service.cc b/chrome/browser/android/download/download_manager_service.cc
index 1ff1531..aadb99c 100644
--- a/chrome/browser/android/download/download_manager_service.cc
+++ b/chrome/browser/android/download/download_manager_service.cc
@@ -16,8 +16,6 @@
 #include "base/time/time.h"
 #include "chrome/browser/android/chrome_feature_list.h"
 #include "chrome/browser/android/download/download_controller.h"
-#include "chrome/browser/android/download/download_utils.h"
-#include "chrome/browser/android/download/service/download_task_scheduler.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/download/download_core_service.h"
 #include "chrome/browser/download/download_core_service_factory.h"
@@ -25,8 +23,6 @@
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/chrome_constants.h"
-#include "components/download/network/android/network_status_listener_android.h"
-#include "components/download/public/common/auto_resumption_handler.h"
 #include "components/download/public/common/download_item.h"
 #include "components/download/public/common/download_item_impl.h"
 #include "components/download/public/common/download_url_loader_factory_getter_impl.h"
@@ -55,20 +51,6 @@
 int kDefaultAutoResumptionLimit = 5;
 const char kAutoResumptionLimitParamName[] = "AutoResumptionLimit";
 
-void CreateAutoResumptionHandler() {
-  auto network_listener =
-      std::make_unique<download::NetworkStatusListenerAndroid>();
-  auto task_scheduler =
-      std::make_unique<download::android::DownloadTaskScheduler>();
-  auto task_manager =
-      std::make_unique<download::TaskManager>(std::move(task_scheduler));
-  auto config = std::make_unique<download::AutoResumptionHandler::Config>();
-  config->auto_resumption_size_limit =
-      DownloadUtils::GetAutoResumptionSizeLimit();
-  download::AutoResumptionHandler::Create(
-      std::move(network_listener), std::move(task_manager), std::move(config));
-}
-
 bool ShouldShowDownloadItem(download::DownloadItem* item) {
   return !item->IsTemporary() && !item->IsTransient();
 }
@@ -667,22 +649,6 @@
   if (!in_progress_manager_ && !is_history_query_complete_)
     return;
   is_pending_downloads_loaded_ = true;
-
-  // Kick-off the auto-resumption handler.
-  content::DownloadManager::DownloadVector all_items;
-  if (in_progress_manager_) {
-    in_progress_manager_->GetAllDownloads(&all_items);
-  } else {
-    content::DownloadManager* manager = GetDownloadManager(false);
-    if (manager)
-      manager->GetAllDownloads(&all_items);
-  }
-
-  if (!download::AutoResumptionHandler::Get())
-    CreateAutoResumptionHandler();
-
-  download::AutoResumptionHandler::Get()->SetResumableDownloads(all_items);
-
   for (auto iter = pending_actions_.begin(); iter != pending_actions_.end();
        ++iter) {
     DownloadActionParams params = iter->second;
diff --git a/chrome/browser/android/download/download_utils.cc b/chrome/browser/android/download/download_utils.cc
index 27670e0..c413f75 100644
--- a/chrome/browser/android/download/download_utils.cc
+++ b/chrome/browser/android/download/download_utils.cc
@@ -5,9 +5,6 @@
 #include "chrome/browser/android/download/download_utils.h"
 
 #include "base/android/jni_string.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/strings/string_number_conversions.h"
-#include "chrome/browser/android/chrome_feature_list.h"
 #include "chrome/browser/download/offline_item_utils.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/download/public/common/download_utils.h"
@@ -18,14 +15,6 @@
 using base::android::JavaParamRef;
 using base::android::ScopedJavaLocalRef;
 
-namespace {
-// If received bytes is more than the size limit and resumption will restart
-// from the beginning, throttle it.
-int kDefaultAutoResumptionSizeLimit = 10 * 1024 * 1024;  // 10 MB
-const char kAutoResumptionSizeLimitParamName[] = "AutoResumptionSizeLimit";
-
-}  // namespace
-
 static ScopedJavaLocalRef<jstring> JNI_DownloadUtils_GetFailStateMessage(
     JNIEnv* env,
     jint fail_state) {
@@ -57,14 +46,3 @@
   return base::FilePath(
       base::android::ConvertJavaStringToUTF8(env, uri_jstring));
 }
-
-// static
-int DownloadUtils::GetAutoResumptionSizeLimit() {
-  std::string value = base::GetFieldTrialParamValueByFeature(
-      chrome::android::kDownloadAutoResumptionThrottling,
-      kAutoResumptionSizeLimitParamName);
-  int size_limit;
-  return base::StringToInt(value, &size_limit)
-             ? size_limit
-             : kDefaultAutoResumptionSizeLimit;
-}
diff --git a/chrome/browser/android/download/download_utils.h b/chrome/browser/android/download/download_utils.h
index 73c0b27a..b507ffa 100644
--- a/chrome/browser/android/download/download_utils.h
+++ b/chrome/browser/android/download/download_utils.h
@@ -11,7 +11,6 @@
 class DownloadUtils {
  public:
   static base::FilePath GetUriStringForPath(const base::FilePath& file_path);
-  static int GetAutoResumptionSizeLimit();
 };
 
 #endif  // CHROME_BROWSER_ANDROID_DOWNLOAD_DOWNLOAD_UTILS_H_
diff --git a/chrome/browser/android/download/service/download_background_task.cc b/chrome/browser/android/download/service/download_background_task.cc
index b7cc72db..86c394a 100644
--- a/chrome/browser/android/download/service/download_background_task.cc
+++ b/chrome/browser/android/download/service/download_background_task.cc
@@ -8,7 +8,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
 #include "components/download/public/background_service/download_service.h"
-#include "components/download/public/common/auto_resumption_handler.h"
 #include "content/public/browser/browser_context.h"
 #include "jni/DownloadBackgroundTask_jni.h"
 
@@ -17,13 +16,6 @@
 namespace download {
 namespace android {
 
-DownloadService* GetDownloadService(
-    const base::android::JavaParamRef<jobject>& jprofile) {
-  Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
-  DCHECK(profile);
-  return DownloadServiceFactory::GetForBrowserContext(profile);
-}
-
 // static
 void JNI_DownloadBackgroundTask_StartBackgroundTask(
     JNIEnv* env,
@@ -31,23 +23,17 @@
     const base::android::JavaParamRef<jobject>& jprofile,
     jint task_type,
     const base::android::JavaParamRef<jobject>& jcallback) {
+  Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
+  DCHECK(profile);
+
   TaskFinishedCallback finish_callback =
       base::BindOnce(&base::android::RunBooleanCallbackAndroid,
                      base::android::ScopedJavaGlobalRef<jobject>(jcallback));
 
-  switch (static_cast<DownloadTaskType>(task_type)) {
-    case download::DownloadTaskType::DOWNLOAD_AUTO_RESUMPTION_TASK: {
-      download::AutoResumptionHandler::Get()->OnStartScheduledTask(
-          std::move(finish_callback));
-      break;
-    }
-    case download::DownloadTaskType::DOWNLOAD_TASK:
-      FALLTHROUGH;
-    case download::DownloadTaskType::CLEANUP_TASK:
-      GetDownloadService(jprofile)->OnStartScheduledTask(
-          static_cast<DownloadTaskType>(task_type), std::move(finish_callback));
-      break;
-  }
+  DownloadService* download_service =
+      DownloadServiceFactory::GetForBrowserContext(profile);
+  download_service->OnStartScheduledTask(
+      static_cast<DownloadTaskType>(task_type), std::move(finish_callback));
 }
 
 // static
@@ -56,18 +42,13 @@
     const base::android::JavaParamRef<jobject>& jcaller,
     const base::android::JavaParamRef<jobject>& jprofile,
     jint task_type) {
-  switch (static_cast<DownloadTaskType>(task_type)) {
-    case download::DownloadTaskType::DOWNLOAD_AUTO_RESUMPTION_TASK: {
-      download::AutoResumptionHandler::Get()->OnStopScheduledTask();
-      break;
-    }
-    case download::DownloadTaskType::DOWNLOAD_TASK:
-      FALLTHROUGH;
-    case download::DownloadTaskType::CLEANUP_TASK:
-      return GetDownloadService(jprofile)->OnStopScheduledTask(
-          static_cast<DownloadTaskType>(task_type));
-  }
-  return false;
+  Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
+  DCHECK(profile);
+
+  DownloadService* download_service =
+      DownloadServiceFactory::GetForBrowserContext(profile);
+  return download_service->OnStopScheduledTask(
+      static_cast<DownloadTaskType>(task_type));
 }
 
 }  // namespace android
diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc
index 5e88e20e5..95f22a2 100644
--- a/chrome/browser/android/tab_android.cc
+++ b/chrome/browser/android/tab_android.cc
@@ -316,14 +316,20 @@
     ScopedJavaLocalRef<jstring> jurl(ConvertUTF8ToJavaString(env, url.spec()));
     ScopedJavaLocalRef<jstring> jheaders(
         ConvertUTF8ToJavaString(env, params->extra_headers));
+    ScopedJavaLocalRef<jstring> jinitiator_origin;
+    if (params->initiator_origin) {
+      jinitiator_origin =
+          ConvertUTF8ToJavaString(env, params->initiator_origin->Serialize());
+    }
     ScopedJavaLocalRef<jobject> jpost_data;
     if (params->uses_post && params->post_data) {
       jpost_data = content::ConvertResourceRequestBodyToJavaObject(
           env, params->post_data);
     }
-    Java_Tab_openNewTab(
-        env, jobj, jurl, jheaders, jpost_data, static_cast<int>(disposition),
-        params->created_with_opener, params->is_renderer_initiated);
+    Java_Tab_openNewTab(env, jobj, jurl, jinitiator_origin, jheaders,
+                        jpost_data, static_cast<int>(disposition),
+                        params->created_with_opener,
+                        params->is_renderer_initiated);
   } else {
     NOTIMPLEMENTED();
   }
@@ -510,6 +516,7 @@
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
     const JavaParamRef<jstring>& url,
+    const JavaParamRef<jstring>& j_initiator_origin,
     const JavaParamRef<jstring>& j_extra_headers,
     const JavaParamRef<jobject>& j_post_data,
     jint page_transition,
@@ -578,6 +585,10 @@
           GURL(base::android::ConvertJavaStringToUTF8(env, j_referrer_url)),
           static_cast<network::mojom::ReferrerPolicy>(referrer_policy));
     }
+    if (j_initiator_origin) {
+      load_params.initiator_origin = url::Origin::Create(GURL(
+          base::android::ConvertJavaStringToUTF8(env, j_initiator_origin)));
+    }
     load_params.is_renderer_initiated = is_renderer_initiated;
     load_params.should_replace_current_entry = should_replace_current_entry;
     load_params.has_user_gesture = has_user_gesture;
diff --git a/chrome/browser/android/tab_android.h b/chrome/browser/android/tab_android.h
index 320608e..df27cfa 100644
--- a/chrome/browser/android/tab_android.h
+++ b/chrome/browser/android/tab_android.h
@@ -163,6 +163,7 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
       const base::android::JavaParamRef<jstring>& url,
+      const base::android::JavaParamRef<jstring>& j_initiator_origin,
       const base::android::JavaParamRef<jstring>& j_extra_headers,
       const base::android::JavaParamRef<jobject>& j_post_data,
       jint page_transition,
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 1fe7877..49fb338 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -218,6 +218,7 @@
 #include "components/previews/content/previews_user_data.h"
 #include "components/previews/core/previews_decider.h"
 #include "components/previews/core/previews_experiments.h"
+#include "components/previews/core/previews_lite_page_redirect.h"
 #include "components/rappor/public/rappor_utils.h"
 #include "components/rappor/rappor_recorder_impl.h"
 #include "components/rappor/rappor_service_impl.h"
@@ -1647,6 +1648,10 @@
   ChromeContentBrowserClientExtensionsPart::OverrideNavigationParams(
       site_instance, transition, is_renderer_initiated, referrer);
 #endif
+
+  // Clear the referrer if it is for the internal lite page preview domain.
+  if (previews::IsLitePageRedirectPreviewDomain(referrer->url))
+    *referrer = content::Referrer();
 }
 
 bool ChromeContentBrowserClient::ShouldStayInParentProcessForNTP(
diff --git a/chrome/browser/conflicts/module_blacklist_cache_util_win.cc b/chrome/browser/conflicts/module_blacklist_cache_util_win.cc
index 7b3d908..f0a0dc3 100644
--- a/chrome/browser/conflicts/module_blacklist_cache_util_win.cc
+++ b/chrome/browser/conflicts/module_blacklist_cache_util_win.cc
@@ -9,6 +9,7 @@
 #include <iterator>
 #include <string>
 #include <tuple>
+#include <type_traits>
 #include <utility>
 
 #include "base/files/file.h"
@@ -232,7 +233,7 @@
   return static_cast<int64_t>(sizeof(third_party_dlls::PackedListMetadata) +
                               packed_list_metadata.module_count *
                                   sizeof(third_party_dlls::PackedListModule) +
-                              arraysize(base::MD5Digest::a));
+                              std::extent<decltype(base::MD5Digest::a)>());
 }
 
 bool ModuleLess::operator()(
diff --git a/chrome/browser/conflicts/module_blacklist_cache_util_win_unittest.cc b/chrome/browser/conflicts/module_blacklist_cache_util_win_unittest.cc
index dd53d2e..813f7a5 100644
--- a/chrome/browser/conflicts/module_blacklist_cache_util_win_unittest.cc
+++ b/chrome/browser/conflicts/module_blacklist_cache_util_win_unittest.cc
@@ -8,6 +8,7 @@
 #include <memory>
 #include <random>
 #include <set>
+#include <type_traits>
 #include <utility>
 
 #include "base/files/file_util.h"
@@ -130,7 +131,7 @@
       0x9F, 0x1F, 0xDB, 0xEE, 0x7F, 0x58, 0x74, 0xCB,
   };
 
-  for (size_t i = 0; i < arraysize(base::MD5Digest::a); ++i)
+  for (size_t i = 0; i < std::extent<decltype(base::MD5Digest::a)>(); ++i)
     EXPECT_EQ(expected.a[i], md5_digest.a[i]);
 }
 
@@ -175,7 +176,7 @@
                       read_blacklisted_modules.size() *
                           sizeof(third_party_dlls::PackedListModule)));
 
-  for (size_t i = 0; i < arraysize(base::MD5Digest::a); ++i)
+  for (size_t i = 0; i < std::extent<decltype(base::MD5Digest::a)>(); ++i)
     EXPECT_EQ(md5_digest.a[i], read_md5_digest.a[i]);
 }
 
diff --git a/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.cc b/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.cc
index fe51f11..07c1e6e 100644
--- a/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.cc
+++ b/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.cc
@@ -297,7 +297,8 @@
     return data;
 
   if (handle->WasResponseCached() &&
-      headers->HasHeader(data_reduction_proxy::chrome_proxy_header())) {
+      headers->HasHeader(data_reduction_proxy::chrome_proxy_header()) &&
+      !handle->GetURL().SchemeIsCryptographic()) {
     data->set_was_cached_data_reduction_proxy_response(true);
   }
 
diff --git a/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_unittest.cc b/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_unittest.cc
index aed6c59..5dea19b 100644
--- a/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_unittest.cc
+++ b/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_unittest.cc
@@ -431,6 +431,21 @@
   EXPECT_TRUE(data->was_cached_data_reduction_proxy_response());
 }
 
+TEST_F(DataReductionProxyChromeSettingsTest, CreateHTTPSDataCachedResponse) {
+  std::unique_ptr<content::NavigationHandle> handle =
+      content::NavigationHandle::CreateNavigationHandleForTesting(
+          GURL("https://secure.com"), main_rfh());
+  std::string headers = "HTTP/1.0 200 OK\nchrome-proxy: foo\n";
+  handle->CallWillProcessResponseForTesting(
+      main_rfh(),
+      net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()), true,
+      net::ProxyServer::Direct());
+  auto data = drp_chrome_settings_->CreateDataFromNavigationHandle(
+      handle.get(), handle->GetResponseHeaders());
+
+  EXPECT_FALSE(data->was_cached_data_reduction_proxy_response());
+}
+
 TEST_F(DataReductionProxyChromeSettingsTest, CreateDataWithLitePage) {
   std::unique_ptr<content::NavigationHandle> handle =
       content::NavigationHandle::CreateNavigationHandleForTesting(GURL(kUrl),
diff --git a/chrome/browser/download/download_task_scheduler_impl.cc b/chrome/browser/download/download_task_scheduler_impl.cc
index d12556d..325ae9b 100644
--- a/chrome/browser/download/download_task_scheduler_impl.cc
+++ b/chrome/browser/download/download_task_scheduler_impl.cc
@@ -49,11 +49,6 @@
 
 void DownloadTaskSchedulerImpl::RunScheduledTask(
     download::DownloadTaskType task_type) {
-  if (task_type == download::DownloadTaskType::DOWNLOAD_AUTO_RESUMPTION_TASK) {
-    NOTREACHED();
-    return;
-  }
-
   download::DownloadService* download_service =
       DownloadServiceFactory::GetForBrowserContext(context_);
   download_service->OnStartScheduledTask(
diff --git a/chrome/browser/extensions/process_manager_browsertest.cc b/chrome/browser/extensions/process_manager_browsertest.cc
index 850ce1d..c8cd7370 100644
--- a/chrome/browser/extensions/process_manager_browsertest.cc
+++ b/chrome/browser/extensions/process_manager_browsertest.cc
@@ -1161,16 +1161,10 @@
 // https://crbug.com/656752.  These requests should still be allowed inside
 // actual <webview> guest processes created by a Chrome app; this is checked in
 // WebViewTest.Shim_TestBlobURL.
+// TODO(alexmos): Enable this test once checks are implemented in the
+// extensions NavigationThrottle. See https://crbug.com/919194.
 IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest,
-                       NestedURLNavigationsToAppBlocked) {
-  // TODO(alexmos):  Re-enable this test for PlzNavigate after tightening
-  // nested URL blocking for apps with the "webview" permission in
-  // ExtensionNavigationThrottle and removing the corresponding check from
-  // ChromeExtensionsNetworkDelegate.  The latter is incompatible with
-  // PlzNavigate.
-  if (content::IsBrowserSideNavigationEnabled())
-    return;
-
+                       DISABLED_NestedURLNavigationsToAppBlocked) {
   // Disabling web security is necessary to test the browser enforcement;
   // without it, the loads in this test would be blocked by
   // SecurityOrigin::canDisplay() as invalid local resource loads.
diff --git a/chrome/browser/file_select_helper.cc b/chrome/browser/file_select_helper.cc
index 73d1a887..f51db38 100644
--- a/chrome/browser/file_select_helper.cc
+++ b/chrome/browser/file_select_helper.cc
@@ -254,6 +254,14 @@
 }
 
 void FileSelectHelper::OnListDone(int error) {
+  if (!web_contents_) {
+    // Web contents was destroyed under us (probably by closing the tab). We
+    // must notify |listener_| and release our reference to
+    // ourself. RunFileChooserEnd() performs this.
+    RunFileChooserEnd();
+    return;
+  }
+
   // This entry needs to be cleaned up when this function is done.
   std::unique_ptr<ActiveDirectoryEnumeration> entry =
       std::move(directory_enumeration_);
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 7433b88..68c173a9 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3703,15 +3703,6 @@
 
 #endif  // defined(TOOLKIT_VIEWS) || defined(OS_ANDROID)
 
-#if defined(WEBRTC_USE_PIPEWIRE)
-
-extern const char kWebrtcPipeWireCapturerName[] = "WebRTC PipeWire support";
-extern const char kWebrtcPipeWireCapturerDescription[] =
-    "When enabled the WebRTC will use the PipeWire multimedia server for "
-    "capturing the desktop content on the Wayland display server.";
-
-#endif  // #if defined(WEBRTC_USE_PIPEWIRE)
-
 #if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
 
 const char kReopenTabInProductHelpName[] = "Reopen tab in-product help";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 3e6ac68..f44ed0a38 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2243,13 +2243,6 @@
 
 #endif  // defined(TOOLKIT_VIEWS) || defined(OS_ANDROID)
 
-#if defined(WEBRTC_USE_PIPEWIRE)
-
-extern const char kWebrtcPipeWireCapturerName[];
-extern const char kWebrtcPipeWireCapturerDescription[];
-
-#endif  // #if defined(WEBRTC_USE_PIPEWIRE)
-
 #if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
 
 extern const char kReopenTabInProductHelpName[];
diff --git a/chrome/browser/net/dns_probe_browsertest.cc b/chrome/browser/net/dns_probe_browsertest.cc
index a73797b5..3b0a5f20 100644
--- a/chrome/browser/net/dns_probe_browsertest.cc
+++ b/chrome/browser/net/dns_probe_browsertest.cc
@@ -36,11 +36,6 @@
 #include "net/dns/dns_test_util.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/url_request/url_request_failed_job.h"
-#include "net/test/url_request/url_request_mock_http_job.h"
-#include "net/url_request/url_request_filter.h"
-#include "net/url_request/url_request_interceptor.h"
-#include "net/url_request/url_request_job.h"
-#include "net/url_request/url_request_test_job.h"
 #include "services/network/public/cpp/features.h"
 
 using base::Bind;
@@ -55,13 +50,7 @@
 using error_page::DnsProbeStatus;
 using google_util::LinkDoctorBaseURL;
 using net::MockDnsClientRule;
-using net::NetworkDelegate;
-using net::URLRequest;
 using net::URLRequestFailedJob;
-using net::URLRequestFilter;
-using net::URLRequestInterceptor;
-using net::URLRequestJob;
-using net::URLRequestMockHTTPJob;
 using ui_test_utils::NavigateToURL;
 using ui_test_utils::NavigateToURLBlockUntilNavigationsComplete;
 
@@ -123,7 +112,8 @@
  public:
   // Called by a DelayableRequest if it was set to be delayed, and has been
   // destroyed without Undelay being called.
-  typedef base::Callback<void(DelayableRequest* request)> DestructionCallback;
+  typedef base::OnceCallback<void(DelayableRequest* request)>
+      DestructionCallback;
 
   virtual void Resume() = 0;
 
@@ -131,133 +121,90 @@
   virtual ~DelayableRequest() {}
 };
 
-class DelayableURLRequestFailedJob : public URLRequestFailedJob,
-                                     public DelayableRequest {
+class DelayedURLLoader : public network::mojom::URLLoader,
+                         public DelayableRequest {
  public:
-  // |destruction_callback| is only called if a delayed request is destroyed
-  // without being resumed.
-  DelayableURLRequestFailedJob(net::URLRequest* request,
-                               net::NetworkDelegate* network_delegate,
-                               int net_error,
-                               bool should_delay,
-                               const DestructionCallback& destruction_callback)
-      : URLRequestFailedJob(request, network_delegate, net_error),
+  DelayedURLLoader(network::mojom::URLLoaderRequest request,
+                   network::mojom::URLLoaderClientPtr client,
+                   int net_error,
+                   bool should_delay,
+                   DestructionCallback destruction_callback)
+      : binding_(this, std::move(request)),
+        client_(std::move(client)),
+        net_error_(net_error),
         should_delay_(should_delay),
-        start_delayed_(false),
-        destruction_callback_(destruction_callback) {}
-
-  void Start() override {
-    if (should_delay_) {
-      DCHECK(!start_delayed_);
-      start_delayed_ = true;
-      return;
-    }
-    URLRequestFailedJob::Start();
+        destruction_callback_(std::move(destruction_callback)) {
+    binding_.set_connection_error_handler(base::BindOnce(
+        &DelayedURLLoader::OnConnectionError, base::Unretained(this)));
+    if (!should_delay)
+      SendResponse();
   }
 
   void Resume() override {
     DCHECK(should_delay_);
     should_delay_ = false;
-    if (start_delayed_) {
-      start_delayed_ = false;
-      Start();
-    }
+    SendResponse();
   }
 
- private:
-  ~DelayableURLRequestFailedJob() override {
-    if (should_delay_)
-      destruction_callback_.Run(this);
-  }
-
-  bool should_delay_;
-  bool start_delayed_;
-  const DestructionCallback destruction_callback_;
-};
-
-class DelayableURLRequestMockHTTPJob : public URLRequestMockHTTPJob,
-                                       public DelayableRequest {
- public:
-  DelayableURLRequestMockHTTPJob(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate,
-      const base::FilePath& file_path,
-      bool should_delay,
-      const DestructionCallback& destruction_callback)
-      : URLRequestMockHTTPJob(request, network_delegate, file_path),
-        should_delay_(should_delay),
-        start_delayed_(false),
-        destruction_callback_(destruction_callback) {}
-
-  void Start() override {
-    if (should_delay_) {
-      DCHECK(!start_delayed_);
-      start_delayed_ = true;
+  void SendResponse() {
+    if (net_error_ == net::OK) {
+      content::URLLoaderInterceptor::WriteResponse(GetMockLinkDoctorFilePath(),
+                                                   client_.get());
       return;
     }
-    URLRequestMockHTTPJob::Start();
-  }
 
-  void Resume() override {
-    DCHECK(should_delay_);
-    should_delay_ = false;
-    if (start_delayed_) {
-      start_delayed_ = false;
-      Start();
-    }
+    client_->OnComplete(network::URLLoaderCompletionStatus(net_error_));
   }
 
  private:
-  ~DelayableURLRequestMockHTTPJob() override {
+  ~DelayedURLLoader() override {
     if (should_delay_)
-      destruction_callback_.Run(this);
+      std::move(destruction_callback_).Run(this);
   }
 
+  void OnConnectionError() { delete this; }
+
+  // mojom::URLLoader implementation:
+  void FollowRedirect(
+      const base::Optional<std::vector<std::string>>&
+          to_be_removed_request_headers,
+      const base::Optional<net::HttpRequestHeaders>& modified_request_headers,
+      const base::Optional<GURL>& new_url) override {}
+  void ProceedWithResponse() override {}
+  void SetPriority(net::RequestPriority priority,
+                   int32_t intra_priority_value) override {}
+  void PauseReadingBodyFromNet() override {}
+  void ResumeReadingBodyFromNet() override {}
+
+  mojo::Binding<network::mojom::URLLoader> binding_;
+  network::mojom::URLLoaderClientPtr client_;
+  int net_error_;
   bool should_delay_;
-  bool start_delayed_;
-  const DestructionCallback destruction_callback_;
+  DestructionCallback destruction_callback_;
 };
 
 // Interceptor for navigation correction requests.  Can cause requests to
 // fail with an error, and/or delay a request until a test allows to continue.
 // Also can run a callback when a delayed request is cancelled.
-class BreakableCorrectionInterceptor : public URLRequestInterceptor {
+class BreakableCorrectionInterceptor {
  public:
-  explicit BreakableCorrectionInterceptor(
-      const FilePath& mock_corrections_file_path)
-      : mock_corrections_file_path_(mock_corrections_file_path),
-        net_error_(net::OK),
-        delay_requests_(false),
-        on_request_destroyed_callback_(
-            base::Bind(&BreakableCorrectionInterceptor::OnRequestDestroyed,
-                       base::Unretained(this))) {
-  }
+  BreakableCorrectionInterceptor()
+      : net_error_(net::OK), delay_requests_(false) {}
 
-  ~BreakableCorrectionInterceptor() override {
+  ~BreakableCorrectionInterceptor() {
     // All delayed requests should have been resumed or cancelled by this point.
     EXPECT_TRUE(delayed_requests_.empty());
   }
 
-  URLRequestJob* MaybeInterceptRequest(
-      URLRequest* request,
-      NetworkDelegate* network_delegate) const override {
-    if (net_error_ != net::OK) {
-      DelayableURLRequestFailedJob* job =
-          new DelayableURLRequestFailedJob(
-              request, network_delegate, net_error_, delay_requests_,
-              on_request_destroyed_callback_);
-      if (delay_requests_)
-        delayed_requests_.insert(job);
-      return job;
-    } else {
-      DelayableURLRequestMockHTTPJob* job =
-          new DelayableURLRequestMockHTTPJob(
-              request, network_delegate, mock_corrections_file_path_,
-              delay_requests_, on_request_destroyed_callback_);
-      if (delay_requests_)
-        delayed_requests_.insert(job);
-      return job;
-    }
+  void InterceptURLLoaderRequest(
+      content::URLLoaderInterceptor::RequestParams* params) {
+    DelayedURLLoader* job = new DelayedURLLoader(
+        std::move(params->request), std::move(params->client), net_error_,
+        delay_requests_,
+        base::BindOnce(&BreakableCorrectionInterceptor::OnRequestDestroyed,
+                       base::Unretained(this)));
+    if (delay_requests_)
+      delayed_requests_.insert(job);
   }
 
   void set_net_error(int net_error) { net_error_ = net_error; }
@@ -297,17 +244,10 @@
   }
 
  private:
-  const FilePath mock_corrections_file_path_;
   int net_error_;
   bool delay_requests_;
 
-  // Called when a request is destroyed.  Memeber variable because
-  // MaybeCreateJob is "const", so calling base::Bind in that function does
-  // not work well.
-  const DelayableRequest::DestructionCallback on_request_destroyed_callback_;
-
-  // Mutable is needed because MaybeCreateJob is const.
-  mutable std::set<DelayableRequest*> delayed_requests_;
+  std::set<DelayableRequest*> delayed_requests_;
 
   base::Closure delayed_request_destruction_callback_;
 };
@@ -325,21 +265,21 @@
   void SetCorrectionServiceDelayRequests(bool delay_requests);
   void SetRequestDestructionCallback(const base::Closure& callback);
   void StartDelayedProbes(int expected_delayed_probe_count);
+  void InterceptURLLoaderRequest(
+      content::URLLoaderInterceptor::RequestParams* params);
 
  private:
   IOThread* io_thread_;
   DnsProbeService* original_dns_probe_service_;
   DelayingDnsProbeService* delaying_dns_probe_service_;
   BreakableCorrectionInterceptor* interceptor_;
-  FilePath mock_corrections_file_path_;
 };
 
 DnsProbeBrowserTestIOThreadHelper::DnsProbeBrowserTestIOThreadHelper()
     : io_thread_(NULL),
       original_dns_probe_service_(NULL),
       delaying_dns_probe_service_(NULL),
-      interceptor_(NULL),
-      mock_corrections_file_path_(GetMockLinkDoctorFilePath()) {}
+      interceptor_(NULL) {}
 
 void DnsProbeBrowserTestIOThreadHelper::SetUpOnIOThread(IOThread* io_thread) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
@@ -357,20 +297,12 @@
   original_dns_probe_service_ = globals->dns_probe_service.release();
   globals->dns_probe_service.reset(delaying_dns_probe_service_);
 
-  URLRequestFailedJob::AddUrlHandler();
-
-  interceptor_ =
-      new BreakableCorrectionInterceptor(mock_corrections_file_path_);
-  URLRequestFilter::GetInstance()->AddUrlInterceptor(
-      LinkDoctorBaseURL(),
-      std::unique_ptr<URLRequestInterceptor>(interceptor_));
+  interceptor_ = new BreakableCorrectionInterceptor;
 }
 
 void DnsProbeBrowserTestIOThreadHelper::CleanUpOnIOThreadAndDeleteHelper() {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
 
-  URLRequestFilter::GetInstance()->ClearHandlers();
-
   IOThread::Globals* globals = io_thread_->globals();
   std::unique_ptr<DnsProbeService> delaying_dns_probe_service(
       globals->dns_probe_service.release());
@@ -427,6 +359,13 @@
   delaying_dns_probe_service_->StartDelayedProbes();
 }
 
+void DnsProbeBrowserTestIOThreadHelper::InterceptURLLoaderRequest(
+    content::URLLoaderInterceptor::RequestParams* params) {
+  CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  interceptor_->InterceptURLLoaderRequest(params);
+}
+
 class DnsProbeBrowserTest : public InProcessBrowserTest {
  public:
   DnsProbeBrowserTest();
@@ -484,12 +423,9 @@
   NetErrorTabHelper* monitored_tab_helper_;
 
   bool awaiting_dns_probe_status_;
-  bool corrections_service_working_;
   // Queue of statuses received but not yet consumed by WaitForSentStatus().
   std::list<DnsProbeStatus> dns_probe_status_queue_;
 
-  // Implements handling of http(s)://mock.failed.request for network service
-  // that URLRequestFailedJob does.
   std::unique_ptr<content::URLLoaderInterceptor> url_loader_interceptor_;
 };
 
@@ -497,8 +433,7 @@
     : helper_(new DnsProbeBrowserTestIOThreadHelper()),
       active_browser_(NULL),
       monitored_tab_helper_(NULL),
-      awaiting_dns_probe_status_(false),
-      corrections_service_working_(true) {}
+      awaiting_dns_probe_status_(false) {}
 
 DnsProbeBrowserTest::~DnsProbeBrowserTest() {
   // No tests should have any unconsumed probe statuses.
@@ -506,8 +441,7 @@
 }
 
 void DnsProbeBrowserTest::SetUpOnMainThread() {
-  NetErrorTabHelper::set_state_for_testing(
-      NetErrorTabHelper::TESTING_DEFAULT);
+  NetErrorTabHelper::set_state_for_testing(NetErrorTabHelper::TESTING_DEFAULT);
 
   browser()->profile()->GetPrefs()->SetBoolean(
       prefs::kAlternateErrorPagesEnabled, true);
@@ -519,13 +453,9 @@
 
   ASSERT_TRUE(embedded_test_server()->Start());
 
-  if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
-    // NOTE: Need to intercept requests for subresources to catch the Link
-    // Doctor requests.
-    url_loader_interceptor_ = std::make_unique<content::URLLoaderInterceptor>(
-        base::BindRepeating(&DnsProbeBrowserTest::InterceptURLLoaderRequest,
-                            base::Unretained(this)));
-  }
+  url_loader_interceptor_ = std::make_unique<content::URLLoaderInterceptor>(
+      base::BindRepeating(&DnsProbeBrowserTest::InterceptURLLoaderRequest,
+                          base::Unretained(this)));
 
   SetActiveBrowser(browser());
 }
@@ -539,16 +469,20 @@
 
   url_loader_interceptor_.reset();
 
-  NetErrorTabHelper::set_state_for_testing(
-      NetErrorTabHelper::TESTING_DEFAULT);
+  NetErrorTabHelper::set_state_for_testing(NetErrorTabHelper::TESTING_DEFAULT);
 }
 
 bool DnsProbeBrowserTest::InterceptURLLoaderRequest(
     content::URLLoaderInterceptor::RequestParams* params) {
-  if (params->url_request.url.spec() == LinkDoctorBaseURL().spec() &&
-      corrections_service_working_) {
-    return chrome_browser_net::WriteFileToURLLoader(
-        embedded_test_server(), params, "mock-link-doctor.json");
+  if (params->url_request.url == LinkDoctorBaseURL()) {
+    helper_->InterceptURLLoaderRequest(params);
+    return true;
+  }
+
+  if (params->url_request.url.spec() == "http://mock.http/title2.html") {
+    content::URLLoaderInterceptor::WriteResponse("chrome/test/data/title2.html",
+                                                 params->client.get());
+    return true;
   }
 
   // Just returning false is enough to respond to http(s)://mock.failed.request
@@ -573,7 +507,6 @@
 
 void DnsProbeBrowserTest::SetCorrectionServiceBroken(bool broken) {
   int net_error = broken ? net::ERR_NAME_NOT_RESOLVED : net::OK;
-  corrections_service_working_ = (net_error == net::OK);
 
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::IO},
@@ -624,8 +557,7 @@
                Unretained(helper_), system_result, public_result));
 }
 
-void DnsProbeBrowserTest::StartDelayedProbes(
-    int expected_delayed_probe_count) {
+void DnsProbeBrowserTest::StartDelayedProbes(int expected_delayed_probe_count) {
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::IO},
       BindOnce(&DnsProbeBrowserTestIOThreadHelper::StartDelayedProbes,
@@ -655,9 +587,7 @@
       active_browser_->tab_strip_model()->GetActiveWebContents();
 
   bool rv = content::ExecuteScriptAndExtractString(
-      contents,
-      "domAutomationController.send(document.title);",
-      &title);
+      contents, "domAutomationController.send(document.title);", &title);
   if (!rv)
     return "";
 
@@ -687,14 +617,7 @@
 
 void DnsProbeBrowserTest::ExpectDisplayingCorrections(
     const std::string& status_text) {
-  // NOTE: In the case of the Network Service, the expected URL has a port
-  // inserted.
-  GURL url;
-  if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
-    url = embedded_test_server()->GetURL("mock.http", "/title2.html");
-  } else {
-    url = GURL("http://mock.http/title2.html");
-  }
+  GURL url("http://mock.http/title2.html");
   EXPECT_TRUE(PageContains(url.spec()));
   EXPECT_TRUE(PageContains(status_text));
 }
@@ -741,8 +664,7 @@
 
   StartDelayedProbes(1);
 
-  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_NXDOMAIN,
-            WaitForSentStatus());
+  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_NXDOMAIN, WaitForSentStatus());
   EXPECT_EQ(0, pending_status_count());
   ExpectDisplayingCorrections("ERR_NAME_NOT_RESOLVED");
 }
@@ -766,8 +688,7 @@
   EXPECT_EQ("", Title());
 
   StartDelayedProbes(1);
-  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_NXDOMAIN,
-            WaitForSentStatus());
+  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_NXDOMAIN, WaitForSentStatus());
   EXPECT_EQ(0, pending_status_count());
   EXPECT_EQ("", Title());
 
@@ -781,8 +702,7 @@
 
   // Committing the corections page should trigger sending the probe result
   // again.
-  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_NXDOMAIN,
-            WaitForSentStatus());
+  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_NXDOMAIN, WaitForSentStatus());
   ExpectDisplayingCorrections("ERR_NAME_NOT_RESOLVED");
 }
 
@@ -790,8 +710,7 @@
 IN_PROC_BROWSER_TEST_F(DnsProbeBrowserTest,
                        NoInternetProbeResultWithBrokenCorrections) {
   SetCorrectionServiceBroken(true);
-  SetMockDnsClientRules(MockDnsClientRule::TIMEOUT,
-                        MockDnsClientRule::TIMEOUT);
+  SetMockDnsClientRules(MockDnsClientRule::TIMEOUT, MockDnsClientRule::TIMEOUT);
 
   NavigateToDnsError(2);
 
@@ -805,8 +724,7 @@
 
   StartDelayedProbes(1);
 
-  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_NO_INTERNET,
-            WaitForSentStatus());
+  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_NO_INTERNET, WaitForSentStatus());
 
   // Checking the page runs the RunLoop, so make sure nothing hairy happens.
   EXPECT_EQ(0, pending_status_count());
@@ -819,8 +737,7 @@
                        NoInternetProbeResultWithSlowBrokenCorrections) {
   SetCorrectionServiceBroken(true);
   SetCorrectionServiceDelayRequests(true);
-  SetMockDnsClientRules(MockDnsClientRule::TIMEOUT,
-                        MockDnsClientRule::TIMEOUT);
+  SetMockDnsClientRules(MockDnsClientRule::TIMEOUT, MockDnsClientRule::TIMEOUT);
 
   NavigateToDnsError(1);
   // A blank page should be displayed while the corrections load.
@@ -833,8 +750,7 @@
   EXPECT_EQ("", Title());
 
   StartDelayedProbes(1);
-  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_NO_INTERNET,
-            WaitForSentStatus());
+  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_NO_INTERNET, WaitForSentStatus());
   EXPECT_EQ("", Title());
   EXPECT_EQ(0, pending_status_count());
 
@@ -845,8 +761,7 @@
   // Wait for the DNS error page to load instead.
   observer.Wait();
   // The page committing should result in sending the probe results again.
-  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_NO_INTERNET,
-            WaitForSentStatus());
+  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_NO_INTERNET, WaitForSentStatus());
 
   EXPECT_EQ(0, pending_status_count());
   ExpectDisplayingLocalErrorPage("DNS_PROBE_FINISHED_NO_INTERNET");
@@ -869,8 +784,7 @@
 
   StartDelayedProbes(1);
 
-  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_INCONCLUSIVE,
-            WaitForSentStatus());
+  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_INCONCLUSIVE, WaitForSentStatus());
 
   // Checking the page runs the RunLoop, so make sure nothing hairy happens.
   EXPECT_EQ(0, pending_status_count());
@@ -891,8 +805,7 @@
 
   EXPECT_EQ(error_page::DNS_PROBE_STARTED, WaitForSentStatus());
   StartDelayedProbes(1);
-  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_NO_INTERNET,
-            WaitForSentStatus());
+  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_NO_INTERNET, WaitForSentStatus());
 
   EXPECT_EQ("", Title());
   EXPECT_EQ(0, pending_status_count());
@@ -925,8 +838,7 @@
   EXPECT_EQ(0, pending_status_count());
 
   StartDelayedProbes(1);
-  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_NO_INTERNET,
-            WaitForSentStatus());
+  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_NO_INTERNET, WaitForSentStatus());
 
   EXPECT_EQ("", Title());
 }
@@ -991,8 +903,7 @@
 
   StartDelayedProbes(1);
 
-  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_INCONCLUSIVE,
-            WaitForSentStatus());
+  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_INCONCLUSIVE, WaitForSentStatus());
   EXPECT_EQ(0, pending_status_count());
   ExpectDisplayingLocalErrorPage("ERR_NAME_NOT_RESOLVED");
 }
@@ -1020,8 +931,7 @@
 
   StartDelayedProbes(1);
 
-  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_INCONCLUSIVE,
-            WaitForSentStatus());
+  EXPECT_EQ(error_page::DNS_PROBE_FINISHED_INCONCLUSIVE, WaitForSentStatus());
   EXPECT_EQ(0, pending_status_count());
   ExpectDisplayingLocalErrorPage("ERR_NAME_NOT_RESOLVED");
 }
diff --git a/chrome/browser/net/errorpage_browsertest.cc b/chrome/browser/net/errorpage_browsertest.cc
index 317574b..ed2b3bc 100644
--- a/chrome/browser/net/errorpage_browsertest.cc
+++ b/chrome/browser/net/errorpage_browsertest.cc
@@ -171,13 +171,12 @@
 // Checks that an error page with information retrieved from the navigation
 // correction service is being displayed, with the specified specified error
 // string.
-void ExpectDisplayingNavigationCorrections(const std::string& url,
-                                           Browser* browser,
+void ExpectDisplayingNavigationCorrections(Browser* browser,
                                            const std::string& error_string) {
   EXPECT_TRUE(IsDisplayingText(browser, error_string));
 
   // Check that the mock navigation corrections are displayed.
-  EXPECT_TRUE(IsDisplayingText(browser, url));
+  EXPECT_TRUE(IsDisplayingText(browser, "http://mock.http/title2.html"));
 
   // Check that the search terms are displayed as a link.
   EXPECT_TRUE(IsDisplayingText(browser, "search query"));
@@ -190,10 +189,9 @@
 // Checks that an error page with information retrieved from the navigation
 // correction service is being displayed, with the specified specified error
 // code.
-void ExpectDisplayingNavigationCorrections(const std::string& url,
-                                           Browser* browser,
+void ExpectDisplayingNavigationCorrections(Browser* browser,
                                            net::Error error_code) {
-  ExpectDisplayingNavigationCorrections(url, browser,
+  ExpectDisplayingNavigationCorrections(browser,
                                         net::ErrorToShortString(error_code));
 }
 
@@ -383,17 +381,27 @@
                     FROM_HERE, {BrowserThread::UI},
                     base::BindOnce(&DNSErrorPageTest::RequestCreated,
                                    base::Unretained(owner)));
-                return chrome_browser_net::WriteFileToURLLoader(
-                    owner->embedded_test_server(), params,
-                    "mock-link-doctor.json");
+                content::URLLoaderInterceptor::WriteResponse(
+                    "chrome/test/data/mock-link-doctor.json",
+                    params->client.get());
+                return true;
+              }
+
+              // Referenced by mock Link Doctor page.
+              if (params->url_request.url.spec() ==
+                  "http://mock.http/title2.html") {
+                content::URLLoaderInterceptor::WriteResponse(
+                    "chrome/test/data/title2.html", params->client.get());
+                return true;
               }
 
               // Add an interceptor for the search engine the error page will
               // use.
               if (params->url_request.url.host() ==
                   owner->search_term_url_.host()) {
-                return chrome_browser_net::WriteFileToURLLoader(
-                    owner->embedded_test_server(), params, "title3.html");
+                content::URLLoaderInterceptor::WriteResponse(
+                    "chrome/test/data/title3.html", params->client.get());
+                return true;
               }
 
               return false;
@@ -512,7 +520,6 @@
   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
        browser(), GetDnsErrorURL(), 2);
   ExpectDisplayingNavigationCorrections(
-      embedded_test_server()->GetURL("mock.http", "/title2.html").spec(),
       browser(), net::ERR_NAME_NOT_RESOLVED);
   EXPECT_EQ(1, num_requests());
 }
@@ -524,7 +531,6 @@
   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
        browser(), GetDnsErrorURL(), 2);
   ExpectDisplayingNavigationCorrections(
-      embedded_test_server()->GetURL("mock.http", "/title2.html").spec(),
       browser(), net::ERR_NAME_NOT_RESOLVED);
   GoBackAndWaitForTitle("Title Of Awesomeness", 1);
   EXPECT_EQ(1, num_requests());
@@ -538,7 +544,6 @@
   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
        browser(), GetDnsErrorURL(), 2);
   ExpectDisplayingNavigationCorrections(
-      embedded_test_server()->GetURL("mock.http", "/title2.html").spec(),
       browser(), net::ERR_NAME_NOT_RESOLVED);
   EXPECT_EQ(1, num_requests());
 
@@ -546,7 +551,6 @@
 
   GoBackAndWaitForNavigations(2);
   ExpectDisplayingNavigationCorrections(
-      embedded_test_server()->GetURL("mock.http", "/title2.html").spec(),
       browser(), net::ERR_NAME_NOT_RESOLVED);
   EXPECT_EQ(2, num_requests());
 
@@ -562,24 +566,19 @@
   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
        browser(), GetDnsErrorURL(), 2);
 
-  std::string url =
-      embedded_test_server()->GetURL("mock.http", "/title2.html").spec();
-  ExpectDisplayingNavigationCorrections(url, browser(),
-                                        net::ERR_NAME_NOT_RESOLVED);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
   EXPECT_EQ(1, num_requests());
 
   NavigateToFileURL("/title3.html");
 
   GoBackAndWaitForNavigations(2);
-  ExpectDisplayingNavigationCorrections(url, browser(),
-                                        net::ERR_NAME_NOT_RESOLVED);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
   EXPECT_EQ(2, num_requests());
 
   GoBackAndWaitForTitle("Title Of Awesomeness", 1);
 
   GoForwardAndWaitForNavigations(2);
-  ExpectDisplayingNavigationCorrections(url, browser(),
-                                        net::ERR_NAME_NOT_RESOLVED);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
   EXPECT_EQ(3, num_requests());
 }
 
@@ -588,26 +587,21 @@
 IN_PROC_BROWSER_TEST_F(DNSErrorPageTest, DNSError_GoBack2Forward2) {
   NavigateToFileURL("/title3.html");
 
-  std::string url =
-      embedded_test_server()->GetURL("mock.http", "/title2.html").spec();
   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
        browser(), GetDnsErrorURL(), 2);
-  ExpectDisplayingNavigationCorrections(url, browser(),
-                                        net::ERR_NAME_NOT_RESOLVED);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
   EXPECT_EQ(1, num_requests());
 
   NavigateToFileURL("/title2.html");
 
   GoBackAndWaitForNavigations(2);
-  ExpectDisplayingNavigationCorrections(url, browser(),
-                                        net::ERR_NAME_NOT_RESOLVED);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
   EXPECT_EQ(2, num_requests());
 
   GoBackAndWaitForTitle("Title Of More Awesomeness", 1);
 
   GoForwardAndWaitForNavigations(2);
-  ExpectDisplayingNavigationCorrections(url, browser(),
-                                        net::ERR_NAME_NOT_RESOLVED);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
   EXPECT_EQ(3, num_requests());
 
   GoForwardAndWaitForTitle("Title Of Awesomeness", 1);
@@ -621,7 +615,6 @@
   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
        browser(), GetDnsErrorURL(), 2);
   ExpectDisplayingNavigationCorrections(
-      embedded_test_server()->GetURL("mock.http", "/title2.html").spec(),
       browser(), net::ERR_NAME_NOT_RESOLVED);
   EXPECT_EQ(1, num_requests());
 
@@ -659,7 +652,6 @@
   // Go back to the error page, to make sure the history is correct.
   GoBackAndWaitForNavigations(2);
   ExpectDisplayingNavigationCorrections(
-      embedded_test_server()->GetURL("mock.http", "/title2.html").spec(),
       browser(), net::ERR_NAME_NOT_RESOLVED);
   EXPECT_EQ(3, num_requests());
 }
@@ -672,8 +664,7 @@
       embedded_test_server()->GetURL("mock.http", "/title2.html").spec();
   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
        browser(), GetDnsErrorURL(), 2);
-  ExpectDisplayingNavigationCorrections(url, browser(),
-                                        net::ERR_NAME_NOT_RESOLVED);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
   EXPECT_EQ(1, num_requests());
 
   content::WebContents* web_contents =
@@ -688,8 +679,7 @@
   web_contents->GetMainFrame()->ExecuteJavaScriptForTests(
       base::ASCIIToUTF16("document.getElementById('reload-button').click();"));
   nav_observer.Wait();
-  ExpectDisplayingNavigationCorrections(url, browser(),
-                                        net::ERR_NAME_NOT_RESOLVED);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
 
   // There should have been two more requests to the correction service:  One
   // for the new error page, and one for tracking purposes.  Have to make sure
@@ -709,8 +699,7 @@
       embedded_test_server()->GetURL("mock.http", "/title2.html").spec();
   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
        browser(), GetDnsErrorURL(), 2);
-  ExpectDisplayingNavigationCorrections(url, browser(),
-                                        net::ERR_NAME_NOT_RESOLVED);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
   EXPECT_EQ(1, num_requests());
 
   content::WebContents* web_contents =
@@ -722,8 +711,7 @@
       base::ASCIIToUTF16("document.location='#';"));
   content::WaitForLoadStop(web_contents);
   // Page being displayed should not change.
-  ExpectDisplayingNavigationCorrections(url, browser(),
-                                        net::ERR_NAME_NOT_RESOLVED);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
   // No new requests should have been issued.
   EXPECT_EQ(1, num_requests());
 
@@ -736,8 +724,7 @@
   web_contents->GetMainFrame()->ExecuteJavaScriptForTests(
       base::ASCIIToUTF16("document.getElementById('reload-button').click();"));
   nav_observer2.Wait();
-  ExpectDisplayingNavigationCorrections(url, browser(),
-                                        net::ERR_NAME_NOT_RESOLVED);
+  ExpectDisplayingNavigationCorrections(browser(), net::ERR_NAME_NOT_RESOLVED);
 
   // There should have been two more requests to the correction service:  One
   // for the new error page, and one for tracking purposes.  Have to make sure
@@ -754,7 +741,6 @@
   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
        browser(), GetDnsErrorURL(), 2);
   ExpectDisplayingNavigationCorrections(
-      embedded_test_server()->GetURL("mock.http", "/title2.html").spec(),
       browser(), net::ERR_NAME_NOT_RESOLVED);
   EXPECT_EQ(1, num_requests());
 
@@ -767,7 +753,7 @@
       web_contents,
       base::ASCIIToUTF16("Title Of Awesomeness"));
   std::string link_selector =
-      "document.querySelector('a[href=\"" + url.spec() + "\"]')";
+      "document.querySelector('a[href=\"http://mock.http/title2.html\"]')";
   // The tracking request is triggered by onmousedown, so it catches middle
   // mouse button clicks, as well as left clicks.
   web_contents->GetMainFrame()->ExecuteJavaScriptForTests(
@@ -919,7 +905,6 @@
   // This depends on the non-internationalized error ID string in
   // localized_error.cc.
   ExpectDisplayingNavigationCorrections(
-      embedded_test_server()->GetURL("mock.http", "/title2.html").spec(),
       browser(), "HTTP ERROR 404");
   EXPECT_EQ(1, num_requests());
 }
diff --git a/chrome/browser/net/url_request_mock_util.cc b/chrome/browser/net/url_request_mock_util.cc
index 8532920..1ea7a4d 100644
--- a/chrome/browser/net/url_request_mock_util.cc
+++ b/chrome/browser/net/url_request_mock_util.cc
@@ -47,36 +47,4 @@
   }
 }
 
-bool WriteFileToURLLoader(net::EmbeddedTestServer* test_server,
-                          content::URLLoaderInterceptor::RequestParams* params,
-                          std::string path) {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-
-  if (path[0] == '/')
-    path.erase(0, 1);
-
-  if (path == "favicon.ico")
-    return false;
-
-  base::FilePath file_path;
-  base::PathService::Get(chrome::DIR_TEST_DATA, &file_path);
-  file_path = file_path.AppendASCII(path);
-
-  std::string contents;
-  const bool result = base::ReadFileToString(file_path, &contents);
-  EXPECT_TRUE(result);
-
-  if (path == "mock-link-doctor.json") {
-    GURL url = test_server->GetURL("mock.http", "/title2.html");
-
-    std::string placeholder = "http://mock.http/title2.html";
-    contents.replace(contents.find(placeholder), placeholder.length(),
-                     url.spec());
-  }
-
-  content::URLLoaderInterceptor::WriteResponse(
-      net::URLRequestTestJob::test_headers(), contents, params->client.get());
-  return true;
-}
-
 }  // namespace chrome_browser_net
diff --git a/chrome/browser/net/url_request_mock_util.h b/chrome/browser/net/url_request_mock_util.h
index 5c2f1c1..0613a92b6 100644
--- a/chrome/browser/net/url_request_mock_util.h
+++ b/chrome/browser/net/url_request_mock_util.h
@@ -5,7 +5,6 @@
 #ifndef CHROME_BROWSER_NET_URL_REQUEST_MOCK_UTIL_H_
 #define CHROME_BROWSER_NET_URL_REQUEST_MOCK_UTIL_H_
 
-#include "content/public/test/url_loader_interceptor.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 
 // You should use routines in this file only for test code!
@@ -15,15 +14,6 @@
 // Enables or disables url request filters for mocked url requests.
 void SetUrlRequestMocksEnabled(bool enabled);
 
-// Writes the contents of |path| as a response to a request intercepted
-// by the URLLoaderInterceptor associated with |params->client()|. Notes:
-// - |path| is assumed to be relative to chrome::DIR_TEST_DATA.
-// - If |path| is |mock_link_doctor.json|, rewrites the that
-//   file's|urlCorrection| URL as it is mapped by |test_server|.
-bool WriteFileToURLLoader(net::EmbeddedTestServer* test_server,
-                          content::URLLoaderInterceptor::RequestParams* params,
-                          std::string path);
-
 }  // namespace chrome_browser_net
 
 #endif  // CHROME_BROWSER_NET_URL_REQUEST_MOCK_UTIL_H_
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc
index 0c3220181..245fe83c 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc
@@ -308,11 +308,6 @@
   return STOP_OBSERVING;
 }
 
-void AdsPageLoadMetricsObserver::OnLoadedResource(
-    const page_load_metrics::ExtraRequestCompleteInfo& extra_request_info) {
-  ProcessLoadedResource(extra_request_info);
-}
-
 void AdsPageLoadMetricsObserver::OnComplete(
     const page_load_metrics::mojom::PageLoadTiming& timing,
     const page_load_metrics::PageLoadExtraInfo& info) {
@@ -322,10 +317,11 @@
 }
 
 void AdsPageLoadMetricsObserver::OnResourceDataUseObserved(
+    FrameTreeNodeId frame_tree_node_id,
     const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
         resources) {
   for (auto const& resource : resources)
-    UpdateResource(resource);
+    UpdateResource(frame_tree_node_id, resource);
 }
 
 void AdsPageLoadMetricsObserver::OnSubframeNavigationEvaluated(
@@ -382,29 +378,22 @@
   return ad_types;
 }
 
-void AdsPageLoadMetricsObserver::ProcessLoadedResource(
-    const page_load_metrics::ExtraRequestCompleteInfo& extra_request_info) {
-  const auto& id_and_data =
-      ad_frames_data_.find(extra_request_info.frame_tree_node_id);
+void AdsPageLoadMetricsObserver::ProcessResourceForFrame(
+    FrameTreeNodeId frame_tree_node_id,
+    const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
+  const auto& id_and_data = ad_frames_data_.find(frame_tree_node_id);
   if (id_and_data == ad_frames_data_.end()) {
-    if (extra_request_info.resource_type == content::RESOURCE_TYPE_MAIN_FRAME ||
-        extra_request_info.resource_type == content::RESOURCE_TYPE_SUB_FRAME) {
+    if (resource->is_primary_frame_resource) {
+      // Only hold onto primary resources if their load has finished.
+      if (!resource->is_complete)
+        return;
+
       // This resource request is the primary resource load for a frame that
       // hasn't yet finished navigating. Hang onto the request info and replay
       // it once the frame finishes navigating.
       ongoing_navigation_resources_.emplace(
-          std::piecewise_construct,
-          std::forward_as_tuple(extra_request_info.frame_tree_node_id),
-          std::forward_as_tuple(
-              extra_request_info.url, extra_request_info.host_port_pair,
-              extra_request_info.frame_tree_node_id,
-              extra_request_info.was_cached, extra_request_info.raw_body_bytes,
-              extra_request_info.original_network_content_length, nullptr,
-              extra_request_info.resource_type, extra_request_info.net_error,
-              extra_request_info.load_timing_info
-                  ? std::make_unique<net::LoadTimingInfo>(
-                        *extra_request_info.load_timing_info)
-                  : nullptr));
+          std::piecewise_construct, std::forward_as_tuple(frame_tree_node_id),
+          std::forward_as_tuple(resource.Clone()));
     } else {
       // This is unexpected, it could be:
       // 1. a resource from a previous navigation that started its resource
@@ -415,18 +404,21 @@
     return;
   }
 
-  page_bytes_ += extra_request_info.raw_body_bytes;
-  if (!extra_request_info.was_cached)
-    uncached_page_bytes_ += extra_request_info.raw_body_bytes;
+  if (resource->is_complete) {
+    page_bytes_ += resource->encoded_body_length;
+    if (!resource->was_fetched_via_cache)
+      uncached_page_bytes_ += resource->encoded_body_length;
+  }
 
   // Determine if the frame (or its ancestor) is an ad, if so attribute the
   // bytes to the highest ad ancestor.
   AdFrameData* ancestor_data = id_and_data->second;
 
   if (ancestor_data) {
-    ancestor_data->frame_bytes += extra_request_info.raw_body_bytes;
-    if (!extra_request_info.was_cached) {
-      ancestor_data->frame_bytes_uncached += extra_request_info.raw_body_bytes;
+    if (resource->is_complete) {
+      ancestor_data->frame_bytes += resource->encoded_body_length;
+      if (!resource->was_fetched_via_cache)
+        ancestor_data->frame_bytes_uncached += resource->encoded_body_length;
     }
   }
 }
@@ -457,7 +449,9 @@
 }
 
 void AdsPageLoadMetricsObserver::UpdateResource(
+    FrameTreeNodeId frame_tree_node_id,
     const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
+  ProcessResourceForFrame(frame_tree_node_id, resource);
   auto it = page_resources_.find(resource->request_id);
   // A new resource has been observed.
   if (it == page_resources_.end())
@@ -690,6 +684,6 @@
   if (frame_id_and_request == ongoing_navigation_resources_.end())
     return;
 
-  ProcessLoadedResource(frame_id_and_request->second);
+  ProcessResourceForFrame(frame_tree_node_id, frame_id_and_request->second);
   ongoing_navigation_resources_.erase(frame_id_and_request);
 }
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h
index 21e3bed5db..3edc01f 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h
@@ -81,11 +81,10 @@
   ObservePolicy FlushMetricsOnAppEnterBackground(
       const page_load_metrics::mojom::PageLoadTiming& timing,
       const page_load_metrics::PageLoadExtraInfo& extra_info) override;
-  void OnLoadedResource(const page_load_metrics::ExtraRequestCompleteInfo&
-                            extra_request_info) override;
   void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
                   const page_load_metrics::PageLoadExtraInfo& info) override;
   void OnResourceDataUseObserved(
+      FrameTreeNodeId frame_tree_node_id,
       const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
           resources) override;
   void OnPageInteractive(
@@ -98,6 +97,8 @@
                 AdTypes ad_types,
                 AdOriginStatus origin_status,
                 bool frame_navigated);
+
+    // Total prefilter (body) bytes loaded for complete resources.
     size_t frame_bytes;
     size_t frame_bytes_uncached;
     const FrameTreeNodeId frame_tree_node_id;
@@ -124,8 +125,9 @@
   // each call in order to free up memory.
   AdTypes DetectAds(content::NavigationHandle* navigation_handle);
 
-  void ProcessLoadedResource(
-      const page_load_metrics::ExtraRequestCompleteInfo& extra_request_info);
+  void ProcessResourceForFrame(
+      FrameTreeNodeId frame_tree_node_id,
+      const page_load_metrics::mojom::ResourceDataUpdatePtr& resource);
 
   // Get the mime type of a resource. This only returns a subset of mime types,
   // grouped at a higher level. For example, all video mime types return the
@@ -137,6 +139,7 @@
   // update. Updates |page_resources_| to reflect the new state of the resource.
   // Called once per ResourceDataUpdate.
   void UpdateResource(
+      FrameTreeNodeId frame_tree_node_id,
       const page_load_metrics::mojom::ResourceDataUpdatePtr& resource);
 
   // Records size of resources by mime type.
@@ -152,7 +155,7 @@
 
   // Checks to see if a resource is waiting for a navigation with the given
   // |frame_tree_node_id| to commit before it can be processed. If so, call
-  // OnLoadedResource for the delayed resource.
+  // OnResourceDataUpdate for the delayed resource.
   void ProcessOngoingNavigationResource(FrameTreeNodeId frame_tree_node_id);
 
   // Stores the size data of each ad frame. Pointed to by ad_frames_ so use a
@@ -174,7 +177,7 @@
   // When the observer receives report of a document resource loading for a
   // sub-frame before the sub-frame commit occurs, hold onto the resource
   // request info (delay it) until the sub-frame commits.
-  std::map<FrameTreeNodeId, page_load_metrics::ExtraRequestCompleteInfo>
+  std::map<FrameTreeNodeId, page_load_metrics::mojom::ResourceDataUpdatePtr>
       ongoing_navigation_resources_;
 
   // Maps a request_id for a blink resource to the metadata for the resource
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
index 9faf3a95..ce0bf3d4 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
@@ -87,6 +87,14 @@
   }
   ~AdsPageLoadMetricsObserverBrowserTest() override {}
 
+  std::unique_ptr<page_load_metrics::PageLoadMetricsTestWaiter>
+  CreatePageLoadMetricsTestWaiter() {
+    content::WebContents* web_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    return std::make_unique<page_load_metrics::PageLoadMetricsTestWaiter>(
+        web_contents);
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 
@@ -97,9 +105,12 @@
 IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                        OriginStatusMetricEmbedded) {
   base::HistogramTester histogram_tester;
+  auto waiter = CreatePageLoadMetricsTestWaiter();
   ui_test_utils::NavigateToURL(
       browser(),
       embedded_test_server()->GetURL("/ads_observer/srcdoc_embedded_ad.html"));
+  waiter->AddMinimumCompleteResourcesExpectation(3);
+  waiter->Wait();
   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
   histogram_tester.ExpectUniqueSample(
       kCrossOriginHistogramId,
@@ -121,9 +132,13 @@
 IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
                        OriginStatusMetricSame) {
   base::HistogramTester histogram_tester;
+  auto waiter = CreatePageLoadMetricsTestWaiter();
   ui_test_utils::NavigateToURL(
       browser(),
       embedded_test_server()->GetURL("/ads_observer/same_origin_ad.html"));
+  waiter->AddMinimumCompleteResourcesExpectation(3);
+  waiter->Wait();
+
   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
   histogram_tester.ExpectUniqueSample(
       kCrossOriginHistogramId,
@@ -135,6 +150,8 @@
                        OriginStatusMetricCross) {
   // Note: Cannot navigate cross-origin without dynamically generating the URL.
   base::HistogramTester histogram_tester;
+  auto waiter = CreatePageLoadMetricsTestWaiter();
+
   ui_test_utils::NavigateToURL(
       browser(), embedded_test_server()->GetURL("/iframe_blank.html"));
   // Note that the initial iframe is not an ad, so the metric doesn't observe
@@ -144,6 +161,11 @@
   NavigateIframeToURL(web_contents(), "test",
                       embedded_test_server()->GetURL(
                           "a.com", "/ads_observer/same_origin_ad.html"));
+
+  // Wait until all resource data updates are sent.
+  waiter->AddPageExpectation(
+      page_load_metrics::PageLoadMetricsTestWaiter::TimingField::kLoadEvent);
+  waiter->Wait();
   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
   histogram_tester.ExpectUniqueSample(
       kCrossOriginHistogramId,
@@ -168,6 +190,24 @@
   // Navigate away to force the histogram recording.
   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
 
+  // TODO(johnidel): Check that the subresources of the new frame are reported
+  // correctly. Resources from a failed provisional load are not reported to
+  // resource data updates, causing this adframe to not be recorded. This is an
+  // uncommon case but should be reported. See crbug.com/914893.
+}
+
+// Test that a blank ad subframe that is docwritten correctly reports metrics.
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
+                       DocWriteAboutBlankAdframe) {
+  base::HistogramTester histogram_tester;
+  auto waiter = CreatePageLoadMetricsTestWaiter();
+  ui_test_utils::NavigateToURL(browser(),
+                               embedded_test_server()->GetURL(
+                                   "/ads_observer/docwrite_blank_frame.html"));
+  waiter->AddMinimumCompleteResourcesExpectation(4);
+  waiter->Wait();
+  // Navigate away to force the histogram recording.
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
   histogram_tester.ExpectUniqueSample(
       "PageLoad.Clients.Ads.Google.FrameCounts.AnyParentFrame.AdFrames", 1, 1);
   histogram_tester.ExpectUniqueSample(
@@ -188,7 +228,13 @@
   const GURL main_url(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b,b,c,d)"));
 
+  auto waiter = CreatePageLoadMetricsTestWaiter();
   ui_test_utils::NavigateToURL(browser(), main_url);
+
+  // One favicon resource and 2 resources for each frame.
+  waiter->AddMinimumCompleteResourcesExpectation(11);
+  waiter->Wait();
+
   // Navigate away to force the histogram recording.
   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
 
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc
index a177291f..20f0704f 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc
@@ -56,7 +56,7 @@
 };
 
 enum class AdType { GOOGLE = 0, SUBRESOURCE_FILTER = 1, ALL = 2 };
-enum class ResourceCached { NOT_CACHED, CACHED };
+enum class ResourceCached { NOT_CACHED = false, CACHED = true };
 enum class FrameType { AD = 0, NON_AD };
 
 const char kAdUrl[] = "https://tpc.googlesyndication.com/safeframe/1";
@@ -97,15 +97,23 @@
     DCHECK(observer);
 
     // Load a resource for the main frame before it commits.
-    observer->OnRequestComplete(
-        GURL(kNonAdUrl), net::HostPortPair(),
-        navigation_handle()->GetRenderFrameHost()->GetFrameTreeNodeId(),
-        navigation_handle()->GetGlobalRequestID(),
-        navigation_handle()->GetRenderFrameHost(),
-        content::RESOURCE_TYPE_MAIN_FRAME, false /* was_cached */,
-        nullptr /* data_reduction_proxy */, 10 * 1024 /* raw_body_bytes */,
-        0 /* original_network_content_length */, base::TimeTicks::Now(), 0,
-        nullptr /* load_timing_info */);
+    std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr> resources;
+    page_load_metrics::mojom::ResourceDataUpdatePtr resource =
+        page_load_metrics::mojom::ResourceDataUpdate::New();
+    resource->received_data_length = 10 * 1024;
+    resource->delta_bytes = 10 * 1024;
+    resource->encoded_body_length = 10 * 1024;
+    resource->was_fetched_via_cache = false;
+    resource->is_complete = true;
+    resource->is_primary_frame_resource = true;
+    resources.push_back(std::move(resource));
+    auto timing = page_load_metrics::mojom::PageLoadTimingPtr(base::in_place);
+    page_load_metrics::InitPageLoadTimingForTest(timing.get());
+    observer->OnTimingUpdated(
+        navigation_handle()->GetRenderFrameHost(), std::move(timing),
+        page_load_metrics::mojom::PageLoadMetadataPtr(base::in_place),
+        page_load_metrics::mojom::PageLoadFeaturesPtr(base::in_place),
+        resources, page_load_metrics::mojom::PageRenderDataPtr(base::in_place));
   }
 
   DISALLOW_COPY_AND_ASSIGN(ResourceLoadingCancellingThrottle);
@@ -263,31 +271,25 @@
     return navigation_simulator->GetFinalRenderFrameHost();
   }
 
-  void LoadResource(RenderFrameHost* frame,
-                    ResourceCached resource_cached,
-                    int resource_size_in_kb) {
-    page_load_metrics::ExtraRequestCompleteInfo request(
-        GURL(kNonAdUrl), net::HostPortPair(), frame->GetFrameTreeNodeId(),
-        resource_cached == ResourceCached::CACHED, resource_size_in_kb * 1024,
-        0,       /* original_network_content_length */
-        nullptr, /* data_reduction_proxy_data */
-        content::RESOURCE_TYPE_SUB_FRAME, 0, nullptr /* load_timing_info */);
-    tester_->SimulateLoadedResource(request);
-  }
-
-  void ResourceDataUpdate(int resource_size_in_kbyte,
-                          std::string mime_type,
-                          bool is_ad_resource) {
+  void ResourceDataUpdate(RenderFrameHost* render_frame_host,
+                          ResourceCached resource_cached,
+                          int resource_size_in_kbyte,
+                          std::string mime_type = "",
+                          bool is_ad_resource = false) {
     std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr> resources;
     page_load_metrics::mojom::ResourceDataUpdatePtr resource =
         page_load_metrics::mojom::ResourceDataUpdate::New();
-    resource->received_data_length = resource_size_in_kbyte;
-    resource->delta_bytes = resource_size_in_kbyte;
+    resource->received_data_length =
+        static_cast<bool>(resource_cached) ? 0 : resource_size_in_kbyte << 10;
+    resource->delta_bytes = resource->received_data_length;
+    resource->encoded_body_length = resource_size_in_kbyte << 10;
     resource->reported_as_ad_resource = is_ad_resource;
     resource->is_complete = true;
+    resource->was_fetched_via_cache = static_cast<bool>(resource_cached);
     resource->mime_type = mime_type;
+    resource->is_primary_frame_resource = true;
     resources.push_back(std::move(resource));
-    tester_->SimulateResourceDataUseUpdate(resources);
+    tester_->SimulateResourceDataUseUpdate(resources, render_frame_host);
   }
 
   void TimingUpdate(const page_load_metrics::mojom::PageLoadTiming& timing) {
@@ -317,9 +319,9 @@
       CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
   RenderFrameHost* frame2 =
       CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
-  LoadResource(main_frame, ResourceCached::NOT_CACHED, 10);
-  LoadResource(frame1, ResourceCached::NOT_CACHED, 10);
-  LoadResource(frame2, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(frame1, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(frame2, ResourceCached::NOT_CACHED, 10);
 
   // Navigate again to trigger histograms.
   NavigateFrame(kNonAdUrl, main_frame);
@@ -336,20 +338,15 @@
 TEST_F(AdsPageLoadMetricsObserverTest, ResourceBeforeAdFrameCommits) {
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
 
-  LoadResource(main_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
 
-  // Assume that the next frame's id will be the main frame + 1 and load a
-  // resource for that frame. Make sure it gets counted.
-  page_load_metrics::ExtraRequestCompleteInfo request(
-      GURL(kNonAdUrl), net::HostPortPair(),
-      main_frame->GetFrameTreeNodeId() + 1, false /* cached */,
-      10 * 1024 /* size */, 0 /* original_network_content_length */,
-      nullptr
-      /* data_reduction_proxy_data */,
-      content::RESOURCE_TYPE_SUB_FRAME, 0, nullptr /* load_timing_info */);
-  tester()->SimulateLoadedResource(request);
-
-  CreateAndNavigateSubFrame(kNonAdUrl, kAdName, main_frame);
+  // Create subframe and load resource before commit.
+  RenderFrameHost* subframe =
+      RenderFrameHostTester::For(main_frame)->AppendChild(kAdName);
+  auto navigation_simulator =
+      NavigationSimulator::CreateRendererInitiated(GURL(kNonAdUrl), subframe);
+  ResourceDataUpdate(subframe, ResourceCached::NOT_CACHED, 10);
+  navigation_simulator->Commit();
 
   // Navigate again to trigger histograms.
   NavigateFrame(kNonAdUrl, main_frame);
@@ -392,14 +389,14 @@
 
   // 70KB total in page, 50 from ads, 40 from network, and 30 of those
   // are from ads.
-  LoadResource(main_frame, ResourceCached::NOT_CACHED, 10);
-  LoadResource(non_ad_frame, ResourceCached::CACHED, 10);
-  LoadResource(non_ad_frame2, ResourceCached::CACHED, 10);
-  LoadResource(google_frame1, ResourceCached::CACHED, 10);
-  LoadResource(google_frame2, ResourceCached::NOT_CACHED, 10);
-  LoadResource(srf_frame1, ResourceCached::NOT_CACHED, 10);
-  LoadResource(srf_frame2, ResourceCached::NOT_CACHED, 10);
-  LoadResource(nested_srf_frame3, ResourceCached::CACHED, 10);
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(non_ad_frame, ResourceCached::CACHED, 10);
+  ResourceDataUpdate(non_ad_frame2, ResourceCached::CACHED, 10);
+  ResourceDataUpdate(google_frame1, ResourceCached::CACHED, 10);
+  ResourceDataUpdate(google_frame2, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(srf_frame1, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(srf_frame2, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(nested_srf_frame3, ResourceCached::CACHED, 10);
 
   // Navigate again to trigger histograms.
   NavigateFrame(kNonAdUrl, main_frame);
@@ -430,10 +427,11 @@
     RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
     RenderFrameHost* ad_sub_frame =
         CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame);
-    LoadResource(main_frame, ResourceCached::NOT_CACHED, 10);
-    LoadResource(ad_sub_frame, ResourceCached::NOT_CACHED, 10);
-    LoadResource(CreateAndNavigateSubFrame(kAdUrl, kNonAdName, ad_sub_frame),
-                 ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(ad_sub_frame, ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(
+        CreateAndNavigateSubFrame(kAdUrl, kNonAdName, ad_sub_frame),
+        ResourceCached::NOT_CACHED, 10);
     // Trigger histograms by navigating away, then test them.
     NavigateFrame(kAdUrl, main_frame);
     histograms.ExpectUniqueSample(
@@ -446,11 +444,13 @@
   {
     base::HistogramTester histograms;
     RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-    LoadResource(main_frame, ResourceCached::NOT_CACHED, 10);
-    LoadResource(CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame),
-                 ResourceCached::NOT_CACHED, 10);
-    LoadResource(CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame),
-                 ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(
+        CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame),
+        ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(
+        CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame),
+        ResourceCached::NOT_CACHED, 10);
     // Trigger histograms by navigating away, then test them.
     NavigateFrame(kAdUrl, main_frame);
     histograms.ExpectUniqueSample(
@@ -464,9 +464,10 @@
   {
     base::HistogramTester histograms;
     RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrlSameOrigin);
-    LoadResource(main_frame, ResourceCached::NOT_CACHED, 10);
-    LoadResource(CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame),
-                 ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(
+        CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame),
+        ResourceCached::NOT_CACHED, 10);
     // Trigger histograms by navigating away, then test them.
     NavigateFrame(kAdUrl, main_frame);
     histograms.ExpectUniqueSample(
@@ -480,14 +481,14 @@
   RenderFrameHost* ad_frame =
       CreateAndNavigateSubFrame(kNonAdUrl, kAdName, main_frame);
 
-  LoadResource(main_frame, ResourceCached::NOT_CACHED, 10);
-  LoadResource(ad_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(ad_frame, ResourceCached::NOT_CACHED, 10);
 
   // Navigate the ad frame again.
   ad_frame = NavigateFrame(kNonAdUrl, ad_frame);
 
   // In total, 30KB for entire page and 20 in one ad frame.
-  LoadResource(ad_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(ad_frame, ResourceCached::NOT_CACHED, 10);
 
   // Navigate again to trigger histograms.
   NavigateFrame(kNonAdUrl, main_frame);
@@ -508,13 +509,13 @@
   RenderFrameHost* sub_frame_child_ad =
       CreateAndNavigateSubFrame(kNonAdUrl2, kAdName, sub_frame);
 
-  LoadResource(main_frame, ResourceCached::NOT_CACHED, 10);
-  LoadResource(sub_frame, ResourceCached::NOT_CACHED, 10);
-  LoadResource(sub_frame_child_ad, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(sub_frame_child_ad, ResourceCached::NOT_CACHED, 10);
 
   // Navigate the subframe again, this time it's an ad.
   sub_frame = NavigateFrame(kAdUrl, sub_frame);
-  LoadResource(sub_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
 
   // In total, 40KB was loaded for the entire page and 20KB from ad
   // frames (the original child ad frame and the renavigated frame which
@@ -531,7 +532,7 @@
 TEST_F(AdsPageLoadMetricsObserverTest, CountAbortedNavigation) {
   // If the first navigation in a frame is aborted, keep track of its bytes.
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  LoadResource(main_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
 
   // Create an ad subframe that aborts before committing.
   RenderFrameHost* subframe_ad =
@@ -545,8 +546,8 @@
   // Load resources for the aborted frame (e.g., simulate the navigation
   // aborting due to a doc.write during provisional navigation). They should
   // be counted.
-  LoadResource(subframe_ad, ResourceCached::NOT_CACHED, 10);
-  LoadResource(subframe_ad, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(subframe_ad, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(subframe_ad, ResourceCached::NOT_CACHED, 10);
 
   // Navigate again to trigger histograms.
   NavigateFrame(kNonAdUrl, main_frame);
@@ -557,12 +558,12 @@
 
 TEST_F(AdsPageLoadMetricsObserverTest, CountAbortedSecondNavigationForFrame) {
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  LoadResource(main_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
 
   // Sub frame that is not an ad.
   RenderFrameHost* sub_frame =
       CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
-  LoadResource(sub_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
 
   // Now navigate (and abort) the subframe to an ad.
   auto navigation_simulator =
@@ -574,8 +575,8 @@
   // Load resources for the aborted frame (e.g., simulate the navigation
   // aborting due to a doc.write during provisional navigation). Since the
   // frame attempted to load an ad, the frame is tagged forever as an ad.
-  LoadResource(sub_frame, ResourceCached::NOT_CACHED, 10);
-  LoadResource(sub_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
 
   // Navigate again to trigger histograms.
   NavigateFrame(kNonAdUrl, main_frame);
@@ -587,21 +588,15 @@
 TEST_F(AdsPageLoadMetricsObserverTest, TwoResourceLoadsBeforeCommit) {
   // Main frame.
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  LoadResource(main_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
 
   // Now open a subframe and have its resource load before notification of
   // navigation finishing.
-  page_load_metrics::ExtraRequestCompleteInfo request(
-      GURL(kNonAdUrl), net::HostPortPair(),
-      main_frame->GetFrameTreeNodeId() + 1, false /* cached */,
-      10 * 1024 /* size */, false /* data_reduction_proxy_used */,
-      0 /* original_network_content_length */, content::RESOURCE_TYPE_SUB_FRAME,
-      0, nullptr /* load_timing_info */);
-  tester()->SimulateLoadedResource(request);
   RenderFrameHost* subframe_ad =
       RenderFrameHostTester::For(main_frame)->AppendChild(kAdName);
   auto navigation_simulator = NavigationSimulator::CreateRendererInitiated(
       GURL(kNonAdUrl), subframe_ad);
+  ResourceDataUpdate(subframe_ad, ResourceCached::NOT_CACHED, 10);
 
   // The sub-frame renavigates before it commits.
   navigation_simulator->Start();
@@ -609,7 +604,7 @@
 
   // Renavigate the subframe to a successful commit. But again, the resource
   // loads before the observer sees the finished navigation.
-  tester()->SimulateLoadedResource(request);
+  ResourceDataUpdate(subframe_ad, ResourceCached::NOT_CACHED, 10);
   NavigateFrame(kNonAdUrl, subframe_ad);
 
   // Navigate again to trigger histograms.
@@ -644,7 +639,7 @@
 
   // Test that a resource loaded into an unknown frame doesn't cause any
   // issues.
-  LoadResource(child_of_subframe, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(child_of_subframe, ResourceCached::NOT_CACHED, 10);
 }
 
 TEST_F(AdsPageLoadMetricsObserverTest, MainFrameResource) {
@@ -652,19 +647,10 @@
   auto navigation_simulator = NavigationSimulator::CreateRendererInitiated(
       GURL(kNonAdUrl), web_contents()->GetMainFrame());
   navigation_simulator->Start();
-  int frame_tree_node_id =
-      navigation_simulator->GetNavigationHandle()->GetFrameTreeNodeId();
   navigation_simulator->Commit();
 
-  page_load_metrics::ExtraRequestCompleteInfo request(
-      GURL(kNonAdUrl), net::HostPortPair(), frame_tree_node_id,
-      false /* was_cached */, 10 * 1024 /* raw_body_bytes */,
-      0 /* original_network_content_length */,
-      nullptr /* data_reduction_proxy_data */,
-      content::RESOURCE_TYPE_MAIN_FRAME, 0, nullptr /* load_timing_info */);
-
-  tester()->SimulateLoadedResource(request,
-                                   navigation_simulator->GetGlobalRequestID());
+  ResourceDataUpdate(navigation_simulator->GetFinalRenderFrameHost(),
+                     ResourceCached::NOT_CACHED, 10);
 
   NavigateMainFrame(kNonAdUrl);
 
@@ -710,14 +696,14 @@
   ConfigureAsSubresourceFilterOnlyURL(GURL(kNonAdUrl));
   NavigateMainFrame(kNonAdUrl);
 
-  LoadResource(main_rfh(), ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(main_rfh(), ResourceCached::NOT_CACHED, 10);
 
   RenderFrameHost* subframe =
       RenderFrameHostTester::For(main_rfh())->AppendChild(kNonAdName);
   std::unique_ptr<NavigationSimulator> simulator =
       NavigationSimulator::CreateRendererInitiated(GURL(kDefaultDisallowedUrl),
                                                    subframe);
-  LoadResource(subframe, ResourceCached::CACHED, 10);
+  ResourceDataUpdate(subframe, ResourceCached::CACHED, 10);
   simulator->Commit();
 
   EXPECT_NE(content::NavigationThrottle::PROCEED,
@@ -741,13 +727,14 @@
   timing.interactive_timing->interactive = base::TimeDelta::FromSeconds(0);
   PopulateRequiredTimingFields(&timing);
   TimingUpdate(timing);
-  ResourceDataUpdate(10 << 10 /* resource_size_in_kbyte */,
-                     "application/javascript" /* mime_type */,
-                     false /* is_ad_resource */);
-  ResourceDataUpdate(10 << 10 /* resource_size_in_kbyte */,
-                     "application/javascript" /* mime_type */,
-                     true /* is_ad_resource */);
-  ResourceDataUpdate(10 << 10 /* resource_size_in_kbyte */,
+  ResourceDataUpdate(
+      main_rfh(), ResourceCached::NOT_CACHED, 10 /* resource_size_in_kbyte */,
+      "application/javascript" /* mime_type */, false /* is_ad_resource */);
+  ResourceDataUpdate(
+      main_rfh(), ResourceCached::NOT_CACHED, 10 /* resource_size_in_kbyte */,
+      "application/javascript" /* mime_type */, true /* is_ad_resource */);
+  ResourceDataUpdate(main_rfh(), ResourceCached::NOT_CACHED,
+                     10 /* resource_size_in_kbyte */,
                      "video/webm" /* mime_type */, true /* is_ad_resource */);
   NavigateMainFrame(kNonAdUrl);
 
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
index 990c1f9..04458af2 100644
--- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
@@ -716,6 +716,7 @@
 }
 
 void CorePageLoadMetricsObserver::OnResourceDataUseObserved(
+    FrameTreeNodeId frame_tree_node_id,
     const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
         resources) {
   for (auto const& resource : resources) {
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h
index bc5a0b6..de700e6 100644
--- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h
@@ -214,6 +214,7 @@
       const page_load_metrics::mojom::PageLoadTiming& timing,
       const page_load_metrics::PageLoadExtraInfo& extra_info) override;
   void OnResourceDataUseObserved(
+      FrameTreeNodeId frame_tree_node_id,
       const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
           resources) override;
 
diff --git a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.cc b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.cc
index 91e8ee0b..12656c2 100644
--- a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.cc
+++ b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.cc
@@ -354,6 +354,7 @@
 }
 
 void DataReductionProxyMetricsObserverBase::OnResourceDataUseObserved(
+    FrameTreeNodeId frame_tree_node_id,
     const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
         resources) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.h b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.h
index 367e2f6..4f91243 100644
--- a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.h
+++ b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base.h
@@ -52,6 +52,7 @@
   void OnLoadedResource(const page_load_metrics::ExtraRequestCompleteInfo&
                             extra_request_compelte_info) override;
   void OnResourceDataUseObserved(
+      FrameTreeNodeId frame_tree_node_id,
       const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
           resources) override;
   void OnEventOccurred(const void* const event_key) override;
diff --git a/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.cc
index a1cd7663..230f86c2 100644
--- a/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.cc
@@ -42,6 +42,7 @@
 }
 
 void DataSaverSiteBreakdownMetricsObserver::OnResourceDataUseObserved(
+    FrameTreeNodeId frame_tree_node_id,
     const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
         resources) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.h b/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.h
index 1b8ee51..aff170b5 100644
--- a/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.h
@@ -31,6 +31,7 @@
                          ukm::SourceId source_id) override;
 
   void OnResourceDataUseObserved(
+      FrameTreeNodeId frame_tree_node_id,
       const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
           resources) override;
 
diff --git a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.cc
index f2d519f3..8e7a5ac 100644
--- a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.cc
@@ -24,6 +24,7 @@
 MediaPageLoadMetricsObserver::~MediaPageLoadMetricsObserver() = default;
 
 void MediaPageLoadMetricsObserver::OnResourceDataUseObserved(
+    FrameTreeNodeId frame_tree_node_id,
     const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
         resources) {
   for (auto const& resource : resources) {
diff --git a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h
index 9f6b4d5..3de0f68 100644
--- a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer.h
@@ -27,6 +27,7 @@
       const page_load_metrics::mojom::PageLoadTiming& timing,
       const page_load_metrics::PageLoadExtraInfo& info) override;
   void OnResourceDataUseObserved(
+      FrameTreeNodeId frame_tree_node_id,
       const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
           resources) override;
   void MediaStartedPlaying(
diff --git a/chrome/browser/page_load_metrics/observers/page_capping_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/page_capping_page_load_metrics_observer.cc
index e387d86..89bdf5f 100644
--- a/chrome/browser/page_load_metrics/observers/page_capping_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/page_capping_page_load_metrics_observer.cc
@@ -111,6 +111,7 @@
 }
 
 void PageCappingPageLoadMetricsObserver::OnResourceDataUseObserved(
+    FrameTreeNodeId frame_tree_node_id,
     const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
         resources) {
   last_data_use_time_ = clock_->NowTicks();
diff --git a/chrome/browser/page_load_metrics/observers/page_capping_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/page_capping_page_load_metrics_observer.h
index 3716f6b2..f94a4c6 100644
--- a/chrome/browser/page_load_metrics/observers/page_capping_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/page_capping_page_load_metrics_observer.h
@@ -75,6 +75,7 @@
  private:
   // page_load_metrics::PageLoadMetricsObserver:
   void OnResourceDataUseObserved(
+      FrameTreeNodeId frame_tree_node_id,
       const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
           resources) override;
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
diff --git a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc
index ead46b0e..78d8d7d5 100644
--- a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc
+++ b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc
@@ -70,6 +70,12 @@
   tester_->SimulateResourceDataUseUpdate(resources);
 }
 
+void PageLoadMetricsObserverTestHarness::SimulateResourceDataUseUpdate(
+    const std::vector<mojom::ResourceDataUpdatePtr>& resources,
+    content::RenderFrameHost* render_frame_host) {
+  tester_->SimulateResourceDataUseUpdate(resources, render_frame_host);
+}
+
 void PageLoadMetricsObserverTestHarness::SimulateFeaturesUpdate(
     const mojom::PageLoadFeatures& new_features) {
   tester_->SimulateFeaturesUpdate(new_features);
diff --git a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h
index e20eca9..d76b19e 100644
--- a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h
+++ b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h
@@ -83,6 +83,9 @@
   void SimulateFeaturesUpdate(const mojom::PageLoadFeatures& new_features);
   void SimulateResourceDataUseUpdate(
       const std::vector<mojom::ResourceDataUpdatePtr>& resources);
+  void SimulateResourceDataUseUpdate(
+      const std::vector<mojom::ResourceDataUpdatePtr>& resources,
+      content::RenderFrameHost* render_frame_host);
   void SimulateRenderDataUpdate(const mojom::PageRenderData& render_data);
 
   // Simulates a loaded resource. Main frame resources must specify a
diff --git a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc
index 171db00..d8bba72d 100644
--- a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc
+++ b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc
@@ -119,11 +119,19 @@
 
 void PageLoadMetricsObserverTester::SimulateResourceDataUseUpdate(
     const std::vector<mojom::ResourceDataUpdatePtr>& resources) {
-  observer_->OnTimingUpdated(
-      web_contents()->GetMainFrame(), mojom::PageLoadTimingPtr(base::in_place),
-      mojom::PageLoadMetadataPtr(base::in_place),
-      mojom::PageLoadFeaturesPtr(base::in_place), resources,
-      mojom::PageRenderDataPtr(base::in_place));
+  SimulateResourceDataUseUpdate(resources, web_contents()->GetMainFrame());
+}
+
+void PageLoadMetricsObserverTester::SimulateResourceDataUseUpdate(
+    const std::vector<mojom::ResourceDataUpdatePtr>& resources,
+    content::RenderFrameHost* render_frame_host) {
+  auto timing = mojom::PageLoadTimingPtr(base::in_place);
+  InitPageLoadTimingForTest(timing.get());
+  observer_->OnTimingUpdated(render_frame_host, std::move(timing),
+                             mojom::PageLoadMetadataPtr(base::in_place),
+                             mojom::PageLoadFeaturesPtr(base::in_place),
+                             resources,
+                             mojom::PageRenderDataPtr(base::in_place));
 }
 
 void PageLoadMetricsObserverTester::SimulateLoadedResource(
diff --git a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.h b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.h
index 9f3fc48d..a0a35b4 100644
--- a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.h
+++ b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.h
@@ -54,6 +54,9 @@
   void SimulateFeaturesUpdate(const mojom::PageLoadFeatures& new_features);
   void SimulateResourceDataUseUpdate(
       const std::vector<mojom::ResourceDataUpdatePtr>& resources);
+  void SimulateResourceDataUseUpdate(
+      const std::vector<mojom::ResourceDataUpdatePtr>& resources,
+      content::RenderFrameHost* render_frame_host);
   void SimulateRenderDataUpdate(const mojom::PageRenderData& render_data);
 
   // Simulates a loaded resource. Main frame resources must specify a
diff --git a/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.cc
index 721c57f..2f92419 100644
--- a/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.cc
@@ -220,6 +220,7 @@
 }
 
 void PreviewsPageLoadMetricsObserver::OnResourceDataUseObserved(
+    FrameTreeNodeId frame_tree_node_id,
     const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
         resources) {
   for (auto const& resource : resources) {
diff --git a/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.h
index 02470b7..0d0b7e9 100644
--- a/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer.h
@@ -40,6 +40,7 @@
       const page_load_metrics::mojom::PageLoadTiming& timing,
       const page_load_metrics::PageLoadExtraInfo& info) override;
   void OnResourceDataUseObserved(
+      FrameTreeNodeId frame_tree_node_id,
       const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
           resources) override;
 
diff --git a/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.cc
index 9d32311c..a9866b4 100644
--- a/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.cc
@@ -40,6 +40,7 @@
 }
 
 void TabRestorePageLoadMetricsObserver::OnResourceDataUseObserved(
+    FrameTreeNodeId frame_tree_node_id,
     const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
         resources) {
   for (auto const& resource : resources) {
diff --git a/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.h
index 90e51867..843fa5a 100644
--- a/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.h
@@ -30,6 +30,7 @@
       const GURL& currently_committed_url,
       bool started_in_foreground) override;
   void OnResourceDataUseObserved(
+      FrameTreeNodeId frame_tree_node_id,
       const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
           resources) override;
   page_load_metrics::PageLoadMetricsObserver::ObservePolicy
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_observer.h b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
index 71c1cdb..ddc0bfd 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
@@ -432,9 +432,10 @@
                                        const PageLoadExtraInfo& extra_info) {}
 
   // Invoked when there is data use for loading a resource on the page
-  // across all frames. This only contains resources that have had new
-  // data use since the last callback.
+  // for a given render frame host. This only contains resources that have had
+  // new data use since the last callback.
   virtual void OnResourceDataUseObserved(
+      FrameTreeNodeId frame_tree_node_id,
       const std::vector<mojom::ResourceDataUpdatePtr>& resources) {}
 
   // Invoked when a media element starts playing.
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.cc b/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.cc
index ba81035..ee0ebc2 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.cc
@@ -114,6 +114,7 @@
 }
 
 void PageLoadMetricsTestWaiter::OnResourceDataUseObserved(
+    FrameTreeNodeId frame_tree_node_id,
     const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
         resources) {
   for (auto const& resource : resources) {
@@ -257,10 +258,11 @@
 
 void PageLoadMetricsTestWaiter::WaiterMetricsObserver::
     OnResourceDataUseObserved(
+        FrameTreeNodeId frame_tree_node_id,
         const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
             resources) {
   if (waiter_)
-    waiter_->OnResourceDataUseObserved(resources);
+    waiter_->OnResourceDataUseObserved(frame_tree_node_id, resources);
 }
 
 void PageLoadMetricsTestWaiter::WaiterMetricsObserver::OnFeaturesUsageObserved(
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h b/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h
index 972c20e..a41de23 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h
@@ -29,6 +29,8 @@
     // kLoadTimingInfo waits for main frame timing info only.
     kLoadTimingInfo = 1 << 6,
   };
+  using FrameTreeNodeId =
+      page_load_metrics::PageLoadMetricsObserver::FrameTreeNodeId;
 
   explicit PageLoadMetricsTestWaiter(content::WebContents* web_contents);
 
@@ -77,6 +79,8 @@
   class WaiterMetricsObserver
       : public page_load_metrics::PageLoadMetricsObserver {
    public:
+    using FrameTreeNodeId =
+        page_load_metrics::PageLoadMetricsObserver::FrameTreeNodeId;
     // We use a WeakPtr to the PageLoadMetricsTestWaiter because |waiter| can be
     // destroyed before this WaiterMetricsObserver.
     explicit WaiterMetricsObserver(
@@ -92,6 +96,7 @@
                               extra_request_complete_info) override;
 
     void OnResourceDataUseObserved(
+        FrameTreeNodeId frame_tree_node_id,
         const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
             resources) override;
 
@@ -158,6 +163,7 @@
   // from a resource load. Stops waiting if expectations are satisfied after
   // update.
   void OnResourceDataUseObserved(
+      FrameTreeNodeId frame_tree_node_id,
       const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
           resources);
 
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc
index 0ab6b1f..f357ae0 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc
@@ -444,7 +444,8 @@
 
   // Report data usage before new timing and metadata for messages that have
   // both updates.
-  client_->UpdateResourceDataUse(resources);
+  client_->UpdateResourceDataUse(render_frame_host->GetFrameTreeNodeId(),
+                                 resources);
   if (render_frame_host->GetParent() == nullptr) {
     UpdateMainFrameMetadata(std::move(new_metadata));
     UpdateMainFrameTiming(std::move(new_timing));
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h
index 656218d..1aaf3f59b 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h
@@ -112,6 +112,7 @@
         content::RenderFrameHost* rfh,
         const mojom::PageLoadFeatures& new_features) = 0;
     virtual void UpdateResourceDataUse(
+        int frame_tree_node_id,
         const std::vector<mojom::ResourceDataUpdatePtr>& resources) = 0;
   };
 
diff --git a/chrome/browser/page_load_metrics/page_load_tracker.cc b/chrome/browser/page_load_metrics/page_load_tracker.cc
index d6ce577..a24ba73 100644
--- a/chrome/browser/page_load_metrics/page_load_tracker.cc
+++ b/chrome/browser/page_load_metrics/page_load_tracker.cc
@@ -651,9 +651,10 @@
 }
 
 void PageLoadTracker::UpdateResourceDataUse(
+    int frame_tree_node_id,
     const std::vector<mojom::ResourceDataUpdatePtr>& resources) {
   for (const auto& observer : observers_) {
-    observer->OnResourceDataUseObserved(resources);
+    observer->OnResourceDataUseObserved(frame_tree_node_id, resources);
   }
 }
 
diff --git a/chrome/browser/page_load_metrics/page_load_tracker.h b/chrome/browser/page_load_metrics/page_load_tracker.h
index 6c57e4f2..45a47ea4 100644
--- a/chrome/browser/page_load_metrics/page_load_tracker.h
+++ b/chrome/browser/page_load_metrics/page_load_tracker.h
@@ -182,6 +182,7 @@
       content::RenderFrameHost* rfh,
       const mojom::PageLoadFeatures& new_features) override;
   void UpdateResourceDataUse(
+      int frame_tree_node_id,
       const std::vector<mojom::ResourceDataUpdatePtr>& resources) override;
 
   void Redirect(content::NavigationHandle* navigation_handle);
diff --git a/chrome/browser/previews/previews_lite_page_browsertest.cc b/chrome/browser/previews/previews_lite_page_browsertest.cc
index 4a2a165..f4950c17 100644
--- a/chrome/browser/previews/previews_lite_page_browsertest.cc
+++ b/chrome/browser/previews/previews_lite_page_browsertest.cc
@@ -1170,6 +1170,39 @@
   VerifyPreviewLoaded();
 }
 
+IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerBrowserTest,
+                       DISABLE_ON_WIN_MAC(LitePagePreviewsReferrer)) {
+  // Referrers should be copied across navigations unless the referrer is the
+  // lite page domain, in which case the referrer should not be set.
+  {
+    browser()->OpenURL(content::OpenURLParams(
+        HttpsLitePageURL(kSuccess),
+        content::Referrer(GURL("https://www.google.com"),
+                          network::mojom::ReferrerPolicy::kDefault),
+        WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
+        false /* is_renderer_initiated */));
+    VerifyPreviewLoaded();
+
+    content::NavigationEntry* entry =
+        GetWebContents()->GetController().GetLastCommittedEntry();
+    EXPECT_EQ(entry->GetReferrer().url, GURL("https://www.google.com"));
+  }
+
+  {
+    browser()->OpenURL(content::OpenURLParams(
+        HttpsLitePageURL(kSuccess),
+        content::Referrer(previews_server(),
+                          network::mojom::ReferrerPolicy::kDefault),
+        WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
+        false /* is_renderer_initiated */));
+    VerifyPreviewLoaded();
+
+    content::NavigationEntry* entry =
+        GetWebContents()->GetController().GetLastCommittedEntry();
+    EXPECT_EQ(entry->GetReferrer().url, content::Referrer().url);
+  }
+}
+
 class PreviewsLitePageServerTimeoutBrowserTest
     : public PreviewsLitePageServerBrowserTest {
  public:
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
index 11044fc..02be218 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
@@ -89,6 +89,10 @@
   url_params.frame_tree_node_id = handle->GetFrameTreeNodeId();
   url_params.user_gesture = handle->HasUserGesture();
   url_params.started_from_context_menu = handle->WasStartedFromContextMenu();
+
+  if (previews::IsLitePageRedirectPreviewDomain(handle->GetReferrer().url))
+    url_params.referrer = content::Referrer();
+
   return url_params;
 }
 
diff --git a/chrome/browser/resources/chromeos/switch_access/navigation_manager.js b/chrome/browser/resources/chromeos/switch_access/navigation_manager.js
index a13ba09d..183d8a6 100644
--- a/chrome/browser/resources/chromeos/switch_access/navigation_manager.js
+++ b/chrome/browser/resources/chromeos/switch_access/navigation_manager.js
@@ -266,7 +266,7 @@
     this.node_ = event.target;
 
     // In case the node that gained focus is not a subtreeLeaf.
-    if (SwitchAccessPredicate.isSubtreeLeaf(this.node_, this.scope_))
+    if (SwitchAccessPredicate.isInteresting(this.node_, this.scope_))
       this.updateFocusRing_();
     else
       this.moveForward();
diff --git a/chrome/browser/resources/chromeos/switch_access/switch_access_predicate.js b/chrome/browser/resources/chromeos/switch_access/switch_access_predicate.js
index 78462fe..3144135 100644
--- a/chrome/browser/resources/chromeos/switch_access/switch_access_predicate.js
+++ b/chrome/browser/resources/chromeos/switch_access/switch_access_predicate.js
@@ -7,150 +7,22 @@
 const DefaultActionVerb = chrome.automation.DefaultActionVerb;
 
 /**
- * Contains predicates for the chrome automation API. Each predicate can be run
- * on one or more AutomationNodes and returns a boolean value.
+ * Contains predicates for the chrome automation API. The following basic
+ * predicates are available:
+ *    - isActionable
+ *    - isGroup
+ *    - isInteresting
+ *    - isInterestingSubtree
+ *    - isNotContainer
+ *    - isContextMenu
+ *
+ * In addition to these basic predicates, there are also methods to get the
+ * restrictions required by TreeWalker for specific traversal situations.
  */
 const SwitchAccessPredicate = {
   /**
-   * Returns a Restrictions object ready to be passed to AutomationTreeWalker.
-   *
-   * @param {!chrome.automation.AutomationNode} scope
-   * @return {!AutomationTreeWalkerRestriction}
-   */
-  restrictions: (scope) => {
-    return {
-      leaf: SwitchAccessPredicate.leaf(scope),
-      root: SwitchAccessPredicate.root(scope),
-      visit: SwitchAccessPredicate.visit(scope)
-    };
-  },
-
-  /**
-   * Creates a function that confirms if |node| is a terminal leaf node of a
-   * SwitchAccess scope tree when |scope| is the root.
-   *
-   * @param {!chrome.automation.AutomationNode} scope
-   * @return {function(!chrome.automation.AutomationNode): boolean}
-   */
-  leaf: function(scope) {
-    return (node) => node.state[StateType.INVISIBLE] ||
-        (node !== scope && SwitchAccessPredicate.isSubtreeLeaf(node, scope)) ||
-        !SwitchAccessPredicate.isInterestingSubtree(node);
-  },
-
-  /**
-   * Creates a function that confirms if |node| is the root of a SwitchAccess
-   * scope tree when |scope| is the root.
-   *
-   * @param {!chrome.automation.AutomationNode} scope
-   * @return {function(!chrome.automation.AutomationNode): boolean}
-   */
-  root: function(scope) {
-    return (node) => node === scope;
-  },
-
-  /**
-   * Creates a function that determines whether |node| is to be visited in the
-   * SwitchAccess scope tree with |scope| as the root.
-   *
-   * @param {!chrome.automation.AutomationNode} scope
-   * @return {function(!chrome.automation.AutomationNode): boolean}
-   */
-  visit: function(scope) {
-    return (node) => node.role !== RoleType.DESKTOP &&
-        SwitchAccessPredicate.isSubtreeLeaf(node, scope);
-  },
-
-  /**
-   * Returns a Restrictions object for finding the Context Menu root.
-   * @return {!AutomationTreeWalkerRestriction}
-   */
-  contextMenuDiscoveryRestrictions: () => {
-    return {
-      leaf: SwitchAccessPredicate.isNotContainer,
-      visit: SwitchAccessPredicate.isContextMenu
-    };
-  },
-
-  /**
-   * Returns true if |node| does not have a role of desktop, window, web view,
-   * or root web area.
-   * @param {!chrome.automation.AutomationNode} node
-   * @return {boolean}
-   */
-  isNotContainer: (node) => node.role !== RoleType.ROOT_WEB_AREA &&
-      node.role !== RoleType.WINDOW && node.role !== RoleType.DESKTOP &&
-      node.role !== RoleType.WEB_VIEW,
-
-  /**
-   * Returns true if |node| is the context menu.
-   * @param {!chrome.automation.AutomationNode} node
-   * @return {boolean}
-   */
-  isContextMenu: (node) => node.htmlAttributes.id === ContextMenuManager.MenuId,
-
-  /**
-   * Returns true if |node| is a subtreeLeaf, meaning that |node|
-   * is either interesting or a group (both defined below).
-   *
-   * @param {!chrome.automation.AutomationNode} node
-   * @param {!chrome.automation.AutomationNode} scope
-   * @return {boolean}
-   */
-  isSubtreeLeaf: (node, scope) => SwitchAccessPredicate.isActionable(node) ||
-      SwitchAccessPredicate.isGroup(node, scope),
-
-  /**
-   * Returns true if |node| is a group, meaning that the node has more than one
-   * interesting descendant, and that its interesting descendants exist in more
-   * than one subtree of its immediate children.
-   *
-   * Additionally, for |node| to be a group, it cannot have the same bounding
-   * box as its scope.
-   *
-   * @param {!chrome.automation.AutomationNode} node
-   * @param {!chrome.automation.AutomationNode} scope
-   * @return {boolean}
-   */
-  isGroup: (node, scope) => {
-    if (node !== scope && SwitchAccessPredicate.hasSameLocation_(node, scope))
-      return false;
-    if (node.state[StateType.INVISIBLE])
-      return false;
-
-    // Work around for client nested in client. No need to have user select both
-    // clients for a window. Once locations for outer client updates correctly,
-    // this won't be needed.
-    if (node.role === RoleType.CLIENT && node.role === scope.role &&
-        node !== scope)
-      return false;
-
-    let interestingBranchesCount =
-        SwitchAccessPredicate.isActionable(node) ? 1 : 0;
-    let child = node.firstChild;
-    while (child) {
-      if (SwitchAccessPredicate.isInterestingSubtree(child))
-        interestingBranchesCount += 1;
-      if (interestingBranchesCount > 1)
-        return true;
-      child = child.nextSibling;
-    }
-    return false;
-  },
-
-  /**
-   * Returns true if there is an interesting node in the subtree containing
-   * |node| as its root (including |node| itself).
-   *
-   * @param {!chrome.automation.AutomationNode} node
-   * @return {boolean}
-   */
-  isInterestingSubtree: (node) => SwitchAccessPredicate.isActionable(node) ||
-      node.children.some(SwitchAccessPredicate.isInterestingSubtree),
-
-  /**
-   * Returns true if |node| is interesting, meaning that a user can perform some
-   * type of action on it.
+   * Returns true if |node| is actionable, meaning that a user can interact with
+   * it in some way.
    *
    * @param {!chrome.automation.AutomationNode} node
    * @return {boolean}
@@ -210,15 +82,155 @@
     // Current heuristic is to show as actionble any focusable item where no
     // child is an interesting subtree.
     if (state[StateType.FOCUSABLE]) {
-      return !(
-          node.children &&
-          node.children.some(SwitchAccessPredicate.isInterestingSubtree));
+      return !node.children ||
+          !node.children.some(SwitchAccessPredicate.isInterestingSubtree);
     }
 
     return false;
   },
 
   /**
+   * Returns true if |node| is a group, meaning that the node has more than one
+   * interesting descendant, and that its interesting descendants exist in more
+   * than one subtree of its immediate children.
+   *
+   * Additionally, for |node| to be a group, it cannot have the same bounding
+   * box as its scope.
+   *
+   * @param {!chrome.automation.AutomationNode} node
+   * @param {!chrome.automation.AutomationNode} scope
+   * @return {boolean}
+   */
+  isGroup: (node, scope) => {
+    if (node !== scope && SwitchAccessPredicate.hasSameLocation_(node, scope))
+      return false;
+    if (node.state[StateType.INVISIBLE])
+      return false;
+
+    // Work around for client nested in client. No need to have user select both
+    // clients for a window. Once locations for outer client updates correctly,
+    // this won't be needed.
+    if (node.role === RoleType.CLIENT && node.role === scope.role &&
+        node !== scope)
+      return false;
+
+    let interestingBranchesCount =
+        SwitchAccessPredicate.isActionable(node) ? 1 : 0;
+    let child = node.firstChild;
+    while (child) {
+      if (SwitchAccessPredicate.isInterestingSubtree(child))
+        interestingBranchesCount += 1;
+      if (interestingBranchesCount > 1)
+        return true;
+      child = child.nextSibling;
+    }
+    return false;
+  },
+
+  /**
+   * Returns true if |node| is interesting for the user, meaning that |node|
+   * is either actionable or a group.
+   *
+   * @param {!chrome.automation.AutomationNode} node
+   * @param {!chrome.automation.AutomationNode} scope
+   * @return {boolean}
+   */
+  isInteresting: (node, scope) => SwitchAccessPredicate.isActionable(node) ||
+      SwitchAccessPredicate.isGroup(node, scope),
+
+  /**
+   * Returns true if there is an interesting node in the subtree containing
+   * |node| as its root (including |node| itself).
+   *
+   * This function does not call isInteresting directly, because that would
+   * cause a loop (isInteresting calls isGroup, and isGroup calls
+   * isInterestingSubtree).
+   *
+   * @param {!chrome.automation.AutomationNode} node
+   * @return {boolean}
+   */
+  isInterestingSubtree: (node) => SwitchAccessPredicate.isActionable(node) ||
+      node.children.some(SwitchAccessPredicate.isInterestingSubtree),
+
+  /**
+   * Returns true if |node| does not have a role of desktop, window, web view,
+   * or root web area.
+   * @param {!chrome.automation.AutomationNode} node
+   * @return {boolean}
+   */
+  isNotContainer: (node) => node.role !== RoleType.ROOT_WEB_AREA &&
+      node.role !== RoleType.WINDOW && node.role !== RoleType.DESKTOP &&
+      node.role !== RoleType.WEB_VIEW,
+
+  /**
+   * Returns true if |node| is the context menu.
+   * @param {!chrome.automation.AutomationNode} node
+   * @return {boolean}
+   */
+  isContextMenu: (node) => node.htmlAttributes.id === ContextMenuManager.MenuId,
+
+  /**
+   * Returns a Restrictions object ready to be passed to AutomationTreeWalker.
+   *
+   * @param {!chrome.automation.AutomationNode} scope
+   * @return {!AutomationTreeWalkerRestriction}
+   */
+  restrictions: (scope) => {
+    return {
+      leaf: SwitchAccessPredicate.leaf(scope),
+      root: SwitchAccessPredicate.root(scope),
+      visit: SwitchAccessPredicate.visit(scope)
+    };
+  },
+
+  /**
+   * Creates a function that confirms if |node| is a terminal leaf node of a
+   * SwitchAccess scope tree when |scope| is the root.
+   *
+   * @param {!chrome.automation.AutomationNode} scope
+   * @return {function(!chrome.automation.AutomationNode): boolean}
+   */
+  leaf: function(scope) {
+    return (node) => node.state[StateType.INVISIBLE] ||
+        (node !== scope && SwitchAccessPredicate.isInteresting(node, scope)) ||
+        !SwitchAccessPredicate.isInterestingSubtree(node);
+  },
+
+  /**
+   * Creates a function that confirms if |node| is the root of a SwitchAccess
+   * scope tree when |scope| is the root.
+   *
+   * @param {!chrome.automation.AutomationNode} scope
+   * @return {function(!chrome.automation.AutomationNode): boolean}
+   */
+  root: function(scope) {
+    return (node) => node === scope;
+  },
+
+  /**
+   * Creates a function that determines whether |node| is to be visited in the
+   * SwitchAccess scope tree with |scope| as the root.
+   *
+   * @param {!chrome.automation.AutomationNode} scope
+   * @return {function(!chrome.automation.AutomationNode): boolean}
+   */
+  visit: function(scope) {
+    return (node) => node.role !== RoleType.DESKTOP &&
+        SwitchAccessPredicate.isInteresting(node, scope);
+  },
+
+  /**
+   * Returns a Restrictions object for finding the Context Menu root.
+   * @return {!AutomationTreeWalkerRestriction}
+   */
+  contextMenuDiscoveryRestrictions: () => {
+    return {
+      leaf: SwitchAccessPredicate.isNotContainer,
+      visit: SwitchAccessPredicate.isContextMenu
+    };
+  },
+
+  /**
    * Returns true if the two nodes have the same location.
    *
    * @param {!chrome.automation.AutomationNode} node1
diff --git a/chrome/browser/resources/chromeos/switch_access/switch_access_predicate_test.extjs b/chrome/browser/resources/chromeos/switch_access/switch_access_predicate_test.extjs
index c56e2b4..4b0d0bfa 100644
--- a/chrome/browser/resources/chromeos/switch_access/switch_access_predicate_test.extjs
+++ b/chrome/browser/resources/chromeos/switch_access/switch_access_predicate_test.extjs
@@ -96,24 +96,24 @@
            leaf4, leaf5, leaf6, leaf7 };
 }
 
-TEST_F('SwitchAccessPredicateTest', 'IsSubtreeLeaf', function() {
+TEST_F('SwitchAccessPredicateTest', 'IsInteresting', function() {
   this.runWithLoadedTree(testWebsite(),
       function(desktop) {
         const t = getTree(desktop);
 
-        assertTrue(SwitchAccessPredicate.isSubtreeLeaf(t.root, t.root));
-        assertTrue(SwitchAccessPredicate.isSubtreeLeaf(t.upper1, t.root));
-        assertTrue(SwitchAccessPredicate.isSubtreeLeaf(t.upper2, t.root));
-        assertTrue(SwitchAccessPredicate.isSubtreeLeaf(t.lower1, t.upper1));
-        assertFalse(SwitchAccessPredicate.isSubtreeLeaf(t.lower2, t.upper1));
-        assertFalse(SwitchAccessPredicate.isSubtreeLeaf(t.lower3, t.root));
-        assertTrue(SwitchAccessPredicate.isSubtreeLeaf(t.leaf1, t.lower1));
-        assertFalse(SwitchAccessPredicate.isSubtreeLeaf(t.leaf2, t.lower1));
-        assertTrue(SwitchAccessPredicate.isSubtreeLeaf(t.leaf3, t.lower1));
-        assertFalse(SwitchAccessPredicate.isSubtreeLeaf(t.leaf4, t.upper1));
-        assertTrue(SwitchAccessPredicate.isSubtreeLeaf(t.leaf5, t.upper1));
-        assertFalse(SwitchAccessPredicate.isSubtreeLeaf(t.leaf6, t.lower3));
-        assertFalse(SwitchAccessPredicate.isSubtreeLeaf(t.leaf7, t.lower3));
+        assertTrue(SwitchAccessPredicate.isInteresting(t.root, t.root));
+        assertTrue(SwitchAccessPredicate.isInteresting(t.upper1, t.root));
+        assertTrue(SwitchAccessPredicate.isInteresting(t.upper2, t.root));
+        assertTrue(SwitchAccessPredicate.isInteresting(t.lower1, t.upper1));
+        assertFalse(SwitchAccessPredicate.isInteresting(t.lower2, t.upper1));
+        assertFalse(SwitchAccessPredicate.isInteresting(t.lower3, t.root));
+        assertTrue(SwitchAccessPredicate.isInteresting(t.leaf1, t.lower1));
+        assertFalse(SwitchAccessPredicate.isInteresting(t.leaf2, t.lower1));
+        assertTrue(SwitchAccessPredicate.isInteresting(t.leaf3, t.lower1));
+        assertFalse(SwitchAccessPredicate.isInteresting(t.leaf4, t.upper1));
+        assertTrue(SwitchAccessPredicate.isInteresting(t.leaf5, t.upper1));
+        assertFalse(SwitchAccessPredicate.isInteresting(t.leaf6, t.lower3));
+        assertFalse(SwitchAccessPredicate.isInteresting(t.leaf7, t.lower3));
       }
   );
 });
diff --git a/chrome/browser/resources/md_extensions/activity_log.html b/chrome/browser/resources/md_extensions/activity_log.html
index ab686391..54fb91c 100644
--- a/chrome/browser/resources/md_extensions/activity_log.html
+++ b/chrome/browser/resources/md_extensions/activity_log.html
@@ -2,6 +2,7 @@
 
 <link rel="import" href="chrome://resources/cr_elements/cr_container_shadow_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_search_field/cr_search_field.html">
+<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/promise_resolver.html">
@@ -12,7 +13,7 @@
 
 <dom-module id="extensions-activity-log">
   <template>
-    <style include="iron-flex cr-shared-style shared-style">
+    <style include="iron-flex cr-shared-style shared-style paper-button-style">
       .activity-message {
         color: #6e6e6e;
         font-size: 123%;  /* Should be 16px when 100% is 13px. */
@@ -26,6 +27,10 @@
         margin-inline-start: 16px;
       }
 
+      #clear-activities-button {
+        margin-inline-start: 8px;
+      }
+
       cr-search-field  {
         padding-inline-end: 8px;
       }
@@ -41,6 +46,10 @@
           <cr-search-field label="$i18n{activityLogSearchLabel}"
               on-search-changed="onSearchChanged_">
           </cr-search-field >
+          <paper-button id="clear-activities-button"
+              on-click="onClearButtonTap_">
+            $i18n{clearActivities}
+          </paper-button>
         </div>
         <div id="loading-activities" class="activity-message"
             hidden$="[[!shouldShowLoadingMessage_(
diff --git a/chrome/browser/resources/md_extensions/activity_log.js b/chrome/browser/resources/md_extensions/activity_log.js
index 8cbd20af..ccb04b99 100644
--- a/chrome/browser/resources/md_extensions/activity_log.js
+++ b/chrome/browser/resources/md_extensions/activity_log.js
@@ -33,6 +33,12 @@
      * @return {!Promise<!chrome.activityLogPrivate.ActivityResultSet>}
      */
     getFilteredExtensionActivityLog(extensionId, searchTerm) {}
+
+    /**
+     * @param {string} extensionId
+     * @return {!Promise<void>}
+     */
+    deleteActivitiesFromExtension(extensionId) {}
   }
 
   /**
@@ -194,6 +200,13 @@
     },
 
     /** @private */
+    onClearButtonTap_: function() {
+      this.delegate.deleteActivitiesFromExtension(this.extensionId).then(() => {
+        this.processActivities_([]);
+      });
+    },
+
+    /** @private */
     onCloseButtonTap_: function() {
       extensions.navigation.navigateTo(
           {page: Page.DETAILS, extensionId: this.extensionId});
diff --git a/chrome/browser/resources/md_extensions/service.js b/chrome/browser/resources/md_extensions/service.js
index d455924..d5a32f7c 100644
--- a/chrome/browser/resources/md_extensions/service.js
+++ b/chrome/browser/resources/md_extensions/service.js
@@ -398,6 +398,14 @@
         return {activities: Array.from(activitiesById.values())};
       });
     }
+
+    /** @override */
+    deleteActivitiesFromExtension(extensionId) {
+      return new Promise(function(resolve, reject) {
+        chrome.activityLogPrivate.deleteActivitiesByExtension(
+            extensionId, resolve);
+      });
+    }
   }
 
   cr.addSingletonGetter(Service);
diff --git a/chrome/browser/resources/print_preview/cloud_print_interface.js b/chrome/browser/resources/print_preview/cloud_print_interface.js
index 0f6f1d1..3b8c9d44 100644
--- a/chrome/browser/resources/print_preview/cloud_print_interface.js
+++ b/chrome/browser/resources/print_preview/cloud_print_interface.js
@@ -95,10 +95,10 @@
      * @param {!print_preview.Destination} destination Cloud destination to
      *     print to.
      * @param {string} printTicket The print ticket to print.
-     * @param {!print_preview.DocumentInfo} documentInfo Document data model.
+     * @param {string} documentTitle Title of the document.
      * @param {string} data Base64 encoded data of the document.
      */
-    submit(destination, printTicket, documentInfo, data) {}
+    submit(destination, printTicket, documentTitle, data) {}
 
     /**
      * Sends a Google Cloud Print printer API request.
diff --git a/chrome/browser/resources/print_preview/cloud_print_interface_js.js b/chrome/browser/resources/print_preview/cloud_print_interface_js.js
index d92ff2e..d6d9bd6f 100644
--- a/chrome/browser/resources/print_preview/cloud_print_interface_js.js
+++ b/chrome/browser/resources/print_preview/cloud_print_interface_js.js
@@ -155,7 +155,7 @@
     }
 
     /** @override */
-    submit(destination, printTicket, documentInfo, data) {
+    submit(destination, printTicket, documentTitle, data) {
       const result = VERSION_REGEXP_.exec(navigator.userAgent);
       let chromeVersion = 'unknown';
       if (result && result.length == 2) {
@@ -164,7 +164,7 @@
       const params = [
         new HttpParam('printerid', destination.id),
         new HttpParam('contentType', 'dataUrl'),
-        new HttpParam('title', documentInfo.title),
+        new HttpParam('title', documentTitle),
         new HttpParam('ticket', printTicket),
         new HttpParam('content', 'data:application/pdf;base64,' + data),
         new HttpParam('tag', '__google__chrome_version=' + chromeVersion),
diff --git a/chrome/browser/resources/print_preview/cloud_print_interface_native.js b/chrome/browser/resources/print_preview/cloud_print_interface_native.js
index 808b8e18..5455aabb 100644
--- a/chrome/browser/resources/print_preview/cloud_print_interface_native.js
+++ b/chrome/browser/resources/print_preview/cloud_print_interface_native.js
@@ -25,7 +25,7 @@
     processInvite(invitation, accept) {}
 
     /** @override */
-    submit(destination, printTicket, documentInfo, data) {}
+    submit(destination, printTicket, documentTitle, data) {}
 
     /** @override */
     printer(printerId, origin, account) {}
diff --git a/chrome/browser/resources/print_preview/data/BUILD.gn b/chrome/browser/resources/print_preview/data/BUILD.gn
index 7909d63..080d53e 100644
--- a/chrome/browser/resources/print_preview/data/BUILD.gn
+++ b/chrome/browser/resources/print_preview/data/BUILD.gn
@@ -94,7 +94,7 @@
     ":printable_area",
     ":size",
     "//ui/webui/resources/js:cr",
-    "//ui/webui/resources/js/cr:event_target",
+    "//ui/webui/resources/js:web_ui_listener_behavior",
   ]
 }
 
diff --git a/chrome/browser/resources/print_preview/data/cloud_parsers.js b/chrome/browser/resources/print_preview/data/cloud_parsers.js
index 4069b25..2648923 100644
--- a/chrome/browser/resources/print_preview/data/cloud_parsers.js
+++ b/chrome/browser/resources/print_preview/data/cloud_parsers.js
@@ -111,7 +111,7 @@
     const optionalParams = {
       account: account,
       tags: tags,
-      isOwned: arrayContains(tags, OWNED_TAG),
+      isOwned: tags.includes(OWNED_TAG),
       lastAccessTime:
           parseInt(json[CloudDestinationField.LAST_ACCESS], 10) || Date.now(),
       cloudID: id,
@@ -121,7 +121,7 @@
     const cloudDest = new print_preview.Destination(
         id, parseType(json[CloudDestinationField.TYPE]), origin,
         json[CloudDestinationField.DISPLAY_NAME],
-        arrayContains(tags, RECENT_TAG) /*isRecent*/, connectionStatus,
+        tags.includes(RECENT_TAG) /*isRecent*/, connectionStatus,
         optionalParams);
     if (json.hasOwnProperty(CloudDestinationField.CAPABILITIES)) {
       cloudDest.capabilities = /** @type {!print_preview.Cdd} */ (
diff --git a/chrome/browser/resources/print_preview/data/coordinate2d.js b/chrome/browser/resources/print_preview/data/coordinate2d.js
index f4dff04..2944e47d 100644
--- a/chrome/browser/resources/print_preview/data/coordinate2d.js
+++ b/chrome/browser/resources/print_preview/data/coordinate2d.js
@@ -39,25 +39,6 @@
     }
 
     /**
-     * @param {number} x Amount to translate in the X dimension.
-     * @param {number} y Amount to translate in the Y dimension.
-     * @return {!print_preview.Coordinate2d} A new two-dimensional point
-     *     translated along the X and Y dimensions.
-     */
-    translate(x, y) {
-      return new Coordinate2d(this.x_ + x, this.y_ + y);
-    }
-
-    /**
-     * @param {number} factor Amount to scale the X and Y dimensions.
-     * @return {!print_preview.Coordinate2d} A new two-dimensional point scaled
-     *     by the given factor.
-     */
-    scale(factor) {
-      return new Coordinate2d(this.x_ * factor, this.y_ * factor);
-    }
-
-    /**
      * @param {print_preview.Coordinate2d} other The point to compare against.
      * @return {boolean} Whether another point is equal to this one.
      */
diff --git a/chrome/browser/resources/print_preview/data/destination.js b/chrome/browser/resources/print_preview/data/destination.js
index 5580239c5..6b0cfaec 100644
--- a/chrome/browser/resources/print_preview/data/destination.js
+++ b/chrome/browser/resources/print_preview/data/destination.js
@@ -607,12 +607,10 @@
 
     /** @return {boolean} Whether the destination is considered offline. */
     get isOffline() {
-      return arrayContains(
-          [
-            print_preview.DestinationConnectionStatus.OFFLINE,
-            print_preview.DestinationConnectionStatus.DORMANT
-          ],
-          this.connectionStatus_);
+      return [
+        print_preview.DestinationConnectionStatus.OFFLINE,
+        print_preview.DestinationConnectionStatus.DORMANT
+      ].includes(this.connectionStatus_);
     }
 
     /**
diff --git a/chrome/browser/resources/print_preview/data/destination_match.js b/chrome/browser/resources/print_preview/data/destination_match.js
index be030065..e30339c 100644
--- a/chrome/browser/resources/print_preview/data/destination_match.js
+++ b/chrome/browser/resources/print_preview/data/destination_match.js
@@ -51,16 +51,17 @@
     }
 
     /**
-     * @param {string} origin Origin to match.
+     * @param {!print_preview.DestinationOrigin} origin Origin to match.
      * @return {boolean} Whether the origin is one of the {@code origins_}.
      */
     matchOrigin(origin) {
-      return arrayContains(this.origins_, origin);
+      return this.origins_.includes(origin);
     }
 
     /**
      * @param {string} id Id of the destination.
-     * @param {string} origin Origin of the destination.
+     * @param {!print_preview.DestinationOrigin} origin Origin of the
+     *     destination.
      * @return {boolean} Whether destination is the same as initial.
      */
     matchIdAndOrigin(id, origin) {
@@ -98,13 +99,11 @@
      * @private
      */
     isVirtualDestination_(destination) {
-      if (destination.origin == print_preview.DestinationOrigin.LOCAL) {
-        return arrayContains(
-            [print_preview.Destination.GooglePromotedId.SAVE_AS_PDF],
-            destination.id);
+      if (destination.origin === print_preview.DestinationOrigin.LOCAL) {
+        return destination.id ===
+            print_preview.Destination.GooglePromotedId.SAVE_AS_PDF;
       }
-      return arrayContains(
-          [print_preview.Destination.GooglePromotedId.DOCS], destination.id);
+      return destination.id === print_preview.Destination.GooglePromotedId.DOCS;
     }
 
     /**
diff --git a/chrome/browser/resources/print_preview/data/document_info.html b/chrome/browser/resources/print_preview/data/document_info.html
index 6ab60e3..59729c4 100644
--- a/chrome/browser/resources/print_preview/data/document_info.html
+++ b/chrome/browser/resources/print_preview/data/document_info.html
@@ -1,5 +1,7 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/html/cr/event_target.html">
 <link rel="import" href="coordinate2d.html">
 <link rel="import" href="margins.html">
 <link rel="import" href="printable_area.html">
diff --git a/chrome/browser/resources/print_preview/data/document_info.js b/chrome/browser/resources/print_preview/data/document_info.js
index caea2b5..aa87754 100644
--- a/chrome/browser/resources/print_preview/data/document_info.js
+++ b/chrome/browser/resources/print_preview/data/document_info.js
@@ -2,227 +2,178 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.define('print_preview', function() {
-  'use strict';
+cr.exportPath('print_preview');
 
-  class DocumentInfo extends cr.EventTarget {
-    /**
-     * Data model which contains information related to the document to print.
-     */
-    constructor() {
-      super();
+/**
+ * @typedef {{
+ *   hasCssMediaStyles: boolean,
+ *   hasSelection: boolean,
+ *   isModifiable: boolean,
+ *   isScalingDisabled: boolean,
+ *   fitToPageScaling: number,
+ *   pageCount: number,
+ *   title: string,
+ * }}
+ */
+print_preview.DocumentSettings;
 
-      /**
-       * Whether the document is styled by CSS media styles.
-       * @private {boolean}
-       */
-      this.hasCssMediaStyles_ = false;
+Polymer({
+  is: 'print-preview-document-info',
 
-      /**
-       * Whether the document has selected content.
-       * @private {boolean}
-       */
-      this.hasSelection_ = false;
+  behaviors: [WebUIListenerBehavior],
 
-      /**
-       * Whether the document to print is modifiable (i.e. can be reflowed).
-       * @private {boolean}
-       */
-      this.isModifiable_ = true;
+  properties: {
+    /** @type {!print_preview.DocumentSettings} */
+    documentSettings: {
+      type: Object,
+      notify: true,
+      value: function() {
+        return {
+          hasCssMediaStyles: false,
+          hasSelection: false,
+          isModifiable: true,
+          isScalingDisabled: false,
+          fitToPageScaling: 100,
+          pageCount: 0,
+          title: '',
+        };
+      },
+    },
 
-      /**
-       * Whether scaling of the document is prohibited.
-       * @private {boolean}
-       */
-      this.isScalingDisabled_ = false;
+    inFlightRequestId: {
+      type: Number,
+      value: -1,
+    },
 
-      /**
-       * Scaling required to fit to page.
-       * @private {number}
-       */
-      this.fitToPageScaling_ = 100;
-
-      /**
-       * Margins of the document in points.
-       * @private {print_preview.Margins}
-       */
-      this.margins_ = null;
-
-      /**
-       * Number of pages in the document to print.
-       * @private {number}
-       */
-      this.pageCount_ = 0;
-
-      /**
-       * Size of the pages of the document in points. Actual page-related
-       * information won't be set until preview generation occurs, so use
-       * a default value until then. This way, the print ticket store will be
-       * valid even if no preview can be generated.
-       * @private {!print_preview.Size}
-       */
-      this.pageSize_ = new print_preview.Size(612, 792);  // 8.5"x11"
-
-      /**
-       * Printable area of the document in points.
-       * @private {!print_preview.PrintableArea}
-       */
-      this.printableArea_ = new print_preview.PrintableArea(
-          new print_preview.Coordinate2d(0, 0), this.pageSize_);
-
-      /**
-       * Title of document.
-       * @private {string}
-       */
-      this.title_ = '';
-
-      /**
-       * Whether this data model has been initialized.
-       * @private {boolean}
-       */
-      this.isInitialized_ = false;
-    }
-
-    /** @return {boolean} Whether the document is styled by CSS media styles. */
-    get hasCssMediaStyles() {
-      return this.hasCssMediaStyles_;
-    }
-
-    /** @return {boolean} Whether the document has selected content. */
-    get hasSelection() {
-      return this.hasSelection_;
-    }
+    /** @type {print_preview.Margins} */
+    margins: {
+      type: Object,
+      notify: true,
+    },
 
     /**
-     * @return {boolean} Whether the document to print is modifiable (i.e. can
-     *     be reflowed).
+     * Size of the pages of the document in points. Actual page-related
+     * information won't be set until preview generation occurs, so use
+     * a default value until then.
+     * @type {!print_preview.Size}
      */
-    get isModifiable() {
-      return this.isModifiable_;
-    }
-
-    /** @return {boolean} Whether scaling of the document is prohibited. */
-    get isScalingDisabled() {
-      return this.isScalingDisabled_;
-    }
-
-    /** @return {number} Scaling required to fit to page. */
-    get fitToPageScaling() {
-      return this.fitToPageScaling_;
-    }
-
-    /** @return {print_preview.Margins} Margins of the document in points. */
-    get margins() {
-      return this.margins_;
-    }
-
-    /** @return {number} Number of pages in the document to print. */
-    get pageCount() {
-      return this.pageCount_;
-    }
+    pageSize: {
+      type: Object,
+      notify: true,
+      value: function() {
+        return new print_preview.Size(612, 792);
+      },
+    },
 
     /**
-     * @return {!print_preview.Size} Size of the pages of the document in
-     *     points.
+     * Printable area of the document in points.
+     * @type {!print_preview.PrintableArea}
      */
-    get pageSize() {
-      return this.pageSize_;
-    }
-
-    /**
-     * @return {!print_preview.PrintableArea} Printable area of the document in
-     *     points.
-     */
-    get printableArea() {
-      return this.printableArea_;
-    }
-
-    /** @return {string} Title of document. */
-    get title() {
-      return this.title_;
-    }
-
-    /**
-     * Initializes the state of the data model and dispatches a CHANGE event.
-     * @param {boolean} isModifiable Whether the document is modifiable.
-     * @param {string} title Title of the document.
-     * @param {boolean} hasSelection Whether the document has user-selected
-     *     content.
-     */
-    init(isModifiable, title, hasSelection) {
-      this.isModifiable_ = isModifiable;
-      this.title_ = title;
-      this.hasSelection_ = hasSelection;
-      this.isInitialized_ = true;
-      cr.dispatchSimpleEvent(this, DocumentInfo.EventType.CHANGE);
-    }
-
-    /**
-     * Updates whether scaling is disabled for the document and dispatches a
-     * CHANGE event.
-     * @param {boolean} isScalingDisabled Whether scaling of the document is
-     *     prohibited.
-     */
-    updateIsScalingDisabled(isScalingDisabled) {
-      if (this.isInitialized_ && this.isScalingDisabled_ != isScalingDisabled) {
-        this.isScalingDisabled_ = isScalingDisabled;
-        cr.dispatchSimpleEvent(this, DocumentInfo.EventType.CHANGE);
-      }
-    }
-
-    /**
-     * Updates the total number of pages in the document and dispatches a CHANGE
-     * event.
-     * @param {number} pageCount Number of pages in the document.
-     */
-    updatePageCount(pageCount) {
-      if (this.isInitialized_ && this.pageCount_ != pageCount) {
-        this.pageCount_ = pageCount;
-        cr.dispatchSimpleEvent(this, DocumentInfo.EventType.CHANGE);
-      }
-    }
-
-    /**
-     * Updates the fit to page scaling. Does not dispatch a CHANGE event, since
-     * this is only called in tests and in the new print preview UI, which uses
-     * data bindings to notify UI elements of the change.
-     * @param {number} scaleFactor The fit to page scale factor.
-     */
-    updateFitToPageScaling(scaleFactor) {
-      this.fitToPageScaling_ = scaleFactor;
-    }
-
-    /**
-     * Updates information about each page and dispatches a CHANGE event.
-     * @param {!print_preview.PrintableArea} printableArea Printable area of the
-     *     document in points.
-     * @param {!print_preview.Size} pageSize Size of the pages of the document
-     *     in points.
-     * @param {boolean} hasCssMediaStyles Whether the document is styled by CSS
-     *     media styles.
-     * @param {print_preview.Margins} margins Margins of the document in points.
-     */
-    updatePageInfo(printableArea, pageSize, hasCssMediaStyles, margins) {
-      if (this.isInitialized_ &&
-          (!this.printableArea_.equals(printableArea) ||
-           !this.pageSize_.equals(pageSize) ||
-           this.hasCssMediaStyles_ != hasCssMediaStyles ||
-           this.margins_ == null || !this.margins_.equals(margins))) {
-        this.printableArea_ = printableArea;
-        this.pageSize_ = pageSize;
-        this.hasCssMediaStyles_ = hasCssMediaStyles;
-        this.margins_ = margins;
-        cr.dispatchSimpleEvent(this, DocumentInfo.EventType.CHANGE);
-      }
-    }
-  }
+    printableArea: {
+      type: Object,
+      notify: true,
+      value: function() {
+        return new print_preview.PrintableArea(
+            new print_preview.Coordinate2d(0, 0),
+            new print_preview.Size(612, 792));
+      },
+    },
+  },
 
   /**
-   * Event types dispatched by this data model.
-   * @enum {string}
+   * Whether this data model has been initialized.
+   * @private {boolean}
    */
-  DocumentInfo.EventType = {CHANGE: 'print_preview.DocumentInfo.CHANGE'};
+  isInitialized_: false,
 
-  // Export
-  return {DocumentInfo: DocumentInfo};
+  /** @override */
+  attached: function() {
+    this.addWebUIListener(
+        'page-count-ready', this.onPageCountReady_.bind(this));
+    this.addWebUIListener(
+        'page-layout-ready', this.onPageLayoutReady_.bind(this));
+  },
+
+  /**
+   * Initializes the state of the data model.
+   * @param {boolean} isModifiable Whether the document is modifiable.
+   * @param {string} title Title of the document.
+   * @param {boolean} hasSelection Whether the document has user-selected
+   *     content.
+   */
+  init: function(isModifiable, title, hasSelection) {
+    this.isInitialized_ = true;
+    this.set('documentSettings.isModifiable', isModifiable);
+    this.set('documentSettings.title', title);
+    this.set('documentSettings.hasSelection', hasSelection);
+  },
+
+  /**
+   * Updates whether scaling is disabled for the document.
+   * @param {boolean} isScalingDisabled Whether scaling of the document is
+   *     prohibited.
+   */
+  updateIsScalingDisabled: function(isScalingDisabled) {
+    if (this.isInitialized_) {
+      this.set('documentSettings.isScalingDisabled', isScalingDisabled);
+    }
+  },
+
+  /**
+   * Called when the page layout of the document is ready. Always occurs
+   * as a result of a preview request.
+   * @param {{marginTop: number,
+   *          marginLeft: number,
+   *          marginBottom: number,
+   *          marginRight: number,
+   *          contentWidth: number,
+   *          contentHeight: number,
+   *          printableAreaX: number,
+   *          printableAreaY: number,
+   *          printableAreaWidth: number,
+   *          printableAreaHeight: number,
+   *        }} pageLayout Layout information about the document.
+   * @param {boolean} hasCustomPageSizeStyle Whether this document has a
+   *     custom page size or style to use.
+   * @private
+   */
+  onPageLayoutReady_: function(pageLayout, hasCustomPageSizeStyle) {
+    const origin = new print_preview.Coordinate2d(
+        pageLayout.printableAreaX, pageLayout.printableAreaY);
+    const size = new print_preview.Size(
+        pageLayout.printableAreaWidth, pageLayout.printableAreaHeight);
+
+    const margins = new print_preview.Margins(
+        Math.round(pageLayout.marginTop), Math.round(pageLayout.marginRight),
+        Math.round(pageLayout.marginBottom), Math.round(pageLayout.marginLeft));
+
+    const o = print_preview.ticket_items.CustomMarginsOrientation;
+    const pageSize = new print_preview.Size(
+        pageLayout.contentWidth + margins.get(o.LEFT) + margins.get(o.RIGHT),
+        pageLayout.contentHeight + margins.get(o.TOP) + margins.get(o.BOTTOM));
+
+    if (this.isInitialized_) {
+      this.printableArea = new print_preview.PrintableArea(origin, size);
+      this.pageSize = pageSize;
+      this.set('documentSettings.hasCssMediaStyles', hasCustomPageSizeStyle);
+      this.margins = margins;
+    }
+  },
+
+  /**
+   * Called when the document page count is received from the native layer.
+   * Always occurs as a result of a preview request.
+   * @param {number} pageCount The document's page count.
+   * @param {number} previewResponseId The request ID for this page count event.
+   * @param {number} fitToPageScaling The scaling required to fit the document
+   *     to page.
+   * @private
+   */
+  onPageCountReady_: function(pageCount, previewResponseId, fitToPageScaling) {
+    if (this.inFlightRequestId != previewResponseId || !this.isInitialized_) {
+      return;
+    }
+    this.set('documentSettings.pageCount', pageCount);
+    this.set('documentSettings.fitToPageScaling', fitToPageScaling);
+  },
 });
diff --git a/chrome/browser/resources/print_preview/data/measurement_system.js b/chrome/browser/resources/print_preview/data/measurement_system.js
index eaaa45c6..aa422f2 100644
--- a/chrome/browser/resources/print_preview/data/measurement_system.js
+++ b/chrome/browser/resources/print_preview/data/measurement_system.js
@@ -77,20 +77,6 @@
     }
 
     /**
-     * Sets the measurement system based on the delimeters and unit type.
-     * @param {string} thousandsDelimeter The thousands delimeter to use
-     * @param {string} decimalDelimeter The decimal delimeter to use
-     * @param {!print_preview.MeasurementSystemUnitType} unitType Measurement
-     *     unit type of the system.
-     */
-    setSystem(thousandsDelimeter, decimalDelimeter, unitType) {
-      this.thousandsDelimeter_ = thousandsDelimeter;
-      this.decimalDelimeter_ = decimalDelimeter;
-      assert(measurementSystemPrefs.has(unitType));
-      this.measurementSystemPrefs_ = measurementSystemPrefs.get(unitType);
-    }
-
-    /**
      * Rounds a value in the local system's units to the appropriate precision.
      * @param {number} value Value to round.
      * @return {number} Rounded value.
diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js
index c73e0c7..5ff5a02f 100644
--- a/chrome/browser/resources/print_preview/native_layer.js
+++ b/chrome/browser/resources/print_preview/native_layer.js
@@ -303,14 +303,6 @@
   /** @private {?print_preview.NativeLayer} */
   let currentInstance = null;
 
-  /**
-   * Version of the serialized state of the print preview.
-   * @type {number}
-   * @const
-   * @private
-   */
-  NativeLayer.SERIALIZED_STATE_VERSION_ = 1;
-
   // Export
   return {
     NativeLayer: NativeLayer
diff --git a/chrome/browser/resources/print_preview/new/BUILD.gn b/chrome/browser/resources/print_preview/new/BUILD.gn
index 8dc6030..3ab301f 100644
--- a/chrome/browser/resources/print_preview/new/BUILD.gn
+++ b/chrome/browser/resources/print_preview/new/BUILD.gn
@@ -109,7 +109,6 @@
   deps = [
     ":input_behavior",
     ":settings_behavior",
-    "../data:document_info",
     "//ui/webui/resources/cr_elements/cr_input:cr_input",
     "//ui/webui/resources/js:load_time_data",
   ]
@@ -170,7 +169,6 @@
   deps = [
     ":number_settings_section",
     ":settings_behavior",
-    "../data:document_info",
   ]
 }
 
@@ -248,7 +246,6 @@
     "../../pdf:pdf_scripting_api",
     "../data:coordinate2d",
     "../data:destination",
-    "../data:document_info",
     "../data:margins",
     "../data:printable_area",
     "../data:size",
diff --git a/chrome/browser/resources/print_preview/new/app.html b/chrome/browser/resources/print_preview/new/app.html
index 9e47084..adf6563 100644
--- a/chrome/browser/resources/print_preview/new/app.html
+++ b/chrome/browser/resources/print_preview/new/app.html
@@ -86,10 +86,15 @@
     </style>
     <print-preview-state id="state" state="{{state}}"></print-preview-state>
     <print-preview-model id="model" settings="{{settings}}"
-        destination="{{destination_}}" document-info="{{documentInfo_}}"
+        destination="{{destination_}}" document-settings="[[documentSettings_]]"
+        margins="[[margins_]]" page-size="[[pageSize_]]"
         recent-destinations="{{recentDestinations_}}"
         on-save-sticky-settings="onSaveStickySettings_">
     </print-preview-model>
+    <print-preview-document-info id="documentInfo"
+        document-settings="{{documentSettings_}}" margins="{{margins_}}"
+        page-size="{{pageSize_}}">
+    </print-preview-document-info>
     <div id="sidebar" on-setting-valid-changed="onSettingValidChanged_">
       <print-preview-header destination="[[destination_]]" state="[[state]]"
           error-message="[[errorMessage_]]" settings="[[settings]]"
@@ -106,7 +111,7 @@
             user-info="{{userInfo_}}" available class="settings-section">
         </print-preview-destination-settings>
         <print-preview-pages-settings settings="{{settings}}"
-            document-info="[[documentInfo_]]"
+            page-count="[[documentSettings_.pageCount]]"
             disabled="[[controlsDisabled_]]"
             hidden$="[[!settings.pages.available]]" class="settings-section">
         </print-preview-pages-settings>
@@ -154,7 +159,8 @@
               hidden$="[[!settings.dpi.available]]" class="settings-section">
           </print-preview-dpi-settings>
           <print-preview-scaling-settings settings="{{settings}}"
-              document-info="[[documentInfo_]]" disabled="[[controlsDisabled_]]"
+              fit-to-page-scaling="[[documentSettings_.fitToPageScaling]]"
+              disabled="[[controlsDisabled_]]"
               hidden$="[[!settings.scaling.available]]"
               class="settings-section">
           </print-preview-scaling-settings>
@@ -184,9 +190,11 @@
     </div>
     <div id="preview-area-container">
       <print-preview-preview-area id="previewArea" settings="{{settings}}"
-          destination="[[destination_]]" document-info="{{documentInfo_}}"
-          state="[[state]]" measurement-system="[[measurementSystem_]]"
-          preview-state="{{previewState_}}">
+          destination="[[destination_]]"
+          document-modifiable="[[documentSettings_.isModifiable]]"
+          margins="[[margins_]]" page-size="[[pageSize_]]" state="[[state]]"
+          measurement-system="[[measurementSystem_]]"
+          preview-state="{{previewState_}}" on-preview-start="onPreviewStart_">
       </print-preview-preview-area>
     </div>
   </template>
diff --git a/chrome/browser/resources/print_preview/new/app.js b/chrome/browser/resources/print_preview/new/app.js
index 95510f7..cbb88a2 100644
--- a/chrome/browser/resources/print_preview/new/app.js
+++ b/chrome/browser/resources/print_preview/new/app.js
@@ -44,11 +44,17 @@
       value: null,
     },
 
-    /** @private {print_preview.DocumentInfo} */
-    documentInfo_: {
-      type: Object,
-      notify: true,
-    },
+    /** @private {print_preview.DocumentSettings} */
+    documentSettings_: Object,
+
+    /** @private {print_preview.Margins} */
+    margins_: Object,
+
+    /** @private {!print_preview.Size} */
+    pageSize_: Object,
+
+    /** @private {!print_preview.PrintableArea} */
+    printableArea_: Object,
 
     /** @private {?print_preview.InvitationStore} */
     invitationStore_: {
@@ -178,7 +184,6 @@
   /** @override */
   attached: function() {
     this.nativeLayer_ = print_preview.NativeLayer.getInstance();
-    this.documentInfo_ = new print_preview.DocumentInfo();
     this.userInfo_ = new print_preview.UserInfo();
     this.addWebUIListener(
         'use-cloud-print', this.onCloudPrintEnable_.bind(this));
@@ -337,13 +342,9 @@
    * @private
    */
   onInitialSettingsSet_: function(settings) {
-    this.documentInfo_.init(
+    this.$.documentInfo.init(
         settings.previewModifiable, settings.documentTitle,
         settings.documentHasSelection);
-    this.notifyPath('documentInfo_.isModifiable');
-    this.notifyPath('documentInfo_.hasSelection');
-    this.notifyPath('documentInfo_.title');
-    this.notifyPath('documentInfo_.pageCount');
     this.$.model.setStickySettings(settings.serializedAppStateStr);
     this.$.model.setPolicySettings(
         settings.headerFooter, settings.isHeaderFooterManaged);
@@ -528,7 +529,7 @@
     const destination = assert(this.destinationStore_.selectedDestination);
     this.cloudPrintInterface_.submit(
         destination, this.$.model.createCloudJobTicket(destination),
-        assert(this.documentInfo_), data);
+        this.$.documentInfo.title, data);
   },
 
   // <if expr="not chromeos">
@@ -640,7 +641,7 @@
    */
   onPrintPresetOptions_: function(disableScaling, copies, duplex) {
     if (disableScaling) {
-      this.documentInfo_.updateIsScalingDisabled(true);
+      this.$.documentInfo.updateIsScalingDisabled(true);
     }
 
     if (copies > 0 && this.getSetting('copies').available) {
@@ -684,6 +685,14 @@
     return this.settingsExpandedByUser_ || !this.shouldShowMoreSettings_;
   },
 
+  /**
+   * @param {!CustomEvent} e Contains the new preview request ID.
+   * @private
+   */
+  onPreviewStart_: function(e) {
+    this.$.documentInfo.inFlightRequestId = /** @type {number} */ (e.detail);
+  },
+
   // <if expr="chromeos">
   /** @private */
   onNoDestinationsFound_: function() {
diff --git a/chrome/browser/resources/print_preview/new/model.js b/chrome/browser/resources/print_preview/new/model.js
index f0d7b63..5dfccfb4 100644
--- a/chrome/browser/resources/print_preview/new/model.js
+++ b/chrome/browser/resources/print_preview/new/model.js
@@ -294,6 +294,15 @@
       notify: true,
     },
 
+    /** @type {!print_preview.DocumentSettings} */
+    documentSettings: Object,
+
+    /** @type {print_preview.Margins} */
+    margins: Object,
+
+    /** @type {!print_preview.Size} */
+    pageSize: Object,
+
     /** @type {!Array<!print_preview.RecentDestination>} */
     recentDestinations: {
       type: Array,
@@ -302,21 +311,15 @@
         return [];
       },
     },
-
-    /** @type {print_preview.DocumentInfo} */
-    documentInfo: {
-      type: Object,
-      notify: true,
-    },
   },
 
   observers: [
     'updateSettingsFromDestination_(destination.capabilities)',
-    'updateSettingsAvailabilityFromDocumentInfo_(' +
-        'documentInfo.isModifiable, documentInfo.hasCssMediaStyles,' +
-        'documentInfo.hasSelection)',
+    'updateSettingsAvailabilityFromDocumentSettings_(' +
+        'documentSettings.isModifiable, documentSettings.hasCssMediaStyles,' +
+        'documentSettings.hasSelection, documentSettings.isScalingDisabled)',
     'updateHeaderFooterAvailable_(' +
-        'documentInfo.margins, settings.margins.value, ' +
+        'margins, settings.margins.value, ' +
         'settings.customMargins.value, settings.mediaSize.value)',
     'updateRecentDestinations_(destination, destination.capabilities)',
     'stickySettingsChanged_(' +
@@ -394,23 +397,26 @@
     this.set(
         'settings.vendorItems.available', !!caps && !!caps.vendor_capability);
 
-    if (this.documentInfo) {
-      this.updateSettingsAvailabilityFromDestinationAndDocumentInfo_();
+    if (this.documentSettings) {
+      this.updateSettingsAvailabilityFromDestinationAndDocumentSettings_();
     }
   },
 
   /** @private */
-  updateSettingsAvailabilityFromDestinationAndDocumentInfo_: function() {
+  updateSettingsAvailabilityFromDestinationAndDocumentSettings_: function() {
     const isSaveAsPDF = this.destination.id ==
         print_preview.Destination.GooglePromotedId.SAVE_AS_PDF;
     const knownSizeToSaveAsPdf = isSaveAsPDF &&
-        (!this.documentInfo.isModifiable ||
-         this.documentInfo.hasCssMediaStyles);
+        (!this.documentSettings.isModifiable ||
+         this.documentSettings.hasCssMediaStyles);
     this.set('settings.fitToPage.unavailableValue', !isSaveAsPDF);
     this.set(
         'settings.fitToPage.available',
-        !knownSizeToSaveAsPdf && !this.documentInfo.isModifiable);
-    this.set('settings.scaling.available', !knownSizeToSaveAsPdf);
+        !knownSizeToSaveAsPdf && !this.documentSettings.isModifiable &&
+            !this.documentSettings.isScalingDisabled);
+    this.set(
+        'settings.scaling.available',
+        !knownSizeToSaveAsPdf && !this.documentSettings.isScalingDisabled);
     const caps = (!!this.destination && !!this.destination.capabilities) ?
         this.destination.capabilities.printer :
         null;
@@ -428,29 +434,30 @@
   },
 
   /** @private */
-  updateSettingsAvailabilityFromDocumentInfo_: function() {
-    this.set('settings.margins.available', this.documentInfo.isModifiable);
+  updateSettingsAvailabilityFromDocumentSettings_: function() {
+    this.set('settings.margins.available', this.documentSettings.isModifiable);
     this.set(
-        'settings.customMargins.available', this.documentInfo.isModifiable);
+        'settings.customMargins.available', this.documentSettings.isModifiable);
     this.set(
-        'settings.cssBackground.available', this.documentInfo.isModifiable);
+        'settings.cssBackground.available', this.documentSettings.isModifiable);
     this.set(
         'settings.selectionOnly.available',
-        this.documentInfo.isModifiable && this.documentInfo.hasSelection);
+        this.documentSettings.isModifiable &&
+            this.documentSettings.hasSelection);
     this.set(
         'settings.headerFooter.available', this.isHeaderFooterAvailable_());
     this.set(
         'settings.rasterize.available',
-        !this.documentInfo.isModifiable && !cr.isWindows && !cr.isMac);
+        !this.documentSettings.isModifiable && !cr.isWindows && !cr.isMac);
 
     if (this.destination) {
-      this.updateSettingsAvailabilityFromDestinationAndDocumentInfo_();
+      this.updateSettingsAvailabilityFromDestinationAndDocumentSettings_();
     }
   },
 
   /** @private */
   updateHeaderFooterAvailable_: function() {
-    if (this.documentInfo === undefined) {
+    if (this.documentSettings === undefined) {
       return;
     }
 
@@ -464,7 +471,7 @@
    */
   isHeaderFooterAvailable_: function() {
     // Always unavailable for PDFs.
-    if (!this.documentInfo.isModifiable) {
+    if (!this.documentSettings.isModifiable) {
       return false;
     }
 
@@ -483,10 +490,10 @@
             this.getSettingValue('margins'));
     switch (marginsType) {
       case print_preview.ticket_items.MarginsTypeValue.DEFAULT:
-        available = !this.documentInfo.margins ||
-            this.documentInfo.margins.get(
+        available = !this.margins ||
+            this.margins.get(
                 print_preview.ticket_items.CustomMarginsOrientation.TOP) > 0 ||
-            this.documentInfo.margins.get(
+            this.margins.get(
                 print_preview.ticket_items.CustomMarginsOrientation.BOTTOM) > 0;
         break;
       case print_preview.ticket_items.MarginsTypeValue.NO_MARGINS:
@@ -510,8 +517,8 @@
    */
   isLayoutAvailable_: function(caps) {
     if (!caps || !caps.page_orientation || !caps.page_orientation.option ||
-        !this.documentInfo.isModifiable ||
-        this.documentInfo.hasCssMediaStyles) {
+        !this.documentSettings.isModifiable ||
+        this.documentSettings.hasCssMediaStyles) {
       return false;
     }
     let hasAutoOrPortraitOption = false;
@@ -806,7 +813,7 @@
       collate: this.getSettingValue('collate'),
       shouldPrintBackgrounds: this.getSettingValue('cssBackground'),
       shouldPrintSelectionOnly: false,  // only used in print preview
-      previewModifiable: this.documentInfo.isModifiable,
+      previewModifiable: this.documentSettings.isModifiable,
       printToPDF: destination.id ==
           print_preview.Destination.GooglePromotedId.SAVE_AS_PDF,
       printToGoogleDrive:
@@ -822,8 +829,8 @@
       dpiDefault: (dpi && 'is_default' in dpi) ? dpi.is_default : false,
       deviceName: destination.id,
       fitToPageEnabled: this.getSettingValue('fitToPage'),
-      pageWidth: this.documentInfo.pageSize.width,
-      pageHeight: this.documentInfo.pageSize.height,
+      pageWidth: this.pageSize.width,
+      pageHeight: this.pageSize.height,
       showSystemDialog: showSystemDialog,
     };
 
diff --git a/chrome/browser/resources/print_preview/new/pages_settings.html b/chrome/browser/resources/print_preview/new/pages_settings.html
index dbd3d2e..38fdb3c 100644
--- a/chrome/browser/resources/print_preview/new/pages_settings.html
+++ b/chrome/browser/resources/print_preview/new/pages_settings.html
@@ -3,7 +3,6 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_radio_button/cr_radio_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_radio_group/cr_radio_group.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
-<link rel="import" href="../data/document_info.html">
 <link rel="import" href="../print_preview_utils.html">
 <link rel="import" href="input_behavior.html">
 <link rel="import" href="print_preview_shared_css.html">
@@ -62,8 +61,7 @@
                 tabindex="[[computeTabIndex_(optionSelected_)]]"
                 placeholder="$i18n{examplePageRangeText}"
                 invalid="[[hasError_]]"
-                error-message="[[getHintMessage_(errorState_,
-                    documentInfo.pageCount)]]">
+                error-message="[[getHintMessage_(errorState_, pageCount)]]">
             </cr-input>
           </cr-radio-button>
         </cr-radio-group>
diff --git a/chrome/browser/resources/print_preview/new/pages_settings.js b/chrome/browser/resources/print_preview/new/pages_settings.js
index 4807ff9..bbc06f28 100644
--- a/chrome/browser/resources/print_preview/new/pages_settings.js
+++ b/chrome/browser/resources/print_preview/new/pages_settings.js
@@ -40,8 +40,7 @@
   behaviors: [SettingsBehavior, print_preview_new.InputBehavior],
 
   properties: {
-    /** @type {!print_preview.DocumentInfo} */
-    documentInfo: Object,
+    pageCount: Number,
 
     /** @private {string} */
     inputString_: {
@@ -52,7 +51,7 @@
     /** @private {!Array<number>} */
     allPagesArray_: {
       type: Array,
-      computed: 'computeAllPagesArray_(documentInfo.pageCount)',
+      computed: 'computeAllPagesArray_(pageCount)',
     },
 
     /** @private {string} */
@@ -148,16 +147,7 @@
    * @private
    */
   computeAllPagesArray_: function() {
-    // This computed function will unnecessarily get triggered if
-    // this.documentInfo is set to a new object, which happens whenever the
-    // preview refreshes, but the page count is the same as before. We do not
-    // want to trigger all observers unnecessarily.
-    if (!!this.allPagesArray_ &&
-        this.allPagesArray_.length == this.documentInfo.pageCount) {
-      return this.allPagesArray_;
-    }
-
-    const array = new Array(this.documentInfo.pageCount);
+    const array = new Array(this.pageCount);
     for (let i = 0; i < array.length; i++) {
       array[i] = i + 1;
     }
@@ -452,11 +442,10 @@
           'pageRangeSyntaxInstruction',
           loadTimeData.getString('examplePageRangeText'));
     } else {
-      formattedMessage = (this.documentInfo === undefined) ?
+      formattedMessage = (this.pageCount === 0) ?
           '' :
           loadTimeData.getStringF(
-              'pageRangeLimitInstructionWithValue',
-              this.documentInfo.pageCount);
+              'pageRangeLimitInstructionWithValue', this.pageCount);
     }
     return formattedMessage.replace(/<\/b>|<b>/g, '');
   },
diff --git a/chrome/browser/resources/print_preview/new/preview_area.html b/chrome/browser/resources/print_preview/new/preview_area.html
index 4146449..baeebf38 100644
--- a/chrome/browser/resources/print_preview/new/preview_area.html
+++ b/chrome/browser/resources/print_preview/new/preview_area.html
@@ -11,7 +11,6 @@
 <link rel="import" href="../print_preview_utils.html">
 <link rel="import" href="../data/coordinate2d.html">
 <link rel="import" href="../data/destination.html">
-<link rel="import" href="../data/document_info.html">
 <link rel="import" href="../data/margins.html">
 <link rel="import" href="../data/printable_area.html">
 <link rel="import" href="../data/size.html">
@@ -132,8 +131,8 @@
           data="chrome://print/dummy.pdf"></object>
     </div>
     <print-preview-margin-control-container id="marginControlContainer"
-        page-size="[[documentInfo.pageSize]]" settings="{{settings}}"
-        document-margins="[[documentInfo.margins]]"
+        page-size="[[pageSize]]" settings="{{settings}}"
+        document-margins="[[margins]]"
         measurement-system="[[measurementSystem]]" state="[[state]]"
         preview-loaded="[[previewLoaded_]]"
         on-text-focus-position="onTextFocusPosition_"
diff --git a/chrome/browser/resources/print_preview/new/preview_area.js b/chrome/browser/resources/print_preview/new/preview_area.js
index 58f339b..a2f4ec6 100644
--- a/chrome/browser/resources/print_preview/new/preview_area.js
+++ b/chrome/browser/resources/print_preview/new/preview_area.js
@@ -33,21 +33,26 @@
   behaviors: [WebUIListenerBehavior, SettingsBehavior, I18nBehavior],
 
   properties: {
-    /** @type {print_preview.DocumentInfo} */
-    documentInfo: Object,
-
     /** @type {print_preview.Destination} */
     destination: Object,
 
+    documentModifiable: Boolean,
+
+    /** @type {print_preview.Margins} */
+    margins: Object,
+
+    /** @type {?print_preview.MeasurementSystem} */
+    measurementSystem: Object,
+
+    /** @type {!print_preview.Size} */
+    pageSize: Object,
+
     /** @type {!print_preview_new.State} */
     state: {
       type: Number,
       observer: 'onStateChanged_',
     },
 
-    /** @type {?print_preview.MeasurementSystem} */
-    measurementSystem: Object,
-
     /** @private {boolean} Whether the plugin is loaded */
     pluginLoaded_: {
       type: Boolean,
@@ -122,10 +127,6 @@
   attached: function() {
     this.nativeLayer_ = print_preview.NativeLayer.getInstance();
     this.addWebUIListener(
-        'page-count-ready', this.onPageCountReady_.bind(this));
-    this.addWebUIListener(
-        'page-layout-ready', this.onPageLayoutReady_.bind(this));
-    this.addWebUIListener(
         'page-preview-ready', this.onPagePreviewReady_.bind(this));
 
     this.pluginProxy_ = print_preview_new.PluginProxy.getInstance();
@@ -303,7 +304,7 @@
     this.documentReady_ = false;
     this.getPreview_().then(
         previewUid => {
-          if (!this.documentInfo.isModifiable) {
+          if (!this.documentModifiable) {
             this.onPreviewStart_(previewUid, -1);
           }
           this.documentReady_ = true;
@@ -367,68 +368,7 @@
     this.pluginProxy_.resetPrintPreviewMode(
         previewUid, index, !this.getSettingValue('color'),
         /** @type {!Array<number>} */ (this.getSettingValue('pages')),
-        this.documentInfo.isModifiable);
-  },
-
-  /**
-   * Called when the page layout of the document is ready. Always occurs
-   * as a result of a preview request.
-   * @param {{marginTop: number,
-   *          marginLeft: number,
-   *          marginBottom: number,
-   *          marginRight: number,
-   *          contentWidth: number,
-   *          contentHeight: number,
-   *          printableAreaX: number,
-   *          printableAreaY: number,
-   *          printableAreaWidth: number,
-   *          printableAreaHeight: number,
-   *        }} pageLayout Layout information about the document.
-   * @param {boolean} hasCustomPageSizeStyle Whether this document has a
-   *     custom page size or style to use.
-   * @private
-   */
-  onPageLayoutReady_: function(pageLayout, hasCustomPageSizeStyle) {
-    const origin = new print_preview.Coordinate2d(
-        pageLayout.printableAreaX, pageLayout.printableAreaY);
-    const size = new print_preview.Size(
-        pageLayout.printableAreaWidth, pageLayout.printableAreaHeight);
-
-    const margins = new print_preview.Margins(
-        Math.round(pageLayout.marginTop), Math.round(pageLayout.marginRight),
-        Math.round(pageLayout.marginBottom), Math.round(pageLayout.marginLeft));
-
-    const o = print_preview.ticket_items.CustomMarginsOrientation;
-    const pageSize = new print_preview.Size(
-        pageLayout.contentWidth + margins.get(o.LEFT) + margins.get(o.RIGHT),
-        pageLayout.contentHeight + margins.get(o.TOP) + margins.get(o.BOTTOM));
-
-    this.documentInfo.updatePageInfo(
-        new print_preview.PrintableArea(origin, size), pageSize,
-        hasCustomPageSizeStyle, margins);
-    this.notifyPath('documentInfo.printableArea');
-    this.notifyPath('documentInfo.pageSize');
-    this.notifyPath('documentInfo.margins');
-    this.notifyPath('documentInfo.hasCssMediaStyles');
-  },
-
-  /**
-   * Called when the document page count is received from the native layer.
-   * Always occurs as a result of a preview request.
-   * @param {number} pageCount The document's page count.
-   * @param {number} previewResponseId The request ID for this page count event.
-   * @param {number} fitToPageScaling The scaling required to fit the document
-   *     to page.
-   * @private
-   */
-  onPageCountReady_: function(pageCount, previewResponseId, fitToPageScaling) {
-    if (this.inFlightRequestId_ != previewResponseId) {
-      return;
-    }
-    this.documentInfo.updatePageCount(pageCount);
-    this.documentInfo.updateFitToPageScaling(fitToPageScaling);
-    this.notifyPath('documentInfo.pageCount');
-    this.notifyPath('documentInfo.fitToPageScaling');
+        this.documentModifiable);
   },
 
   /**
@@ -462,7 +402,7 @@
     this.$.marginControlContainer.updateTranslationTransform(
         new print_preview.Coordinate2d(pageX, pageY));
     this.$.marginControlContainer.updateScaleTransform(
-        pageWidth / this.documentInfo.pageSize.width);
+        pageWidth / this.pageSize.width);
     this.$.marginControlContainer.updateClippingMask(
         new print_preview.Size(viewportWidth, viewportHeight));
   },
@@ -601,13 +541,12 @@
                print_preview.ticket_items.CustomMarginsOrientation)) {
         const key = print_preview_new.MARGIN_KEY_MAP.get(side);
         // If custom margins are undefined, return and wait for them to be set.
-        if (customMargins[key] === undefined || !this.documentInfo ||
-            !this.documentInfo.margins) {
+        if (customMargins[key] === undefined || !this.margins) {
           return;
         }
 
         // Start a preview request if the margins actually changed.
-        if (this.documentInfo.margins.get(side) != customMargins[key]) {
+        if (this.margins.get(side) != customMargins[key]) {
           this.onSettingsChanged_();
           break;
         }
@@ -684,7 +623,7 @@
       pagesPerSheet: this.getSettingValue('pagesPerSheet'),
       isFirstRequest: this.inFlightRequestId_ == 0,
       requestID: this.inFlightRequestId_,
-      previewModifiable: this.documentInfo.isModifiable,
+      previewModifiable: this.documentModifiable,
       fitToPageEnabled: this.getSettingValue('fitToPage'),
       scaleFactor: parseInt(this.getSettingValue('scaling'), 10),
       shouldPrintBackgrounds: this.getSettingValue('cssBackground'),
@@ -717,6 +656,8 @@
         print_preview.ticket_items.MarginsTypeValue.CUSTOM) {
       ticket.marginsCustom = this.getSettingValue('customMargins');
     }
+
+    this.fire('preview-start', this.inFlightRequestId_);
     return this.nativeLayer_.getPreview(JSON.stringify(ticket));
   },
 });
diff --git a/chrome/browser/resources/print_preview/new/scaling_settings.html b/chrome/browser/resources/print_preview/new/scaling_settings.html
index bd6c505..29fac7a4 100644
--- a/chrome/browser/resources/print_preview/new/scaling_settings.html
+++ b/chrome/browser/resources/print_preview/new/scaling_settings.html
@@ -1,7 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
-<link rel="import" href="../data/document_info.html">
 <link rel="import" href="number_settings_section.html">
 <link rel="import" href="print_preview_shared_css.html">
 <link rel="import" href="settings_behavior.html">
diff --git a/chrome/browser/resources/print_preview/new/scaling_settings.js b/chrome/browser/resources/print_preview/new/scaling_settings.js
index 59a83525..b69e116 100644
--- a/chrome/browser/resources/print_preview/new/scaling_settings.js
+++ b/chrome/browser/resources/print_preview/new/scaling_settings.js
@@ -26,8 +26,10 @@
   behaviors: [SettingsBehavior],
 
   properties: {
-    /** @type {Object} */
-    documentInfo: Object,
+    fitToPageScaling: {
+      type: Number,
+      observer: 'onFitToPageScalingSet_',
+    },
 
     /** @private {string} */
     currentValue_: {
@@ -53,7 +55,6 @@
 
   observers: [
     'onFitToPageSettingChange_(settings.fitToPage.value)',
-    'onFitToPageScalingSet_(documentInfo.fitToPageScaling)',
     'onScalingSettingChanged_(settings.scaling.value)',
     'onScalingValidChanged_(settings.scaling.valid)',
   ],
@@ -102,9 +103,7 @@
    * @private
    */
   getFitToPageScalingDisplayValue_: function() {
-    return this.documentInfo.fitToPageScaling > 0 ?
-        this.documentInfo.fitToPageScaling.toString() :
-        '';
+    return this.fitToPageScaling > 0 ? this.fitToPageScaling.toString() : '';
   },
 
   /** @private */
diff --git a/chrome/browser/resources/print_preview/print_preview_utils.js b/chrome/browser/resources/print_preview/print_preview_utils.js
index 7fbc963..a17e8f62 100644
--- a/chrome/browser/resources/print_preview/print_preview_utils.js
+++ b/chrome/browser/resources/print_preview/print_preview_utils.js
@@ -3,46 +3,9 @@
 // found in the LICENSE file.
 
 /**
- * @param {string} toTest The string to be tested.
- * @return {boolean} True if |toTest| contains only digits. Leading and trailing
- *     whitespace is allowed.
- */
-function isInteger(toTest) {
-  const numericExp = /^\s*[0-9]+\s*$/;
-  return numericExp.test(toTest);
-}
-
-/**
- * Returns true if |value| is a valid non zero positive integer.
- * @param {string} value The string to be tested.
- * @return {boolean} true if the |value| is valid non zero positive integer.
- */
-function isPositiveInteger(value) {
-  return isInteger(value) && parseInt(value, 10) > 0;
-}
-
-/**
- * Returns true if the contents of the two arrays are equal.
- * @param {Array<{from: number, to: number}>} array1 The first array.
- * @param {Array<{from: number, to: number}>} array2 The second array.
- * @return {boolean} true if the arrays are equal.
- */
-function areArraysEqual(array1, array2) {
-  if (array1.length != array2.length) {
-    return false;
-  }
-  for (let i = 0; i < array1.length; i++) {
-    if (array1[i] !== array2[i]) {
-      return false;
-    }
-  }
-  return true;
-}
-
-/**
  * Returns true if the contents of the two page ranges are equal.
- * @param {Array} array1 The first array.
- * @param {Array} array2 The second array.
+ * @param {!Array<{ to: number, from: number }>} array1 The first array.
+ * @param {!Array<{ to: number, from: number }>} array2 The second array.
  * @return {boolean} true if the arrays are equal.
  */
 function areRangesEqual(array1, array2) {
@@ -58,179 +21,6 @@
 }
 
 /**
- * Removes duplicate elements from |inArray| and returns a new array.
- * |inArray| is not affected. It assumes that |inArray| is already sorted.
- * @param {!Array<number>} inArray The array to be processed.
- * @return {!Array<number>} The array after processing.
- */
-function removeDuplicates(inArray) {
-  const out = [];
-
-  if (inArray.length == 0) {
-    return out;
-  }
-
-  out.push(inArray[0]);
-  for (let i = 1; i < inArray.length; ++i) {
-    if (inArray[i] != inArray[i - 1]) {
-      out.push(inArray[i]);
-    }
-  }
-  return out;
-}
-
-/** @enum {number} */
-const PageRangeStatus = {
-  NO_ERROR: 0,
-  SYNTAX_ERROR: -1,
-  LIMIT_ERROR: -2
-};
-
-/**
- * Returns a list of ranges in |pageRangeText|. The ranges are
- * listed in the order they appear in |pageRangeText| and duplicates are not
- * eliminated. If |pageRangeText| is not valid, PageRangeStatus.SYNTAX_ERROR
- * is returned.
- * A valid selection has a parsable format and every page identifier is
- * greater than 0 unless wildcards are used(see examples).
- * If a page is greater than |totalPageCount|, PageRangeStatus.LIMIT_ERROR
- * is returned.
- * If |totalPageCount| is 0 or undefined function uses impossibly large number
- * instead.
- * Wildcard the first number must be larger than 0 and less or equal then
- * |totalPageCount|. If it's missed then 1 is used as the first number.
- * Wildcard the second number must be larger then the first number. If it's
- * missed then |totalPageCount| is used as the second number.
- * Example: "1-4, 9, 3-6, 10, 11" is valid, assuming |totalPageCount| >= 11.
- * Example: "1-4, -6" is valid, assuming |totalPageCount| >= 6.
- * Example: "2-" is valid, assuming |totalPageCount| >= 2, means from 2 to the
- *          end.
- * Example: "4-2, 11, -6" is invalid.
- * Example: "-" is valid, assuming |totalPageCount| >= 1.
- * Example: "1-4dsf, 11" is invalid regardless of |totalPageCount|.
- * @param {string} pageRangeText The text to be checked.
- * @param {number=} opt_totalPageCount The total number of pages.
- * @return {!PageRangeStatus|!Array<{from: number, to: number}>}
- */
-function pageRangeTextToPageRanges(pageRangeText, opt_totalPageCount) {
-  if (pageRangeText == '') {
-    return [];
-  }
-
-  const MAX_PAGE_NUMBER = 1000000000;
-  const totalPageCount =
-      opt_totalPageCount ? opt_totalPageCount : MAX_PAGE_NUMBER;
-
-  const regex = /^\s*([0-9]*)\s*-\s*([0-9]*)\s*$/;
-  const parts = pageRangeText.split(/,|\u3001/);
-
-  const pageRanges = [];
-  for (let i = 0; i < parts.length; ++i) {
-    const match = parts[i].match(regex);
-    if (match) {
-      if (!isPositiveInteger(match[1]) && match[1] !== '') {
-        return PageRangeStatus.SYNTAX_ERROR;
-      }
-      if (!isPositiveInteger(match[2]) && match[2] !== '') {
-        return PageRangeStatus.SYNTAX_ERROR;
-      }
-      const from = match[1] ? parseInt(match[1], 10) : 1;
-      const to = match[2] ? parseInt(match[2], 10) : totalPageCount;
-      if (from > to) {
-        return PageRangeStatus.SYNTAX_ERROR;
-      }
-      if (to > totalPageCount) {
-        return PageRangeStatus.LIMIT_ERROR;
-      }
-      pageRanges.push({'from': from, 'to': to});
-    } else {
-      if (!isPositiveInteger(parts[i])) {
-        return PageRangeStatus.SYNTAX_ERROR;
-      }
-      const singlePageNumber = parseInt(parts[i], 10);
-      if (singlePageNumber > totalPageCount) {
-        return PageRangeStatus.LIMIT_ERROR;
-      }
-      pageRanges.push({'from': singlePageNumber, 'to': singlePageNumber});
-    }
-  }
-  return pageRanges;
-}
-
-/**
- * Returns a list of pages defined by |pagesRangeText|. The pages are
- * listed in the order they appear in |pageRangeText| and duplicates are not
- * eliminated. If |pageRangeText| is not valid according or
- * |totalPageCount| undefined [1,2,...,totalPageCount] is returned.
- * See pageRangeTextToPageRanges for details.
- * @param {string} pageRangeText The text to be checked.
- * @param {number} totalPageCount The total number of pages.
- * @return {!Array<number>} A list of all pages.
- */
-function pageRangeTextToPageList(pageRangeText, totalPageCount) {
-  const pageRanges = pageRangeTextToPageRanges(pageRangeText, totalPageCount);
-  const pageList = [];
-  if (Array.isArray(pageRanges)) {
-    for (let i = 0; i < pageRanges.length; ++i) {
-      for (let j = pageRanges[i].from;
-           j <= Math.min(pageRanges[i].to, totalPageCount); ++j) {
-        pageList.push(j);
-      }
-    }
-  }
-  if (pageList.length == 0) {
-    for (let j = 1; j <= totalPageCount; ++j) {
-      pageList.push(j);
-    }
-  }
-  return pageList;
-}
-
-/**
- * @param {!Array<number>} pageList The list to be processed.
- * @return {!Array<number>} The contents of |pageList| in ascending order and
- *     without any duplicates. |pageList| is not affected.
- */
-function pageListToPageSet(pageList) {
-  let pageSet = [];
-  if (pageList.length == 0) {
-    return pageSet;
-  }
-  pageSet = pageList.slice(0);
-  pageSet.sort(function(a, b) {
-    return /** @type {number} */ (a) - /** @type {number} */ (b);
-  });
-  pageSet = removeDuplicates(pageSet);
-  return pageSet;
-}
-
-/**
- * @param {!HTMLElement} element Element to check for visibility.
- * @return {boolean} Whether the given element is visible.
- */
-function getIsVisible(element) {
-  return !element.hidden;
-}
-
-/**
- * Shows or hides an element.
- * @param {!HTMLElement} element Element to show or hide.
- * @param {boolean} isVisible Whether the element should be visible or not.
- */
-function setIsVisible(element, isVisible) {
-  element.hidden = !isVisible;
-}
-
-/**
- * @param {!Array} array Array to check for item.
- * @param {*} item Item to look for in array.
- * @return {boolean} Whether the item is in the array.
- */
-function arrayContains(array, item) {
-  return array.indexOf(item) != -1;
-}
-
-/**
  * @param {!Array<!{locale: string, value: string}>} localizedStrings An array
  *     of strings with corresponding locales.
  * @param {string} locale Locale to look the string up for.
diff --git a/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs b/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs
deleted file mode 100644
index 17fa25be..0000000
--- a/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * Test fixture for print preview utils.
- * @constructor
- * @extends {testing.Test}
- */
-function PrintPreviewUtilsUnitTest () {
-  testing.Test.call(this);
-}
-
-PrintPreviewUtilsUnitTest.prototype = {
-  __proto__: testing.Test.prototype,
-
-  /** @override */
-  extraLibraries: [
-    'print_preview_utils.js',
-  ]
-};
-
-TEST_F('PrintPreviewUtilsUnitTest', 'IsInteger', function() {
-  assertFalse(isInteger("  abc "));
-  assertFalse(isInteger("-7"));
-  assertFalse(isInteger("7.0"));
-  assertFalse(isInteger("a7a"));
-
-  assertTrue(isInteger("0"));
-  assertTrue(isInteger(" 100  "));
-  assertTrue(isInteger("0055 "));
-});
-
-TEST_F('PrintPreviewUtilsUnitTest', 'IsPositiveInteger', function() {
-  assertTrue(isPositiveInteger("100"));
-  assertTrue(isPositiveInteger("0055"));
-
-  assertFalse(isPositiveInteger("0"));
-  assertFalse(isPositiveInteger("-100"));
-  assertFalse(isPositiveInteger("sdfs"));
-});
-
-TEST_F('PrintPreviewUtilsUnitTest', 'AreArraysEqual', function() {
-  assertTrue(areArraysEqual([2,4,6,8,10], [2,4,6,8,10]));
-  assertTrue(areArraysEqual([], []));
-
-  assertFalse(areArraysEqual([2,4,6,8,10,12], [2,4,6,8,10]));
-  assertFalse(areArraysEqual([], [2,4,6,8,10]));
-});
-
-TEST_F('PrintPreviewUtilsUnitTest', 'RemoveDuplicates', function() {
-  var array1 = [1,2,2,3,6,6,6,7,9,10];
-  assertTrue(areArraysEqual(removeDuplicates(array1), [1,2,3,6,7,9,10]));
-});
-
-TEST_F('PrintPreviewUtilsUnitTest', 'PageRanges', function() {
-  function assertRangesEqual(simpleRange1, range2) {
-    var range1 = []
-    for (var i = 0; i < simpleRange1.length; i++) {
-      var from;
-      var to;
-      if (Array.isArray(simpleRange1[i])) {
-        from = simpleRange1[i][0];
-        to = simpleRange1[i][1];
-      } else {
-        from = simpleRange1[i];
-        to = simpleRange1[i];
-      }
-      range1.push({'from': from, 'to': to});
-    }
-    assertTrue(areRangesEqual(range1, range2));
-  };
-  assertRangesEqual([1, 2, 3, 1, 56],
-                    pageRangeTextToPageRanges("1,2,3,1,56", 100));
-  assertRangesEqual([[1, 3],[6, 9], [6, 10]],
-                    pageRangeTextToPageRanges("1-3, 6-9,6-10 ", 100));
-  assertRangesEqual([[10, 100]],
-                    pageRangeTextToPageRanges("10-", 100));
-  assertRangesEqual([[10, 100000]],
-                    pageRangeTextToPageRanges("10-100000", 100000));
-  assertRangesEqual([[1, 100]],
-                    pageRangeTextToPageRanges("-", 100));
-  assertRangesEqual([1, 2],
-                    pageRangeTextToPageRanges("1,2", undefined));
-  assertRangesEqual([[1, 1000000000]],
-                    pageRangeTextToPageRanges("-", null));
-  assertRangesEqual([[1, 1000000000]],
-                    pageRangeTextToPageRanges("-", 0));
-
-  // https://crbug.com/806165
-  assertRangesEqual([1, 2, 3, 1, 56],
-                    pageRangeTextToPageRanges("1\u30012\u30013\u30011\u300156", 100));
-  assertRangesEqual([1, 2, 3, 1, 56],
-                    pageRangeTextToPageRanges("1,2,3\u30011\u300156", 100));
-});
-
-TEST_F('PrintPreviewUtilsUnitTest', 'InvalidPageRanges', function() {
-  assertEquals(PageRangeStatus.LIMIT_ERROR,
-      pageRangeTextToPageRanges("10-100000", 100));
-  assertEquals(PageRangeStatus.LIMIT_ERROR,
-      pageRangeTextToPageRanges("1,100000", 100));
-  assertEquals(PageRangeStatus.SYNTAX_ERROR,
-      pageRangeTextToPageRanges("1,2,0,56", 100));
-  assertEquals(PageRangeStatus.SYNTAX_ERROR,
-      pageRangeTextToPageRanges("-1,1,2,,56", 100));
-  assertEquals(PageRangeStatus.SYNTAX_ERROR,
-      pageRangeTextToPageRanges("1,2,56-40", 100));
-  assertEquals(PageRangeStatus.LIMIT_ERROR,
-      pageRangeTextToPageRanges("101-110", 100));
-
-  assertEquals(PageRangeStatus.SYNTAX_ERROR,
-      pageRangeTextToPageRanges("1\u30012\u30010\u300156", 100));
-  assertEquals(PageRangeStatus.SYNTAX_ERROR,
-      pageRangeTextToPageRanges("-1,1,2\u3001\u300156", 100));
-});
-
-TEST_F('PrintPreviewUtilsUnitTest', 'PageRangeTextToPageList', function() {
-  assertTrue(areArraysEqual([1],
-                            pageRangeTextToPageList("1", 10)));
-  assertTrue(areArraysEqual([1,2,3,4],
-                            pageRangeTextToPageList("1-4", 10)));
-  assertTrue(areArraysEqual([1,2,3,4,2,3,4],
-                            pageRangeTextToPageList("1-4, 2-4", 10)));
-  assertTrue(areArraysEqual([1,2,5,7,8,9,10,2,2,3],
-                            pageRangeTextToPageList("1-2, 5, 7-10, 2, 2, 3",
-                                                    10)));
-  assertTrue(areArraysEqual([5,6,7,8,9,10],
-                            pageRangeTextToPageList("5-", 10)));
-  assertTrue(areArraysEqual([],
-                            pageRangeTextToPageList("1-4", undefined)));
-  assertTrue(areArraysEqual([1,2,3,4,5,6,7,8,9,10],
-                            pageRangeTextToPageList("1-abcd", 10)));
-});
-
-TEST_F('PrintPreviewUtilsUnitTest', 'PageListToPageSet', function() {
-  assertTrue(areArraysEqual([1,2,3,4], pageListToPageSet([4,3,2,1,1,1])));
-  assertTrue(areArraysEqual([1,2,3,4], pageListToPageSet([1,2,2,3,4,1,1,1])));
-  assertTrue(areArraysEqual([], pageListToPageSet([])));
-});
diff --git a/chrome/browser/resources/safe_browsing/download_file_types.asciipb b/chrome/browser/resources/safe_browsing/download_file_types.asciipb
index e0d80fa..760a573 100644
--- a/chrome/browser/resources/safe_browsing/download_file_types.asciipb
+++ b/chrome/browser/resources/safe_browsing/download_file_types.asciipb
@@ -8,7 +8,7 @@
 ##
 ## Top level settings
 ##
-version_id: 25
+version_id: 26
 sampled_ping_probability: 0.01
 max_archived_binaries_to_report: 10
 default_file_type {
@@ -281,180 +281,210 @@
   uma_value: 195
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r01"
   uma_value: 196
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r02"
   uma_value: 197
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r03"
   uma_value: 198
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r04"
   uma_value: 199
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r05"
   uma_value: 200
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r06"
   uma_value: 201
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r07"
   uma_value: 202
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r08"
   uma_value: 203
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r09"
   uma_value: 204
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r10"
   uma_value: 205
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r11"
   uma_value: 206
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r12"
   uma_value: 207
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r13"
   uma_value: 208
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r14"
   uma_value: 209
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r15"
   uma_value: 210
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r16"
   uma_value: 211
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r17"
   uma_value: 212
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r18"
   uma_value: 213
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r19"
   uma_value: 214
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r20"
   uma_value: 215
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r21"
   uma_value: 216
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r22"
   uma_value: 217
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r23"
   uma_value: 218
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r24"
   uma_value: 219
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r25"
   uma_value: 220
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r26"
   uma_value: 221
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r27"
   uma_value: 222
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r28"
   uma_value: 223
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "r29"
   uma_value: 224
   is_archive: true
   ping_setting: FULL_PING
+  inspection_type: RAR
 }
 file_types {
   extension: "rar"
diff --git a/chrome/browser/resources/settings/languages_page/add_languages_dialog.js b/chrome/browser/resources/settings/languages_page/add_languages_dialog.js
index 6579954..30527e2 100644
--- a/chrome/browser/resources/settings/languages_page/add_languages_dialog.js
+++ b/chrome/browser/resources/settings/languages_page/add_languages_dialog.js
@@ -48,25 +48,25 @@
   /** @override */
   attached: function() {
     this.$.dialog.showModal();
-    this.becomeActiveFindShortcutListener();
-  },
-
-  /** @override */
-  detached: function() {
-    this.removeSelfAsFindShortcutListener();
   },
 
   // Override FindShortcutBehavior methods.
   handleFindShortcut: function(modalContextOpen) {
     // Assumes this is the only open modal.
     const searchInput = this.$.search.getSearchInput();
-    if (searchInput != this.$.search.shadowRoot.activeElement) {
-      searchInput.scrollIntoViewIfNeeded();
+    searchInput.scrollIntoViewIfNeeded();
+    if (!this.searchInputHasFocus()) {
       searchInput.focus();
     }
     return true;
   },
 
+  // Override FindShortcutBehavior methods.
+  searchInputHasFocus: function() {
+    return this.$.search.getSearchInput() ==
+        this.$.search.shadowRoot.activeElement;
+  },
+
   /**
    * @param {!CustomEvent} e
    * @private
diff --git a/chrome/browser/resources/settings/settings_page/settings_subpage.js b/chrome/browser/resources/settings/settings_page/settings_subpage.js
index 0a53d04b..132cfcb 100644
--- a/chrome/browser/resources/settings/settings_page/settings_subpage.js
+++ b/chrome/browser/resources/settings/settings_page/settings_subpage.js
@@ -59,6 +59,9 @@
   /** @private {boolean} */
   lastActiveValue_: false,
 
+  // Override FindShortcutBehavior property.
+  findShortcutListenOnAttach: false,
+
   /** @override */
   attached: function() {
     if (!!this.searchLabel) {
@@ -138,11 +141,13 @@
     if (modalContextOpen) {
       return false;
     }
-    const subpageSearch = this.$$('cr-search-field');
-    const searchInput = subpageSearch.getSearchInput();
-    if (searchInput != subpageSearch.shadowRoot.activeElement) {
-      searchInput.focus();
-    }
+    this.$$('cr-search-field').getSearchInput().focus();
     return true;
   },
+
+  // Override FindShortcutBehavior methods.
+  searchInputHasFocus: function() {
+    const field = this.$$('cr-search-field');
+    return field.getSearchInput() == field.shadowRoot.activeElement;
+  },
 });
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chrome/browser/resources/settings/settings_ui/settings_ui.js
index 1a99156..c0f76f1dd 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.js
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.js
@@ -181,13 +181,10 @@
       scrollToTop(e.detail.bottom - this.$.container.clientHeight)
           .then(e.detail.callback);
     });
-
-    this.becomeActiveFindShortcutListener();
   },
 
   /** @override */
   detached: function() {
-    this.removeSelfAsFindShortcutListener();
     settings.resetRouteForTesting();
   },
 
@@ -224,6 +221,11 @@
     return true;
   },
 
+  // Override FindShortcutBehavior methods.
+  searchInputHasFocus: function() {
+    return this.$$('cr-toolbar').getSearchField().isSearchFocused();
+  },
+
   /**
    * @param {!CustomEvent} e
    * @private
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/OWNERS b/chrome/browser/resources/welcome/OWNERS
similarity index 100%
rename from chrome/browser/resources/welcome/onboarding_welcome/OWNERS
rename to chrome/browser/resources/welcome/OWNERS
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/BUILD.gn b/chrome/browser/resources/welcome/onboarding_welcome/BUILD.gn
index 01c8a3b..ed072f6e 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/BUILD.gn
+++ b/chrome/browser/resources/welcome/onboarding_welcome/BUILD.gn
@@ -26,7 +26,7 @@
   deps = [
     ":email_interstitial_proxy",
     ":welcome_browser_proxy",
-    "./email/:nux_email_proxy",
+    "./email/:email_app_proxy",
   ]
 }
 
@@ -57,7 +57,7 @@
     ":navigation_behavior",
     ":signin_view_proxy",
     ":welcome_browser_proxy",
-    "./email/:nux_email_proxy",
+    "./email/:email_app_proxy",
   ]
 }
 
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/BUILD.gn b/chrome/browser/resources/welcome/onboarding_welcome/email/BUILD.gn
index cb447f4..15c354b 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/BUILD.gn
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email/BUILD.gn
@@ -6,35 +6,22 @@
 
 js_type_check("closure_compile") {
   deps = [
-    ":email_chooser",
     ":nux_email",
   ]
 }
 
 js_library("nux_email") {
   deps = [
-    ":email_chooser",
+    ":email_app_proxy",
     "../:navigation_behavior",
+    "../shared:app_chooser",
     "../shared:nux_types",
   ]
 }
 
-js_library("email_chooser") {
+js_library("email_app_proxy") {
   deps = [
-    ":nux_email_proxy",
-    "../:navigation_behavior",
-    "../shared:bookmark_proxy",
-    "../shared:module_metrics_proxy",
-    "../shared:nux_types",
-    "//third_party/polymer/v1_0/components-chromium/iron-a11y-announcer:iron-a11y-announcer-extracted",
-    "//ui/webui/resources/js:cr",
-    "//ui/webui/resources/js:i18n_behavior",
-    "//ui/webui/resources/js:load_time_data",
-  ]
-}
-
-js_library("nux_email_proxy") {
-  deps = [
+    "../shared:app_proxy",
     "../shared:nux_types",
     "//ui/webui/resources/js:cr",
     "//ui/webui/resources/js:load_time_data",
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email_proxy.html b/chrome/browser/resources/welcome/onboarding_welcome/email/email_app_proxy.html
similarity index 82%
rename from chrome/browser/resources/welcome/onboarding_welcome/email/nux_email_proxy.html
rename to chrome/browser/resources/welcome/onboarding_welcome/email/email_app_proxy.html
index ee62604..3dc5c92b 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email_proxy.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email/email_app_proxy.html
@@ -1,4 +1,4 @@
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://welcome/shared/i18n_setup.html">
 <link rel="import" href="chrome://welcome/shared/module_metrics_proxy.html">
-<script src="nux_email_proxy.js"></script>
+<script src="email_app_proxy.js"></script>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/email_app_proxy.js b/chrome/browser/resources/welcome/onboarding_welcome/email/email_app_proxy.js
new file mode 100644
index 0000000..6538691
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email/email_app_proxy.js
@@ -0,0 +1,42 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('nux', function() {
+  /** @implements {nux.AppProxy} */
+  class EmailAppProxyImpl {
+    constructor() {
+      /** @private {number} */
+      this.savedProvider_;
+    }
+
+    /** @return {number} */
+    getSavedProvider() {
+      return this.savedProvider_;
+    }
+
+    /** @override */
+    cacheBookmarkIcon(emailProviderId) {
+      chrome.send('cacheEmailIcon', [emailProviderId]);
+    }
+
+    /** @override */
+    getAppList() {
+      return cr.sendWithPromise('getEmailList');
+    }
+
+    /** @override */
+    recordProviderSelected(providerId) {
+      this.savedProvider_ = providerId;
+      chrome.metricsPrivate.recordEnumerationValue(
+          'FirstRun.NewUserExperience.EmailProvidersSelection', providerId,
+          loadTimeData.getInteger('email_providers_enum_count'));
+    }
+  }
+
+  cr.addSingletonGetter(EmailAppProxyImpl);
+
+  return {
+    EmailAppProxyImpl: EmailAppProxyImpl,
+  };
+});
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.html b/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.html
deleted file mode 100644
index ba8b7929..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.html
+++ /dev/null
@@ -1,78 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-announcer/iron-a11y-announcer.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="../navigation_behavior.html">
-<link rel="import" href="../shared/bookmark_proxy.html">
-<link rel="import" href="../shared/chooser_shared_css.html">
-<link rel="import" href="../shared/step_indicator.html">
-<link rel="import" href="nux_email_proxy.html">
-
-<dom-module id="email-chooser">
-  <template>
-    <style include="chooser-shared-css paper-button-style">
-      .gmail {
-        background-image: -webkit-image-set(
-            url(chrome://welcome/images/gmail_1x.png) 1x,
-            url(chrome://welcome/images/gmail_2x.png) 2x);
-      }
-
-      .yahoo {
-        background-image: -webkit-image-set(
-            url(chrome://welcome/images/yahoo_1x.png) 1x,
-            url(chrome://welcome/images/yahoo_2x.png) 2x);
-      }
-
-      .aol {
-        background-image: -webkit-image-set(
-            url(chrome://welcome/images/aol_1x.png) 1x,
-            url(chrome://welcome/images/aol_2x.png) 2x);
-      }
-
-      .icloud {
-        background-image: -webkit-image-set(
-            url(chrome://welcome/images/icloud_1x.png) 1x,
-            url(chrome://welcome/images/icloud_2x.png) 2x);
-      }
-
-      .outlook {
-        background-image: -webkit-image-set(
-            url(chrome://welcome/images/outlook_1x.png) 1x,
-            url(chrome://welcome/images/outlook_2x.png) 2x);
-      }
-    </style>
-
-    <template is="dom-repeat" items="[[emailList_]]">
-      <button active$="[[getSelected_(item, selectedEmailProvider_)]]"
-          on-click="onEmailClick_" on-pointerdown="onEmailPointerDown_"
-          on-keyup="onEmailKeyUp_" class="option">
-        <div class="option-icon-shadow">
-          <div class$="[[item.icon]] option-icon"></div>
-        </div>
-        <div class="option-name">[[item.name]]</div>
-        <iron-icon icon="cr:check"></iron-icon>
-      </button>
-    </template>
-
-    <div class="button-bar">
-      <paper-button on-click="onNoThanksClicked_" id="noThanksButton">
-        $i18n{skip}
-      </paper-button>
-      <step-indicator model="[[indicatorModel]]"></step-indicator>
-      <div on-click="onActionButtonClicked_">
-        <paper-button class="action-button" on-click="onGetStartedClicked_"
-            disabled="[[!selectedEmailProvider_]]">
-          $i18n{next}
-          <iron-icon icon="cr:chevron-right"></iron-icon>
-        </paper-button>
-      </div>
-    </div>
-  </template>
-  <script src="email_chooser.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.js b/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.js
deleted file mode 100644
index cc5e22c..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/email_chooser.js
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-cr.exportPath('nuxEmail');
-
-/**
- * @typedef {{
- *   id: number,
- *   name: string,
- *   icon: string,
- *   url: string,
- *   bookmarkId: (string|undefined|null),
- * }}
- */
-nuxEmail.EmailProviderModel;
-
-Polymer({
-  is: 'email-chooser',
-
-  behaviors: [I18nBehavior],
-
-  properties: {
-    /**
-     * @type {!Array<!nux.BookmarkListItem>}
-     * @private
-     */
-    emailList_: Array,
-
-    /** @private */
-    finalized_: Boolean,
-
-    /** @type {nux.stepIndicatorModel} */
-    indicatorModel: Object,
-
-    /** @private {?nuxEmail.EmailProviderModel} */
-    selectedEmailProvider_: {
-      type: Object,
-      observer: 'onSelectedEmailProviderChange_',
-    },
-  },
-
-  /** @private {nux.NuxEmailProxy} */
-  emailProxy_: null,
-
-  /** @private {nux.BookmarkProxy} */
-  bookmarkProxy_: null,
-
-  /** @private {nux.BookmarkBarManager} */
-  bookmarkBarManager_: null,
-
-  /** @private {boolean} */
-  wasBookmarkBarShownOnInit_: false,
-
-  /** @private {Promise} */
-  listInitialized_: null,
-
-  /** @private {?nux.ModuleMetricsManager} */
-  metricsManager_: null,
-
-  /** @override */
-  attached: function() {
-    Polymer.RenderStatus.afterNextRender(this, function() {
-      Polymer.IronA11yAnnouncer.requestAvailability();
-    });
-  },
-
-  /** @override */
-  ready: function() {
-    this.emailProxy_ = nux.NuxEmailProxyImpl.getInstance();
-    this.bookmarkProxy_ = nux.BookmarkProxyImpl.getInstance();
-    this.bookmarkBarManager_ = nux.BookmarkBarManager.getInstance();
-    this.metricsManager_ =
-        new nux.ModuleMetricsManager(nux.EmailMetricsProxyImpl.getInstance());
-
-    this.listInitialized_ = this.emailProxy_.getEmailList().then(list => {
-      this.emailList_ = list;
-    });
-  },
-
-  onRouteEnter: function() {
-    this.wasBookmarkBarShownOnInit_ = this.bookmarkBarManager_.getShown();
-    this.metricsManager_.recordPageInitialized();
-    this.finalized_ = false;
-
-    assert(this.listInitialized_);
-    this.listInitialized_.then(() => {
-      // If selectedEmailProvider_ was never initialized, and not explicitly
-      // cancelled by the user at some point (in which case it would be null),
-      // then default to the first option.
-      if (this.selectedEmailProvider_ === undefined) {
-        this.selectedEmailProvider_ = this.emailList_[0];
-      }
-
-      if (this.selectedEmailProvider_) {
-        this.addBookmark_(this.selectedEmailProvider_);
-      }
-    });
-  },
-
-  onRouteExit: function() {
-    if (this.finalized_) {
-      return;
-    }
-    this.cleanUp_();
-    this.metricsManager_.recordBrowserBackOrForward();
-  },
-
-  onRouteUnload: function() {
-    if (this.finalized_) {
-      return;
-    }
-    this.cleanUp_();
-    this.metricsManager_.recordNavigatedAway();
-  },
-
-  /**
-   * Removes any bookarks and hides the bookmark bar when finalizing.
-   * @private
-   */
-  cleanUp_: function() {
-    this.finalized_ = true;
-    if (this.selectedEmailProvider_) {
-      this.revertBookmark_();
-      this.bookmarkBarManager_.setShown(this.wasBookmarkBarShownOnInit_);
-    }
-  },
-
-  /**
-   * Handle toggling the email selected.
-   * @param {!{model: {item: !nuxEmail.EmailProviderModel}}} e
-   * @private
-   */
-  onEmailClick_: function(e) {
-    if (this.getSelected_(e.model.item)) {
-      this.selectedEmailProvider_ = null;
-    } else {
-      this.selectedEmailProvider_ = e.model.item;
-    }
-
-    this.metricsManager_.recordClickedOption();
-  },
-
-  /**
-   * @param {!Event} e
-   * @private
-   */
-  onEmailPointerDown_: function(e) {
-    e.currentTarget.classList.remove('keyboard-focused');
-  },
-
-  /**
-   * @param {!Event} e
-   * @private
-   */
-  onEmailKeyUp_: function(e) {
-    e.currentTarget.classList.add('keyboard-focused');
-  },
-
-  /**
-   * Returns whether |item| is selected or not.
-   * @param {!nuxEmail.EmailProviderModel} item
-   * @return boolean
-   * @private
-   */
-  getSelected_: function(item) {
-    return this.selectedEmailProvider_ &&
-        item.name === this.selectedEmailProvider_.name;
-  },
-
-  /**
-   * @param {nuxEmail.EmailProviderModel} emailProvider
-   * @private
-   */
-  addBookmark_: function(emailProvider) {
-    if (emailProvider.bookmarkId) {
-      return;
-    }
-
-    // Indicates that the emailProvider is being added as a bookmark.
-    emailProvider.bookmarkId = 'pending';
-
-    this.emailProxy_.cacheBookmarkIcon(emailProvider.id);
-    this.bookmarkBarManager_.setShown(true);
-    this.bookmarkProxy_.addBookmark(
-        {
-          title: emailProvider.name,
-          url: emailProvider.url,
-          parentId: '1',
-        },
-        results => {
-          this.selectedEmailProvider_.bookmarkId = results.id;
-        });
-  },
-
-  /**
-   * @param {nuxEmail.EmailProviderModel=} opt_emailProvider
-   * @private
-   */
-  revertBookmark_: function(opt_emailProvider) {
-    const emailProvider = opt_emailProvider || this.selectedEmailProvider_;
-
-    if (emailProvider && emailProvider.bookmarkId) {
-      this.bookmarkProxy_.removeBookmark(emailProvider.bookmarkId);
-      emailProvider.bookmarkId = null;
-    }
-  },
-
-  /**
-   * @param {nuxEmail.EmailProviderModel} newEmail
-   * @param {nuxEmail.EmailProviderModel} prevEmail
-   * @private
-   */
-  onSelectedEmailProviderChange_: function(newEmail, prevEmail) {
-    if (!this.emailProxy_ || !this.bookmarkProxy_) {
-      return;
-    }
-
-    if (prevEmail) {
-      // If it was previously selected, it must've been assigned an id.
-      assert(prevEmail.bookmarkId);
-      this.revertBookmark_(prevEmail);
-    }
-
-    if (newEmail) {
-      this.addBookmark_(newEmail);
-    } else {
-      this.bookmarkBarManager_.setShown(this.wasBookmarkBarShownOnInit_);
-    }
-
-    // Announcements are mutually exclusive, so keeping separate.
-    if (prevEmail && newEmail) {
-      this.fire('iron-announce', {text: this.i18n('bookmarkReplaced')});
-    } else if (prevEmail) {
-      this.fire('iron-announce', {text: this.i18n('bookmarkRemoved')});
-    } else if (newEmail) {
-      this.fire('iron-announce', {text: this.i18n('bookmarkAdded')});
-    }
-  },
-
-  /** @private */
-  onNoThanksClicked_: function() {
-    this.cleanUp_();
-    this.metricsManager_.recordNoThanks();
-    welcome.navigateToNextStep();
-  },
-
-  /** @private */
-  onGetStartedClicked_: function() {
-    this.finalized_ = true;
-    this.emailProxy_.recordProviderSelected(
-        this.selectedEmailProvider_.id, this.emailList_.length);
-    this.metricsManager_.recordGetStarted();
-    welcome.navigateToNextStep();
-  },
-
-  /** @private */
-  onActionButtonClicked_: function() {
-    if (this.$$('.action-button').disabled) {
-      this.metricsManager_.recordClickedDisabledButton();
-    }
-  },
-});
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email.html b/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email.html
index e03bcd23..c41be0b 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email.html
@@ -2,7 +2,8 @@
 
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="../navigation_behavior.html">
-<link rel="import" href="email_chooser.html">
+<link rel="import" href="../shared/app_chooser.html">
+<link rel="import" href="email_app_proxy.html">
 
 <dom-module id="nux-email">
   <template>
@@ -32,8 +33,9 @@
     <div class="email-ask">
       <div class="email-logo" alt=""></div>
       <h1>$i18n{emailProviderTitle}</h1>
-      <email-chooser id="emailChooser" indicator-model="[[indicatorModel]]">
-      </email-chooser>
+      <app-chooser id="emailChooser" indicator-model="[[indicatorModel]]"
+          single-select>
+      </app-chooser>
     </div>
   </template>
   <script src="nux_email.js"></script>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email.js b/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email.js
index 5dc72237..8dabf5e1 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email.js
@@ -12,6 +12,13 @@
     indicatorModel: Object,
   },
 
+  /** @override */
+  ready: function() {
+    this.$.emailChooser.appProxy = nux.EmailAppProxyImpl.getInstance();
+    this.$.emailChooser.metricsManager =
+        new nux.ModuleMetricsManager(nux.EmailMetricsProxyImpl.getInstance());
+  },
+
   onRouteEnter: function() {
     this.$.emailChooser.onRouteEnter();
   },
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email_proxy.js b/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email_proxy.js
deleted file mode 100644
index ec7ba19..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/email/nux_email_proxy.js
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-cr.define('nux', function() {
-  // The metrics name corresponding to Nux EmailProvidersInteraction histogram.
-  const EMAIL_SELECTION_METRIC_NAME =
-      'FirstRun.NewUserExperience.EmailProvidersSelection';
-
-  /** @interface */
-  class NuxEmailProxy {
-    /**
-     * Email provider IDs are local to the list of email providers, so their
-     * icon must be cached by the handler that provided the IDs.
-     * @param {number} emailProviderId
-     */
-    cacheBookmarkIcon(emailProviderId) {}
-
-    /**
-     * Returns a promise for an array of email providers.
-     * @return {!Promise<!Array<!nux.BookmarkListItem>>}
-     */
-    getEmailList() {}
-
-    /** @return {number} */
-    getSavedProvider() {}
-
-    /**
-     * @param {number} providerId This should match one of the histogram enum
-     *     value for NuxEmailProvidersSelections.
-     * @param {number} length
-     */
-    recordProviderSelected(providerId, length) {}
-  }
-
-  /** @implements {nux.NuxEmailProxy} */
-  class NuxEmailProxyImpl {
-    constructor() {
-      /** @private {number} */
-      this.savedProvider_;
-    }
-
-    /** @override */
-    cacheBookmarkIcon(emailProviderId) {
-      chrome.send('cacheEmailIcon', [emailProviderId]);
-    }
-
-    /** @override */
-    getEmailList() {
-      return cr.sendWithPromise('getEmailList');
-    }
-
-    /** @override */
-    getSavedProvider() {
-      return this.savedProvider_;
-    }
-
-    /** @override */
-    recordProviderSelected(providerId, length) {
-      this.savedProvider_ = providerId;
-      chrome.metricsPrivate.recordEnumerationValue(
-          EMAIL_SELECTION_METRIC_NAME, providerId,
-          loadTimeData.getInteger('email_providers_enum_count'));
-    }
-  }
-
-  cr.addSingletonGetter(NuxEmailProxyImpl);
-
-  return {
-    NuxEmailProxy: NuxEmailProxy,
-    NuxEmailProxyImpl: NuxEmailProxyImpl,
-  };
-});
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email_interstitial.html b/chrome/browser/resources/welcome/onboarding_welcome/email_interstitial.html
index 07d54931..1218912 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email_interstitial.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email_interstitial.html
@@ -9,7 +9,7 @@
     <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
     <link rel="import" href="chrome://resources/html/assert.html">
     <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-    <link rel="import" href="email/nux_email_proxy.html">
+    <link rel="import" href="email/email_app_proxy.html">
     <link rel="import" href="email_interstitial_proxy.html">
     <link rel="import" href="shared/action_link_style_css.html">
     <link rel="import" href="shared/onboarding_background.html">
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/email_interstitial.js b/chrome/browser/resources/welcome/onboarding_welcome/email_interstitial.js
index c28f6cd2..ee15f6c 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/email_interstitial.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/email_interstitial.js
@@ -43,7 +43,7 @@
     this.emailInterstitialProxy_.recordNext();
     const providerId =
         (new URL(window.location.href)).searchParams.get('provider');
-    nux.NuxEmailProxyImpl.getInstance().getEmailList().then(list => {
+    nux.EmailAppProxyImpl.getInstance().getAppList().then(list => {
       for (let i = 0; i < list.length; i++) {
         if (list[i].id == providerId) {
           this.welcomeBrowserProxy_.goToURL(list[i].url);
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/BUILD.gn b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/BUILD.gn
index 505478a..068db0a 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/BUILD.gn
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/BUILD.gn
@@ -6,33 +6,22 @@
 
 js_type_check("closure_compile") {
   deps = [
-    ":apps_chooser",
     ":nux_google_apps",
   ]
 }
 
-js_library("apps_chooser") {
-  deps = [
-    ":nux_google_apps_proxy",
-    "../shared:bookmark_proxy",
-    "../shared:module_metrics_proxy",
-    "../shared:step_indicator",
-    "//third_party/polymer/v1_0/components-chromium/iron-a11y-announcer:iron-a11y-announcer-extracted",
-    "//ui/webui/resources/js:cr",
-    "//ui/webui/resources/js:i18n_behavior",
-  ]
-}
-
 js_library("nux_google_apps") {
   deps = [
-    ":apps_chooser",
+    ":google_app_proxy",
     "../:navigation_behavior",
+    "../shared:app_chooser",
     "../shared:nux_types",
   ]
 }
 
-js_library("nux_google_apps_proxy") {
+js_library("google_app_proxy") {
   deps = [
+    "../shared:app_proxy",
     "//ui/webui/resources/js:cr",
   ]
   externs_list = [ "$externs_path/chrome_send.js" ]
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps_proxy.html b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/google_app_proxy.html
similarity index 72%
rename from chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps_proxy.html
rename to chrome/browser/resources/welcome/onboarding_welcome/google_apps/google_app_proxy.html
index 06964db..88d70dd9 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps_proxy.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/google_app_proxy.html
@@ -1,3 +1,3 @@
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://welcome/shared/i18n_setup.html">
-<script src="nux_google_apps_proxy.js"></script>
\ No newline at end of file
+<script src="google_app_proxy.js"></script>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/google_app_proxy.js b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/google_app_proxy.js
new file mode 100644
index 0000000..fb620e6e
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/google_app_proxy.js
@@ -0,0 +1,47 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('nux', function() {
+  /**
+   * NuxGoogleAppsSelections enum.
+   * These values are persisted to logs and should not be renumbered or
+   * re-used.
+   * See tools/metrics/histograms/enums.xml.
+   * @enum {number}
+   */
+  const NuxGoogleAppsSelections = {
+    GMAIL_DEPRECATED: 0,
+    YOU_TUBE: 1,
+    MAPS: 2,
+    TRANSLATE: 3,
+    NEWS: 4,
+    CHROME_WEB_STORE: 5,
+  };
+
+  /** @implements {nux.AppProxy} */
+  class GoogleAppProxyImpl {
+    /** @override */
+    cacheBookmarkIcon(appId) {
+      chrome.send('cacheGoogleAppIcon', [appId]);
+    }
+
+    /** @override */
+    getAppList() {
+      return cr.sendWithPromise('getGoogleAppsList');
+    }
+
+    /** @override */
+    recordProviderSelected(providerId) {
+      chrome.metricsPrivate.recordEnumerationValue(
+          'FirstRun.NewUserExperience.GoogleAppsSelection', providerId,
+          Object.keys(NuxGoogleAppsSelections).length);
+    }
+  }
+
+  cr.addSingletonGetter(GoogleAppProxyImpl);
+
+  return {
+    GoogleAppProxyImpl: GoogleAppProxyImpl,
+  };
+});
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
index b0612cac..81a2d90 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
@@ -2,7 +2,8 @@
 
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="../navigation_behavior.html">
-<link rel="import" href="apps_chooser.html">
+<link rel="import" href="../shared/app_chooser.html">
+<link rel="import" href="google_app_proxy.html">
 
 <dom-module id="nux-google-apps">
   <template>
@@ -33,8 +34,8 @@
     <div class="apps-ask">
       <div class="chrome-logo" alt=""></div>
       <h1>$i18n{googleAppsDescription}</h1>
-      <apps-chooser id="appChooser" indicator-model="[[indicatorModel]]">
-      </apps-chooser>
+      <app-chooser id="appChooser" indicator-model="[[indicatorModel]]">
+      </app-chooser>
     </div>
   </template>
   <script src="nux_google_apps.js"></script>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.js b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.js
index 6d23f6c..13ee8c7b 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.js
@@ -12,6 +12,13 @@
     indicatorModel: Object,
   },
 
+  /** @override */
+  ready: function() {
+    this.$.appChooser.appProxy = nux.GoogleAppProxyImpl.getInstance();
+    this.$.appChooser.metricsManager = new nux.ModuleMetricsManager(
+        nux.GoogleAppsMetricsProxyImpl.getInstance());
+  },
+
   onRouteEnter: function() {
     this.$.appChooser.onRouteEnter();
   },
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps_proxy.js b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps_proxy.js
deleted file mode 100644
index 9ea9b07..0000000
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps_proxy.js
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-cr.define('nux', function() {
-  // The metrics name corresponding to Nux EmailProvidersInteraction histogram.
-  const GOOGLE_APPS_SELECTION_METRIC_NAME =
-      'FirstRun.NewUserExperience.GoogleAppsSelection';
-
-  /**
-   * NuxGoogleAppsSelections enum.
-   * These values are persisted to logs and should not be renumbered or
-   * re-used.
-   * See tools/metrics/histograms/enums.xml.
-   * @enum {number}
-   */
-  const NuxGoogleAppsSelections = {
-    Gmail_DEPRECATED: 0,
-    YouTube: 1,
-    Maps: 2,
-    Translate: 3,
-    News: 4,
-    ChromeWebStore: 5,
-  };
-
-  /** @interface */
-  class NuxGoogleAppsProxy {
-    /**
-     * Google app IDs are local to the list of Google apps, so their icon must
-     * be cached by the handler that provided the IDs.
-     * @param {number} appId
-     */
-    cacheBookmarkIcon(appId) {}
-
-    /**
-     * Returns a promise for an array of Google apps.
-     * @return {!Promise<!Array<!nux.BookmarkListItem>>}
-     */
-    getGoogleAppsList() {}
-
-    /**
-     * @param {number} providerId This should match one of the histogram enum
-     *     value for NuxGoogleAppsSelections.
-     */
-    recordProviderSelected(providerId) {}
-  }
-
-  /** @implements {nux.NuxGoogleAppsProxy} */
-  class NuxGoogleAppsProxyImpl {
-    /** @override */
-    cacheBookmarkIcon(appId) {
-      chrome.send('cacheGoogleAppIcon', [appId]);
-    }
-
-    /** @override */
-    getGoogleAppsList() {
-      return cr.sendWithPromise('getGoogleAppsList');
-    }
-
-    /** @override */
-    recordProviderSelected(providerId) {
-      chrome.metricsPrivate.recordEnumerationValue(
-          GOOGLE_APPS_SELECTION_METRIC_NAME, providerId,
-          Object.keys(NuxGoogleAppsSelections).length);
-    }
-  }
-
-  cr.addSingletonGetter(NuxGoogleAppsProxyImpl);
-
-  return {
-    NuxGoogleAppsProxy: NuxGoogleAppsProxy,
-    NuxGoogleAppsProxyImpl: NuxGoogleAppsProxyImpl,
-  };
-});
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd b/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd
index a5cfb53..d922664 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd
+++ b/chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd
@@ -89,6 +89,12 @@
                  file="navigation_behavior.js"
                  type="chrome_html"
                  preprocess="true"/>
+      <structure name="IDR_NUX_SHARED_APP_CHOOSER_HTML"
+                 file="shared\app_chooser.html"
+                 type="chrome_html" />
+      <structure name="IDR_NUX_SHARED_APP_CHOOSER_JS"
+                 file="shared\app_chooser.js"
+                 type="chrome_html" />
       <structure name="IDR_WELCOME_ONBOARDING_WELCOME_SHARED_ACTION_LINK_STYLE_JS"
                  file="shared\action_link_style.js"
                  type="chrome_html" />
@@ -172,17 +178,11 @@
                  preprocess="true"/>
 
        <!-- NUX Email-->
-      <structure name="IDR_NUX_EMAIL_CHOOSER_HTML"
-                 file="email\email_chooser.html"
+      <structure name="IDR_EMAIL_APP_PROXY_HTML"
+                 file="email\email_app_proxy.html"
                  type="chrome_html" />
-      <structure name="IDR_NUX_EMAIL_CHOOSER_JS"
-                 file="email\email_chooser.js"
-                 type="chrome_html" />
-      <structure name="IDR_NUX_EMAIL_PROXY_HTML"
-                 file="email\nux_email_proxy.html"
-                 type="chrome_html" />
-      <structure name="IDR_NUX_EMAIL_PROXY_JS"
-                 file="email\nux_email_proxy.js"
+      <structure name="IDR_EMAIL_APP_PROXY_JS"
+                 file="email\email_app_proxy.js"
                  type="chrome_html" />
       <structure name="IDR_NUX_EMAIL_HTML"
                  file="email\nux_email.html"
@@ -192,23 +192,17 @@
                  type="chrome_html" />
 
        <!-- NUX Google apps-->
-      <structure name="IDR_NUX_GOOGLE_APPS_CHOOSER_HTML"
-                 file="google_apps\apps_chooser.html"
-                 type="chrome_html" />
-      <structure name="IDR_NUX_GOOGLE_APPS_CHOOSER_JS"
-                 file="google_apps\apps_chooser.js"
-                 type="chrome_html" />
       <structure name="IDR_NUX_GOOGLE_APPS_HTML"
                  file="google_apps\nux_google_apps.html"
                  type="chrome_html" />
       <structure name="IDR_NUX_GOOGLE_APPS_JS"
                  file="google_apps\nux_google_apps.js"
                  type="chrome_html" />
-      <structure name="IDR_NUX_GOOGLE_APPS_PROXY_HTML"
-                 file="google_apps\nux_google_apps_proxy.html"
+      <structure name="IDR_GOOGLE_APP_PROXY_HTML"
+                 file="google_apps\google_app_proxy.html"
                  type="chrome_html" />
-      <structure name="IDR_NUX_GOOGLE_APPS_PROXY_JS"
-                 file="google_apps\nux_google_apps_proxy.js"
+      <structure name="IDR_GOOGLE_APP_PROXY_JS"
+                 file="google_apps\google_app_proxy.js"
                  type="chrome_html" />
       <structure name="IDR_NUX_SET_AS_DEFAULT_HTML"
                  file="set_as_default\nux_set_as_default.html"
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/BUILD.gn b/chrome/browser/resources/welcome/onboarding_welcome/shared/BUILD.gn
index d2c36998..31e4036 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/shared/BUILD.gn
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/BUILD.gn
@@ -12,6 +12,17 @@
   ]
 }
 
+js_library("app_chooser") {
+  deps = [
+    ":bookmark_proxy",
+    ":module_metrics_proxy",
+    ":step_indicator",
+    "//third_party/polymer/v1_0/components-chromium/iron-a11y-announcer:iron-a11y-announcer-extracted",
+    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:i18n_behavior",
+  ]
+}
+
 js_library("bookmark_proxy") {
   deps = [
     "//ui/webui/resources/js:cr",
@@ -22,6 +33,12 @@
   ]
 }
 
+js_library("app_proxy") {
+  deps = [
+    "//ui/webui/resources/js:cr",
+  ]
+}
+
 js_library("module_metrics_proxy") {
   deps = [
     "//ui/webui/resources/js:cr",
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.html b/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.html
similarity index 69%
rename from chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.html
rename to chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.html
index e2f36f2..a3359d4 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.html
@@ -9,12 +9,13 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="../navigation_behavior.html">
-<link rel="import" href="../shared/bookmark_proxy.html">
-<link rel="import" href="../shared/chooser_shared_css.html">
-<link rel="import" href="../shared/step_indicator.html">
-<link rel="import" href="nux_google_apps_proxy.html">
+<link rel="import" href="bookmark_proxy.html">
+<link rel="import" href="chooser_shared_css.html">
+<link rel="import" href="i18n_setup.html">
+<link rel="import" href="module_metrics_proxy.html">
+<link rel="import" href="step_indicator.html">
 
-<dom-module id="apps-chooser">
+<dom-module id="app-chooser">
   <template>
     <style include="chooser-shared-css paper-button-style">
       .gmail {
@@ -23,6 +24,30 @@
             url(chrome://welcome/images/gmail_2x.png) 2x);
       }
 
+      .yahoo {
+        background-image: -webkit-image-set(
+            url(chrome://welcome/images/yahoo_1x.png) 1x,
+            url(chrome://welcome/images/yahoo_2x.png) 2x);
+      }
+
+      .aol {
+        background-image: -webkit-image-set(
+            url(chrome://welcome/images/aol_1x.png) 1x,
+            url(chrome://welcome/images/aol_2x.png) 2x);
+      }
+
+      .icloud {
+        background-image: -webkit-image-set(
+            url(chrome://welcome/images/icloud_1x.png) 1x,
+            url(chrome://welcome/images/icloud_2x.png) 2x);
+      }
+
+      .outlook {
+        background-image: -webkit-image-set(
+            url(chrome://welcome/images/outlook_1x.png) 1x,
+            url(chrome://welcome/images/outlook_2x.png) 2x);
+      }
+
       .youtube {
         content: -webkit-image-set(
             url(chrome://welcome/images/youtube_1x.png) 1x,
@@ -67,16 +92,16 @@
     </template>
 
     <div class="button-bar">
-      <paper-button on-click="onNoThanksClicked_">
+      <paper-button id="noThanksButton" on-click="onNoThanksClicked_">
         $i18n{skip}
       </paper-button>
       <step-indicator model="[[indicatorModel]]"></step-indicator>
       <paper-button class="action-button" disabled$="[[!hasAppsSelected_]]"
-                    on-click="onGetStartedClicked_">
+          on-click="onGetStartedClicked_">
         $i18n{next}
         <iron-icon icon="cr:chevron-right"></iron-icon>
       </paper-button>
     </div>
   </template>
-  <script src="apps_chooser.js"></script>
+  <script src="app_chooser.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.js b/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.js
similarity index 61%
rename from chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.js
rename to chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.js
index 404328b..01e2f053 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/apps_chooser.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.exportPath('nuxGoogleApps');
+cr.exportPath('nux');
 
 /**
  * @typedef {{
@@ -14,18 +14,18 @@
  *   selected: boolean,
  * }}
  */
-nuxGoogleApps.AppItem;
+nux.AppItem;
 
 /**
  * @typedef {{
- *   item: !nuxGoogleApps.AppItem,
+ *   item: !nux.AppItem,
  *   set: function(string, boolean):void
  * }}
  */
-nuxGoogleApps.AppItemModel;
+nux.AppItemModel;
 
 Polymer({
-  is: 'apps-chooser',
+  is: 'app-chooser',
 
   behaviors: [I18nBehavior],
 
@@ -33,8 +33,13 @@
     /** @type {nux.stepIndicatorModel} */
     indicatorModel: Object,
 
+    singleSelect: {
+      type: Boolean,
+      value: false,
+    },
+
     /**
-     * @type {!Array<!nuxGoogleApps.AppItem>}
+     * @type {!Array<!nux.AppItem>}
      * @private
      */
     appList_: Array,
@@ -46,21 +51,27 @@
     },
   },
 
+  /**
+   * Should be set in parent element's |ready| method.
+   * @type {nux.AppProxy}
+   */
+  appProxy: null,
+
+  /**
+   * Should be set in parent element's |ready| method.
+   * @type {?nux.ModuleMetricsManager}
+   */
+  metricsManager: null,
+
   /** @private */
   finalized_: false,
 
-  /** @private {nux.NuxGoogleAppsProxy} */
-  appsProxy_: null,
-
   /** @private {nux.BookmarkProxy} */
   bookmarkProxy_: null,
 
   /** @private {nux.BookmarkBarManager} */
   bookmarkBarManager_: null,
 
-  /** @private {?nux.ModuleMetricsManager} */
-  metricsManager_: null,
-
   /** @private {boolean} */
   wasBookmarkBarShownOnInit_: false,
 
@@ -72,80 +83,38 @@
   },
 
   /** @override */
-  ready() {
-    this.appsProxy_ = nux.NuxGoogleAppsProxyImpl.getInstance();
+  ready: function() {
     this.bookmarkProxy_ = nux.BookmarkProxyImpl.getInstance();
     this.bookmarkBarManager_ = nux.BookmarkBarManager.getInstance();
-    this.metricsManager_ = new nux.ModuleMetricsManager(
-        nux.GoogleAppsMetricsProxyImpl.getInstance());
   },
 
-  onRouteEnter() {
+  onRouteEnter: function() {
     this.finalized_ = false;
-    this.metricsManager_.recordPageInitialized();
-    this.populateAllBookmarks();
+    this.metricsManager.recordPageInitialized();
+    this.populateAllBookmarks_();
   },
 
-  onRouteExit() {
+  onRouteExit: function() {
     if (this.finalized_) {
       return;
     }
     this.cleanUp_();
-    this.metricsManager_.recordBrowserBackOrForward();
+    this.metricsManager.recordBrowserBackOrForward();
   },
 
-  onRouteUnload() {
+  onRouteUnload: function() {
     if (this.finalized_) {
       return;
     }
     this.cleanUp_();
-    this.metricsManager_.recordNavigatedAway();
-  },
-
-  /** @private */
-  onNoThanksClicked_: function() {
-    this.cleanUp_();
-    this.metricsManager_.recordNoThanks();
-    welcome.navigateToNextStep();
-  },
-
-  /** @private */
-  onGetStartedClicked_: function() {
-    this.finalized_ = true;
-    this.appList_.forEach(app => {
-      if (app.selected) {
-        this.appsProxy_.recordProviderSelected(app.id);
-      }
-    });
-    this.metricsManager_.recordGetStarted();
-    welcome.navigateToNextStep();
-  },
-
-  /** Called when bookmarks should be created for all selected apps. */
-  populateAllBookmarks() {
-    this.wasBookmarkBarShownOnInit_ = this.bookmarkBarManager_.getShown();
-
-    if (this.appList_) {
-      this.appList_.forEach(app => this.updateBookmark(app));
-    } else {
-      this.appsProxy_.getGoogleAppsList().then(list => {
-        this.appList_ = list;
-        this.appList_.forEach((app, index) => {
-          // Default select first few items.
-          app.selected = index < 3;
-          this.updateBookmark(app);
-        });
-        this.updateHasAppsSelected();
-        this.fire('iron-announce', {text: this.i18n('bookmarksAdded')});
-      });
-    }
+    this.metricsManager.recordNavigatedAway();
   },
 
   /**
    * Called when bookmarks should be removed for all selected apps.
    * @private
    */
-  cleanUp_() {
+  cleanUp_: function() {
     this.finalized_ = true;
 
     if (!this.appList_) {
@@ -155,7 +124,7 @@
     let removedBookmarks = false;
     this.appList_.forEach(app => {
       if (app.selected && app.bookmarkId) {
-        // Don't call |updateBookmark| b/c we want to save the selection in the
+        // Don't call |updateBookmark_| b/c we want to save the selection in the
         // event of a browser back/forward.
         this.bookmarkProxy_.removeBookmark(app.bookmarkId);
         app.bookmarkId = null;
@@ -170,10 +139,111 @@
   },
 
   /**
-   * @param {!nuxGoogleApps.AppItem} item
+   * Handle toggling the apps selected.
+   * @param {!{model: !nux.AppItemModel}} e
    * @private
    */
-  updateBookmark(item) {
+  onAppClick_: function(e) {
+    const item = e.model.item;
+    let prevItemIndex = -1;
+
+    if (this.singleSelect) {
+      this.appList_.forEach((app, index) => {
+        if (app.selected) {
+          prevItemIndex = index;
+        }
+      });
+    }
+
+    e.model.set('item.selected', !item.selected);
+
+    if (this.singleSelect && item.selected && prevItemIndex > -1) {
+      this.set(`appList_.${prevItemIndex}.selected`, false);
+      this.updateBookmark_(this.appList_[prevItemIndex]);
+    }
+
+    this.updateBookmark_(item);
+    this.updateHasAppsSelected_();
+
+    this.metricsManager.recordClickedOption();
+
+    // Announcements should NOT be in |updateBookmark_| because there should be
+    // a different utterance when all app bookmarks are added/removed.
+    let i18nKey = 'bookmarkRemoved';
+    if (item.selected) {
+      i18nKey = prevItemIndex > -1 ? 'bookmarkReplaced' : 'bookmarkAdded';
+    }
+    this.fire('iron-announce', {text: this.i18n(i18nKey)});
+  },
+
+  /**
+   * @param {!Event} e
+   * @private
+   */
+  onAppKeyUp_: function(e) {
+    e.currentTarget.classList.add('keyboard-focused');
+  },
+
+  /**
+   * @param {!Event} e
+   * @private
+   */
+  onAppPointerDown_: function(e) {
+    e.currentTarget.classList.remove('keyboard-focused');
+  },
+
+  /** @private */
+  onGetStartedClicked_: function() {
+    this.finalized_ = true;
+    this.appList_.forEach(app => {
+      if (app.selected) {
+        this.appProxy.recordProviderSelected(app.id);
+      }
+    });
+    this.metricsManager.recordGetStarted();
+    welcome.navigateToNextStep();
+  },
+
+  /** @private */
+  onNoThanksClicked_: function() {
+    this.cleanUp_();
+    this.metricsManager.recordNoThanks();
+    welcome.navigateToNextStep();
+  },
+
+  /**
+   * Called when bookmarks should be created for all selected apps.
+   * @private
+   */
+  populateAllBookmarks_: function() {
+    this.wasBookmarkBarShownOnInit_ = this.bookmarkBarManager_.getShown();
+
+    if (this.appList_) {
+      this.appList_.forEach(app => this.updateBookmark_(app));
+    } else {
+      this.appProxy.getAppList().then(list => {
+        this.appList_ = list;
+        this.appList_.forEach((app, index) => {
+          if (this.singleSelect) {
+            // Default select the first item.
+            app.selected = index == 0;
+          } else {
+            // Default select first few items.
+            app.selected = index < 3;
+          }
+          this.updateBookmark_(app);
+        });
+        this.updateHasAppsSelected_();
+        this.fire('iron-announce', {text: this.i18n('bookmarksAdded')});
+      });
+    }
+  },
+
+  /**
+   * @param {!nux.AppItem} item
+   * @private
+   */
+  updateBookmark_: function(item) {
     if (item.selected && !item.bookmarkId) {
       this.bookmarkBarManager_.setShown(true);
       this.bookmarkProxy_.addBookmark(
@@ -186,7 +256,7 @@
             item.bookmarkId = result.id;
           });
       // Cache bookmark icon.
-      this.appsProxy_.cacheBookmarkIcon(item.id);
+      this.appProxy.cacheBookmarkIcon(item.id);
     } else if (!item.selected && item.bookmarkId) {
       this.bookmarkProxy_.removeBookmark(item.bookmarkId);
       item.bookmarkId = null;
@@ -194,48 +264,10 @@
   },
 
   /**
-   * Handle toggling the apps selected.
-   * @param {!{model: !nuxGoogleApps.AppItemModel}} e
-   * @private
-   */
-  onAppClick_: function(e) {
-    const item = e.model.item;
-    e.model.set('item.selected', !item.selected);
-    this.updateBookmark(item);
-    this.updateHasAppsSelected();
-
-    this.metricsManager_.recordClickedOption();
-
-    // Announcements should NOT be in |updateBookmark| because there should be a
-    // different utterance when all app bookmarks are added/removed.
-    if (item.selected) {
-      this.fire('iron-announce', {text: this.i18n('bookmarkAdded')});
-    } else {
-      this.fire('iron-announce', {text: this.i18n('bookmarkRemoved')});
-    }
-  },
-
-  /**
-   * @param {!Event} e
-   * @private
-   */
-  onAppPointerDown_: function(e) {
-    e.currentTarget.classList.remove('keyboard-focused');
-  },
-
-  /**
-   * @param {!Event} e
-   * @private
-   */
-  onAppKeyUp_: function(e) {
-    e.currentTarget.classList.add('keyboard-focused');
-  },
-
-  /**
    * Updates the value of hasAppsSelected_.
    * @private
    */
-  updateHasAppsSelected: function() {
+  updateHasAppsSelected_: function() {
     this.hasAppsSelected_ =
         this.appList_ && this.appList_.some(a => a.selected);
     if (!this.hasAppsSelected_) {
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/app_proxy.js b/chrome/browser/resources/welcome/onboarding_welcome/shared/app_proxy.js
new file mode 100644
index 0000000..1b04fd07
--- /dev/null
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/app_proxy.js
@@ -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.
+
+cr.define('nux', function() {
+  /** @interface */
+  class AppProxy {
+    /**
+     * Google app IDs are local to the list of Google apps, so their icon must
+     * be cached by the handler that provided the IDs.
+     * @param {number} appId
+     */
+    cacheBookmarkIcon(appId) {}
+
+    /**
+     * Returns a promise for an array of Google apps.
+     * @return {!Promise<!Array<!nux.BookmarkListItem>>}
+     */
+    getAppList() {}
+
+    /**
+     * @param {number} providerId This should match one of the histogram enum
+     *     value for NuxGoogleAppsSelections.
+     */
+    recordProviderSelected(providerId) {}
+  }
+
+  return {
+    AppProxy: AppProxy,
+  };
+});
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/signin_view.html b/chrome/browser/resources/welcome/onboarding_welcome/signin_view.html
index f7db619..611e487 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/signin_view.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/signin_view.html
@@ -3,7 +3,7 @@
 <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
-<link rel="import" href="email/nux_email_proxy.html">
+<link rel="import" href="email/email_app_proxy.html">
 <link rel="import" href="navigation_behavior.html">
 <link rel="import" href="shared/action_link_style_css.html">
 <link rel="import" href="shared/i18n_setup.html">
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/signin_view.js b/chrome/browser/resources/welcome/onboarding_welcome/signin_view.js
index 0480a60..e875ef7 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/signin_view.js
+++ b/chrome/browser/resources/welcome/onboarding_welcome/signin_view.js
@@ -54,7 +54,7 @@
    */
   getTargetUrl_: function() {
     const savedProvider =
-        nux.NuxEmailProxyImpl.getInstance().getSavedProvider();
+        nux.EmailAppProxyImpl.getInstance().getSavedProvider();
     if (savedProvider != undefined && this.shouldShowEmailInterstitial_) {
       return `chrome://welcome/email-interstitial?provider=${savedProvider}`;
     } else {
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service_browsertest.cc b/chrome/browser/safe_browsing/chrome_password_protection_service_browsertest.cc
index 2dc7a13..da3f9c1 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service_browsertest.cc
@@ -611,9 +611,6 @@
                 ->GetActiveWebContents()
                 ->GetLastCommittedURL(),
             embedded_test_server()->GetURL(kChangePasswordUrl));
-  EXPECT_THAT(histograms.GetAllSamples(
-                  "PasswordProtection.InterstitialActionByUserNavigation"),
-              testing::ElementsAre(base::Bucket(0, 1), base::Bucket(1, 1)));
 }
 
 IN_PROC_BROWSER_TEST_F(ChromePasswordProtectionServiceBrowserTest,
diff --git a/chrome/browser/subresource_filter/ad_tagging_browsertest.cc b/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
index 4ce563c..f0defba8 100644
--- a/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
+++ b/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/metrics/subprocess_metrics_provider.h"
 #include "chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h"
 #include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -92,6 +93,12 @@
     return embedded_test_server()->GetURL("/ad_tagging/" + page);
   }
 
+  std::unique_ptr<page_load_metrics::PageLoadMetricsTestWaiter>
+  CreatePageLoadMetricsTestWaiter() {
+    return std::make_unique<page_load_metrics::PageLoadMetricsTestWaiter>(
+        GetWebContents());
+  }
+
  private:
   content::RenderFrameHost* CreateFrameImpl(
       const content::ToRenderFrameHost& adapter,
@@ -241,9 +248,9 @@
 
   // Navigate away and ensure we report cross origin.
   ui_test_utils::NavigateToURL(browser(), GetURL(url::kAboutBlankURL));
-  histogram_tester.ExpectUniqueSample(
-      kAllOriginStatusHistogram,
-      AdsPageLoadMetricsObserver::AdOriginStatus::kCross, 1);
+
+  // TODO(johnidel): Check that frame was reported properly. See
+  // crbug.com/914893.
 }
 
 // Ad script creates a frame and navigates it cross origin.
@@ -251,12 +258,19 @@
                        VerifyCrossOriginWithImmediateNavigate) {
   base::HistogramTester histogram_tester;
 
+  auto waiter = CreatePageLoadMetricsTestWaiter();
   // Create the main frame and cross origin subframe from an ad script.
   // This triggers both subresource_filter and Google ad detection.
   ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html"));
   CreateSrcFrameFromAdScript(GetWebContents(),
                              embedded_test_server()->GetURL(
                                  "b.com", "/ads_observer/same_origin_ad.html"));
+  // Wait for all of the subresources to finish loading (includes favicon).
+  // Waiting for the navigation to finish is not sufficient, as it blocks on the
+  // main resource load finishing, not the iframe resource. Page loads 4
+  // resources, a favicon, and 2 resources for the iframe.
+  waiter->AddMinimumCompleteResourcesExpectation(7);
+  waiter->Wait();
 
   // Navigate away and ensure we report cross origin.
   ui_test_utils::NavigateToURL(browser(), GetURL(url::kAboutBlankURL));
diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc
index 0c29127..d6d2031 100644
--- a/chrome/browser/themes/browser_theme_pack.cc
+++ b/chrome/browser/themes/browser_theme_pack.cc
@@ -58,7 +58,7 @@
 // theme packs that aren't int-equal to this. Increment this number if you
 // change default theme assets or if you need themes to recreate their generated
 // images (which are cached).
-const int kThemePackVersion = 61;
+const int kThemePackVersion = 62;
 
 // IDs that are in the DataPack won't clash with the positive integer
 // uint16_t. kHeaderID should always have the maximum value because we want the
@@ -264,6 +264,10 @@
     TP::COLOR_WINDOW_CONTROL_BUTTON_BACKGROUND_INACTIVE,
     TP::COLOR_WINDOW_CONTROL_BUTTON_BACKGROUND_INCOGNITO_ACTIVE,
     TP::COLOR_WINDOW_CONTROL_BUTTON_BACKGROUND_INCOGNITO_INACTIVE,
+    TP::COLOR_DETACHED_BOOKMARK_BAR_BACKGROUND,
+    TP::COLOR_INFOBAR,
+    TP::COLOR_DOWNLOAD_SHELF,
+    TP::COLOR_STATUS_BUBBLE,
 };
 constexpr size_t kNonOverwritableColorTableLength =
     base::size(kNonOverwritableColorTable);
@@ -587,17 +591,20 @@
   colors_[first_available_color].color = color;
 }
 
-void BrowserThemePack::ComputeColorFromImage(int color_id,
-                                             int height,
-                                             const gfx::Image& image) {
+void BrowserThemePack::SetColorIfUnspecified(int id, SkColor color) {
   SkColor temp_color;
-  if (!GetColor(color_id, &temp_color)) {
-    // Include all colors in the analysis.
-    constexpr color_utils::HSL kNoBounds = {-1, -1, -1};
-    const SkColor color = color_utils::CalculateKMeanColorOfBitmap(
-        *image.ToSkBitmap(), height, kNoBounds, kNoBounds, false);
-    SetColor(color_id, color);
-  }
+  if (!GetColor(id, &temp_color))
+    SetColor(id, color);
+}
+
+SkColor BrowserThemePack::ComputeImageColor(const gfx::Image& image,
+                                            int height) {
+  // Include all colors in the analysis.
+  constexpr color_utils::HSL kNoBounds = {-1, -1, -1};
+  const SkColor color = color_utils::CalculateKMeanColorOfBitmap(
+      *image.ToSkBitmap(), height, kNoBounds, kNoBounds, false);
+
+  return color;
 }
 
 // static
@@ -627,9 +634,14 @@
 
   pack->CropImages(&pack->images_);
 
+  // Create toolbar image, and generate toolbar color from image where relevant.
+  // This must be done after reading colors from JSON (so they can be used for
+  // compositing the image).
+  pack->CreateToolbarImageAndColors(&pack->images_);
+
   // Create frame images, and generate frame colors from images where relevant.
-  // This must be done after reading colors from JSON (so they aren't
-  // overwritten).
+  // This must be done after reading colors from JSON (so they can be used for
+  // compositing the image).
   pack->CreateFrameImagesAndColors(&pack->images_);
 
   // Generate any missing frame colors.  This must be done after generating
@@ -1260,6 +1272,47 @@
   }
 }
 
+void BrowserThemePack::CreateToolbarImageAndColors(ImageCache* images) {
+  ImageCache temp_output;
+
+  constexpr int kSrcImageId = PRS_THEME_TOOLBAR;
+
+  const auto image_it = images->find(kSrcImageId);
+  if (image_it == images->end())
+    return;
+
+  auto image = image_it->second.AsImageSkia();
+
+  constexpr int kToolbarColorId = TP::COLOR_TOOLBAR;
+  SkColor toolbar_color;
+  // Propagate the user-specified Toolbar Color to similar elements (for
+  // backwards-compatibility with themes written before this toolbar processing
+  // was introduced).
+  if (GetColor(kToolbarColorId, &toolbar_color)) {
+    SetColor(TP::COLOR_DETACHED_BOOKMARK_BAR_BACKGROUND, toolbar_color);
+    SetColor(TP::COLOR_INFOBAR, toolbar_color);
+    SetColor(TP::COLOR_DOWNLOAD_SHELF, toolbar_color);
+    SetColor(TP::COLOR_STATUS_BUBBLE, toolbar_color);
+  } else {
+    toolbar_color = TP::GetDefaultColor(kToolbarColorId, false);
+  }
+
+  // Generate a composite image by drawing the toolbar image on top of the
+  // specified toolbar color (if any).
+  color_utils::HSL hsl_shift{-1, -1, -1};
+  gfx::ImageSkia overlay;
+  auto source = std::make_unique<TabBackgroundImageSource>(
+      toolbar_color, image, overlay, hsl_shift, 0);
+  gfx::Size dest_size = image.size();
+
+  const gfx::Image dest_image(gfx::ImageSkia(std::move(source), dest_size));
+  temp_output[kSrcImageId] = dest_image;
+
+  SetColor(kToolbarColorId, ComputeImageColor(dest_image, dest_size.height()));
+
+  MergeImageCaches(temp_output, images);
+}
+
 void BrowserThemePack::CreateFrameImagesAndColors(ImageCache* images) {
   static constexpr struct FrameValues {
     int prs_id;
@@ -1313,8 +1366,8 @@
       temp_output[frame_values.prs_id] = dest_image;
 
       if (frame_values.color_id) {
-        ComputeColorFromImage(frame_values.color_id.value(),
-                              kTallestFrameHeight, dest_image);
+        SetColor(frame_values.color_id.value(),
+                 ComputeImageColor(dest_image, kTallestFrameHeight));
       }
     }
   }
@@ -1414,7 +1467,8 @@
         base_color, bg_image, dest_size);
     const gfx::Image dest_image(gfx::ImageSkia(std::move(source), dest_size));
 
-    ComputeColorFromImage(bg_pair.color_id, dest_size.height(), dest_image);
+    SetColorIfUnspecified(bg_pair.color_id,
+                          ComputeImageColor(dest_image, dest_size.height()));
   }
 }
 
@@ -1489,8 +1543,8 @@
       const gfx::Image dest_image(gfx::ImageSkia(std::move(source), dest_size));
       temp_output[tab_id] = dest_image;
 
-      ComputeColorFromImage(kTabBackgroundMap[i].color_id, kTallestTabHeight,
-                            dest_image);
+      SetColorIfUnspecified(kTabBackgroundMap[i].color_id,
+                            ComputeImageColor(dest_image, kTallestTabHeight));
     }
   }
   MergeImageCaches(temp_output, images);
diff --git a/chrome/browser/themes/browser_theme_pack.h b/chrome/browser/themes/browser_theme_pack.h
index 56909a3..d6d97fb9 100644
--- a/chrome/browser/themes/browser_theme_pack.h
+++ b/chrome/browser/themes/browser_theme_pack.h
@@ -115,9 +115,14 @@
   // valid to call after BuildColorsFromJSON(), which creates |colors_|.
   void SetColor(int id, SkColor color);
 
-  // If |colors_| does not already contain an entry with identifier |id|, sets
-  // it to the dominant color of the top |height| rows of |image|.
-  void ComputeColorFromImage(int id, int height, const gfx::Image& image);
+  // If |colors_| does not already contain an entry with identifier |id|,
+  // modifies |colors_| to set the entry with identifier |id| to |color|.  If an
+  // entry for |id| already exists, does nothing.
+  // Only valid to call after BuildColorsFromJSON(), which creates |colors_|.
+  void SetColorIfUnspecified(int id, SkColor color);
+
+  // Calculates the dominant color of the top |height| rows of |image|.
+  SkColor ComputeImageColor(const gfx::Image& image, int height);
 
   // Builds a header ready to write to disk.
   void BuildHeader(const extensions::Extension* extension);
@@ -162,6 +167,10 @@
   // can be of any size. Source and destination is |images|.
   void CropImages(ImageCache* images) const;
 
+  // Creates a composited toolbar image. Source and destination is |images|.
+  // Also sets toolbar color corresponding to this image.
+  void CreateToolbarImageAndColors(ImageCache* images);
+
   // Creates tinted and composited frame images. Source and destination is
   // |images|.  Also sets frame colors corresponding to these images if no
   // explicit color has been specified for these colors.
diff --git a/chrome/browser/themes/browser_theme_pack_unittest.cc b/chrome/browser/themes/browser_theme_pack_unittest.cc
index c3415b00..7ef4a00 100644
--- a/chrome/browser/themes/browser_theme_pack_unittest.cc
+++ b/chrome/browser/themes/browser_theme_pack_unittest.cc
@@ -989,3 +989,57 @@
     EXPECT_TRUE(color_utils::IsDark(control_button_color));
   }
 }
+
+// Ensure that a specified 'toolbar' color is propagated to other 'bar' and
+// 'shelf' colors (before a new color is computed from the toolbar image).
+TEST_F(BrowserThemePackTest, TestToolbarColorPropagation) {
+  scoped_refptr<BrowserThemePack> pack;
+  BuildTestExtensionTheme("theme_test_toolbar_frame_images_and_colors", &pack);
+
+  SkColor infobar_color;
+  SkColor bookmark_bar_color;
+  SkColor download_shelf_color;
+  SkColor status_bubble_color;
+
+  EXPECT_TRUE(pack->GetColor(TP::COLOR_INFOBAR, &infobar_color));
+  EXPECT_TRUE(pack->GetColor(TP::COLOR_DETACHED_BOOKMARK_BAR_BACKGROUND,
+                             &bookmark_bar_color));
+  EXPECT_TRUE(pack->GetColor(TP::COLOR_DOWNLOAD_SHELF, &download_shelf_color));
+  EXPECT_TRUE(pack->GetColor(TP::COLOR_STATUS_BUBBLE, &status_bubble_color));
+
+  constexpr SkColor kExpectedColor = SkColorSetRGB(0, 255, 0);
+  EXPECT_EQ(infobar_color, kExpectedColor);
+  EXPECT_EQ(infobar_color, bookmark_bar_color);
+  EXPECT_EQ(infobar_color, download_shelf_color);
+  EXPECT_EQ(infobar_color, status_bubble_color);
+}
+
+// Ensure that, given an explicit toolbar color and a toolbar image, the output
+// color in COLOR_TOOLBAR reflects the color of the image (not the explicit
+// color).
+TEST_F(BrowserThemePackTest,
+       TestToolbarColorComputedFromImageOverridesInputColor) {
+  scoped_refptr<BrowserThemePack> pack;
+  BuildTestExtensionTheme("theme_test_toolbar_frame_images_and_colors", &pack);
+
+  SkColor toolbar_color;
+  EXPECT_TRUE(pack->GetColor(TP::COLOR_TOOLBAR, &toolbar_color));
+
+  constexpr SkColor kExplicitColor = SkColorSetRGB(0, 255, 0);
+  EXPECT_NE(toolbar_color, kExplicitColor);
+}
+
+// Ensure that, given an explicit frame color and a frame image, the output
+// color in COLOR_FRAME reflects the color of the image (not the explicit
+// color).
+TEST_F(BrowserThemePackTest,
+       TestFrameColorComputedFromImageOverridesInputColor) {
+  scoped_refptr<BrowserThemePack> pack;
+  BuildTestExtensionTheme("theme_test_toolbar_frame_images_and_colors", &pack);
+
+  SkColor frame_color;
+  EXPECT_TRUE(pack->GetColor(TP::COLOR_FRAME, &frame_color));
+
+  constexpr SkColor kExplicitColor = SkColorSetRGB(255, 0, 255);
+  EXPECT_NE(frame_color, kExplicitColor);
+}
diff --git a/chrome/browser/themes/theme_properties.cc b/chrome/browser/themes/theme_properties.cc
index 478dcb6..7ba33fa 100644
--- a/chrome/browser/themes/theme_properties.cc
+++ b/chrome/browser/themes/theme_properties.cc
@@ -42,6 +42,9 @@
     case ThemeProperties::COLOR_FRAME_INACTIVE:
     case ThemeProperties::COLOR_BACKGROUND_TAB_INACTIVE:
       return gfx::kGoogleGrey800;
+    case ThemeProperties::COLOR_DOWNLOAD_SHELF:
+    case ThemeProperties::COLOR_STATUS_BUBBLE:
+    case ThemeProperties::COLOR_INFOBAR:
     case ThemeProperties::COLOR_TOOLBAR:
     case ThemeProperties::COLOR_DETACHED_BOOKMARK_BAR_BACKGROUND:
     case ThemeProperties::COLOR_NTP_BACKGROUND:
@@ -214,7 +217,10 @@
     case COLOR_FRAME_INACTIVE:
     case COLOR_BACKGROUND_TAB_INACTIVE:
       return SkColorSetRGB(0xE7, 0xEA, 0xED);
+    case COLOR_DOWNLOAD_SHELF:
+    case COLOR_INFOBAR:
     case COLOR_TOOLBAR:
+    case COLOR_STATUS_BUBBLE:
       return SK_ColorWHITE;
     case COLOR_BOOKMARK_TEXT:
     case COLOR_TAB_TEXT:
diff --git a/chrome/browser/themes/theme_properties.h b/chrome/browser/themes/theme_properties.h
index a8f7d0c..936ca703 100644
--- a/chrome/browser/themes/theme_properties.h
+++ b/chrome/browser/themes/theme_properties.h
@@ -118,6 +118,11 @@
     COLOR_DETACHED_BOOKMARK_BAR_BACKGROUND,
     COLOR_DETACHED_BOOKMARK_BAR_SEPARATOR,
 
+    // Color used for various 'shelves' and 'bars'.
+    COLOR_DOWNLOAD_SHELF,
+    COLOR_INFOBAR,
+    COLOR_STATUS_BUBBLE,
+
     // The throbber colors for tabs or anything on a toolbar (currently, only
     // the download shelf). If you're adding a throbber elsewhere, such as in
     // a dialog or bubble, you likely want
diff --git a/chrome/browser/themes/theme_service_browsertest.cc b/chrome/browser/themes/theme_service_browsertest.cc
index 5bf13c11..da440a9 100644
--- a/chrome/browser/themes/theme_service_browsertest.cc
+++ b/chrome/browser/themes/theme_service_browsertest.cc
@@ -18,8 +18,8 @@
 
 namespace {
 
-// The toolbar color specified in the theme.
-const SkColor kThemeToolbarColor = 0xFFCFDDC0;
+// The ntp link color specified in the theme.
+constexpr SkColor kThemeNtpLinkColor = SkColorSetRGB(36, 70, 0);
 
 bool UsingCustomTheme(const ThemeService& theme_service) {
   return !theme_service.UsingSystemTheme() &&
@@ -53,8 +53,8 @@
 
   // Test initial state.
   EXPECT_FALSE(UsingCustomTheme(*theme_service));
-  EXPECT_NE(kThemeToolbarColor,
-            theme_provider.GetColor(ThemeProperties::COLOR_TOOLBAR));
+  EXPECT_NE(kThemeNtpLinkColor,
+            theme_provider.GetColor(ThemeProperties::COLOR_NTP_LINK));
   EXPECT_EQ(base::FilePath(),
             profile->GetPrefs()->GetFilePath(prefs::kCurrentThemePackFilename));
 
@@ -66,8 +66,8 @@
 
   // Check that the theme was installed.
   EXPECT_TRUE(UsingCustomTheme(*theme_service));
-  EXPECT_EQ(kThemeToolbarColor,
-            theme_provider.GetColor(ThemeProperties::COLOR_TOOLBAR));
+  EXPECT_EQ(kThemeNtpLinkColor,
+            theme_provider.GetColor(ThemeProperties::COLOR_NTP_LINK));
   EXPECT_NE(base::FilePath(),
             profile->GetPrefs()->GetFilePath(prefs::kCurrentThemePackFilename));
 
@@ -84,8 +84,8 @@
   const ui::ThemeProvider& theme_provider =
       ThemeService::GetThemeProviderForProfile(browser()->profile());
   EXPECT_TRUE(UsingCustomTheme(*theme_service));
-  EXPECT_EQ(kThemeToolbarColor,
-            theme_provider.GetColor(ThemeProperties::COLOR_TOOLBAR));
+  EXPECT_EQ(kThemeNtpLinkColor,
+            theme_provider.GetColor(ThemeProperties::COLOR_NTP_LINK));
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/autofill/local_card_migration_dialog_controller_impl.cc b/chrome/browser/ui/autofill/local_card_migration_dialog_controller_impl.cc
index ea53a563..45835f1 100644
--- a/chrome/browser/ui/autofill/local_card_migration_dialog_controller_impl.cc
+++ b/chrome/browser/ui/autofill/local_card_migration_dialog_controller_impl.cc
@@ -98,17 +98,25 @@
 }
 
 void LocalCardMigrationDialogControllerImpl::ShowFeedbackDialog() {
+  AutofillMetrics::LogLocalCardMigrationDialogOfferMetric(
+      AutofillMetrics::LOCAL_CARD_MIGRATION_DIALOG_FEEDBACK_SHOWN);
+
   local_card_migration_dialog_ =
       CreateLocalCardMigrationDialogView(this, web_contents());
   local_card_migration_dialog_->ShowDialog();
   UpdateIcon();
+  dialog_is_visible_duration_timer_ = base::ElapsedTimer();
 }
 
 void LocalCardMigrationDialogControllerImpl::ShowErrorDialog() {
+  AutofillMetrics::LogLocalCardMigrationDialogOfferMetric(
+      AutofillMetrics::LOCAL_CARD_MIGRATION_DIALOG_FEEDBACK_SERVER_ERROR_SHOWN);
+
   local_card_migration_dialog_ =
       CreateLocalCardMigrationErrorDialogView(this, web_contents());
   UpdateIcon();
   local_card_migration_dialog_->ShowDialog();
+  dialog_is_visible_duration_timer_ = base::ElapsedTimer();
 }
 
 void LocalCardMigrationDialogControllerImpl::AddObserver(
@@ -138,9 +146,10 @@
 
 void LocalCardMigrationDialogControllerImpl::OnSaveButtonClicked(
     const std::vector<std::string>& selected_cards_guids) {
+  AutofillMetrics::LogLocalCardMigrationDialogUserSelectionPercentageMetric(
+      selected_cards_guids.size(), migratable_credit_cards_.size());
   AutofillMetrics::LogLocalCardMigrationDialogUserInteractionMetric(
-      dialog_is_visible_duration_timer_.Elapsed(), selected_cards_guids.size(),
-      migratable_credit_cards_.size(),
+      dialog_is_visible_duration_timer_.Elapsed(),
       AutofillMetrics::LOCAL_CARD_MIGRATION_DIALOG_CLOSED_SAVE_BUTTON_CLICKED);
 
   std::move(start_migrating_cards_callback_).Run(selected_cards_guids);
@@ -153,8 +162,7 @@
 
 void LocalCardMigrationDialogControllerImpl::OnCancelButtonClicked() {
   AutofillMetrics::LogLocalCardMigrationDialogUserInteractionMetric(
-      dialog_is_visible_duration_timer_.Elapsed(), 0,
-      migratable_credit_cards_.size(),
+      dialog_is_visible_duration_timer_.Elapsed(),
       AutofillMetrics::
           LOCAL_CARD_MIGRATION_DIALOG_CLOSED_CANCEL_BUTTON_CLICKED);
 
@@ -165,11 +173,18 @@
 }
 
 void LocalCardMigrationDialogControllerImpl::OnDoneButtonClicked() {
+  AutofillMetrics::LogLocalCardMigrationDialogUserInteractionMetric(
+      dialog_is_visible_duration_timer_.Elapsed(),
+      AutofillMetrics::LOCAL_CARD_MIGRATION_DIALOG_CLOSED_DONE_BUTTON_CLICKED);
   NotifyMigrationNoLongerAvailable();
 }
 
 void LocalCardMigrationDialogControllerImpl::OnViewCardsButtonClicked() {
-  // TODO(crbug.com/867194): Add metrics.
+  AutofillMetrics::LogLocalCardMigrationDialogUserInteractionMetric(
+      dialog_is_visible_duration_timer_.Elapsed(),
+      AutofillMetrics::
+          LOCAL_CARD_MIGRATION_DIALOG_CLOSED_VIEW_CARDS_BUTTON_CLICKED);
+
   constexpr int kPaymentsProfileUserIndex = 0;
   OpenUrl(payments::GetManageInstrumentsUrl(kPaymentsProfileUserIndex));
   NotifyMigrationNoLongerAvailable();
@@ -179,8 +194,7 @@
     const GURL& url) {
   OpenUrl(url);
   AutofillMetrics::LogLocalCardMigrationDialogUserInteractionMetric(
-      dialog_is_visible_duration_timer_.Elapsed(), 0,
-      migratable_credit_cards_.size(),
+      dialog_is_visible_duration_timer_.Elapsed(),
       AutofillMetrics::LOCAL_CARD_MIGRATION_DIALOG_LEGAL_MESSAGE_CLICKED);
 }
 
@@ -201,6 +215,10 @@
     view_state_ = LocalCardMigrationDialogState::kFinished;
     delete_local_card_callback_.Reset();
   }
+
+  AutofillMetrics::LogLocalCardMigrationDialogUserInteractionMetric(
+      dialog_is_visible_duration_timer_.Elapsed(),
+      AutofillMetrics::LOCAL_CARD_MIGRATION_DIALOG_DELETE_CARD_ICON_CLICKED);
 }
 
 void LocalCardMigrationDialogControllerImpl::OnDialogClosed() {
diff --git a/chrome/browser/ui/views/autofill/local_card_migration_dialog_view.cc b/chrome/browser/ui/views/autofill/local_card_migration_dialog_view.cc
index d07071b6..5eb93e5 100644
--- a/chrome/browser/ui/views/autofill/local_card_migration_dialog_view.cc
+++ b/chrome/browser/ui/views/autofill/local_card_migration_dialog_view.cc
@@ -151,10 +151,14 @@
   ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
   // Set up the tip text container with inset, background and a solid border.
   auto tip_text_container = std::make_unique<views::View>();
+  gfx::Insets container_insets =
+      provider->GetInsetsMetric(views::INSETS_DIALOG_SUBSECTION);
+  int container_child_space =
+      provider->GetDistanceMetric(views::DISTANCE_RELATED_LABEL_HORIZONTAL);
+
   tip_text_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::kHorizontal,
-      gfx::Insets(provider->GetInsetsMetric(views::INSETS_DIALOG_SUBSECTION)),
-      provider->GetDistanceMetric(views::DISTANCE_RELATED_LABEL_HORIZONTAL)));
+      views::BoxLayout::kHorizontal, gfx::Insets(container_insets),
+      container_child_space));
   tip_text_container->SetBackground(
       views::CreateSolidBackground(gfx::kGoogleGrey050));
   constexpr int kTipValuePromptBorderThickness = 1;
@@ -173,8 +177,12 @@
                                ChromeTextStyle::STYLE_SECONDARY);
   tip->SetMultiLine(true);
   tip->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-
+  tip->SizeToFit(
+      provider->GetDistanceMetric(DISTANCE_LARGE_MODAL_DIALOG_PREFERRED_WIDTH) -
+      kMigrationDialogInsets.width() - container_insets.width() -
+      kTipImageSize - container_child_space);
   tip_text_container->AddChildView(tip);
+
   return tip_text_container;
 }
 
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index b78bbc3..6c58db7 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -168,12 +168,6 @@
   return border;
 }
 
-SkColor GetBookmarkBarTextColor(const ui::ThemeProvider* theme_provider) {
-  return color_utils::GetColorWithMinimumContrast(
-      theme_provider->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT),
-      theme_provider->GetColor(ThemeProperties::COLOR_TOOLBAR));
-}
-
 // BookmarkButtonBase -----------------------------------------------
 
 // Base class for non-menu hosting buttons used on the bookmark bar.
@@ -993,8 +987,7 @@
 
     ui::PaintRecorder recorder(paint_info.context(), size());
     // TODO(sky/glen): make me pretty!
-    recorder.canvas()->FillRect(indicator_bounds,
-                                GetBookmarkBarTextColor(GetThemeProvider()));
+    recorder.canvas()->FillRect(indicator_bounds, GetBookmarkBarTextColor());
   }
 }
 
@@ -1631,7 +1624,7 @@
   // We don't always have a theme provider (ui tests, for example).
   const ui::ThemeProvider* const tp = GetThemeProvider();
   if (tp) {
-    SkColor color = GetBookmarkBarTextColor(tp);
+    SkColor color = GetBookmarkBarTextColor();
     button->SetEnabledTextColors(color);
     if (node->is_folder()) {
       button->SetImage(views::Button::STATE_NORMAL,
@@ -1651,9 +1644,8 @@
         // This favicon currently does not match the default favicon icon used
         // elsewhere in the codebase.
         // See https://crbug/814447
-        const gfx::ImageSkia icon =
-            gfx::CreateVectorIcon(kDefaultTouchFaviconIcon,
-                                  GetBookmarkBarTextColor(GetThemeProvider()));
+        const gfx::ImageSkia icon = gfx::CreateVectorIcon(
+            kDefaultTouchFaviconIcon, GetBookmarkBarTextColor());
         const gfx::ImageSkia mask =
             gfx::CreateVectorIcon(kDefaultTouchFaviconMaskIcon, SK_ColorBLACK);
         favicon = gfx::ImageSkiaOperations::CreateMaskedImage(icon, mask);
@@ -1965,7 +1957,7 @@
                     GetBookmarkButton(i));
   }
 
-  const SkColor color = GetBookmarkBarTextColor(theme_provider);
+  const SkColor color = GetBookmarkBarTextColor();
   other_bookmarks_button_->SetEnabledTextColors(color);
   other_bookmarks_button_->SetImage(views::Button::STATE_NORMAL,
                                     chrome::GetBookmarkFolderIcon(color));
@@ -2064,3 +2056,14 @@
 
   return it - bookmark_buttons_.cbegin();
 }
+
+SkColor BookmarkBarView::GetBookmarkBarTextColor() {
+  const ui::ThemeProvider* theme_provider = GetThemeProvider();
+  int background_color_id =
+      bookmark_bar_state_ == BookmarkBar::DETACHED
+          ? ThemeProperties::COLOR_DETACHED_BOOKMARK_BAR_BACKGROUND
+          : ThemeProperties::COLOR_TOOLBAR;
+  return color_utils::GetColorWithMinimumContrast(
+      theme_provider->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT),
+      theme_provider->GetColor(background_color_id));
+}
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h
index 60f2297..46cc2eba 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h
@@ -380,6 +380,9 @@
   // or -1 if |button| is not a bookmark button from this bar.
   int GetIndexForButton(views::View* button);
 
+  // Returns the color that should be used to draw text on the bookmark bar.
+  SkColor GetBookmarkBarTextColor();
+
   // Needed to react to kShowAppsShortcutInBookmarkBar changes.
   PrefChangeRegistrar profile_pref_registrar_;
 
diff --git a/chrome/browser/ui/views/download/download_item_view.cc b/chrome/browser/ui/views/download/download_item_view.cc
index dfd3de8..e1fcf3f5 100644
--- a/chrome/browser/ui/views/download/download_item_view.cc
+++ b/chrome/browser/ui/views/download/download_item_view.cc
@@ -575,7 +575,7 @@
   // color for opaque canvases).
   canvas->DrawColor(SK_ColorBLACK);
   canvas->DrawColor(
-      GetThemeProvider()->GetColor(ThemeProperties::COLOR_TOOLBAR));
+      GetThemeProvider()->GetColor(ThemeProperties::COLOR_DOWNLOAD_SHELF));
 
   DrawStatusText(canvas);
   DrawFilename(canvas);
diff --git a/chrome/browser/ui/views/download/download_shelf_view.cc b/chrome/browser/ui/views/download/download_shelf_view.cc
index 07aabb42..971a5fb 100644
--- a/chrome/browser/ui/views/download/download_shelf_view.cc
+++ b/chrome/browser/ui/views/download/download_shelf_view.cc
@@ -156,7 +156,8 @@
     // For custom themes, we have to make up a background color for the
     // button. Use a slight tint of the shelf background.
     bg_color = color_utils::BlendTowardOppositeLuma(
-        GetThemeProvider()->GetColor(ThemeProperties::COLOR_TOOLBAR), 0x10);
+        GetThemeProvider()->GetColor(ThemeProperties::COLOR_DOWNLOAD_SHELF),
+        0x10);
   }
   button->SetBgColorOverride(bg_color);
 }
@@ -305,7 +306,7 @@
   ConfigureButtonForTheme(show_all_view_);
 
   SetBackground(views::CreateSolidBackground(
-      GetThemeProvider()->GetColor(ThemeProperties::COLOR_TOOLBAR)));
+      GetThemeProvider()->GetColor(ThemeProperties::COLOR_DOWNLOAD_SHELF)));
 
   views::SetImageFromVectorIcon(
       close_button_, vector_icons::kCloseRoundedIcon,
diff --git a/chrome/browser/ui/views/infobars/infobar_view.cc b/chrome/browser/ui/views/infobars/infobar_view.cc
index c38831b..ff143fc 100644
--- a/chrome/browser/ui/views/infobars/infobar_view.cc
+++ b/chrome/browser/ui/views/infobars/infobar_view.cc
@@ -58,8 +58,7 @@
 DEFINE_UI_CLASS_PROPERTY_KEY(LabelType, kLabelType, LabelType::kNone);
 
 // IDs of the colors to use for infobar elements.
-constexpr int kInfoBarLabelBackgroundColor =
-    ThemeProperties::COLOR_DETACHED_BOOKMARK_BAR_BACKGROUND;
+constexpr int kInfoBarLabelBackgroundColor = ThemeProperties::COLOR_INFOBAR;
 constexpr int kInfoBarLabelTextColor = ThemeProperties::COLOR_BOOKMARK_TEXT;
 
 bool SortLabelsByDecreasingWidth(views::Label* label_1, views::Label* label_2) {
diff --git a/chrome/browser/ui/views/status_bubble_views.cc b/chrome/browser/ui/views/status_bubble_views.cc
index 6357089d..8d771ebf8 100644
--- a/chrome/browser/ui/views/status_bubble_views.cc
+++ b/chrome/browser/ui/views/status_bubble_views.cc
@@ -478,7 +478,7 @@
   Op(path, stroke_path, kDifference_SkPathOp, &fill_path);
   flags.setStyle(cc::PaintFlags::kFill_Style);
   const SkColor bubble_color =
-      theme_provider_->GetColor(ThemeProperties::COLOR_TOOLBAR);
+      theme_provider_->GetColor(ThemeProperties::COLOR_STATUS_BUBBLE);
   flags.setColor(bubble_color);
   canvas->sk_canvas()->drawPath(fill_path, flags);
 
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
index 12e4f7c..32f8a7ac 100644
--- a/chrome/browser/ui/views/tabs/tab.cc
+++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -23,6 +23,7 @@
 #include "cc/paint/paint_flags.h"
 #include "cc/paint/paint_recorder.h"
 #include "cc/paint/paint_shader.h"
+#include "chrome/browser/browser_features.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/layout_constants.h"
@@ -561,6 +562,11 @@
 }
 
 bool Tab::GetTooltipText(const gfx::Point& p, base::string16* tooltip) const {
+  // TODO(corising): Make sure that accessibility is solved properly for hover
+  // cards.
+  // Tab hover cards replace tooltips.
+  if (base::FeatureList::IsEnabled(features::kTabHoverCards))
+    return false;
   // Note: Anything that affects the tooltip text should be accounted for when
   // calling TooltipTextChanged() from Tab::SetData().
   *tooltip = GetTooltipText(data_.title, data_.alert_state);
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index 66aeb1b..26542e8 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -431,25 +431,14 @@
 
   // The following methods update one of the mouse or touch input depending upon
   // the InputSource.
-  bool PressInput(const gfx::Point& location) {
+  bool PressInput(const gfx::Point& location, int id = 0) {
     if (input_source() == INPUT_SOURCE_MOUSE) {
       return ui_test_utils::SendMouseMoveSync(location) &&
           ui_test_utils::SendMouseEventsSync(
               ui_controls::LEFT, ui_controls::DOWN);
     }
 #if defined(OS_CHROMEOS)
-    SendTouchEventsSync(ui_controls::PRESS, 0, location);
-#else
-    NOTREACHED();
-#endif
-    return true;
-  }
-
-  bool PressInput2() {
-    // Second touch input is only used for touch sequence tests.
-    EXPECT_EQ(INPUT_SOURCE_TOUCH, input_source());
-#if defined(OS_CHROMEOS)
-    SendTouchEventsSync(ui_controls::PRESS, 1, gfx::Point());
+    SendTouchEventsSync(ui_controls::PRESS, id, location);
 #else
     NOTREACHED();
 #endif
@@ -479,59 +468,38 @@
     return true;
   }
 
-  bool DragInputToNotifyWhenDone(int x, int y, base::OnceClosure task) {
-    if (input_source() == INPUT_SOURCE_MOUSE)
-      return ui_controls::SendMouseMoveNotifyWhenDone(x, y, std::move(task));
-#if defined(OS_CHROMEOS)
-    ui_controls::SendTouchEventsNotifyWhenDone(ui_controls::MOVE, 0, x, y,
-                                               std::move(task));
-#else
-    NOTREACHED();
-#endif
-    return true;
-  }
-
-  bool DragInput2ToNotifyWhenDone(int x,
-                                 int y,
-                                 const base::Closure& task) {
-    if (input_source() == INPUT_SOURCE_MOUSE)
-      return ui_controls::SendMouseMoveNotifyWhenDone(x, y, task);
-#if defined(OS_CHROMEOS)
-    ui_controls::SendTouchEventsNotifyWhenDone(ui_controls::MOVE, 1, x, y,
-                                               std::move(task));
-#else
-    NOTREACHED();
-#endif
-    return true;
-  }
-
-  bool ReleaseInput() {
+  bool DragInputToNotifyWhenDone(const gfx::Point& location,
+                                 base::OnceClosure task) {
     if (input_source() == INPUT_SOURCE_MOUSE) {
-      return ui_test_utils::SendMouseEventsSync(
-              ui_controls::LEFT, ui_controls::UP);
+      return ui_controls::SendMouseMoveNotifyWhenDone(
+          location.x(), location.y(), std::move(task));
+    }
+
+#if defined(OS_CHROMEOS)
+    ui_controls::SendTouchEventsNotifyWhenDone(
+        ui_controls::MOVE, 0, location.x(), location.y(), std::move(task));
+#else
+    NOTREACHED();
+#endif
+    return true;
+  }
+
+  bool ReleaseInput(int id = 0, bool async = false) {
+    if (input_source() == INPUT_SOURCE_MOUSE) {
+      return async ? ui_controls::SendMouseEvents(ui_controls::LEFT,
+                                                  ui_controls::UP)
+                   : ui_test_utils::SendMouseEventsSync(ui_controls::LEFT,
+                                                        ui_controls::UP);
     }
 #if defined(OS_CHROMEOS)
-    SendTouchEventsSync(ui_controls::RELEASE, 0, gfx::Point());
+    SendTouchEventsSync(ui_controls::RELEASE, id, gfx::Point());
 #else
     NOTREACHED();
 #endif
     return true;
   }
 
-  bool ReleaseInput2() {
-    if (input_source() == INPUT_SOURCE_MOUSE) {
-      return ui_test_utils::SendMouseEventsSync(
-              ui_controls::LEFT, ui_controls::UP);
-    }
-#if defined(OS_CHROMEOS)
-    SendTouchEventsSync(ui_controls::RELEASE, 1, gfx::Point());
-#else
-    NOTREACHED();
-#endif
-    return true;
-  }
-
-  void ReleaseMouseAfterWindowDetached() {
+  void ReleaseInputAfterWindowDetached() {
     // On macOS, we want to avoid generating the input event [which requires an
     // associated window] until the window has been detached. Failure to do so
     // causes odd behavior [e.g. on macOS 10.10, the mouse-up will reactivate
@@ -540,18 +508,14 @@
       base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
           FROM_HERE,
           base::BindOnce(&DetachToBrowserTabDragControllerTest::
-                             ReleaseMouseAfterWindowDetached,
+                             ReleaseInputAfterWindowDetached,
                          base::Unretained(this)),
           base::TimeDelta::FromMilliseconds(1));
       return;
     }
 
-    ASSERT_TRUE(ReleaseMouseAsync());
-  }
-
-  bool ReleaseMouseAsync() {
-    return input_source() == INPUT_SOURCE_MOUSE &&
-        ui_controls::SendMouseEvents(ui_controls::LEFT, ui_controls::UP);
+    // Windows hangs if you use a sync mouse event here.
+    ASSERT_TRUE(ReleaseInput(0, true));
   }
 
   bool MoveInputTo(const gfx::Point& location) {
@@ -620,6 +584,20 @@
 #endif
   }
 
+  void DragTabAndNotify(TabStrip* tab_strip,
+                        base::OnceClosure task,
+                        int tab = 0,
+                        int drag_x_offset = 0) {
+    // Move to the tab and drag it enough so that it detaches.
+    const gfx::Point tab_0_center =
+        GetCenterInScreenCoordinates(tab_strip->tab_at(tab));
+    ASSERT_TRUE(PressInput(tab_0_center));
+    const gfx::Point target =
+        tab_0_center + gfx::Vector2d(drag_x_offset, GetDetachY(tab_strip));
+    ASSERT_TRUE(DragInputToNotifyWhenDone(target, std::move(task)));
+    QuitWhenNotDragging();
+  }
+
   Browser* browser() const { return InProcessBrowserTest::browser(); }
 
  private:
@@ -766,13 +744,8 @@
 
   // Move to the first tab and drag it enough so that it detaches, but not
   // enough that it attaches to browser2.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-                  base::Bind(&DragToSeparateWindowStep2,
-                             this, tab_strip, tab_strip2)));
-  QuitWhenNotDragging();
+  DragTabAndNotify(tab_strip, base::BindOnce(&DragToSeparateWindowStep2, this,
+                                             tab_strip, tab_strip2));
 
   // Should now be attached to tab_strip2.
   ASSERT_TRUE(tab_strip2->IsDragSessionActive());
@@ -850,11 +823,6 @@
 
 namespace {
 
-void DetachToOwnWindowStep2(DetachToBrowserTabDragControllerTest* test) {
-  if (test->input_source() == INPUT_SOURCE_TOUCH)
-    ASSERT_TRUE(test->ReleaseInput());
-}
-
 #if defined(OS_CHROMEOS)
 bool IsWindowPositionManaged(aura::Window* window) {
   return test::GetWindowForProperties(window)->GetProperty(
@@ -928,15 +896,10 @@
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Move to the first tab and drag it enough so that it detaches.
-  gfx::Point tab_0_center(
-      GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-                  base::Bind(&DetachToOwnWindowStep2, this)));
-  if (input_source() == INPUT_SOURCE_MOUSE)
-    ReleaseMouseAfterWindowDetached();
-  QuitWhenNotDragging();
+  DragTabAndNotify(tab_strip,
+                   base::BindOnce(&DetachToBrowserTabDragControllerTest::
+                                      ReleaseInputAfterWindowDetached,
+                                  base::Unretained(this)));
 
   // Should no longer be dragging.
   ASSERT_FALSE(tab_strip->IsDragSessionActive());
@@ -1000,14 +963,10 @@
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Move to the first tab and drag it enough so that it detaches.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-      base::Bind(&DetachToOwnWindowStep2, this)));
-  if (input_source() == INPUT_SOURCE_MOUSE)
-    ReleaseMouseAfterWindowDetached();
-  QuitWhenNotDragging();
+  DragTabAndNotify(tab_strip,
+                   base::BindOnce(&DetachToBrowserTabDragControllerTest::
+                                      ReleaseInputAfterWindowDetached,
+                                  base::Unretained(this)));
 
   // Should no longer be dragging.
   ASSERT_FALSE(tab_strip->IsDragSessionActive());
@@ -1055,15 +1014,10 @@
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Move to the first tab and drag it enough so that it detaches.
-  gfx::Point tab_0_center(
-      GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-                  base::Bind(&DetachToOwnWindowStep2, this)));
-  if (input_source() == INPUT_SOURCE_MOUSE)
-    ReleaseMouseAfterWindowDetached();
-  QuitWhenNotDragging();
+  DragTabAndNotify(tab_strip,
+                   base::BindOnce(&DetachToBrowserTabDragControllerTest::
+                                      ReleaseInputAfterWindowDetached,
+                                  base::Unretained(this)));
 
   // Should no longer be dragging.
   ASSERT_FALSE(tab_strip->IsDragSessionActive());
@@ -1119,14 +1073,10 @@
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Move to the first tab and drag it enough so that it detaches.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-      base::Bind(&DetachToOwnWindowStep2, this)));
-  if (input_source() == INPUT_SOURCE_MOUSE)
-    ReleaseMouseAfterWindowDetached();
-  QuitWhenNotDragging();
+  DragTabAndNotify(tab_strip,
+                   base::BindOnce(&DetachToBrowserTabDragControllerTest::
+                                      ReleaseInputAfterWindowDetached,
+                                  base::Unretained(this)));
 
   // Should no longer be dragging.
   ASSERT_FALSE(tab_strip->IsDragSessionActive());
@@ -1271,16 +1221,12 @@
 
   // Click the first tab and select two middle tabs.
   gfx::Point tab_1_center(GetCenterInScreenCoordinates(tab_strip->tab_at(1)));
-  gfx::Point tab_2_center(GetCenterInScreenCoordinates(tab_strip->tab_at(2)));
   ASSERT_TRUE(PressInput(tab_1_center));
   ASSERT_TRUE(ReleaseInput());
   browser()->tab_strip_model()->ToggleSelectionAt(2);
   // Press mouse button in the second tab and drag it enough to detach.
-  ASSERT_TRUE(PressInput(tab_2_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_2_center.x(), tab_2_center.y() + GetDetachY(tab_strip),
-      base::Bind(&CloseTabsWhileDetachedStep2, browser_list)));
-  QuitWhenNotDragging();
+  DragTabAndNotify(
+      tab_strip, base::BindOnce(&CloseTabsWhileDetachedStep2, browser_list), 2);
 
   // Should not be dragging.
   ASSERT_EQ(1u, browser_list->size());
@@ -1314,12 +1260,8 @@
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Move to the first tab and drag it enough so that it detaches.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-      base::Bind(&PressEscapeWhileDetachedStep2, browser_list)));
-  QuitWhenNotDragging();
+  DragTabAndNotify(
+      tab_strip, base::BindOnce(&PressEscapeWhileDetachedStep2, browser_list));
 
   // Should not be dragging.
   ASSERT_FALSE(tab_strip->IsDragSessionActive());
@@ -1344,11 +1286,8 @@
                   const BrowserList* browser_list) {
   // Should only be one window.
   ASSERT_EQ(1u, browser_list->size());
-  if (test->input_source() == INPUT_SOURCE_TOUCH) {
-    ASSERT_TRUE(test->ReleaseInput());
-  } else {
-    ASSERT_TRUE(test->ReleaseMouseAsync());
-  }
+  // Windows hangs if you use a sync mouse event here.
+  ASSERT_TRUE(test->ReleaseInput(0, true));
 }
 
 }  // namespace
@@ -1363,12 +1302,8 @@
 
   // Move to the first tab and drag it enough so that it would normally
   // detach.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-      base::Bind(&DragAllStep2, this, browser_list)));
-  QuitWhenNotDragging();
+  DragTabAndNotify(tab_strip,
+                   base::BindOnce(&DragAllStep2, this, browser_list));
 
   // Should not be dragging.
   ASSERT_FALSE(tab_strip->IsDragSessionActive());
@@ -1430,14 +1365,9 @@
 
   // Move to the first tab and drag it enough so that it detaches, but not
   // enough that it attaches to browser2.
-  gfx::Point tab_0_center(
-      GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-      base::Bind(&DragAllToSeparateWindowStep2, this, tab_strip, tab_strip2,
-                 browser_list)));
-  QuitWhenNotDragging();
+  DragTabAndNotify(
+      tab_strip, base::BindOnce(&DragAllToSeparateWindowStep2, this, tab_strip,
+                                tab_strip2, browser_list));
 
   // Should now be attached to tab_strip2.
   ASSERT_TRUE(tab_strip2->IsDragSessionActive());
@@ -1496,14 +1426,9 @@
 
   // Move to the first tab and drag it enough so that it detaches, but not
   // enough that it attaches to browser2.
-  gfx::Point tab_0_center(
-      GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-                  base::Bind(&DragAllToSeparateWindowAndCancelStep2, this,
-                             tab_strip, tab_strip2, browser_list)));
-  QuitWhenNotDragging();
+  DragTabAndNotify(tab_strip,
+                   base::BindOnce(&DragAllToSeparateWindowAndCancelStep2, this,
+                                  tab_strip, tab_strip2, browser_list));
 
   // Should now be attached to tab_strip2.
   ASSERT_TRUE(tab_strip2->IsDragSessionActive());
@@ -1607,14 +1532,9 @@
 
   // Move to the first tab and drag it enough so that it detaches, but not
   // enough that it attaches to browser2.
-  gfx::Point tab_0_center(
-      GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-      base::Bind(&DragAllToSeparateWindowStep2, this, tab_strip, tab_strip2,
-                 browser_list)));
-  QuitWhenNotDragging();
+  DragTabAndNotify(
+      tab_strip, base::BindOnce(&DragAllToSeparateWindowStep2, this, tab_strip,
+                                tab_strip2, browser_list));
 
   // Should now be attached to tab_strip2.
   ASSERT_TRUE(tab_strip2->IsDragSessionActive());
@@ -1677,7 +1597,7 @@
       content::NOTIFICATION_LOAD_STOP,
       content::NotificationService::AllSources());
   ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
+      tab_0_center + gfx::Vector2d(0, GetDetachY(tab_strip)),
       base::Bind(&CancelOnNewTabWhenDraggingStep2, this, browser_list)));
   observer.Wait();
 
@@ -1728,14 +1648,9 @@
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
 
   // Move to the first tab and drag it enough so that it detaches.
-  gfx::Point tab_0_center(
-      GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-      base::Bind(&DragInMaximizedWindowStep2, this, browser(), tab_strip,
-                 browser_list)));
-  QuitWhenNotDragging();
+  DragTabAndNotify(tab_strip,
+                   base::BindOnce(&DragInMaximizedWindowStep2, this, browser(),
+                                  tab_strip, browser_list));
 
   ASSERT_FALSE(TabDragController::IsActive());
 
@@ -1756,801 +1671,6 @@
   EXPECT_TRUE(new_browser->window()->IsMaximized());
 }
 
-// Subclass of DetachToBrowserTabDragControllerTest that
-// creates multiple displays.
-class DetachToBrowserInSeparateDisplayTabDragControllerTest
-    : public DetachToBrowserTabDragControllerTest {
- public:
-  DetachToBrowserInSeparateDisplayTabDragControllerTest() {}
-  virtual ~DetachToBrowserInSeparateDisplayTabDragControllerTest() {}
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    DetachToBrowserTabDragControllerTest::SetUpCommandLine(command_line);
-    // Make screens sufficiently wide to host 2 browsers side by side.
-    command_line->AppendSwitchASCII("ash-host-window-bounds",
-                                    "0+0-600x600,600+0-600x600");
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(
-      DetachToBrowserInSeparateDisplayTabDragControllerTest);
-};
-
-// Subclass of DetachToBrowserTabDragControllerTest that runs tests only with
-// touch input.
-class DetachToBrowserTabDragControllerTestTouch
-    : public DetachToBrowserTabDragControllerTest {
- public:
-  DetachToBrowserTabDragControllerTestTouch() {}
-  virtual ~DetachToBrowserTabDragControllerTestTouch() {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DetachToBrowserTabDragControllerTestTouch);
-};
-
-namespace {
-
-void DragSingleTabToSeparateWindowInSecondDisplayStep3(
-    DetachToBrowserTabDragControllerTest* test) {
-  ASSERT_TRUE(test->ReleaseInput());
-}
-
-void DragSingleTabToSeparateWindowInSecondDisplayStep2(
-    DetachToBrowserTabDragControllerTest* test,
-    const gfx::Point& target_point) {
-  ASSERT_TRUE(test->DragInputToNotifyWhenDone(
-      target_point.x(), target_point.y(),
-      base::Bind(&DragSingleTabToSeparateWindowInSecondDisplayStep3, test)));
-}
-
-}  // namespace
-
-// Drags from browser to a second display and releases input.
-IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
-                       DragSingleTabToSeparateWindowInSecondDisplay) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
-  TabStrip* tab_strip = GetTabStripForBrowser(browser());
-
-  // Move to the first tab and drag it enough so that it detaches.
-  // Then drag it to the final destination on the second screen.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-                  base::Bind(&DragSingleTabToSeparateWindowInSecondDisplayStep2,
-                             this, gfx::Point(600 + tab_0_center.x(),
-                                              tab_0_center.y()
-                                              + GetDetachY(tab_strip)))));
-  QuitWhenNotDragging();
-
-  // Should no longer be dragging.
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-  ASSERT_FALSE(TabDragController::IsActive());
-
-  // There should now be another browser.
-  ASSERT_EQ(2u, browser_list->size());
-  Browser* new_browser = browser_list->get(1);
-  ASSERT_TRUE(new_browser->window()->IsActive());
-  TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
-  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
-
-  // This other browser should be on the second screen (with mouse drag)
-  // With the touch input the browser cannot be dragged from one screen
-  // to another and the window stays on the first screen.
-  if (input_source() == INPUT_SOURCE_MOUSE) {
-    display::Screen* screen = display::Screen::GetScreen();
-    EXPECT_EQ(
-        ui_test_utils::GetSecondaryDisplay(screen).id(),
-        screen
-            ->GetDisplayNearestWindow(new_browser->window()->GetNativeWindow())
-            .id());
-  }
-
-  EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
-  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
-
-  // Both windows should not be maximized
-  EXPECT_FALSE(browser()->window()->IsMaximized());
-  EXPECT_FALSE(new_browser->window()->IsMaximized());
-}
-
-namespace {
-
-// Invoked from the nested run loop.
-void DragTabToWindowInSeparateDisplayStep2(
-    DetachToBrowserTabDragControllerTest* test,
-    TabStrip* not_attached_tab_strip,
-    TabStrip* target_tab_strip) {
-  ASSERT_FALSE(not_attached_tab_strip->IsDragSessionActive());
-  ASSERT_FALSE(target_tab_strip->IsDragSessionActive());
-  ASSERT_TRUE(TabDragController::IsActive());
-
-  // Drag to target_tab_strip. This should stop the nested loop from dragging
-  // the window.
-  gfx::Point target_point(
-      GetCenterInScreenCoordinates(target_tab_strip->tab_at(0)));
-
-  // Move it closer to the beginning of the tab so it will drop before that tab.
-  target_point.Offset(-20, 0);
-  ASSERT_TRUE(test->DragInputToAsync(target_point));
-}
-
-}  // namespace
-
-// Drags from browser to another browser on a second display and releases input.
-IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
-                       DragTabToWindowInSeparateDisplay) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
-  TabStrip* tab_strip = GetTabStripForBrowser(browser());
-
-  // Create another browser.
-  Browser* browser2 = CreateBrowser(browser()->profile());
-  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
-  ResetIDs(browser2->tab_strip_model(), 100);
-
-  // Move the second browser to the second display.
-  display::Screen* screen = display::Screen::GetScreen();
-  Display second_display = ui_test_utils::GetSecondaryDisplay(screen);
-  browser2->window()->SetBounds(second_display.work_area());
-  // In Mash, the display change as the result of the bounds change is processed
-  // asynchronously in the window server, it should wait for those changes to
-  // complete.
-  aura::test::WaitForAllChangesToComplete();
-  EXPECT_EQ(
-      second_display.id(),
-      screen->GetDisplayNearestWindow(browser2->window()->GetNativeWindow())
-          .id());
-
-  // Move to the first tab and drag it enough so that it detaches, but not
-  // enough that it attaches to browser2.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-                  base::Bind(&DragTabToWindowInSeparateDisplayStep2,
-                             this, tab_strip, tab_strip2)));
-  QuitWhenNotDragging();
-
-  // Should now be attached to tab_strip2.
-  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-  ASSERT_TRUE(TabDragController::IsActive());
-
-  // Release the mouse, stopping the drag session.
-  ASSERT_TRUE(ReleaseInput());
-  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-  ASSERT_FALSE(TabDragController::IsActive());
-  EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
-  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
-
-  // Both windows should not be maximized
-  EXPECT_FALSE(browser()->window()->IsMaximized());
-  EXPECT_FALSE(browser2->window()->IsMaximized());
-}
-
-IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
-                       DragBrowserWindowWhenMajorityOfBoundsInSecondDisplay) {
-  // Set the browser's window bounds such that the majority of its bounds
-  // resides in the second display.
-  const std::pair<Display, Display> displays =
-      GetDisplays(display::Screen::GetScreen());
-
-  TabStrip* tab_strip = GetTabStripForBrowser(browser());
-  {
-    // Moves the browser window through dragging so that the majority of its
-    // bounds are in the secondary display but it's still be in the primary
-    // display. Do not use SetBounds() or related, it may move the browser
-    // window to the secondary display in some configurations like Mash.
-    int target_x = displays.first.bounds().right() -
-                   browser()->window()->GetBounds().width() / 2 + 20;
-    const gfx::Point tab_0_center =
-        GetCenterInScreenCoordinates(tab_strip->tab_at(0));
-    gfx::Point target_point = tab_0_center;
-    target_point.Offset(target_x - browser()->window()->GetBounds().x(),
-                        GetDetachY(tab_strip));
-
-    ASSERT_TRUE(PressInput(tab_0_center));
-    ASSERT_TRUE(DragInputToNotifyWhenDone(
-        tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-        base::BindRepeating(&DragSingleTabToSeparateWindowInSecondDisplayStep2,
-                            this, target_point)));
-    QuitWhenNotDragging();
-    StopAnimating(tab_strip);
-  }
-  EXPECT_EQ(displays.first.id(),
-            browser()->window()->GetNativeWindow()->GetHost()->GetDisplayId());
-
-  // Start dragging the window by the tab strip, and move it only to the edge
-  // of the first display. Expect at that point mouse would warp and the window
-  // will therefore reside in the second display when mouse is released.
-  const gfx::Point tab_0_center =
-      GetCenterInScreenCoordinates(tab_strip->tab_at(0));
-  const int offset_x = tab_0_center.x() - browser()->window()->GetBounds().x();
-  const int detach_y = tab_0_center.y() + GetDetachY(tab_strip);
-  const int first_display_warp_edge_x = displays.first.bounds().right() - 1;
-  const gfx::Point warped_point(displays.second.bounds().x() + 1, detach_y);
-
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), detach_y, base::BindLambdaForTesting([&]() {
-        // This makes another event on the warped location because the test
-        // system does not create it automatically as the result of pointer
-        // warp.
-        ASSERT_TRUE(DragInputToNotifyWhenDone(
-            first_display_warp_edge_x, detach_y,
-            base::BindRepeating(
-                &DragSingleTabToSeparateWindowInSecondDisplayStep2, this,
-                warped_point)));
-      })));
-  QuitWhenNotDragging();
-
-  // Should no longer be dragging.
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-  ASSERT_FALSE(TabDragController::IsActive());
-
-  // There should only be a single browser.
-  ASSERT_EQ(1u, browser_list->size());
-  ASSERT_EQ(browser(), browser_list->get(0));
-  ASSERT_TRUE(browser()->window()->IsActive());
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-
-  // Browser now resides in display 2.
-  EXPECT_EQ(warped_point.x() - offset_x, browser()->window()->GetBounds().x());
-  EXPECT_EQ(displays.second.id(),
-            browser()->window()->GetNativeWindow()->GetHost()->GetDisplayId());
-}
-
-// Drags from browser to another browser on a second display and releases input.
-IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
-                       DragTabToWindowOnSecondDisplay) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
-  TabStrip* tab_strip = GetTabStripForBrowser(browser());
-
-  // Create another browser.
-  Browser* browser2 = CreateBrowser(browser()->profile());
-  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
-  ResetIDs(browser2->tab_strip_model(), 100);
-
-  // Move both browsers to be side by side on the second display.
-  display::Screen* screen = display::Screen::GetScreen();
-  Display second_display = ui_test_utils::GetSecondaryDisplay(screen);
-  gfx::Rect work_area = second_display.work_area();
-  work_area.set_width(work_area.width() / 2);
-  browser()->window()->SetBounds(work_area);
-  work_area.set_x(work_area.right());
-  browser2->window()->SetBounds(work_area);
-  // Wait for the display changes. See the ealier comments for the details.
-  aura::test::WaitForAllChangesToComplete();
-  EXPECT_EQ(
-      second_display.id(),
-      screen->GetDisplayNearestWindow(browser()->window()->GetNativeWindow())
-          .id());
-  EXPECT_EQ(
-      second_display.id(),
-      screen->GetDisplayNearestWindow(browser2->window()->GetNativeWindow())
-          .id());
-
-  // Move to the first tab and drag it enough so that it detaches, but not
-  // enough that it attaches to browser2.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-                  base::Bind(&DragTabToWindowInSeparateDisplayStep2,
-                             this, tab_strip, tab_strip2)));
-  QuitWhenNotDragging();
-
-  // Should now be attached to tab_strip2.
-  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-  ASSERT_TRUE(TabDragController::IsActive());
-
-  // Release the mouse, stopping the drag session.
-  ASSERT_TRUE(ReleaseInput());
-  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-  ASSERT_FALSE(TabDragController::IsActive());
-  EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
-  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
-
-  // Both windows should not be maximized
-  EXPECT_FALSE(browser()->window()->IsMaximized());
-  EXPECT_FALSE(browser2->window()->IsMaximized());
-}
-
-// Drags from a maximized browser to another non-maximized browser on a second
-// display and releases input.
-IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
-                       DragMaxTabToNonMaxWindowInSeparateDisplay) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
-  browser()->window()->Maximize();
-  TabStrip* tab_strip = GetTabStripForBrowser(browser());
-
-  // Create another browser on the second display.
-  display::Screen* screen = display::Screen::GetScreen();
-  ASSERT_EQ(2, screen->GetNumDisplays());
-  const std::pair<Display, Display> displays = GetDisplays(screen);
-  gfx::Rect work_area = displays.second.work_area();
-  work_area.Inset(20, 20, 20, 60);
-  Browser::CreateParams params(browser()->profile(), true);
-  params.initial_show_state = ui::SHOW_STATE_NORMAL;
-  params.initial_bounds = work_area;
-  Browser* browser2 = new Browser(params);
-  AddBlankTabAndShow(browser2);
-
-  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
-  ResetIDs(browser2->tab_strip_model(), 100);
-
-  EXPECT_EQ(
-      displays.second.id(),
-      screen->GetDisplayNearestWindow(browser2->window()->GetNativeWindow())
-          .id());
-  EXPECT_EQ(
-      displays.first.id(),
-      screen->GetDisplayNearestWindow(browser()->window()->GetNativeWindow())
-          .id());
-  EXPECT_EQ(2, tab_strip->tab_count());
-  EXPECT_EQ(1, tab_strip2->tab_count());
-
-  // Move to the first tab and drag it enough so that it detaches, but not
-  // enough that it attaches to browser2.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-                  base::Bind(&DragTabToWindowInSeparateDisplayStep2,
-                             this, tab_strip, tab_strip2)));
-  QuitWhenNotDragging();
-
-  // Should now be attached to tab_strip2.
-  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-  ASSERT_TRUE(TabDragController::IsActive());
-
-  // Release the mouse, stopping the drag session.
-  ASSERT_TRUE(ReleaseInput());
-
-  // tab should have moved
-  EXPECT_EQ(1, tab_strip->tab_count());
-  EXPECT_EQ(2, tab_strip2->tab_count());
-
-  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-  ASSERT_FALSE(TabDragController::IsActive());
-  EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
-  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
-
-  // Source browser should still be maximized, target should not
-  EXPECT_TRUE(browser()->window()->IsMaximized());
-  EXPECT_FALSE(browser2->window()->IsMaximized());
-}
-
-// Drags from a restored browser to an immersive fullscreen browser on a
-// second display and releases input.
-// TODO(pkasting) https://crbug.com/910782 Hangs.
-IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
-                       DISABLED_DragTabToImmersiveBrowserOnSeparateDisplay) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
-  TabStrip* tab_strip = GetTabStripForBrowser(browser());
-
-  // Create another browser.
-  Browser* browser2 = CreateBrowser(browser()->profile());
-  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
-  ResetIDs(browser2->tab_strip_model(), 100);
-
-  // Move the second browser to the second display.
-  display::Screen* screen = display::Screen::GetScreen();
-  const std::pair<Display, Display> displays = GetDisplays(screen);
-  browser2->window()->SetBounds(displays.second.work_area());
-  // Wait for the display changes. See the ealier comments for the details.
-  aura::test::WaitForAllChangesToComplete();
-  EXPECT_EQ(
-      displays.second.id(),
-      screen->GetDisplayNearestWindow(browser2->window()->GetNativeWindow())
-          .id());
-
-  // Put the second browser into immersive fullscreen.
-  BrowserView* browser_view2 = BrowserView::GetBrowserViewForBrowser(browser2);
-  ImmersiveModeController* immersive_controller2 =
-      browser_view2->immersive_mode_controller();
-  ASSERT_EQ(ImmersiveModeController::Type::ASH, immersive_controller2->type());
-  ash::ImmersiveFullscreenControllerTestApi(
-      static_cast<ImmersiveModeControllerAsh*>(immersive_controller2)
-          ->controller())
-      .SetupForTest();
-  chrome::ToggleFullscreenMode(browser2);
-  // For MD, the browser's top chrome is completely offscreen, with tabstrip
-  // visible.
-  ASSERT_TRUE(immersive_controller2->IsEnabled());
-  ASSERT_FALSE(immersive_controller2->IsRevealed());
-  ASSERT_TRUE(tab_strip2->visible());
-
-  // Move to the first tab and drag it enough so that it detaches, but not
-  // enough that it attaches to browser2.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-                  base::Bind(&DragTabToWindowInSeparateDisplayStep2,
-                             this, tab_strip, tab_strip2)));
-  QuitWhenNotDragging();
-
-  // Should now be attached to tab_strip2.
-  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-  ASSERT_TRUE(TabDragController::IsActive());
-
-  // browser2's top chrome should be revealed and the tab strip should be
-  // at normal height while user is tragging tabs_strip2's tabs.
-  ASSERT_TRUE(immersive_controller2->IsRevealed());
-  ASSERT_TRUE(tab_strip2->visible());
-
-  // Release the mouse, stopping the drag session.
-  ASSERT_TRUE(ReleaseInput());
-  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-  ASSERT_FALSE(TabDragController::IsActive());
-  EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
-  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
-
-  // Move the mouse off of browser2's top chrome.
-  ASSERT_TRUE(
-      ui_test_utils::SendMouseMoveSync(displays.first.bounds().CenterPoint()));
-
-  // The first browser window should not be in immersive fullscreen.
-  // browser2 should still be in immersive fullscreen, but the top chrome should
-  // no longer be revealed.
-  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
-  EXPECT_FALSE(browser_view->immersive_mode_controller()->IsEnabled());
-
-  EXPECT_TRUE(immersive_controller2->IsEnabled());
-  EXPECT_FALSE(immersive_controller2->IsRevealed());
-  EXPECT_TRUE(tab_strip2->visible());
-}
-
-// Subclass of DetachToBrowserTabDragControllerTest that
-// creates multiple displays with different device scale factors.
-class DifferentDeviceScaleFactorDisplayTabDragControllerTest
-    : public DetachToBrowserTabDragControllerTest {
- public:
-  DifferentDeviceScaleFactorDisplayTabDragControllerTest() {}
-  virtual ~DifferentDeviceScaleFactorDisplayTabDragControllerTest() {}
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    DetachToBrowserTabDragControllerTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitchASCII("ash-host-window-bounds",
-                                    "400x400,400+0-800x800*2");
-  }
-
-  float GetCursorDeviceScaleFactor() const {
-    ash::CursorManagerTestApi cursor_test_api(
-        ash::Shell::Get()->cursor_manager());
-    return cursor_test_api.GetCurrentCursor().device_scale_factor();
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(
-      DifferentDeviceScaleFactorDisplayTabDragControllerTest);
-};
-
-namespace {
-
-// The points where a tab is dragged in CursorDeviceScaleFactorStep.
-const struct DragPoint {
-  int x;
-  int y;
-} kDragPoints[] = {
-  {300, 200},
-  {399, 200},
-  {500, 200},
-  {400, 200},
-  {300, 200},
-};
-
-// The expected device scale factors after the cursor is moved to the
-// corresponding kDragPoints in CursorDeviceScaleFactorStep.
-const float kDeviceScaleFactorExpectations[] = {
-  1.0f,
-  1.0f,
-  2.0f,
-  2.0f,
-  1.0f,
-};
-
-static_assert(
-    base::size(kDragPoints) == base::size(kDeviceScaleFactorExpectations),
-    "kDragPoints and kDeviceScaleFactorExpectations must have the same "
-    "number of elements");
-
-// Drags tab to |kDragPoints[index]|, then calls the next step function.
-void CursorDeviceScaleFactorStep(
-    DifferentDeviceScaleFactorDisplayTabDragControllerTest* test,
-    TabStrip* not_attached_tab_strip,
-    size_t index) {
-  SCOPED_TRACE(index);
-  ASSERT_FALSE(not_attached_tab_strip->IsDragSessionActive());
-  ASSERT_TRUE(TabDragController::IsActive());
-
-  if (index > 0) {
-    const DragPoint p = kDragPoints[index - 1];
-    EXPECT_EQ(gfx::Point(p.x, p.y),
-              ash::Shell::Get()->aura_env()->last_mouse_location());
-    EXPECT_EQ(kDeviceScaleFactorExpectations[index - 1],
-              test->GetCursorDeviceScaleFactor());
-  }
-
-  if (index < base::size(kDragPoints)) {
-    const DragPoint p = kDragPoints[index];
-    ASSERT_TRUE(test->DragInputToNotifyWhenDone(
-        p.x, p.y, base::Bind(&CursorDeviceScaleFactorStep,
-                             test, not_attached_tab_strip, index + 1)));
-  } else {
-    // Finishes a series of CursorDeviceScaleFactorStep calls and ends drag.
-    ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
-        ui_controls::LEFT, ui_controls::UP));
-  }
-}
-
-}  // namespace
-
-// Verifies cursor's device scale factor is updated when a tab is moved across
-// displays with different device scale factors (http://crbug.com/154183).
-// TODO(pkasting): In interactive_ui_tests, scale factor never changes to 2.
-// https://crbug.com/918731
-// TODO(pkasting): In non_single_process_mash_interactive_ui_tests, pointer is
-// warped during the drag (which results in changing to scale factor 2 early),
-// and scale factor doesn't change back to 1 at the end.
-// https://crbug.com/918732
-IN_PROC_BROWSER_TEST_P(DifferentDeviceScaleFactorDisplayTabDragControllerTest,
-                       DISABLED_CursorDeviceScaleFactor) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
-  TabStrip* tab_strip = GetTabStripForBrowser(browser());
-
-  // Move the second browser to the second display.
-  ASSERT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
-
-  // Move to the first tab and drag it enough so that it detaches.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-                  base::Bind(&CursorDeviceScaleFactorStep,
-                             this, tab_strip, 0)));
-  QuitWhenNotDragging();
-}
-
-namespace {
-
-class DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest
-    : public TabDragControllerTest {
- public:
-  DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest() {}
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    TabDragControllerTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitchASCII("ash-host-window-bounds",
-                                    "0+0-250x250,251+0-250x250");
-  }
-
-  bool Press(const gfx::Point& position) {
-    return ui_test_utils::SendMouseMoveSync(position) &&
-        ui_test_utils::SendMouseEventsSync(ui_controls::LEFT,
-                                           ui_controls::DOWN);
-  }
-
-  bool DragTabAndExecuteTaskWhenDone(const gfx::Point& position,
-                                     base::OnceClosure task) {
-    return ui_controls::SendMouseMoveNotifyWhenDone(position.x(), position.y(),
-                                                    std::move(task));
-  }
-
-  void QuitWhenNotDragging() {
-    DCHECK(TabDragController::IsActive());
-    test::QuitWhenNotDraggingImpl();
-    base::RunLoop().Run();
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(
-      DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest);
-};
-
-// Invoked from the nested run loop.
-void CancelDragTabToWindowInSeparateDisplayStep3(
-    TabStrip* tab_strip,
-    const BrowserList* browser_list) {
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-  ASSERT_TRUE(TabDragController::IsActive());
-  ASSERT_EQ(2u, browser_list->size());
-
-  // Switching display mode should cancel the drag operation.
-  display::DisplayManager* display_manager =
-      ash::Shell::Get()->display_manager();
-  display_manager->AddRemoveDisplay();
-}
-
-// Invoked from the nested run loop.
-void CancelDragTabToWindowInSeparateDisplayStep2(
-    DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest* test,
-    TabStrip* tab_strip,
-    Display current_display,
-    gfx::Point final_destination,
-    const BrowserList* browser_list) {
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-  ASSERT_TRUE(TabDragController::IsActive());
-  ASSERT_EQ(2u, browser_list->size());
-
-  Browser* new_browser = browser_list->get(1);
-  EXPECT_EQ(
-      current_display.id(),
-      display::Screen::GetScreen()
-          ->GetDisplayNearestWindow(new_browser->window()->GetNativeWindow())
-          .id());
-
-  ASSERT_TRUE(test->DragTabAndExecuteTaskWhenDone(
-      final_destination,
-      base::BindOnce(&CancelDragTabToWindowInSeparateDisplayStep3, tab_strip,
-                     browser_list)));
-}
-
-}  // namespace
-
-// Drags from browser to a second display and releases input.
-IN_PROC_BROWSER_TEST_F(
-    DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest,
-    CancelDragTabToWindowIn2ndDisplay) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
-  TabStrip* tab_strip = GetTabStripForBrowser(browser());
-
-  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
-
-  // Move the second browser to the second display.
-  const std::pair<Display, Display> displays =
-      GetDisplays(display::Screen::GetScreen());
-  gfx::Point final_destination = displays.second.work_area().CenterPoint();
-
-  // Move to the first tab and drag it enough so that it detaches, but not
-  // enough to move to another display.
-  gfx::Point tab_0_dst(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(Press(tab_0_dst));
-  tab_0_dst.Offset(0, GetDetachY(tab_strip));
-  ASSERT_TRUE(DragTabAndExecuteTaskWhenDone(
-      tab_0_dst, base::BindOnce(&CancelDragTabToWindowInSeparateDisplayStep2,
-                                this, tab_strip, displays.first,
-                                final_destination, browser_list)));
-  QuitWhenNotDragging();
-
-  ASSERT_EQ(1u, browser_list->size());
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-  ASSERT_FALSE(TabDragController::IsActive());
-  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
-
-  // Release the mouse
-  ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
-      ui_controls::LEFT, ui_controls::UP));
-}
-
-// Drags from browser from a second display to primary and releases input.
-IN_PROC_BROWSER_TEST_F(
-    DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest,
-    CancelDragTabToWindowIn1stDisplay) {
-  display::Screen* screen = display::Screen::GetScreen();
-  const std::pair<Display, Display> displays = GetDisplays(screen);
-
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
-  TabStrip* tab_strip = GetTabStripForBrowser(browser());
-
-  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
-  EXPECT_EQ(
-      displays.first.id(),
-      screen->GetDisplayNearestWindow(browser()->window()->GetNativeWindow())
-          .id());
-
-  browser()->window()->SetBounds(displays.second.work_area());
-  // Wait for the display changes. See the ealier comments for the details.
-  aura::test::WaitForAllChangesToComplete();
-  EXPECT_EQ(
-      displays.second.id(),
-      screen->GetDisplayNearestWindow(browser()->window()->GetNativeWindow())
-          .id());
-
-  // Move the second browser to the display.
-  gfx::Point final_destination = displays.first.work_area().CenterPoint();
-
-  // Move to the first tab and drag it enough so that it detaches, but not
-  // enough to move to another display.
-  gfx::Point tab_0_dst(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(Press(tab_0_dst));
-  tab_0_dst.Offset(0, GetDetachY(tab_strip));
-  ASSERT_TRUE(DragTabAndExecuteTaskWhenDone(
-      tab_0_dst, base::BindOnce(&CancelDragTabToWindowInSeparateDisplayStep2,
-                                this, tab_strip, displays.second,
-                                final_destination, browser_list)));
-  QuitWhenNotDragging();
-
-  ASSERT_EQ(1u, browser_list->size());
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-  ASSERT_FALSE(TabDragController::IsActive());
-  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
-
-  // Release the mouse
-  ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
-      ui_controls::LEFT, ui_controls::UP));
-}
-
-namespace {
-void PressSecondFingerWhileDetachedStep3(
-    DetachToBrowserTabDragControllerTest* test) {
-  ASSERT_TRUE(TabDragController::IsActive());
-  ASSERT_EQ(2u, test->browser_list->size());
-  ASSERT_TRUE(test->browser_list->get(1)->window()->IsActive());
-
-  ASSERT_TRUE(test->ReleaseInput());
-  ASSERT_TRUE(test->ReleaseInput2());
-}
-
-void PressSecondFingerWhileDetachedStep2(
-    DetachToBrowserTabDragControllerTest* test,
-    const gfx::Point& target_point) {
-  ASSERT_TRUE(TabDragController::IsActive());
-  ASSERT_EQ(2u, test->browser_list->size());
-  ASSERT_TRUE(test->browser_list->get(1)->window()->IsActive());
-
-  // Continue dragging after adding a second finger.
-  ASSERT_TRUE(test->PressInput2());
-  ASSERT_TRUE(test->DragInputToNotifyWhenDone(
-      target_point.x(), target_point.y(),
-      base::Bind(&PressSecondFingerWhileDetachedStep3, test)));
-}
-
-}  // namespace
-
-// Detaches a tab and while detached presses a second finger.
-IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTestTouch,
-                       PressSecondFingerWhileDetached) {
-  // Add another tab.
-  AddTabAndResetBrowser(browser());
-  TabStrip* tab_strip = GetTabStripForBrowser(browser());
-  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
-
-  // Move to the first tab and drag it enough so that it detaches. Drag it
-  // slightly more horizontally so that it does not generate a swipe down
-  // gesture that minimizes the detached browser window.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  const int touch_move_delta = GetDetachY(tab_strip);
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x() + touch_move_delta + 5,
-      tab_0_center.y() + touch_move_delta,
-      base::Bind(&PressSecondFingerWhileDetachedStep2, this,
-                 gfx::Point(tab_0_center.x(),
-                            tab_0_center.y() + 2 * GetDetachY(tab_strip)))));
-  QuitWhenNotDragging();
-
-  // Should no longer be dragging.
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-  ASSERT_FALSE(TabDragController::IsActive());
-
-  // There should now be another browser.
-  ASSERT_EQ(2u, browser_list->size());
-  Browser* new_browser = browser_list->get(1);
-  ASSERT_TRUE(new_browser->window()->IsActive());
-  TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
-  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
-
-  EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
-  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
-}
-
 namespace {
 
 void DragToOverviewWindowStep2(DetachToBrowserTabDragControllerTest* test,
@@ -2577,10 +1697,7 @@
   // Test that the dragged tab did not attach to the overview window.
   EXPECT_EQ(3u, test->browser_list->size());
 
-  if (test->input_source() == INPUT_SOURCE_TOUCH)
-    ASSERT_TRUE(test->ReleaseInput());
-  else
-    ASSERT_TRUE(test->ReleaseMouseAsync());
+  ASSERT_TRUE(test->ReleaseInput());
 }
 
 }  // namespace
@@ -2601,12 +1718,8 @@
 
   // Move to the first tab and drag it enough so that it detaches, but not
   // enough that it attaches to browser2.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-      base::BindOnce(&DragToOverviewWindowStep2, this, tab_strip, tab_strip2)));
-  QuitWhenNotDragging();
+  DragTabAndNotify(tab_strip, base::BindOnce(&DragToOverviewWindowStep2, this,
+                                             tab_strip, tab_strip2));
 
   // Now the dragged tab should have been attached to the target tabstrip after
   // the drag ends.
@@ -2632,10 +1745,7 @@
   // focus, it's the textfield in overview that has focus).
   attached_tab_strip->GetFocusManager()->SetFocusedView(nullptr);
 
-  if (test->input_source() == INPUT_SOURCE_TOUCH)
-    ASSERT_TRUE(test->ReleaseInput());
-  else
-    ASSERT_TRUE(test->ReleaseMouseAsync());
+  ASSERT_TRUE(test->ReleaseInput());
 }
 
 }  // namespace
@@ -2652,12 +1762,8 @@
   EXPECT_TRUE(tab_strip->HasFocus());
 
   // Drag the tab long enough so that it moves.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-      base::BindOnce(&DragToOverviewNewWindowItemStep2, this, tab_strip)));
-  QuitWhenNotDragging();
+  DragTabAndNotify(tab_strip, base::BindOnce(&DragToOverviewNewWindowItemStep2,
+                                             this, tab_strip));
 
   ASSERT_FALSE(TabDragController::IsActive());
   EXPECT_EQ(1u, browser_list->size());
@@ -2746,10 +1852,7 @@
   // Start observe the dragged window.
   observer->StartObserving(attached_tab_strip->GetWidget()->GetNativeWindow());
 
-  if (test->input_source() == INPUT_SOURCE_TOUCH)
-    ASSERT_TRUE(test->ReleaseInput());
-  else
-    ASSERT_TRUE(test->ReleaseMouseAsync());
+  ASSERT_TRUE(test->ReleaseInput());
 }
 
 }  // namespace
@@ -2774,13 +1877,9 @@
       tab_strip2->GetWidget()->GetNativeWindow()->bounds(), target_point));
 
   // Drag the tab long enough so that it moves.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-      base::BindOnce(&DoNotObserveDraggedWidgetAfterDragEndsStep2, this,
-                     observer.get(), tab_strip)));
-  QuitWhenNotDragging();
+  DragTabAndNotify(tab_strip,
+                   base::BindOnce(&DoNotObserveDraggedWidgetAfterDragEndsStep2,
+                                  this, observer.get(), tab_strip));
 
   // There should be still two browsers at this moment. |tab_strip| should not
   // be merged into |tab_strip2|.
@@ -2811,10 +1910,7 @@
   views::View::ConvertPointToScreen(target_tab_strip, &target_point);
   ASSERT_TRUE(test->DragInputTo(target_point));
 
-  if (test->input_source() == INPUT_SOURCE_TOUCH)
-    ASSERT_TRUE(test->ReleaseInput());
-  else
-    ASSERT_TRUE(test->ReleaseMouseAsync());
+  ASSERT_TRUE(test->ReleaseInput());
 }
 
 }  // namespace
@@ -2834,13 +1930,8 @@
 
   // Move to the first tab and drag it enough so that it detaches, but not
   // enough that it attaches to browser2.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-      base::BindOnce(&DoNotAttachToOtherWindowTestStep2, this, tab_strip,
-                     tab_strip2)));
-  QuitWhenNotDragging();
+  DragTabAndNotify(tab_strip, base::BindOnce(&DoNotAttachToOtherWindowTestStep2,
+                                             this, tab_strip, tab_strip2));
 
   // Test that the newly created browser window doesn't attach to the target
   // browser window.
@@ -2881,10 +1972,7 @@
   test::GetWindowForTabStrip(target_tab_strip)
       ->ClearProperty(ash::kIsDeferredTabDraggingTargetWindowKey);
 
-  if (test->input_source() == INPUT_SOURCE_TOUCH)
-    ASSERT_TRUE(test->ReleaseInput());
-  else
-    ASSERT_TRUE(test->ReleaseMouseAsync());
+  ASSERT_TRUE(test->ReleaseInput());
 }
 
 }  // namespace
@@ -2905,13 +1993,8 @@
 
   // Move to the first tab and drag it enough so that it detaches, but not
   // enough that it attaches to browser2.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-      base::BindOnce(&DeferredTargetTabStripTestStep2, this, tab_strip,
-                     tab_strip2)));
-  QuitWhenNotDragging();
+  DragTabAndNotify(tab_strip, base::BindOnce(&DeferredTargetTabStripTestStep2,
+                                             this, tab_strip, tab_strip2));
 
   // Now the dragged tab should not be attached to the target tabstrip after
   // the drag ends.
@@ -2921,143 +2004,6 @@
 
 namespace {
 
-void SecondFingerPressTestStep3(DetachToBrowserTabDragControllerTest* test) {
-  ASSERT_TRUE(test->ReleaseInput());
-}
-
-void SecondFingerPressTestStep2(DetachToBrowserTabDragControllerTest* test,
-                                TabStrip* not_attached_tab_strip,
-                                TabStrip* target_tab_strip) {
-  ASSERT_TRUE(TabDragController::IsActive());
-
-  // And there should be three browser windows, including the newly created one
-  // for the dragged tab.
-  EXPECT_EQ(3u, test->browser_list->size());
-
-  // Put the window that accociated with |target_tab_strip| in overview.
-  test::GetWindowForTabStrip(target_tab_strip)
-      ->SetProperty(ash::kIsShowingInOverviewKey, true);
-
-  // Drag to |target_tab_strip|.
-  gfx::Point target_point(target_tab_strip->width() / 2,
-                          target_tab_strip->height() / 2);
-  views::View::ConvertPointToScreen(target_tab_strip, &target_point);
-  ASSERT_TRUE(test->DragInputTo(target_point));
-
-  // Now add a second finger to tap on it.
-  not_attached_tab_strip->GetWidget()->GetNativeWindow()->env()->set_touch_down(
-      true);
-  ASSERT_TRUE(test->PressInput2());
-  ASSERT_TRUE(test->ReleaseInput2());
-
-  ASSERT_TRUE(test->DragInputToNotifyWhenDone(
-      target_point.x(), target_point.y(),
-      base::BindOnce(&SecondFingerPressTestStep3, test)));
-}
-
-}  // namespace
-
-// Tests that when drgging a tab to a browser window that's currently in
-// overview, press the second finger should not cause chrome crash.
-IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTestTouch,
-                       SecondFingerPressTest) {
-  TabStrip* tab_strip = GetTabStripForBrowser(browser());
-
-  // Add another tab to browser().
-  AddTabAndResetBrowser(browser());
-
-  // Create another browser.
-  Browser* browser2 = CreateAnotherBrowserAndResize();
-  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
-
-  // Move to the first tab and drag it enough so that it detaches, but not
-  // enough that it attaches to browser2.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-      base::BindOnce(&SecondFingerPressTestStep2, this, tab_strip,
-                     tab_strip2)));
-  QuitWhenNotDragging();
-
-  // Test that after dragging there is no crash and the dragged tab should now
-  // be merged into the target tabstrip.
-  ASSERT_FALSE(TabDragController::IsActive());
-  EXPECT_EQ(2u, browser_list->size());
-}
-
-IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTestTouch,
-                       LeftSnapShouldntCauseMergeAtEnd) {
-  TabStrip* tab_strip = GetTabStripForBrowser(browser());
-  AddTabAndResetBrowser(browser());
-
-  // Set the last mouse location at the center of tab 0. This shouldn't affect
-  // the touch behavior below. See https://crbug.com/914527#c1 for the details
-  // of how this can affect the result.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  base::RunLoop run_loop;
-  ui_controls::SendMouseMoveNotifyWhenDone(tab_0_center.x(), tab_0_center.y(),
-                                           run_loop.QuitClosure());
-  run_loop.Run();
-
-  // Drag the tab 1 to left-snapping.
-  gfx::Point tab_1_center(GetCenterInScreenCoordinates(tab_strip->tab_at(1)));
-  ASSERT_TRUE(PressInput(tab_1_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_1_center.x(), tab_1_center.y() + GetDetachY(tab_strip),
-      base::BindLambdaForTesting([&]() {
-        gfx::Rect display_bounds =
-            display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
-        ASSERT_TRUE(DragInputToNotifyWhenDone(display_bounds.x(),
-                                              display_bounds.CenterPoint().y(),
-                                              base::BindLambdaForTesting([&]() {
-                                                ASSERT_TRUE(ReleaseInput());
-                                              })));
-      })));
-  QuitWhenNotDragging();
-
-  ASSERT_FALSE(TabDragController::IsActive());
-  EXPECT_EQ(2u, browser_list->size());
-}
-
-IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTestTouch,
-                       FlingDownAtEndOfDrag) {
-  // Reduce the minimum fling velocity for this specific test case to cause the
-  // fling-down gesture in the middle of tab-dragging. This should end up with
-  // minimizing the window. See https://crbug.com/902897 for the details.
-  ui::GestureConfiguration::GetInstance()->set_min_fling_velocity(1);
-
-  TabStrip* tab_strip = GetTabStripForBrowser(browser());
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  int detach_y = GetDetachY(tab_strip);
-  base::SimpleTestTickClock clock;
-  clock.SetNowTicks(base::TimeTicks::Now());
-  ui::SetEventTickClockForTesting(&clock);
-  ASSERT_TRUE(PressInput(tab_0_center));
-  clock.Advance(base::TimeDelta::FromMilliseconds(5));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + detach_y,
-      base::BindLambdaForTesting([&]() {
-        // Drag down again; this should cause a fling-down event.
-        clock.Advance(base::TimeDelta::FromMilliseconds(5));
-        ASSERT_TRUE(DragInputToNotifyWhenDone(
-            tab_0_center.x(), tab_0_center.y() + detach_y * 2,
-            base::BindLambdaForTesting([&]() {
-              clock.Advance(base::TimeDelta::FromMilliseconds(5));
-              ASSERT_TRUE(ReleaseInput());
-            })));
-      })));
-  QuitWhenNotDragging();
-
-  ASSERT_FALSE(tab_strip->IsDragSessionActive());
-  ASSERT_FALSE(TabDragController::IsActive());
-  EXPECT_TRUE(browser()->window()->IsMinimized());
-  EXPECT_FALSE(browser()->window()->IsVisible());
-  ui::SetEventTickClockForTesting(nullptr);
-}
-
-namespace {
-
 // Returns true if the web contents that's accociated with |browser| is using
 // fast resize.
 bool WebContentsIsFastResized(Browser* browser) {
@@ -3086,10 +2032,7 @@
   views::View::ConvertPointToScreen(target_tab_strip, &target_point);
   ASSERT_TRUE(test->DragInputTo(target_point));
 
-  if (test->input_source() == INPUT_SOURCE_TOUCH)
-    ASSERT_TRUE(test->ReleaseInput());
-  else
-    ASSERT_TRUE(test->ReleaseMouseAsync());
+  ASSERT_TRUE(test->ReleaseInput());
 }
 
 }  // namespace
@@ -3113,13 +2056,8 @@
 
   // Move to the first tab and drag it enough so that it detaches, but not
   // enough that it attaches to browser2.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
-      base::BindOnce(&FastResizeDuringDraggingStep2, this, tab_strip,
-                     tab_strip2)));
-  QuitWhenNotDragging();
+  DragTabAndNotify(tab_strip, base::BindOnce(&FastResizeDuringDraggingStep2,
+                                             this, tab_strip, tab_strip2));
 
   EXPECT_FALSE(WebContentsIsFastResized(browser()));
   EXPECT_FALSE(WebContentsIsFastResized(browser2));
@@ -3155,10 +2093,7 @@
   EXPECT_TRUE(
       target_window->GetProperty(ash::kIsDeferredTabDraggingTargetWindowKey));
 
-  if (test->input_source() == INPUT_SOURCE_TOUCH)
-    ASSERT_TRUE(test->ReleaseInput());
-  else
-    ASSERT_TRUE(test->ReleaseMouseAsync());
+  ASSERT_TRUE(test->ReleaseInput());
 }
 
 }  // namespace
@@ -3179,31 +2114,885 @@
   // Move to the first tab of |browser2| and drag it toward to browser(). Note
   // dragging on |browser2| which only has one tab in tablet mode will open
   // overview behind the |browser2|.
-  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip2->tab_at(0)));
-  ASSERT_TRUE(PressInput(tab_0_center));
-  ASSERT_TRUE(DragInputToNotifyWhenDone(
-      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip2),
-      base::BindOnce(&DragToMinimizedOverviewWindowStep2, this, tab_strip2,
-                     tab_strip)));
-  QuitWhenNotDragging();
+  DragTabAndNotify(tab_strip2,
+                   base::BindOnce(&DragToMinimizedOverviewWindowStep2, this,
+                                  tab_strip2, tab_strip));
 
   // |tab_strip| should have been merged into |browser2|. Thus there should only
   // be one browser now.
   EXPECT_EQ(browser_list->size(), 1u);
 }
 
+// Subclass of DetachToBrowserTabDragControllerTest that
+// creates multiple displays.
+class DetachToBrowserInSeparateDisplayTabDragControllerTest
+    : public DetachToBrowserTabDragControllerTest {
+ public:
+  DetachToBrowserInSeparateDisplayTabDragControllerTest() {}
+  virtual ~DetachToBrowserInSeparateDisplayTabDragControllerTest() {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    DetachToBrowserTabDragControllerTest::SetUpCommandLine(command_line);
+    // Make screens sufficiently wide to host 2 browsers side by side.
+    command_line->AppendSwitchASCII("ash-host-window-bounds",
+                                    "0+0-600x600,600+0-600x600");
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(
+      DetachToBrowserInSeparateDisplayTabDragControllerTest);
+};
+
+namespace {
+
+void DragSingleTabToSeparateWindowInSecondDisplayStep3(
+    DetachToBrowserTabDragControllerTest* test) {
+  ASSERT_TRUE(test->ReleaseInput());
+}
+
+void DragSingleTabToSeparateWindowInSecondDisplayStep2(
+    DetachToBrowserTabDragControllerTest* test,
+    const gfx::Point& target_point) {
+  ASSERT_TRUE(test->DragInputToNotifyWhenDone(
+      target_point,
+      base::Bind(&DragSingleTabToSeparateWindowInSecondDisplayStep3, test)));
+}
+
+}  // namespace
+
+// Drags from browser to a second display and releases input.
+IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
+                       DragSingleTabToSeparateWindowInSecondDisplay) {
+  // Add another tab.
+  AddTabAndResetBrowser(browser());
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+  // Move to the first tab and drag it enough so that it detaches.
+  // Then drag it to the final destination on the second screen.
+  const gfx::Point target = GetCenterInScreenCoordinates(tab_strip->tab_at(0)) +
+                            gfx::Vector2d(600, GetDetachY(tab_strip));
+  DragTabAndNotify(
+      tab_strip,
+      base::BindOnce(&DragSingleTabToSeparateWindowInSecondDisplayStep2, this,
+                     target));
+
+  // Should no longer be dragging.
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+  ASSERT_FALSE(TabDragController::IsActive());
+
+  // There should now be another browser.
+  ASSERT_EQ(2u, browser_list->size());
+  Browser* new_browser = browser_list->get(1);
+  ASSERT_TRUE(new_browser->window()->IsActive());
+  TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
+  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
+
+  // This other browser should be on the second screen (with mouse drag)
+  // With the touch input the browser cannot be dragged from one screen
+  // to another and the window stays on the first screen.
+  if (input_source() == INPUT_SOURCE_MOUSE) {
+    display::Screen* screen = display::Screen::GetScreen();
+    EXPECT_EQ(
+        ui_test_utils::GetSecondaryDisplay(screen).id(),
+        screen
+            ->GetDisplayNearestWindow(new_browser->window()->GetNativeWindow())
+            .id());
+  }
+
+  EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
+  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
+
+  // Both windows should not be maximized
+  EXPECT_FALSE(browser()->window()->IsMaximized());
+  EXPECT_FALSE(new_browser->window()->IsMaximized());
+}
+
+namespace {
+
+// Invoked from the nested run loop.
+void DragTabToWindowInSeparateDisplayStep2(
+    DetachToBrowserTabDragControllerTest* test,
+    TabStrip* not_attached_tab_strip,
+    TabStrip* target_tab_strip) {
+  ASSERT_FALSE(not_attached_tab_strip->IsDragSessionActive());
+  ASSERT_FALSE(target_tab_strip->IsDragSessionActive());
+  ASSERT_TRUE(TabDragController::IsActive());
+
+  // Drag to target_tab_strip. This should stop the nested loop from dragging
+  // the window.
+  gfx::Point target_point(
+      GetCenterInScreenCoordinates(target_tab_strip->tab_at(0)));
+
+  // Move it closer to the beginning of the tab so it will drop before that tab.
+  target_point.Offset(-20, 0);
+  ASSERT_TRUE(test->DragInputToAsync(target_point));
+}
+
+}  // namespace
+
+// Drags from browser to another browser on a second display and releases input.
+IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
+                       DragTabToWindowInSeparateDisplay) {
+  // Add another tab.
+  AddTabAndResetBrowser(browser());
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+  // Create another browser.
+  Browser* browser2 = CreateBrowser(browser()->profile());
+  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
+  ResetIDs(browser2->tab_strip_model(), 100);
+
+  // Move the second browser to the second display.
+  display::Screen* screen = display::Screen::GetScreen();
+  Display second_display = ui_test_utils::GetSecondaryDisplay(screen);
+  browser2->window()->SetBounds(second_display.work_area());
+  // In Mash, the display change as the result of the bounds change is processed
+  // asynchronously in the window server, it should wait for those changes to
+  // complete.
+  aura::test::WaitForAllChangesToComplete();
+  EXPECT_EQ(
+      second_display.id(),
+      screen->GetDisplayNearestWindow(browser2->window()->GetNativeWindow())
+          .id());
+
+  // Move to the first tab and drag it enough so that it detaches, but not
+  // enough that it attaches to browser2.
+  DragTabAndNotify(tab_strip,
+                   base::BindOnce(&DragTabToWindowInSeparateDisplayStep2, this,
+                                  tab_strip, tab_strip2));
+
+  // Should now be attached to tab_strip2.
+  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+  ASSERT_TRUE(TabDragController::IsActive());
+
+  // Release the mouse, stopping the drag session.
+  ASSERT_TRUE(ReleaseInput());
+  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+  ASSERT_FALSE(TabDragController::IsActive());
+  EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
+  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
+
+  // Both windows should not be maximized
+  EXPECT_FALSE(browser()->window()->IsMaximized());
+  EXPECT_FALSE(browser2->window()->IsMaximized());
+}
+
+IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
+                       DragBrowserWindowWhenMajorityOfBoundsInSecondDisplay) {
+  // Set the browser's window bounds such that the majority of its bounds
+  // resides in the second display.
+  const std::pair<Display, Display> displays =
+      GetDisplays(display::Screen::GetScreen());
+
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+  {
+    // Moves the browser window through dragging so that the majority of its
+    // bounds are in the secondary display but it's still be in the primary
+    // display. Do not use SetBounds() or related, it may move the browser
+    // window to the secondary display in some configurations like Mash.
+    int target_x = displays.first.bounds().right() -
+                   browser()->window()->GetBounds().width() / 2 + 20;
+    const gfx::Point target_point =
+        GetCenterInScreenCoordinates(tab_strip->tab_at(0)) +
+        gfx::Vector2d(target_x - browser()->window()->GetBounds().x(),
+                      GetDetachY(tab_strip));
+    DragTabAndNotify(
+        tab_strip,
+        base::BindOnce(&DragSingleTabToSeparateWindowInSecondDisplayStep2, this,
+                       target_point));
+    StopAnimating(tab_strip);
+  }
+  EXPECT_EQ(displays.first.id(),
+            browser()->window()->GetNativeWindow()->GetHost()->GetDisplayId());
+
+  // Start dragging the window by the tab strip, and move it only to the edge
+  // of the first display. Expect at that point mouse would warp and the window
+  // will therefore reside in the second display when mouse is released.
+  const gfx::Point tab_0_center =
+      GetCenterInScreenCoordinates(tab_strip->tab_at(0));
+  const int offset_x = tab_0_center.x() - browser()->window()->GetBounds().x();
+  const int detach_y = tab_0_center.y() + GetDetachY(tab_strip);
+  const int first_display_warp_edge_x = displays.first.bounds().right() - 1;
+  const gfx::Point warped_point(displays.second.bounds().x() + 1, detach_y);
+
+  DragTabAndNotify(
+      tab_strip, base::BindLambdaForTesting([&]() {
+        // This makes another event on the warped location because the test
+        // system does not create it automatically as the result of pointer
+        // warp.
+        ASSERT_TRUE(DragInputToNotifyWhenDone(
+            gfx::Point(first_display_warp_edge_x, detach_y),
+            base::BindRepeating(
+                &DragSingleTabToSeparateWindowInSecondDisplayStep2, this,
+                warped_point)));
+      }));
+
+  // Should no longer be dragging.
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+  ASSERT_FALSE(TabDragController::IsActive());
+
+  // There should only be a single browser.
+  ASSERT_EQ(1u, browser_list->size());
+  ASSERT_EQ(browser(), browser_list->get(0));
+  ASSERT_TRUE(browser()->window()->IsActive());
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+
+  // Browser now resides in display 2.
+  EXPECT_EQ(warped_point.x() - offset_x, browser()->window()->GetBounds().x());
+  EXPECT_EQ(displays.second.id(),
+            browser()->window()->GetNativeWindow()->GetHost()->GetDisplayId());
+}
+
+// Drags from browser to another browser on a second display and releases input.
+IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
+                       DragTabToWindowOnSecondDisplay) {
+  // Add another tab.
+  AddTabAndResetBrowser(browser());
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+  // Create another browser.
+  Browser* browser2 = CreateBrowser(browser()->profile());
+  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
+  ResetIDs(browser2->tab_strip_model(), 100);
+
+  // Move both browsers to be side by side on the second display.
+  display::Screen* screen = display::Screen::GetScreen();
+  Display second_display = ui_test_utils::GetSecondaryDisplay(screen);
+  gfx::Rect work_area = second_display.work_area();
+  work_area.set_width(work_area.width() / 2);
+  browser()->window()->SetBounds(work_area);
+  work_area.set_x(work_area.right());
+  browser2->window()->SetBounds(work_area);
+  // Wait for the display changes. See the ealier comments for the details.
+  aura::test::WaitForAllChangesToComplete();
+  EXPECT_EQ(
+      second_display.id(),
+      screen->GetDisplayNearestWindow(browser()->window()->GetNativeWindow())
+          .id());
+  EXPECT_EQ(
+      second_display.id(),
+      screen->GetDisplayNearestWindow(browser2->window()->GetNativeWindow())
+          .id());
+
+  // Move to the first tab and drag it enough so that it detaches, but not
+  // enough that it attaches to browser2.
+  DragTabAndNotify(tab_strip,
+                   base::BindOnce(&DragTabToWindowInSeparateDisplayStep2, this,
+                                  tab_strip, tab_strip2));
+
+  // Should now be attached to tab_strip2.
+  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+  ASSERT_TRUE(TabDragController::IsActive());
+
+  // Release the mouse, stopping the drag session.
+  ASSERT_TRUE(ReleaseInput());
+  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+  ASSERT_FALSE(TabDragController::IsActive());
+  EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
+  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
+
+  // Both windows should not be maximized
+  EXPECT_FALSE(browser()->window()->IsMaximized());
+  EXPECT_FALSE(browser2->window()->IsMaximized());
+}
+
+// Drags from a maximized browser to another non-maximized browser on a second
+// display and releases input.
+IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
+                       DragMaxTabToNonMaxWindowInSeparateDisplay) {
+  // Add another tab.
+  AddTabAndResetBrowser(browser());
+  browser()->window()->Maximize();
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+  // Create another browser on the second display.
+  display::Screen* screen = display::Screen::GetScreen();
+  ASSERT_EQ(2, screen->GetNumDisplays());
+  const std::pair<Display, Display> displays = GetDisplays(screen);
+  gfx::Rect work_area = displays.second.work_area();
+  work_area.Inset(20, 20, 20, 60);
+  Browser::CreateParams params(browser()->profile(), true);
+  params.initial_show_state = ui::SHOW_STATE_NORMAL;
+  params.initial_bounds = work_area;
+  Browser* browser2 = new Browser(params);
+  AddBlankTabAndShow(browser2);
+
+  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
+  ResetIDs(browser2->tab_strip_model(), 100);
+
+  EXPECT_EQ(
+      displays.second.id(),
+      screen->GetDisplayNearestWindow(browser2->window()->GetNativeWindow())
+          .id());
+  EXPECT_EQ(
+      displays.first.id(),
+      screen->GetDisplayNearestWindow(browser()->window()->GetNativeWindow())
+          .id());
+  EXPECT_EQ(2, tab_strip->tab_count());
+  EXPECT_EQ(1, tab_strip2->tab_count());
+
+  // Move to the first tab and drag it enough so that it detaches, but not
+  // enough that it attaches to browser2.
+  DragTabAndNotify(tab_strip,
+                   base::BindOnce(&DragTabToWindowInSeparateDisplayStep2, this,
+                                  tab_strip, tab_strip2));
+
+  // Should now be attached to tab_strip2.
+  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+  ASSERT_TRUE(TabDragController::IsActive());
+
+  // Release the mouse, stopping the drag session.
+  ASSERT_TRUE(ReleaseInput());
+
+  // tab should have moved
+  EXPECT_EQ(1, tab_strip->tab_count());
+  EXPECT_EQ(2, tab_strip2->tab_count());
+
+  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+  ASSERT_FALSE(TabDragController::IsActive());
+  EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
+  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
+
+  // Source browser should still be maximized, target should not
+  EXPECT_TRUE(browser()->window()->IsMaximized());
+  EXPECT_FALSE(browser2->window()->IsMaximized());
+}
+
+// Drags from a restored browser to an immersive fullscreen browser on a
+// second display and releases input.
+// TODO(pkasting) https://crbug.com/910782 Hangs.
+IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
+                       DISABLED_DragTabToImmersiveBrowserOnSeparateDisplay) {
+  // Add another tab.
+  AddTabAndResetBrowser(browser());
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+  // Create another browser.
+  Browser* browser2 = CreateBrowser(browser()->profile());
+  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
+  ResetIDs(browser2->tab_strip_model(), 100);
+
+  // Move the second browser to the second display.
+  display::Screen* screen = display::Screen::GetScreen();
+  const std::pair<Display, Display> displays = GetDisplays(screen);
+  browser2->window()->SetBounds(displays.second.work_area());
+  // Wait for the display changes. See the ealier comments for the details.
+  aura::test::WaitForAllChangesToComplete();
+  EXPECT_EQ(
+      displays.second.id(),
+      screen->GetDisplayNearestWindow(browser2->window()->GetNativeWindow())
+          .id());
+
+  // Put the second browser into immersive fullscreen.
+  BrowserView* browser_view2 = BrowserView::GetBrowserViewForBrowser(browser2);
+  ImmersiveModeController* immersive_controller2 =
+      browser_view2->immersive_mode_controller();
+  ASSERT_EQ(ImmersiveModeController::Type::ASH, immersive_controller2->type());
+  ash::ImmersiveFullscreenControllerTestApi(
+      static_cast<ImmersiveModeControllerAsh*>(immersive_controller2)
+          ->controller())
+      .SetupForTest();
+  chrome::ToggleFullscreenMode(browser2);
+  // For MD, the browser's top chrome is completely offscreen, with tabstrip
+  // visible.
+  ASSERT_TRUE(immersive_controller2->IsEnabled());
+  ASSERT_FALSE(immersive_controller2->IsRevealed());
+  ASSERT_TRUE(tab_strip2->visible());
+
+  // Move to the first tab and drag it enough so that it detaches, but not
+  // enough that it attaches to browser2.
+  DragTabAndNotify(tab_strip,
+                   base::BindOnce(&DragTabToWindowInSeparateDisplayStep2, this,
+                                  tab_strip, tab_strip2));
+
+  // Should now be attached to tab_strip2.
+  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+  ASSERT_TRUE(TabDragController::IsActive());
+
+  // browser2's top chrome should be revealed and the tab strip should be
+  // at normal height while user is tragging tabs_strip2's tabs.
+  ASSERT_TRUE(immersive_controller2->IsRevealed());
+  ASSERT_TRUE(tab_strip2->visible());
+
+  // Release the mouse, stopping the drag session.
+  ASSERT_TRUE(ReleaseInput());
+  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+  ASSERT_FALSE(TabDragController::IsActive());
+  EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
+  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
+
+  // Move the mouse off of browser2's top chrome.
+  ASSERT_TRUE(
+      ui_test_utils::SendMouseMoveSync(displays.first.bounds().CenterPoint()));
+
+  // The first browser window should not be in immersive fullscreen.
+  // browser2 should still be in immersive fullscreen, but the top chrome should
+  // no longer be revealed.
+  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
+  EXPECT_FALSE(browser_view->immersive_mode_controller()->IsEnabled());
+
+  EXPECT_TRUE(immersive_controller2->IsEnabled());
+  EXPECT_FALSE(immersive_controller2->IsRevealed());
+  EXPECT_TRUE(tab_strip2->visible());
+}
+
+// Subclass of DetachToBrowserTabDragControllerTest that
+// creates multiple displays with different device scale factors.
+class DifferentDeviceScaleFactorDisplayTabDragControllerTest
+    : public DetachToBrowserTabDragControllerTest {
+ public:
+  DifferentDeviceScaleFactorDisplayTabDragControllerTest() {}
+  virtual ~DifferentDeviceScaleFactorDisplayTabDragControllerTest() {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    DetachToBrowserTabDragControllerTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitchASCII("ash-host-window-bounds",
+                                    "400x400,400+0-800x800*2");
+  }
+
+  float GetCursorDeviceScaleFactor() const {
+    ash::CursorManagerTestApi cursor_test_api(
+        ash::Shell::Get()->cursor_manager());
+    return cursor_test_api.GetCurrentCursor().device_scale_factor();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(
+      DifferentDeviceScaleFactorDisplayTabDragControllerTest);
+};
+
+namespace {
+
+// The points where a tab is dragged in CursorDeviceScaleFactorStep.
+constexpr gfx::Point kDragPoints[] = {
+    {300, 200}, {399, 200}, {500, 200}, {400, 200}, {300, 200},
+};
+
+// The expected device scale factors after the cursor is moved to the
+// corresponding kDragPoints in CursorDeviceScaleFactorStep.
+const float kDeviceScaleFactorExpectations[] = {
+  1.0f,
+  1.0f,
+  2.0f,
+  2.0f,
+  1.0f,
+};
+
+static_assert(
+    base::size(kDragPoints) == base::size(kDeviceScaleFactorExpectations),
+    "kDragPoints and kDeviceScaleFactorExpectations must have the same "
+    "number of elements");
+
+// Drags tab to |kDragPoints[index]|, then calls the next step function.
+void CursorDeviceScaleFactorStep(
+    DifferentDeviceScaleFactorDisplayTabDragControllerTest* test,
+    TabStrip* not_attached_tab_strip,
+    size_t index) {
+  SCOPED_TRACE(index);
+  ASSERT_FALSE(not_attached_tab_strip->IsDragSessionActive());
+  ASSERT_TRUE(TabDragController::IsActive());
+
+  if (index > 0) {
+    EXPECT_EQ(kDragPoints[index - 1],
+              ash::Shell::Get()->aura_env()->last_mouse_location());
+    EXPECT_EQ(kDeviceScaleFactorExpectations[index - 1],
+              test->GetCursorDeviceScaleFactor());
+  }
+
+  if (index < base::size(kDragPoints)) {
+    ASSERT_TRUE(test->DragInputToNotifyWhenDone(
+        kDragPoints[index], base::Bind(&CursorDeviceScaleFactorStep, test,
+                                       not_attached_tab_strip, index + 1)));
+  } else {
+    // Finishes a series of CursorDeviceScaleFactorStep calls and ends drag.
+    ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+        ui_controls::LEFT, ui_controls::UP));
+  }
+}
+
+}  // namespace
+
+// Verifies cursor's device scale factor is updated when a tab is moved across
+// displays with different device scale factors (http://crbug.com/154183).
+// TODO(pkasting): In interactive_ui_tests, scale factor never changes to 2.
+// https://crbug.com/918731
+// TODO(pkasting): In non_single_process_mash_interactive_ui_tests, pointer is
+// warped during the drag (which results in changing to scale factor 2 early),
+// and scale factor doesn't change back to 1 at the end.
+// https://crbug.com/918732
+IN_PROC_BROWSER_TEST_P(DifferentDeviceScaleFactorDisplayTabDragControllerTest,
+                       DISABLED_CursorDeviceScaleFactor) {
+  // Add another tab.
+  AddTabAndResetBrowser(browser());
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+  // Move the second browser to the second display.
+  ASSERT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
+
+  // Move to the first tab and drag it enough so that it detaches.
+  DragTabAndNotify(tab_strip, base::BindOnce(&CursorDeviceScaleFactorStep, this,
+                                             tab_strip, 0));
+}
+
+namespace {
+
+class DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest
+    : public DetachToBrowserTabDragControllerTest {
+ public:
+  DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest() {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    DetachToBrowserTabDragControllerTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitchASCII("ash-host-window-bounds",
+                                    "0+0-250x250,251+0-250x250");
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(
+      DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest);
+};
+
+// Invoked from the nested run loop.
+void CancelDragTabToWindowInSeparateDisplayStep3(
+    TabStrip* tab_strip,
+    const BrowserList* browser_list) {
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+  ASSERT_TRUE(TabDragController::IsActive());
+  ASSERT_EQ(2u, browser_list->size());
+
+  // Switching display mode should cancel the drag operation.
+  display::DisplayManager* display_manager =
+      ash::Shell::Get()->display_manager();
+  display_manager->AddRemoveDisplay();
+}
+
+// Invoked from the nested run loop.
+void CancelDragTabToWindowInSeparateDisplayStep2(
+    DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest* test,
+    TabStrip* tab_strip,
+    Display current_display,
+    gfx::Point final_destination,
+    const BrowserList* browser_list) {
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+  ASSERT_TRUE(TabDragController::IsActive());
+  ASSERT_EQ(2u, browser_list->size());
+
+  Browser* new_browser = browser_list->get(1);
+  EXPECT_EQ(
+      current_display.id(),
+      display::Screen::GetScreen()
+          ->GetDisplayNearestWindow(new_browser->window()->GetNativeWindow())
+          .id());
+
+  ASSERT_TRUE(test->DragInputToNotifyWhenDone(
+      final_destination,
+      base::BindOnce(&CancelDragTabToWindowInSeparateDisplayStep3, tab_strip,
+                     browser_list)));
+}
+
+}  // namespace
+
+// Drags from browser to a second display and releases input.
+IN_PROC_BROWSER_TEST_P(
+    DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest,
+    CancelDragTabToWindowIn2ndDisplay) {
+  // Add another tab.
+  AddTabAndResetBrowser(browser());
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
+
+  // Move the second browser to the second display.
+  const std::pair<Display, Display> displays =
+      GetDisplays(display::Screen::GetScreen());
+  gfx::Point final_destination = displays.second.work_area().CenterPoint();
+
+  // Move to the first tab and drag it enough so that it detaches, but not
+  // enough to move to another display.
+  DragTabAndNotify(tab_strip,
+                   base::BindOnce(&CancelDragTabToWindowInSeparateDisplayStep2,
+                                  this, tab_strip, displays.first,
+                                  final_destination, browser_list));
+
+  ASSERT_EQ(1u, browser_list->size());
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+  ASSERT_FALSE(TabDragController::IsActive());
+  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
+
+  // Release the mouse
+  ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+      ui_controls::LEFT, ui_controls::UP));
+}
+
+// Drags from browser from a second display to primary and releases input.
+IN_PROC_BROWSER_TEST_P(
+    DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest,
+    CancelDragTabToWindowIn1stDisplay) {
+  display::Screen* screen = display::Screen::GetScreen();
+  const std::pair<Display, Display> displays = GetDisplays(screen);
+
+  // Add another tab.
+  AddTabAndResetBrowser(browser());
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
+  EXPECT_EQ(
+      displays.first.id(),
+      screen->GetDisplayNearestWindow(browser()->window()->GetNativeWindow())
+          .id());
+
+  browser()->window()->SetBounds(displays.second.work_area());
+  // Wait for the display changes. See the ealier comments for the details.
+  aura::test::WaitForAllChangesToComplete();
+  EXPECT_EQ(
+      displays.second.id(),
+      screen->GetDisplayNearestWindow(browser()->window()->GetNativeWindow())
+          .id());
+
+  // Move the second browser to the display.
+  gfx::Point final_destination = displays.first.work_area().CenterPoint();
+
+  // Move to the first tab and drag it enough so that it detaches, but not
+  // enough to move to another display.
+  DragTabAndNotify(tab_strip,
+                   base::BindOnce(&CancelDragTabToWindowInSeparateDisplayStep2,
+                                  this, tab_strip, displays.second,
+                                  final_destination, browser_list));
+
+  ASSERT_EQ(1u, browser_list->size());
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+  ASSERT_FALSE(TabDragController::IsActive());
+  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
+
+  // Release the mouse
+  ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
+      ui_controls::LEFT, ui_controls::UP));
+}
+
+// Subclass of DetachToBrowserTabDragControllerTest that runs tests only with
+// touch input.
+class DetachToBrowserTabDragControllerTestTouch
+    : public DetachToBrowserTabDragControllerTest {
+ public:
+  DetachToBrowserTabDragControllerTestTouch() {}
+  virtual ~DetachToBrowserTabDragControllerTestTouch() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DetachToBrowserTabDragControllerTestTouch);
+};
+
+namespace {
+void PressSecondFingerWhileDetachedStep3(
+    DetachToBrowserTabDragControllerTest* test) {
+  ASSERT_TRUE(TabDragController::IsActive());
+  ASSERT_EQ(2u, test->browser_list->size());
+  ASSERT_TRUE(test->browser_list->get(1)->window()->IsActive());
+
+  ASSERT_TRUE(test->ReleaseInput());
+  ASSERT_TRUE(test->ReleaseInput(1));
+}
+
+void PressSecondFingerWhileDetachedStep2(
+    DetachToBrowserTabDragControllerTest* test,
+    const gfx::Point& target_point) {
+  ASSERT_TRUE(TabDragController::IsActive());
+  ASSERT_EQ(2u, test->browser_list->size());
+  ASSERT_TRUE(test->browser_list->get(1)->window()->IsActive());
+
+  // Continue dragging after adding a second finger.
+  ASSERT_TRUE(test->PressInput(gfx::Point(), 1));
+  ASSERT_TRUE(test->DragInputToNotifyWhenDone(
+      target_point,
+      base::Bind(&PressSecondFingerWhileDetachedStep3, test)));
+}
+
+}  // namespace
+
+// Detaches a tab and while detached presses a second finger.
+IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTestTouch,
+                       PressSecondFingerWhileDetached) {
+  // Add another tab.
+  AddTabAndResetBrowser(browser());
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
+
+  // Move to the first tab and drag it enough so that it detaches. Drag it
+  // slightly more horizontally so that it does not generate a swipe down
+  // gesture that minimizes the detached browser window.
+  const int touch_move_delta = GetDetachY(tab_strip);
+  const gfx::Point target = GetCenterInScreenCoordinates(tab_strip->tab_at(0)) +
+                            gfx::Vector2d(0, 2 * touch_move_delta);
+  DragTabAndNotify(
+      tab_strip,
+      base::BindOnce(&PressSecondFingerWhileDetachedStep2, this, target), 0,
+      touch_move_delta + 5);
+
+  // Should no longer be dragging.
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+  ASSERT_FALSE(TabDragController::IsActive());
+
+  // There should now be another browser.
+  ASSERT_EQ(2u, browser_list->size());
+  Browser* new_browser = browser_list->get(1);
+  ASSERT_TRUE(new_browser->window()->IsActive());
+  TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
+  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
+
+  EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
+  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
+}
+
+namespace {
+
+void SecondFingerPressTestStep3(DetachToBrowserTabDragControllerTest* test) {
+  ASSERT_TRUE(test->ReleaseInput());
+}
+
+void SecondFingerPressTestStep2(DetachToBrowserTabDragControllerTest* test,
+                                TabStrip* not_attached_tab_strip,
+                                TabStrip* target_tab_strip) {
+  ASSERT_TRUE(TabDragController::IsActive());
+
+  // And there should be three browser windows, including the newly created one
+  // for the dragged tab.
+  EXPECT_EQ(3u, test->browser_list->size());
+
+  // Put the window that accociated with |target_tab_strip| in overview.
+  test::GetWindowForTabStrip(target_tab_strip)
+      ->SetProperty(ash::kIsShowingInOverviewKey, true);
+
+  // Drag to |target_tab_strip|.
+  gfx::Point target_point(target_tab_strip->width() / 2,
+                          target_tab_strip->height() / 2);
+  views::View::ConvertPointToScreen(target_tab_strip, &target_point);
+  ASSERT_TRUE(test->DragInputTo(target_point));
+
+  // Now add a second finger to tap on it.
+  not_attached_tab_strip->GetWidget()->GetNativeWindow()->env()->set_touch_down(
+      true);
+  ASSERT_TRUE(test->PressInput(gfx::Point(), 1));
+  ASSERT_TRUE(test->ReleaseInput(1));
+
+  ASSERT_TRUE(test->DragInputToNotifyWhenDone(
+      target_point, base::BindOnce(&SecondFingerPressTestStep3, test)));
+}
+
+}  // namespace
+
+// Tests that when drgging a tab to a browser window that's currently in
+// overview, press the second finger should not cause chrome crash.
+IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTestTouch,
+                       SecondFingerPressTest) {
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+  // Add another tab to browser().
+  AddTabAndResetBrowser(browser());
+
+  // Create another browser.
+  Browser* browser2 = CreateAnotherBrowserAndResize();
+  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
+
+  // Move to the first tab and drag it enough so that it detaches, but not
+  // enough that it attaches to browser2.
+  DragTabAndNotify(tab_strip, base::BindOnce(&SecondFingerPressTestStep2, this,
+                                             tab_strip, tab_strip2));
+
+  // Test that after dragging there is no crash and the dragged tab should now
+  // be merged into the target tabstrip.
+  ASSERT_FALSE(TabDragController::IsActive());
+  EXPECT_EQ(2u, browser_list->size());
+}
+
+IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTestTouch,
+                       LeftSnapShouldntCauseMergeAtEnd) {
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+  AddTabAndResetBrowser(browser());
+
+  // Set the last mouse location at the center of tab 0. This shouldn't affect
+  // the touch behavior below. See https://crbug.com/914527#c1 for the details
+  // of how this can affect the result.
+  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
+  base::RunLoop run_loop;
+  ui_controls::SendMouseMoveNotifyWhenDone(tab_0_center.x(), tab_0_center.y(),
+                                           run_loop.QuitClosure());
+  run_loop.Run();
+
+  // Drag the tab 1 to left-snapping.
+  DragTabAndNotify(
+      tab_strip, base::BindLambdaForTesting([&]() {
+        gfx::Rect display_bounds =
+            display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
+        const gfx::Point target(display_bounds.x(),
+                                display_bounds.CenterPoint().y());
+        ASSERT_TRUE(
+            DragInputToNotifyWhenDone(target, base::BindLambdaForTesting([&]() {
+                                        ASSERT_TRUE(ReleaseInput());
+                                      })));
+      }),
+      1);
+
+  ASSERT_FALSE(TabDragController::IsActive());
+  EXPECT_EQ(2u, browser_list->size());
+}
+
+IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTestTouch,
+                       FlingDownAtEndOfDrag) {
+  // Reduce the minimum fling velocity for this specific test case to cause the
+  // fling-down gesture in the middle of tab-dragging. This should end up with
+  // minimizing the window. See https://crbug.com/902897 for the details.
+  ui::GestureConfiguration::GetInstance()->set_min_fling_velocity(1);
+
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
+  const gfx::Vector2d detach(0, GetDetachY(tab_strip));
+  base::SimpleTestTickClock clock;
+  clock.SetNowTicks(base::TimeTicks::Now());
+  ui::SetEventTickClockForTesting(&clock);
+  ASSERT_TRUE(PressInput(tab_0_center));
+  clock.Advance(base::TimeDelta::FromMilliseconds(5));
+  ASSERT_TRUE(DragInputToNotifyWhenDone(
+      tab_0_center + detach, base::BindLambdaForTesting([&]() {
+        // Drag down again; this should cause a fling-down event.
+        clock.Advance(base::TimeDelta::FromMilliseconds(5));
+        ASSERT_TRUE(DragInputToNotifyWhenDone(
+            tab_0_center + detach + detach, base::BindLambdaForTesting([&]() {
+              clock.Advance(base::TimeDelta::FromMilliseconds(5));
+              ASSERT_TRUE(ReleaseInput());
+            })));
+      })));
+  QuitWhenNotDragging();
+
+  ASSERT_FALSE(tab_strip->IsDragSessionActive());
+  ASSERT_FALSE(TabDragController::IsActive());
+  EXPECT_TRUE(browser()->window()->IsMinimized());
+  EXPECT_FALSE(browser()->window()->IsVisible());
+  ui::SetEventTickClockForTesting(nullptr);
+}
+
 #endif  // OS_CHROMEOS
 
 #if defined(OS_CHROMEOS)
 INSTANTIATE_TEST_CASE_P(TabDragging,
+                        DetachToBrowserTabDragControllerTest,
+                        ::testing::Values("mouse", "touch"));
+INSTANTIATE_TEST_CASE_P(TabDragging,
                         DetachToBrowserInSeparateDisplayTabDragControllerTest,
                         ::testing::Values("mouse"));
 INSTANTIATE_TEST_CASE_P(TabDragging,
                         DifferentDeviceScaleFactorDisplayTabDragControllerTest,
                         ::testing::Values("mouse"));
-INSTANTIATE_TEST_CASE_P(TabDragging,
-                        DetachToBrowserTabDragControllerTest,
-                        ::testing::Values("mouse", "touch"));
+INSTANTIATE_TEST_CASE_P(
+    TabDragging,
+    DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest,
+    ::testing::Values("mouse"));
 INSTANTIATE_TEST_CASE_P(TabDragging,
                         DetachToBrowserTabDragControllerTestTouch,
                         ::testing::Values("touch"));
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_browsertest.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_browsertest.cc
index 80add10..8ffdf861 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_browsertest.cc
@@ -17,9 +17,35 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_observer.h"
 
 using views::Widget;
 
+// Helper to wait until a window is deactivated.
+class WindowDeactivedWaiter : public views::WidgetObserver {
+ public:
+  explicit WindowDeactivedWaiter(BrowserView* window) : window_(window) {
+    window_->frame()->AddObserver(this);
+  }
+  ~WindowDeactivedWaiter() override { window_->frame()->RemoveObserver(this); }
+
+  void Wait() {
+    if (!window_->IsActive())
+      return;
+    run_loop_.Run();
+  }
+
+  // WidgetObserver overrides:
+  void OnWidgetActivationChanged(Widget* widget, bool active) override {
+    if (!active)
+      run_loop_.Quit();
+  }
+
+ private:
+  BrowserView* const window_;
+  base::RunLoop run_loop_;
+};
+
 class TabHoverCardBubbleViewBrowserTest : public DialogBrowserTest {
  public:
   TabHoverCardBubbleViewBrowserTest() = default;
@@ -155,15 +181,31 @@
 // Verify inactive window remains inactive when showing a hover card for a tab
 // in the inactive window.
 IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest,
-                       DISABLED_InactiveWindowStaysInactiveOnHover) {
+                       InactiveWindowStaysInactiveOnHover) {
+  const BrowserList* active_browser_list_ = BrowserList::GetInstance();
+  // Open a second window. On Windows, Widget::Deactivate() works by activating
+  // the next topmost window on the z-order stack. So instead we use two windows
+  // and Widget::Activate() here to prevent Widget::Deactivate() from
+  // undesirably activating another window when tests are being run in parallel.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL(url::kAboutBlankURL), WindowOpenDisposition::NEW_WINDOW,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
   ShowUi("default");
-  BrowserView::GetBrowserViewForBrowser(browser())->Deactivate();
-  ASSERT_FALSE(BrowserView::GetBrowserViewForBrowser(browser())->IsActive());
+  ASSERT_EQ(2u, active_browser_list_->size());
+  Browser* active_window = active_browser_list_->get(0);
+  Browser* inactive_window = active_browser_list_->get(1);
+  WindowDeactivedWaiter waiter(
+      BrowserView::GetBrowserViewForBrowser(inactive_window));
+  BrowserView::GetBrowserViewForBrowser(active_window)->Activate();
+  waiter.Wait();
+  ASSERT_FALSE(
+      BrowserView::GetBrowserViewForBrowser(inactive_window)->IsActive());
   TabStrip* tab_strip =
-      BrowserView::GetBrowserViewForBrowser(browser())->tabstrip();
+      BrowserView::GetBrowserViewForBrowser(inactive_window)->tabstrip();
   Tab* tab = tab_strip->tab_at(0);
   ui::MouseEvent hover_event(ui::ET_MOUSE_ENTERED, gfx::Point(), gfx::Point(),
                              base::TimeTicks(), ui::EF_NONE, 0);
   tab->OnMouseEntered(hover_event);
-  ASSERT_FALSE(BrowserView::GetBrowserViewForBrowser(browser())->IsActive());
+  ASSERT_FALSE(
+      BrowserView::GetBrowserViewForBrowser(inactive_window)->IsActive());
 }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index d228af4..be1236e 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -554,11 +554,9 @@
 
   const ui::ThemeProvider* tp = GetThemeProvider();
 
-  // Always fill the toolbar with its bg color first in case the image is
-  // transparent.
-  canvas->FillRect(GetLocalBounds(),
-                   tp->GetColor(ThemeProperties::COLOR_TOOLBAR));
-
+  // If the toolbar has a theme image, it gets composited against the toolbar
+  // background color when it's imported, so we only need to specificallh draw
+  // the background color if there is no custom image.
   if (tp->HasCustomImage(IDR_THEME_TOOLBAR)) {
     const int x_offset =
         GetMirroredX() + browser_view_->GetMirroredX() +
@@ -568,6 +566,9 @@
                          GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP);
     canvas->TileImageInt(*tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR), x_offset,
                          y_offset, 0, 0, width(), height());
+  } else {
+    canvas->FillRect(GetLocalBounds(),
+                     tp->GetColor(ThemeProperties::COLOR_TOOLBAR));
   }
 
   // Toolbar/content separator.
diff --git a/chrome/browser/ui/webui/extensions/extensions_ui.cc b/chrome/browser/ui/webui/extensions/extensions_ui.cc
index bba349a..1ca7419 100644
--- a/chrome/browser/ui/webui/extensions/extensions_ui.cc
+++ b/chrome/browser/ui/webui/extensions/extensions_ui.cc
@@ -146,6 +146,7 @@
     {"extensionWebsite", IDS_MD_EXTENSIONS_ITEM_EXTENSION_WEBSITE},
     {"dropToInstall", IDS_EXTENSIONS_INSTALL_DROP_TARGET},
     {"errorsPageHeading", IDS_MD_EXTENSIONS_ERROR_PAGE_HEADING},
+    {"clearActivities", IDS_MD_EXTENSIONS_CLEAR_ACTIVITIES},
     {"clearAll", IDS_MD_EXTENSIONS_ERROR_CLEAR_ALL},
     {"clearEntry", IDS_MD_EXTENSIONS_A11Y_CLEAR_ENTRY},
     {"logLevel", IDS_EXTENSIONS_LOG_LEVEL_INFO},
diff --git a/chrome/browser/ui/webui/reset_password/reset_password_ui.cc b/chrome/browser/ui/webui/reset_password/reset_password_ui.cc
index 268c322..dee0444d 100644
--- a/chrome/browser/ui/webui/reset_password/reset_password_ui.cc
+++ b/chrome/browser/ui/webui/reset_password/reset_password_ui.cc
@@ -139,11 +139,7 @@
   bool known_password_type =
       password_type_ !=
       safe_browsing::PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN;
-  if (!known_password_type) {
-    UMA_HISTOGRAM_ENUMERATION(
-        safe_browsing::kInterstitialActionByUserNavigationHistogram,
-        safe_browsing::WarningAction::SHOWN);
-  }
+
   int heading_string_id = known_password_type
                               ? IDS_RESET_PASSWORD_WARNING_HEADING
                               : IDS_RESET_PASSWORD_HEADING;
diff --git a/chrome/browser/ui/webui/sync_internals_browsertest.js b/chrome/browser/ui/webui/sync_internals_browsertest.js
index 51cf439a..f7c211c 100644
--- a/chrome/browser/ui/webui/sync_internals_browsertest.js
+++ b/chrome/browser/ui/webui/sync_internals_browsertest.js
@@ -43,15 +43,18 @@
    */
   hasInDetails: function(isValid, key, value) {
     var details = chrome.sync.aboutInfo.details;
-    if (!details)
+    if (!details) {
       return false;
+    }
     for (var i = 0; i < details.length; ++i) {
-      if (!details[i].data)
+      if (!details[i].data) {
         continue;
+      }
       for (var j = 0; j < details[i].data.length; ++j) {
         var obj = details[i].data[j];
-        if (obj.stat_name == key)
+        if (obj.stat_name == key) {
           return obj.is_valid == isValid && obj.stat_value == value;
+        }
       }
     }
     return false;
@@ -334,11 +337,15 @@
   $('sync-results-list').dataModel = new cr.ui.ArrayDataModel([
     {
       value: 'value 0',
-      toString: function() { return 'node 0'; },
+      toString: function() {
+        return 'node 0';
+      },
     },
     {
       value: 'value 1',
-      toString: function() { return 'node 1'; },
+      toString: function() {
+        return 'node 1';
+      },
     }
   ]);
 
diff --git a/chrome/browser/ui/webui/welcome/nux/OWNERS b/chrome/browser/ui/webui/welcome/OWNERS
similarity index 81%
rename from chrome/browser/ui/webui/welcome/nux/OWNERS
rename to chrome/browser/ui/webui/welcome/OWNERS
index 589a5fa6..c497ef2 100644
--- a/chrome/browser/ui/webui/welcome/nux/OWNERS
+++ b/chrome/browser/ui/webui/welcome/OWNERS
@@ -2,4 +2,3 @@
 scottchen@chromium.org
 
 # COMPONENT: UI>Browser>FirstRun
-# LABEL: Proj-Navi
\ No newline at end of file
diff --git a/chrome/browser/ui/webui/welcome/nux_helper.cc b/chrome/browser/ui/webui/welcome/nux_helper.cc
index 39e746a..e74a933 100644
--- a/chrome/browser/ui/webui/welcome/nux_helper.cc
+++ b/chrome/browser/ui/webui/welcome/nux_helper.cc
@@ -67,7 +67,7 @@
     // User will be tied to their original onboarding group, even after
     // experiment ends.
     ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
-        kNuxOnboardingStudyName, onboard_group);
+        "NaviOnboardingSynthetic", onboard_group);
 
     return base::FeatureList::IsEnabled(nux::kNuxOnboardingFeature);
 #else
diff --git a/chrome/common/page_load_metrics/page_load_metrics.mojom b/chrome/common/page_load_metrics/page_load_metrics.mojom
index 5f68792..86d566e 100644
--- a/chrome/common/page_load_metrics/page_load_metrics.mojom
+++ b/chrome/common/page_load_metrics/page_load_metrics.mojom
@@ -199,6 +199,9 @@
   // Whether this resource was fetched from the http cache.
   bool was_fetched_via_cache;
 
+  // Whether this resource is the primary resource for a frame.
+  bool is_primary_frame_resource;
+
   // Mime type for the resource found in the network response header.
   string mime_type;
 
diff --git a/chrome/elevation_service/service_main.cc b/chrome/elevation_service/service_main.cc
index 2e141f40..83e4b1c 100644
--- a/chrome/elevation_service/service_main.cc
+++ b/chrome/elevation_service/service_main.cc
@@ -11,6 +11,8 @@
 
 #include "chrome/elevation_service/service_main.h"
 
+#include <type_traits>
+
 #include <atlsecurity.h>
 #include <sddl.h>
 #include <wrl/module.h>
@@ -85,13 +87,13 @@
 
   // The pointer in this array is unowned. Do not release it.
   IClassFactory* class_factories[] = {class_factory.Get()};
-  static_assert(base::size(decltype(cookies_){}) ==
-                base::size(class_factories),
-                "Arrays cookies_ and class_factories must be the same size.");
+  static_assert(
+      std::extent<decltype(cookies_)>() == base::size(class_factories),
+      "Arrays cookies_ and class_factories must be the same size.");
 
   IID class_ids[] = {install_static::GetElevatorClsid()};
   DCHECK_EQ(base::size(cookies_), base::size(class_ids));
-  static_assert(base::size(decltype(cookies_){}) == base::size(class_ids),
+  static_assert(std::extent<decltype(cookies_)>() == base::size(class_ids),
                 "Arrays cookies_ and class_ids must be the same size.");
 
   hr = module.RegisterCOMObject(nullptr, class_ids, class_factories, cookies_,
diff --git a/chrome/notification_helper/com_server_module.cc b/chrome/notification_helper/com_server_module.cc
index 2454326d..d871001 100644
--- a/chrome/notification_helper/com_server_module.cc
+++ b/chrome/notification_helper/com_server_module.cc
@@ -11,9 +11,12 @@
 
 #include "chrome/notification_helper/com_server_module.h"
 
+#include <type_traits>
+
 #include <wrl/module.h>
 
 #include "base/metrics/histogram_macros.h"
+#include "base/stl_util.h"
 #include "chrome/install_static/install_util.h"
 #include "chrome/notification_helper/notification_activator.h"
 #include "chrome/notification_helper/trace_util.h"
@@ -99,15 +102,15 @@
 
   // All pointers in this array are unowned. Do not release them.
   IClassFactory* class_factories[] = {class_factory.Get()};
-  static_assert(arraysize(cookies_) == arraysize(class_factories),
+  static_assert(std::extent<decltype(cookies_)>() == std::size(class_factories),
                 "Arrays cookies_ and class_factories must be the same size.");
 
   IID class_ids[] = {install_static::GetToastActivatorClsid()};
-  static_assert(arraysize(cookies_) == arraysize(class_ids),
+  static_assert(std::extent<decltype(cookies_)>() == std::size(class_ids),
                 "Arrays cookies_ and class_ids must be the same size.");
 
   hr = module.RegisterCOMObject(nullptr, class_ids, class_factories, cookies_,
-                                arraysize(cookies_));
+                                std::extent<decltype(cookies_)>());
   if (FAILED(hr)) {
     LogComServerModuleHistogram(ComServerModuleStatus::REGISTRATION_FAILED);
     Trace(L"%hs(NotificationActivator registration failed; hr: 0x%08X)\n",
@@ -119,8 +122,8 @@
 
 HRESULT ComServerModule::UnregisterClassObjects() {
   auto& module = mswr::Module<mswr::OutOfProcDisableCaching>::GetModule();
-  HRESULT hr =
-      module.UnregisterCOMObject(nullptr, cookies_, arraysize(cookies_));
+  HRESULT hr = module.UnregisterCOMObject(nullptr, cookies_,
+                                          std::extent<decltype(cookies_)>());
   if (FAILED(hr)) {
     LogComServerModuleHistogram(ComServerModuleStatus::UNREGISTRATION_FAILED);
     Trace(L"%hs(NotificationActivator unregistration failed; hr: 0x%08X)\n",
diff --git a/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc b/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc
index 2dbed3ea..c504098 100644
--- a/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc
+++ b/chrome/renderer/page_load_metrics/metrics_render_frame_observer.cc
@@ -106,10 +106,10 @@
     // case. There should be a guarantee that DidStartProvisionalLoad be called
     // before DidStartResponse for the frame request.
     provisional_frame_resource_data_use_->DidStartResponse(
-        response_url, request_id, response_head);
+        response_url, request_id, response_head, resource_type);
   } else if (page_timing_metrics_sender_) {
     page_timing_metrics_sender_->DidStartResponse(response_url, request_id,
-                                                  response_head);
+                                                  response_head, resource_type);
     UpdateResourceMetadata(request_id);
   }
 }
diff --git a/chrome/renderer/page_load_metrics/page_resource_data_use.cc b/chrome/renderer/page_load_metrics/page_resource_data_use.cc
index 35d6cbc..792b6d99 100644
--- a/chrome/renderer/page_load_metrics/page_resource_data_use.cc
+++ b/chrome/renderer/page_load_metrics/page_resource_data_use.cc
@@ -32,7 +32,8 @@
 void PageResourceDataUse::DidStartResponse(
     const GURL& response_url,
     int resource_id,
-    const network::ResourceResponseHead& response_head) {
+    const network::ResourceResponseHead& response_head,
+    content::ResourceType resource_type) {
   resource_id_ = resource_id;
   data_reduction_proxy_compression_ratio_estimate_ =
       data_reduction_proxy::EstimateCompressionRatioFromHeaders(&response_head);
@@ -40,6 +41,9 @@
   mime_type_ = response_head.mime_type;
   was_fetched_via_cache_ = response_head.was_fetched_via_cache;
   is_secure_scheme_ = response_url.SchemeIsCryptographic();
+  is_primary_frame_resource_ =
+      resource_type == content::RESOURCE_TYPE_MAIN_FRAME ||
+      resource_type == content::RESOURCE_TYPE_SUB_FRAME;
 }
 
 void PageResourceDataUse::DidReceiveTransferSizeUpdate(
@@ -100,6 +104,7 @@
   resource_data_update->was_fetched_via_cache = was_fetched_via_cache_;
   resource_data_update->is_secure_scheme = is_secure_scheme_;
   resource_data_update->proxy_used = proxy_used_;
+  resource_data_update->is_primary_frame_resource = is_primary_frame_resource_;
   return resource_data_update;
 }
 }  // namespace page_load_metrics
diff --git a/chrome/renderer/page_load_metrics/page_resource_data_use.h b/chrome/renderer/page_load_metrics/page_resource_data_use.h
index 7431f65..6f5112e 100644
--- a/chrome/renderer/page_load_metrics/page_resource_data_use.h
+++ b/chrome/renderer/page_load_metrics/page_resource_data_use.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "chrome/common/page_load_metrics/page_load_metrics.mojom.h"
+#include "content/public/common/resource_type.h"
 
 class GURL;
 
@@ -27,7 +28,8 @@
 
   void DidStartResponse(const GURL& response_url,
                         int resource_id,
-                        const network::ResourceResponseHead& response_head);
+                        const network::ResourceResponseHead& response_head,
+                        content::ResourceType resource_type);
 
   // Updates received bytes.
   void DidReceiveTransferSizeUpdate(int received_data_length);
@@ -74,6 +76,7 @@
   bool was_fetched_via_cache_;
   bool is_secure_scheme_;
   bool proxy_used_;
+  bool is_primary_frame_resource_ = false;
 
   std::string mime_type_;
 
diff --git a/chrome/renderer/page_load_metrics/page_timing_metrics_sender.cc b/chrome/renderer/page_load_metrics/page_timing_metrics_sender.cc
index 718a7f51..6717c874 100644
--- a/chrome/renderer/page_load_metrics/page_timing_metrics_sender.cc
+++ b/chrome/renderer/page_load_metrics/page_timing_metrics_sender.cc
@@ -100,14 +100,15 @@
 void PageTimingMetricsSender::DidStartResponse(
     const GURL& response_url,
     int resource_id,
-    const network::ResourceResponseHead& response_head) {
+    const network::ResourceResponseHead& response_head,
+    content::ResourceType resource_type) {
   DCHECK(!base::ContainsKey(page_resource_data_use_, resource_id));
 
   auto resource_it = page_resource_data_use_.emplace(
       std::piecewise_construct, std::forward_as_tuple(resource_id),
       std::forward_as_tuple(std::make_unique<PageResourceDataUse>()));
   resource_it.first->second->DidStartResponse(response_url, resource_id,
-                                              response_head);
+                                              response_head, resource_type);
 }
 
 void PageTimingMetricsSender::DidReceiveTransferSizeUpdate(
diff --git a/chrome/renderer/page_load_metrics/page_timing_metrics_sender.h b/chrome/renderer/page_load_metrics/page_timing_metrics_sender.h
index 27de17ad..84dd58b4b6 100644
--- a/chrome/renderer/page_load_metrics/page_timing_metrics_sender.h
+++ b/chrome/renderer/page_load_metrics/page_timing_metrics_sender.h
@@ -49,7 +49,8 @@
   void DidObserveLayoutJank(double jank_fraction);
   void DidStartResponse(const GURL& response_url,
                         int resource_id,
-                        const network::ResourceResponseHead& response_head);
+                        const network::ResourceResponseHead& response_head,
+                        content::ResourceType resource_type);
   void DidReceiveTransferSizeUpdate(int resource_id, int received_data_length);
   void DidCompleteResponse(int resource_id,
                            const network::URLLoaderCompletionStatus& status);
diff --git a/chrome/test/chromedriver/server/chromedriver_server.cc b/chrome/test/chromedriver/server/chromedriver_server.cc
index 31e73ff3..b2fbb1e 100644
--- a/chrome/test/chromedriver/server/chromedriver_server.cc
+++ b/chrome/test/chromedriver/server/chromedriver_server.cc
@@ -64,14 +64,14 @@
   std::string binding_ip = net::IPAddress::IPv4Localhost().ToString();
   if (allow_remote)
     binding_ip = net::IPAddress::IPv4AllZeros().ToString();
-  return socket->ListenWithAddressAndPort(binding_ip, port, 1);
+  return socket->ListenWithAddressAndPort(binding_ip, port, 5);
 }
 
 int ListenOnIPv6(net::ServerSocket* socket, uint16_t port, bool allow_remote) {
   std::string binding_ip = net::IPAddress::IPv6Localhost().ToString();
   if (allow_remote)
     binding_ip = net::IPAddress::IPv6AllZeros().ToString();
-  return socket->ListenWithAddressAndPort(binding_ip, port, 1);
+  return socket->ListenWithAddressAndPort(binding_ip, port, 5);
 }
 
 class HttpServer : public net::HttpServer::Delegate {
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 01edb0a..65468f8 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -90,9 +90,6 @@
     'ChromeDriverTest.testHoverOverElement',
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=833
     'ChromeDriverTest.testAlertOnNewWindow',
-    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2579
-    'ChromeDriverTest.testTakeElementScreenshot',
-    'ChromeDriverTest.testTakeElementScreenshotInIframe',
 ]
 
 _VERSION_SPECIFIC_FILTER = {}
@@ -133,6 +130,9 @@
     'ChromeDriverTest.testWindowMinimize',
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1945
     'ChromeDriverTest.testWindowFullScreen',
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2579
+    'ChromeDriverTest.testTakeElementScreenshot',
+    'ChromeDriverTest.testTakeElementScreenshotInIframe',
 ]
 
 _OS_VERSION_SPECIFIC_FILTER = {}
diff --git a/chrome/test/data/ads_observer/docwrite_blank_frame.html b/chrome/test/data/ads_observer/docwrite_blank_frame.html
new file mode 100644
index 0000000..e79fa3f
--- /dev/null
+++ b/chrome/test/data/ads_observer/docwrite_blank_frame.html
@@ -0,0 +1,12 @@
+<html>
+<iframe id="blank_frame" name="google_ads_iframe"></iframe>
+
+<script>
+  let iframe = document.getElementById("blank_frame");
+  let doc = iframe.contentWindow.document;
+  doc.open();
+  doc.write("<html>Rewritten. <img src=pixel.png> <img src=pixel2.png> <img src=pixel3.png></html>");
+  doc.close();
+</script>
+
+</html>
diff --git a/chrome/test/data/extensions/theme/Cached Theme.pak b/chrome/test/data/extensions/theme/Cached Theme.pak
index 8101c57..e012645d 100644
--- a/chrome/test/data/extensions/theme/Cached Theme.pak
+++ b/chrome/test/data/extensions/theme/Cached Theme.pak
Binary files differ
diff --git a/chrome/test/data/extensions/theme_test_toolbar_frame_images_and_colors/images/theme_frame.png b/chrome/test/data/extensions/theme_test_toolbar_frame_images_and_colors/images/theme_frame.png
new file mode 100644
index 0000000..da901de
--- /dev/null
+++ b/chrome/test/data/extensions/theme_test_toolbar_frame_images_and_colors/images/theme_frame.png
Binary files differ
diff --git a/chrome/test/data/extensions/theme_test_toolbar_frame_images_and_colors/images/theme_toolbar.png b/chrome/test/data/extensions/theme_test_toolbar_frame_images_and_colors/images/theme_toolbar.png
new file mode 100644
index 0000000..e03ceed
--- /dev/null
+++ b/chrome/test/data/extensions/theme_test_toolbar_frame_images_and_colors/images/theme_toolbar.png
Binary files differ
diff --git a/chrome/test/data/extensions/theme_test_toolbar_frame_images_and_colors/manifest.json b/chrome/test/data/extensions/theme_test_toolbar_frame_images_and_colors/manifest.json
new file mode 100644
index 0000000..a8d9693
--- /dev/null
+++ b/chrome/test/data/extensions/theme_test_toolbar_frame_images_and_colors/manifest.json
@@ -0,0 +1,16 @@
+{
+  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGMyBRIRAOiUgubfmS+4RHiNi/u/okGcU17xoKTXnziTiwmo7lq4V7unr3Vn4wWSG5bIG+yhQhQAG1TvfsXimBIUrrVKX4b9IADZIHR5akWgb1K4EV111PW/D8RDOwYgTPHPx5eA49TxB0Yw36DNC9LE0tPZOGgDR9hCFgaN1bcszlmJBcBmx39iGCeKPIjKB4bp5hXe33T5IUdoyuW4JHf4JDqC6unwQ1Y9nwzCRUbFis7YevbGQCgWP3I+Hp+bOo30mC2/Gq2UZfIczR4vIOrarxBV8IO4Ue4gzaDl6dGkGKnkZ3JdZKbLeRke1DmaKfjVSsTUp++X2nfMaPZphQIDAQAB",
+  "manifest_version": 2,
+  "name": "Toolbar/Frame Color + Image Test",
+  "theme": {
+    "colors": {
+      "frame": [ 255, 0, 255 ],
+      "toolbar": [ 0, 255, 0 ]
+      },
+    "images": {
+       "theme_frame": "images/theme_frame.png",
+       "theme_toolbar": "images/theme_toolbar.png"
+      }
+  },
+  "version": "0.0.1"
+}
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 293a2b1..d55e2853 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -174,14 +174,11 @@
   test_type = "unit"
   sources = [
     "../../../browser/resources/md_downloads/search_service_unittest.gtestjs",
-    "../../../browser/resources/print_preview/print_preview_utils_unittest.gtestjs",
     "../../../renderer/resources/extensions/notifications_custom_bindings.gtestjs",
     "../unit/framework_unittest.gtestjs",
   ]
   extra_js_files = [
     "../../../browser/resources/md_downloads/browser_proxy.js",
-    "../../../browser/resources/print_preview/data/measurement_system.js",
-    "../../../browser/resources/print_preview/print_preview_utils.js",
     "../../../browser/resources/md_downloads/search_service.js",
     "../../../renderer/resources/extensions/notifications_custom_bindings.js",
     "../../../renderer/resources/extensions/notifications_test_util.js",
diff --git a/chrome/test/data/webui/extensions/activity_log_test.js b/chrome/test/data/webui/extensions/activity_log_test.js
index 4adb5bc1..ac0cb98 100644
--- a/chrome/test/data/webui/extensions/activity_log_test.js
+++ b/chrome/test/data/webui/extensions/activity_log_test.js
@@ -154,6 +154,22 @@
         });
   });
 
+  test('clicking on clear activities button clears activities', function() {
+    activityLog.$$('#clear-activities-button').click();
+
+    return proxyDelegate.whenCalled('deleteActivitiesFromExtension')
+        .then(() => {
+          Polymer.dom.flush();
+          testVisible('#no-activities', true);
+          testVisible('#loading-activities', false);
+          testVisible('#activity-list', false);
+          expectEquals(
+              activityLog.shadowRoot.querySelectorAll('activity-log-item')
+                  .length,
+              0);
+        });
+  });
+
   test('message shown when no activities present for extension', function() {
     // Spoof an API call and pretend that the extension has no activities.
     activityLog.activityData_ = [];
diff --git a/chrome/test/data/webui/extensions/test_service.js b/chrome/test/data/webui/extensions/test_service.js
index d341993..909fb7b 100644
--- a/chrome/test/data/webui/extensions/test_service.js
+++ b/chrome/test/data/webui/extensions/test_service.js
@@ -8,6 +8,7 @@
     constructor() {
       super([
         'addRuntimeHostPermission',
+        'deleteActivitiesFromExtension',
         'getExtensionActivityLog',
         'getExtensionsInfo',
         'getExtensionSize',
@@ -186,6 +187,12 @@
         activities: [...apiCallMatches, ...pageUrlMatches, ...argUrlMatches]
       });
     }
+
+    /** @override */
+    deleteActivitiesFromExtension(extensionId) {
+      this.methodCalled('deleteActivitiesFromExtension', extensionId);
+      return Promise.resolve();
+    }
   }
 
   return {
diff --git a/chrome/test/data/webui/find_shortcut_behavior_test.js b/chrome/test/data/webui/find_shortcut_behavior_test.js
index b29c73d..3fcf11a 100644
--- a/chrome/test/data/webui/find_shortcut_behavior_test.js
+++ b/chrome/test/data/webui/find_shortcut_behavior_test.js
@@ -11,50 +11,9 @@
   /**
    * @type {PromiseResolver<!{modalContextOpen: boolean, self: HTMLElement}>}
    */
-  let waits;
-  /** @type {number} */
-  let nextWait;
-
-  /**
-   * @param {!Array} fns
-   * @return {!Promise}
-   */
-  const promiseSeries = fns =>
-      fns.reduce((acc, fn) => acc.then(fn), Promise.resolve());
-
-  /**
-   * This method registers the |testElement| as a find shortcut listener, runs
-   * the |wrapped| function, then removes |testElement| from being a listener.
-   * The listeners stack is global and should be empty after a successful test.
-   * @param {!HTMLElement} testElement
-   * @return {!function(!Function)): !Promise}
-   */
-  const listenScope = testElement => wrapped => promiseSeries([
-    () => testElement.becomeActiveFindShortcutListener(),
-    wrapped,
-    () => testElement.removeSelfAsFindShortcutListener(),
-  ]);
-
-  /**
-   * Checks that the handleFindShortcut method is being called for all the
-   * |expectedSelves| element references in the order that they are passed in
-   * when a single find shortcut is invoked.
-   * @param {!Array<!HTMLElement>} expectedSelves
-   * @param {?boolean} expectedModalContextOpen
-   * @return {!Promise}
-   */
-  const checkMultiple = (expectedSelves, expectedModalContextOpen) => {
-    waits = expectedSelves.map(() => new PromiseResolver());
-    nextWait = 0;
-    MockInteractions.pressAndReleaseKeyOn(
-        window, 70, cr.isMac ? 'meta' : 'ctrl', 'f');
-    return Promise.all(waits.map(wait => wait.promise)).then(argss => {
-      argss.forEach((args, index) => {
-        assertEquals(expectedSelves[index], args.self);
-        assertEquals(!!expectedModalContextOpen, args.modalContextOpen);
-      });
-    });
-  };
+  let wait;
+  /** @type {boolean} */
+  let resolved;
 
   /**
    * Checks that the handleFindShortcut method is being called for the
@@ -63,18 +22,15 @@
    * @param {?boolean} expectedModalContextOpen
    * @return {!Promise}
    */
-  const check = (expectedSelf, expectedModalContextOpen) =>
-      checkMultiple([expectedSelf], expectedModalContextOpen);
-
-  /**
-   * Register |expectedSelf| element as a listener, check that
-   * handleFindShortcut is called, then remove as a listener.
-   * @param {!HTMLElement} expectedSelf
-   * @param {?boolean} expectedModalContextOpen
-   * @return {!Promise}
-   */
-  const wrappedCheck = (expectedSelf, expectedModalContextOpen) => listenScope(
-      expectedSelf)(() => check(expectedSelf, expectedModalContextOpen));
+  const check = async (expectedSelf, expectedModalContextOpen) => {
+    wait = new PromiseResolver();
+    resolved = false;
+    MockInteractions.pressAndReleaseKeyOn(
+        window, 70, cr.isMac ? 'meta' : 'ctrl', 'f');
+    const args = await wait.promise;
+    assertEquals(expectedSelf, args.self);
+    assertEquals(!!expectedModalContextOpen, args.modalContextOpen);
+  };
 
   /**
    * Registers for a keydown event to check whether the bubbled up event has
@@ -82,22 +38,37 @@
    * @param {boolean} defaultPrevented
    * @return {!Promise}
    */
-  const listenOnceAndCheckDefaultPrevented = defaultPrevented => {
-    const resolver = new PromiseResolver();
-    const handler = e => {
-      window.removeEventListener('keydown', handler);
-      if (e.defaultPrevented == defaultPrevented) {
-        resolver.resolve();
-      } else {
-        resolver.reject();
-      }
-    };
-    window.addEventListener('keydown', handler);
-    return resolver.promise;
+  const listenOnceAndCheckDefaultPrevented = async defaultPrevented => {
+    const e = await test_util.eventToPromise('keydown', window);
+    assertEquals(e.defaultPrevented, defaultPrevented);
   };
 
   suiteSetup(() => {
     document.body.innerHTML = `
+        <dom-module id="find-shortcut-element-manual-listen">
+          <template></template>
+        </dom-module>
+      `;
+
+    Polymer({
+      is: 'find-shortcut-element-manual-listen',
+      behaviors: [FindShortcutBehavior],
+
+      findShortcutListenOnAttach: false,
+      hasFocus: false,
+
+      handleFindShortcut(modalContextOpen) {
+        assert(!resolved);
+        wait.resolve({modalContextOpen, self: this});
+        return true;
+      },
+
+      searchInputHasFocus() {
+        return this.hasFocus;
+      }
+    });
+
+    document.body.innerHTML = `
         <dom-module id="find-shortcut-element">
           <template></template>
         </dom-module>
@@ -108,36 +79,43 @@
       behaviors: [FindShortcutBehavior],
 
       handledResponse: true,
+      hasFocus: false,
 
       handleFindShortcut(modalContextOpen) {
-        assert(nextWait < waits.length);
-        waits[nextWait++].resolve({modalContextOpen, self: this});
+        assert(!resolved);
+        wait.resolve({modalContextOpen, self: this});
         return this.handledResponse;
       },
-    });
-  });
 
-  setup(() => {
+      searchInputHasFocus() {
+        return this.hasFocus;
+      },
+    });
     PolymerTest.clearBody();
   });
 
-  test('handled', () => {
-    document.body.innerHTML = `<find-shortcut-element></find-shortcut-element>`;
-    const testElement = document.body.querySelector('find-shortcut-element');
-    return wrappedCheck(testElement);
+  teardown(() => {
+    PolymerTest.clearBody();
+    assertEquals(0, FindShortcutManager.listeners.length);
   });
 
-  test('handled with modal context open', () => {
+  test('handled', async () => {
+    document.body.innerHTML = `<find-shortcut-element></find-shortcut-element>`;
+    const testElement = document.body.querySelector('find-shortcut-element');
+    await check(testElement);
+  });
+
+  test('handled with modal context open', async () => {
     document.body.innerHTML = `
         <find-shortcut-element></find-shortcut-element>
         <cr-dialog></cr-dialog>`;
     const testElement = document.body.querySelector('find-shortcut-element');
     const dialog = document.body.querySelector('cr-dialog');
     dialog.showModal();
-    return wrappedCheck(testElement, true);
+    await check(testElement, true);
   });
 
-  test('handled with modal context closed', () => {
+  test('handled with modal context closed', async () => {
     document.body.innerHTML = `
         <find-shortcut-element></find-shortcut-element>
         <cr-dialog></cr-dialog>`;
@@ -147,83 +125,112 @@
     assertTrue(dialog.open);
     const whenCloseFired = test_util.eventToPromise('close', dialog);
     dialog.close();
-    return whenCloseFired.then(() => wrappedCheck(testElement));
+    await whenCloseFired;
+    await check(testElement);
   });
 
-  test('last listener is active', () => {
+  test('last listener is active', async () => {
     document.body.innerHTML = `
         <find-shortcut-element></find-shortcut-element>
         <find-shortcut-element></find-shortcut-element>`;
+    assertEquals(2, FindShortcutManager.listeners.length);
     const testElements =
         document.body.querySelectorAll('find-shortcut-element');
-    return promiseSeries([
-      () => listenScope(testElements[0])(() => wrappedCheck(testElements[1])),
-      () => listenScope(testElements[1])(() => wrappedCheck(testElements[0])),
-    ]);
+    await check(testElements[1]);
   });
 
-  test('can remove listeners out of order', () => {
+  test('can remove listeners out of order', async () => {
     document.body.innerHTML = `
-        <find-shortcut-element></find-shortcut-element>
-        <find-shortcut-element></find-shortcut-element>`;
+        <find-shortcut-element-manual-listen>
+        </find-shortcut-element-manual-listen>
+        <find-shortcut-element-manual-listen>
+        </find-shortcut-element-manual-listen>`;
     const testElements =
-        document.body.querySelectorAll('find-shortcut-element');
+        document.body.querySelectorAll('find-shortcut-element-manual-listen');
     testElements[0].becomeActiveFindShortcutListener();
     testElements[1].becomeActiveFindShortcutListener();
     testElements[0].removeSelfAsFindShortcutListener();
-    return check(testElements[1]).then(() => {
-      testElements[1].removeSelfAsFindShortcutListener();
-    });
+    await check(testElements[1]);
+    testElements[1].removeSelfAsFindShortcutListener();
   });
 
   test('removing self when not active throws exception', () => {
-    document.body.innerHTML = `<find-shortcut-element></find-shortcut-element>`;
-    const testElement = document.body.querySelector('find-shortcut-element');
+    document.body.innerHTML = `
+        <find-shortcut-element-manual-listen>
+        </find-shortcut-element-manual-listen>`;
+    const testElement =
+        document.body.querySelector('find-shortcut-element-manual-listen');
     assertThrows(() => testElement.removeSelfAsFindShortcutListener());
   });
 
-  test('becoming active when already active throws exception', () => {
-    document.body.innerHTML = `<find-shortcut-element></find-shortcut-element>`;
-    const testElement = document.body.querySelector('find-shortcut-element');
-    return listenScope(testElement)(() => {
-      assertThrows(() => testElement.becomeActiveFindShortcutListener());
-    });
-  });
-
-  test('becoming active when an inactive listener throws exception', () => {
+  test('throw exception when try to become active already a listener', () => {
     document.body.innerHTML = `
-        <find-shortcut-element></find-shortcut-element>
-        <find-shortcut-element></find-shortcut-element>`;
+        <find-shortcut-element>
+          <find-shortcut-element></find-shortcut-element>
+        </find-shortcut-element>`;
     const testElements =
         document.body.querySelectorAll('find-shortcut-element');
-    return listenScope(testElements[0])(
-        () => listenScope(testElements[1])(() => {
-          assertThrows(
-              () => testElements[0].becomeActiveFindShortcutListener());
-        }));
+    assertThrows(() => testElements[0].becomeActiveFindShortcutListener());
+    assertThrows(() => testElements[1].becomeActiveFindShortcutListener());
   });
 
-  test('cmd+ctrl+f bubbles up (defaultPrevented=false)', () => {
+  test('cmd+ctrl+f bubbles up', async () => {
     const bubbledUp = listenOnceAndCheckDefaultPrevented(false);
     document.body.innerHTML = `<find-shortcut-element></find-shortcut-element>`;
     const testElement = document.body.querySelector('find-shortcut-element');
-    return listenScope(testElement)(() => {
-      MockInteractions.pressAndReleaseKeyOn(window, 70, ['meta', 'ctrl'], 'f');
-      return bubbledUp;
-    });
+    MockInteractions.pressAndReleaseKeyOn(window, 70, ['meta', 'ctrl'], 'f');
+    await bubbledUp;
   });
 
-  test('find shortcut bubbles up (defaultPrevented=true)', () => {
+  test('find shortcut bubbles up', async () => {
     const bubbledUp = listenOnceAndCheckDefaultPrevented(true);
     document.body.innerHTML = `<find-shortcut-element></find-shortcut-element>`;
     const testElement = document.body.querySelector('find-shortcut-element');
-    return Promise.all([wrappedCheck(testElement), bubbledUp]);
+    await check(testElement);
+    await bubbledUp;
   });
 
-  test('shortcut with no listeners bubbles up (defaultPrevented=false)', () => {
+  test('shortcut with no listeners bubbles up', async () => {
     const bubbledUp = listenOnceAndCheckDefaultPrevented(false);
     MockInteractions.pressAndReleaseKeyOn(
         window, 70, cr.isMac ? 'meta' : 'ctrl', 'f');
-    return bubbledUp;
+    await bubbledUp;
+  });
+
+  test('inner listener is active when listening on attach', async () => {
+    document.body.innerHTML = `
+        <find-shortcut-element>
+          <find-shortcut-element></find-shortcut-element>
+        </find-shortcut-element>`;
+    const testElements =
+        document.body.querySelectorAll('find-shortcut-element');
+    assertEquals(2, FindShortcutManager.listeners.length);
+    await check(testElements[1]);
+  });
+
+  test('not handle by listener bubbles up', async () => {
+    const bubbledUp = listenOnceAndCheckDefaultPrevented(false);
+    document.body.innerHTML = `<find-shortcut-element></find-shortcut-element>`;
+    const testElement = document.body.querySelector('find-shortcut-element');
+    testElement.handledResponse = false;
+    await check(testElement);
+    await bubbledUp;
+  });
+
+  test('when element has focus, shortcut is handled by next', async () => {
+    document.body.innerHTML = `
+        <find-shortcut-element></find-shortcut-element>
+        <find-shortcut-element></find-shortcut-element>
+        <find-shortcut-element></find-shortcut-element>`;
+    const testElements =
+        Array.from(document.body.querySelectorAll('find-shortcut-element'));
+    testElements[0].hasFocus = true;
+    await check(testElements[2]);
+    testElements[0].hasFocus = false;
+    testElements[1].hasFocus = true;
+    await check(testElements[0]);
+    testElements[1].hasFocus = false;
+    testElements[2].hasFocus = true;
+    await check(testElements[1]);
   });
 });
diff --git a/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js b/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js
index ed7e927d..297fc78 100644
--- a/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js
+++ b/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js
@@ -75,6 +75,7 @@
 
       page = document.createElement('print-preview-app');
       document.body.appendChild(page);
+      page.$.documentInfo.init(true, 'title', false);
       const previewArea = page.$.previewArea;
       pluginProxy.setLoadCallback(previewArea.onPluginLoad_.bind(previewArea));
     }
diff --git a/chrome/test/data/webui/print_preview/model_test.js b/chrome/test/data/webui/print_preview/model_test.js
index 6bb58810..b29c36f3 100644
--- a/chrome/test/data/webui/print_preview/model_test.js
+++ b/chrome/test/data/webui/print_preview/model_test.js
@@ -189,9 +189,18 @@
     }
 
     function initializeModel() {
-      model.documentInfo = new print_preview.DocumentInfo();
-      model.documentInfo.init(true, 'title', true);
-      model.documentInfo.updatePageCount(3);
+      model.documentSettings = {
+        hasCssMediaStyles: false,
+        hasSelection: true,
+        isModifiable: true,
+        isScalingDisabled: false,
+        fitToPageScaling: 100,
+        pageCount: 3,
+        title: 'title',
+      };
+
+      model.pageSize = new print_preview.Size(612, 792);
+
       // Update pages accordingly.
       model.set('settings.pages.value', [1, 2, 3]);
 
diff --git a/chrome/test/data/webui/print_preview/native_layer_stub.js b/chrome/test/data/webui/print_preview/native_layer_stub.js
index 7e11649..b9b9c753 100644
--- a/chrome/test/data/webui/print_preview/native_layer_stub.js
+++ b/chrome/test/data/webui/print_preview/native_layer_stub.js
@@ -101,9 +101,8 @@
     }
 
     /** @override */
-    getPreview(printTicket, pageCount) {
-      this.methodCalled(
-          'getPreview', {printTicket: printTicket, pageCount: pageCount});
+    getPreview(printTicket) {
+      this.methodCalled('getPreview', {printTicket: printTicket});
       const printTicketParsed = JSON.parse(printTicket);
       if (printTicketParsed.deviceName == this.badPrinterId_) {
         return Promise.reject('SETTINGS_INVALID');
diff --git a/chrome/test/data/webui/print_preview/pages_settings_test.js b/chrome/test/data/webui/print_preview/pages_settings_test.js
index 6ba7c3e..b0c9f5f 100644
--- a/chrome/test/data/webui/print_preview/pages_settings_test.js
+++ b/chrome/test/data/webui/print_preview/pages_settings_test.js
@@ -21,9 +21,6 @@
     /** @type {?PrintPreviewPagesSettingsElement} */
     let pagesSection = null;
 
-    /** @type {?print_preview.DocumentInfo} */
-    let documentInfo = null;
-
     /** @type {!Array<number>} */
     const oneToHundred = Array.from({length: 100}, (x, i) => i + 1);
 
@@ -32,9 +29,6 @@
 
     /** @override */
     setup(function() {
-      documentInfo = new print_preview.DocumentInfo();
-      documentInfo.init(true, 'title', false);
-
       PolymerTest.clearBody();
       pagesSection = document.createElement('print-preview-pages-settings');
       pagesSection.settings = {
@@ -60,7 +54,6 @@
           key: '',
         },
       };
-      pagesSection.documentInfo = documentInfo;
       pagesSection.disabled = false;
       document.body.appendChild(pagesSection);
     });
@@ -75,8 +68,7 @@
      */
     function setupInput(inputString, pageCount) {
       // Set page count.
-      documentInfo.updatePageCount(pageCount);
-      pagesSection.notifyPath('documentInfo.pageCount');
+      pagesSection.pageCount = pageCount;
       Polymer.dom.flush();
       let input = null;
       return test_util.waitForRender(pagesSection)
@@ -299,7 +291,7 @@
     // Tests that the radio buttons and custom input are appropriately
     // inside/outside the tab order.
     test(assert(TestNames.TabOrder), function() {
-      documentInfo.updatePageCount(3);
+      pagesSection.pageCount = 3;
 
       const radioGroup = pagesSection.$$('cr-radio-group');
       const customRadio = pagesSection.$.customRadioButton;
diff --git a/chrome/test/data/webui/print_preview/preview_generation_test.js b/chrome/test/data/webui/print_preview/preview_generation_test.js
index c0b00ef..f31d2c52 100644
--- a/chrome/test/data/webui/print_preview/preview_generation_test.js
+++ b/chrome/test/data/webui/print_preview/preview_generation_test.js
@@ -65,10 +65,10 @@
             nativeLayer.whenCalled('getPrinterCapabilities'),
           ])
           .then(function() {
-            if (!page.documentInfo_.isModifiable) {
-              page.documentInfo_.updateFitToPageScaling(98);
+            if (!page.documentSettings_.isModifiable) {
+              page.documentSettings_.fitToPageScaling = 98;
             }
-            page.documentInfo_.updatePageCount(3);
+            page.documentSettings_.pageCount = 3;
             return nativeLayer.whenCalled('getPreview');
           });
     }
diff --git a/chrome/test/data/webui/print_preview/settings_section_test.js b/chrome/test/data/webui/print_preview/settings_section_test.js
index 9a713d8..4d6adc3 100644
--- a/chrome/test/data/webui/print_preview/settings_section_test.js
+++ b/chrome/test/data/webui/print_preview/settings_section_test.js
@@ -73,21 +73,20 @@
      * @param {boolean} hasSelection Whether the document has a selection.
      */
     function initDocumentInfo(isPdf, hasSelection) {
-      const info = new print_preview.DocumentInfo();
+      const info = page.$.documentInfo;
       info.init(!isPdf, 'title', hasSelection);
       if (isPdf) {
-        info.updateFitToPageScaling(98);
+        info.set('documentSettings.fitToPageScaling', 98);
       }
-      info.updatePageCount(3);
-      page.set('documentInfo_', info);
+      info.set('documentSettings.pageCount', 3);
+      info.margins = null;
       Polymer.dom.flush();
     }
 
     function addSelection() {
       // Add a selection.
-      let info = new print_preview.DocumentInfo();
-      info.init(page.documentInfo_.isModifiable, 'title', true);
-      page.set('documentInfo_', info);
+      page.$.documentInfo.init(
+          page.documentSettings_.isModifiable, 'title', true);
       Polymer.dom.flush();
     }
 
@@ -587,6 +586,7 @@
     });
 
     test(assert(TestNames.SetPages), function() {
+      initDocumentInfo(false, false);
       const pagesElement = page.$$('print-preview-pages-settings');
       // This section is always visible.
       assertFalse(pagesElement.hidden);
@@ -932,7 +932,7 @@
             // to verify that the input matches them.
             if (scalingValid) {
               const scalingDisplay = fitToPage ?
-                  page.documentInfo_.fitToPageScaling.toString() :
+                  page.documentSettings_.fitToPageScaling.toString() :
                   scalingValue;
               assertEquals(scalingDisplay, scalingInput.value);
             }
diff --git a/chrome/test/data/webui/welcome/email_chooser_test.js b/chrome/test/data/webui/welcome/email_chooser_test.js
index 17d872c6..a14be67 100644
--- a/chrome/test/data/webui/welcome/email_chooser_test.js
+++ b/chrome/test/data/webui/welcome/email_chooser_test.js
@@ -33,9 +33,7 @@
 
     setup(function() {
       testEmailBrowserProxy = new TestNuxEmailProxy();
-      nux.NuxEmailProxyImpl.instance_ = testEmailBrowserProxy;
       testEmailMetricsProxy = new TestMetricsProxy();
-      nux.EmailMetricsProxyImpl.instance_ = testEmailMetricsProxy;
       testBookmarkBrowserProxy = new TestBookmarkProxy();
       nux.BookmarkProxyImpl.instance_ = testBookmarkBrowserProxy;
       // Reset w/ new proxy for test.
@@ -44,13 +42,17 @@
       testEmailBrowserProxy.setEmailList(emails);
 
       PolymerTest.clearBody();
-      testElement = document.createElement('email-chooser');
+      testElement = document.createElement('app-chooser');
       document.body.appendChild(testElement);
+      testElement.singleSelect = true;
+      testElement.appProxy = testEmailBrowserProxy;
+      testElement.metricsManager =
+          new nux.ModuleMetricsManager(testEmailMetricsProxy);
       // Simulate nux-email's onRouteEnter call.
       testElement.onRouteEnter();
       return Promise.all([
         testEmailMetricsProxy.whenCalled('recordPageShown'),
-        testEmailBrowserProxy.whenCalled('getEmailList'),
+        testEmailBrowserProxy.whenCalled('getAppList'),
       ]);
     });
 
@@ -140,8 +142,6 @@
 
             // Id for the provider that was selected.
             assertEquals(0, recordProviderSelectedResponse[0]);
-            // Length of the email list array.
-            assertEquals(2, recordProviderSelectedResponse[1]);
           });
     });
   });
diff --git a/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js b/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js
index 73ba24bd..dd1ffb83 100644
--- a/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js
+++ b/chrome/test/data/webui/welcome/onboarding_welcome_browsertest.js
@@ -36,7 +36,7 @@
 OnboardingWelcomeEmailChooserTest = class extends OnboardingWelcomeBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://welcome/email/email_chooser.html';
+    return 'chrome://welcome/shared/app_chooser.html';
   }
 
   /** @override */
diff --git a/chrome/test/data/webui/welcome/signin_view_test.js b/chrome/test/data/webui/welcome/signin_view_test.js
index f141c89..5ab021f 100644
--- a/chrome/test/data/webui/welcome/signin_view_test.js
+++ b/chrome/test/data/webui/welcome/signin_view_test.js
@@ -19,7 +19,7 @@
       welcome.WelcomeBrowserProxyImpl.instance_ = testWelcomeBrowserProxy;
 
       testEmailBrowserProxy = new TestNuxEmailProxy();
-      nux.NuxEmailProxyImpl.instance_ = testEmailBrowserProxy;
+      nux.EmailAppProxyImpl.instance_ = testEmailBrowserProxy;
 
       PolymerTest.clearBody();
       testElement = document.createElement('signin-view');
diff --git a/chrome/test/data/webui/welcome/test_nux_email_proxy.js b/chrome/test/data/webui/welcome/test_nux_email_proxy.js
index 816eda6..6921584 100644
--- a/chrome/test/data/webui/welcome/test_nux_email_proxy.js
+++ b/chrome/test/data/webui/welcome/test_nux_email_proxy.js
@@ -77,12 +77,12 @@
   }
 }
 
-/** @implements {nux.NuxEmailProxy} */
+/** @implements {nux.AppProxy} */
 class TestNuxEmailProxy extends TestBrowserProxy {
   constructor() {
     super([
       'cacheBookmarkIcon',
-      'getEmailList',
+      'getAppList',
       'getSavedProvider',
       'recordProviderSelected',
     ]);
@@ -100,8 +100,8 @@
   }
 
   /** @override */
-  getEmailList() {
-    this.methodCalled('getEmailList');
+  getAppList() {
+    this.methodCalled('getAppList');
     return Promise.resolve(this.emailList_);
   }
 
@@ -110,7 +110,6 @@
     this.methodCalled('cacheBookmarkIcon');
   }
 
-  /** @override */
   getSavedProvider() {
     this.methodCalled('getSavedProvider');
     return this.stubSavedProvider_;
diff --git a/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js b/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js
index 5081b38f..4b39f58 100644
--- a/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js
+++ b/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js
@@ -15,6 +15,7 @@
 var webglCanvas = document.getElementById('webgl-canvas');
 var glAttribs = {
   alpha: false,
+  xrCompatible: true,
 };
 var gl = null;
 var xrDevice = null;
@@ -112,8 +113,6 @@
   session.addEventListener('end', onSessionEnded);
   // Initialize the WebGL context for use with XR if it hasn't been already
   if (!gl) {
-    glAttribs['compatibleXRDevice'] = session.device;
-
     // Create an offscreen canvas and get its context
     let offscreenCanvas = document.createElement('canvas');
     gl = offscreenCanvas.getContext('webgl', glAttribs);
diff --git a/chromecast/media/cdm/cast_cdm_factory.cc b/chromecast/media/cdm/cast_cdm_factory.cc
index 57a5b29..e2784ef0 100644
--- a/chromecast/media/cdm/cast_cdm_factory.cc
+++ b/chromecast/media/cdm/cast_cdm_factory.cc
@@ -43,13 +43,11 @@
 
   CastKeySystem cast_key_system(GetKeySystemByName(key_system));
 
-  scoped_refptr<chromecast::media::CastCdm> cast_cdm;
-  if (cast_key_system == chromecast::media::KEY_SYSTEM_CLEAR_KEY) {
-    // TODO(gunsch): handle ClearKey decryption. See crbug.com/441957
-  } else {
-    cast_cdm =
-        CreatePlatformBrowserCdm(cast_key_system, security_origin, cdm_config);
-  }
+  DCHECK((cast_key_system == chromecast::media::KEY_SYSTEM_PLAYREADY) ||
+         (cast_key_system == chromecast::media::KEY_SYSTEM_WIDEVINE));
+
+  scoped_refptr<chromecast::media::CastCdm> cast_cdm =
+      CreatePlatformBrowserCdm(cast_key_system, security_origin, cdm_config);
 
   if (!cast_cdm) {
     LOG(INFO) << "No matching key system found: " << cast_key_system;
diff --git a/chromeos/cryptohome/async_method_caller.cc b/chromeos/cryptohome/async_method_caller.cc
index 6d6df50..81035ec 100644
--- a/chromeos/cryptohome/async_method_caller.cc
+++ b/chromeos/cryptohome/async_method_caller.cc
@@ -163,8 +163,8 @@
     scoped_refptr<base::SingleThreadTaskRunner> task_runner;
   };
 
-  typedef base::hash_map<int, CallbackElement> CallbackMap;
-  typedef base::hash_map<int, DataCallbackElement> DataCallbackMap;
+  typedef std::unordered_map<int, CallbackElement> CallbackMap;
+  typedef std::unordered_map<int, DataCallbackElement> DataCallbackMap;
 
   // Handles the response for async calls.
   // Below is described how async calls work.
diff --git a/chromeos/dbus/OWNERS b/chromeos/dbus/OWNERS
index fa8612f..7058090 100644
--- a/chromeos/dbus/OWNERS
+++ b/chromeos/dbus/OWNERS
@@ -10,4 +10,5 @@
 per-file *diagnosticsd*=emaxx@chromium.org
 per-file *diagnosticsd*=pmarko@chromium.org
 per-file *power*=derat@chromium.org
+per-file *smb_provider*=baileyberro@chromium.org
 per-file *smb_provider*=zentaro@chromium.org
diff --git a/chromeos/services/device_sync/proto/BUILD.gn b/chromeos/services/device_sync/proto/BUILD.gn
index bc423dd..f7ee566e 100644
--- a/chromeos/services/device_sync/proto/BUILD.gn
+++ b/chromeos/services/device_sync/proto/BUILD.gn
@@ -7,6 +7,11 @@
 proto_library("proto") {
   sources = [
     "cryptauth_api.proto",
+    "cryptauth_better_together_feature_metadata.proto",
+    "cryptauth_client_app_metadata.proto",
+    "cryptauth_common.proto",
+    "cryptauth_directive.proto",
+    "cryptauth_enrollment.proto",
     "securemessage.proto",
   ]
 }
diff --git a/chromeos/services/device_sync/proto/cryptauth_better_together_feature_metadata.proto b/chromeos/services/device_sync/proto/cryptauth_better_together_feature_metadata.proto
new file mode 100644
index 0000000..9e27450
--- /dev/null
+++ b/chromeos/services/device_sync/proto/cryptauth_better_together_feature_metadata.proto
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This message fills the |metadata| bytes field of the FeatureMetadata message
+// (found in the file cryptauth_client_app_metadata.proto) when |feature_type|
+// is FeatureMetadata::Feature::BETTER_TOGETHER.
+syntax = "proto3";
+
+package cryptauthv2;
+
+option optimize_for = LITE_RUNTIME;
+
+message BetterTogetherFeatureMetadata {
+  enum FeatureName {
+    UNKNOWN_FEATURE = 0;
+    BETTER_TOGETHER_HOST = 1;
+    BETTER_TOGETHER_CLIENT = 2;
+    EASY_UNLOCK_HOST = 3;
+    EASY_UNLOCK_CLIENT = 4;
+    MAGIC_TETHER_HOST = 5;
+    MAGIC_TETHER_CLIENT = 6;
+    SMS_CONNECT_HOST = 7;
+    SMS_CONNECT_CLIENT = 8;
+  }
+
+  repeated FeatureName supported_features = 1;
+  repeated FeatureName enabled_features = 2;
+}
diff --git a/chromeos/services/device_sync/proto/cryptauth_client_app_metadata.proto b/chromeos/services/device_sync/proto/cryptauth_client_app_metadata.proto
new file mode 100644
index 0000000..232e11a
--- /dev/null
+++ b/chromeos/services/device_sync/proto/cryptauth_client_app_metadata.proto
@@ -0,0 +1,129 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Client-specific metadata used in the CryptAuth v2 Enrollment protocol, which
+// is serialized and held in |client_app_metadata| of SyncKeysRequest (in file
+// cryptauth_enrollment.proto).
+syntax = "proto3";
+
+package cryptauthv2;
+
+option optimize_for = LITE_RUNTIME;
+
+// Client specific metadata contained in SyncKeysRequest.client_app_metadata.
+// Next id: 31
+message ClientAppMetadata {
+  // App specific metadata from the device. On Android, these should be common
+  // for all the features as they come from GmsCore, however, on IOS and other
+  // devices, there could be multiple apps with the feature.
+  repeated ApplicationSpecificMetadata application_specific_metadata = 1;
+
+  // Subgrouping of device identifiers.
+  // Instance ID: See more info at go/gcm-in-gmscore and
+  // https://g3doc.corp.google.com/java/com/google/wireless/android/iid/g3doc/index.md?cl=head
+  string instance_id = 2;
+  // Token to authenticate the instance ID.
+  string instance_id_token = 3;
+  // Checkin android id of the device.
+  fixed64 android_device_id = 4;
+  // Not all devices have device identifiers that fit in 64 bits.
+  bytes long_device_id = 5;
+
+  // Subgrouping of device features field. These help in targeting specific
+  // class of devices, for ex: Tablets vs phones etc.
+  // Locale of the device.
+  string locale = 6;
+  // The Operating System version.
+  string device_os_version = 7;
+  // The Operating System version number on the device.
+  int64 device_os_version_code = 8;
+  // The Operating system release on the device.
+  string device_os_release = 9;
+  // The Operating system codename on the device.
+  string device_os_codename = 10;
+  // Size of the display in thousandths of an inch (e.g. 7000 mils = 7 in)
+  int32 device_display_diagonal_mils = 11;
+  // Device's model name (e.g., an android.os.Build.MODEL)
+  string device_model = 12;
+  // The device manufacturer name.
+  string device_manufacturer = 13;
+  // The type of device this is.
+  enum DeviceType {
+    UNKNOWN = 0;
+    ANDROID = 1;
+    CHROME = 2;
+    IOS = 3;
+    BROWSER = 4;
+    OSX = 5;
+  }
+  DeviceType device_type = 14;
+
+  // Subgrouping of lock screen related fields. Used by many identity features.
+  // Is this device using a secure screenlock (e.g., a pattern or pin unlock).
+  bool using_secure_screenlock = 15;
+  // Is auto-unlocking the screenlock supported ?
+  bool auto_unlock_screenlock_supported = 16;
+  // Is auto-unlocking the screenlock (e.g., when at "home") enabled ?
+  bool auto_unlock_screenlock_enabled = 17;
+
+  // Subgrouping of bluetooth state related fields on the device. Used by many
+  // features.
+  // Does the device have a Bluetooth (classic) radio?
+  bool bluetooth_radio_supported = 18;
+  // Is the Bluetooth (classic) radio on?
+  bool bluetooth_radio_enabled = 19;
+  // Does the device have a ble radio?
+  bool ble_radio_supported = 20;
+
+  // Does the device hardware support a mobile data connection?
+  bool mobile_data_supported = 21;
+  // Does the device support tethering ?
+  bool tethering_supported = 22;
+  // If a feature wants to upload some metadata common to all its keys.
+  repeated FeatureMetadata feature_metadata = 23;
+
+  // Bluetooth address for EasyUnlock.
+  string bluetooth_address = 24;
+
+  // Is the device a "Pixel Experience" Android device?
+  bool pixel_experience = 25;
+  // Is the device running in the ARC++ container on a chromebook?
+  bool arc_plus_plus = 26;
+  // Does the device support user presence that is backed by hardware
+  // (unspoofable by malware)?
+  bool hardware_user_presence_supported = 27;
+  // Does the device support user verification (E.g., passcode, biometrics)?
+  bool user_verification_supported = 28;
+  // Does the device support creating a key in trusted execution environment?
+  bool trusted_execution_environment_supported = 29;
+  // Does the device support creating a key in a dedicated secure element
+  // hardware?
+  bool dedicated_secure_element_supported = 30;
+}
+
+// Metadata that's app specific.
+// Next id: 6
+message ApplicationSpecificMetadata {
+  // Used for device_address of DeviceInfo field 2, but for GCM capable devices.
+  bytes gcm_registration_id = 1;
+  // Does the user have notifications enabled for the given device address.
+  bool notification_enabled = 2;
+  // The software version running on the device.
+  string device_software_version = 3;
+  // The software version number running on the device.
+  int64 device_software_version_code = 4;
+  // Software package information if applicable.
+  string device_software_package = 5;
+}
+
+// Metadata which is same for different keys belonging to a particular feature.
+message FeatureMetadata {
+  enum Feature {
+    UNKNOWN = 0;
+    AUTHZEN = 1;
+    BETTER_TOGETHER = 2;
+  }
+  Feature feature_type = 1;
+  bytes metadata = 2;
+}
diff --git a/chromeos/services/device_sync/proto/cryptauth_common.proto b/chromeos/services/device_sync/proto/cryptauth_common.proto
new file mode 100644
index 0000000..6b538fb68
--- /dev/null
+++ b/chromeos/services/device_sync/proto/cryptauth_common.proto
@@ -0,0 +1,160 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Contains messages and data types used by request, response, and directive
+// messages in the CryptAuth v2 Enrollment protocol.
+syntax = "proto3";
+
+package cryptauthv2;
+
+option optimize_for = LITE_RUNTIME;
+
+// The types of cryptographic keys that are supported.
+enum KeyType {
+  // Default value. Don't use!
+  KEY_TYPE_UNSPECIFIED = 0;
+
+  // 16-byte random byte string
+  RAW128 = 1;
+  // 32-byte random byte string
+  RAW256 = 2;
+  // Curve25519
+  CURVE25519 = 3;
+  // P256
+  P256 = 4;
+
+  // The key will be provided by the application.
+  CUSTOM = 127;
+}
+
+// The generic format for public-key certificates.
+message Certificate {
+  // The identifier bound to the cert, e.g., an email address or phone number.
+  string common_name = 1;
+  // The raw bytes of the public key.
+  bytes public_key = 2;
+  // The UNIX timestamp when the cert will expire.
+  int64 expire_time_millis = 3;
+
+  // A restriction imposed on the applications using this key.
+  // Claims are validated along with the signature, when this key is used.
+  message Claim {
+    // Claim name.
+    string name = 1;
+    // Whether this claim is critical in the certificate. If it is critical,
+    // the client must fail the validation of the certificate if the client does
+    // not recognize the name of the claim.
+    bool critical = 2;
+    // Claim value.
+    bytes value = 3;
+  }
+  // All claims associated with the use of this key.
+  repeated Claim claims = 4;
+
+  // The signature over all of the above.
+  bytes signature = 5;
+}
+
+// Uniquely identifies a server-side policy instance, which is associated with a
+// key or a client. Subset of this policy is communicated to the client and
+// referenced using this message.
+// A set of related policies are identified by a name. Every time the policy
+// changes, it gets a new unique version number to distinguish it from the
+// policy instance it is based on. Together, following fields uniquely identify
+// a policy instance.
+message PolicyReference {
+  // The name of the policy.
+  string name = 1;
+
+  // The version of the policy.
+  int64 version = 2;
+}
+
+// The client-specific metadata contained in SyncKeysRequest.
+message ClientMetadata {
+  // The counter for how many times the request has been retried.
+  int64 retry_count = 1;
+
+  // The reason why the request has been invoked.
+  enum InvocationReason {
+    // Unspecified invocation reason.
+    INVOCATION_REASON_UNSPECIFIED = 0;
+
+    // First run of the software package invoking this call.
+    INITIALIZATION = 1;
+    // Ordinary periodic actions (e.g., monthly master key rotation).
+    PERIODIC = 2;
+    // Slow-cycle periodic action (e.g., yearly keypair rotation).
+    SLOW_PERIODIC = 3;
+    // Fast-cycle periodic action (e.g., daily sync for Smart Lock users).
+    FAST_PERIODIC = 4;
+
+    // Expired state (e.g., expired credentials, or cached entries) was
+    // detected.
+    EXPIRATION = 5;
+    // An unexpected protocol failure occurred (so attempting to repair state).
+    FAILURE_RECOVERY = 6;
+
+    // A new account has been added to the device.
+    NEW_ACCOUNT = 7;
+    // An existing account on the device has been changed.
+    CHANGED_ACCOUNT = 8;
+
+    // The user toggled the state of a feature (e.g., Smart Lock enabled via
+    // bluetooth).
+    FEATURE_TOGGLED = 9;
+    // A "push" from the server caused this action (e.g., a sync tickle).
+    SERVER_INITIATED = 10;
+
+    // A local address change triggered this (e.g., GCM registration id
+    // changed).
+    ADDRESS_CHANGE = 11;
+    // A software update has triggered this.
+    SOFTWARE_UPDATE = 12;
+
+    // A manual action by the user triggered this (e.g., commands sent via adb).
+    MANUAL = 13;
+
+    // A custom key has been invalidated on the device (e.g. screen lock is
+    // disabled).
+    CUSTOM_KEY_INVALIDATION = 14;
+
+    // Periodic action triggered by auth_proximity
+    PROXIMITY_PERIODIC = 15;
+  }
+  // Reason for invocation.
+  InvocationReason invocation_reason = 2;
+
+  // Whether the platform has hardware supports for certain algorithms.
+  message CryptoHardware {
+    // AES-128
+    bool aes128 = 1;
+    // ASE-256
+    bool aes256 = 2;
+    // Carryless multiplication
+    bool clmul = 3;
+    // Curve25519
+    bool curve25519 = 4;
+    // P256
+    bool p256 = 5;
+  }
+  // Crypto hardware available on the client.
+  CryptoHardware crypto_hardware = 3;
+
+  // If the request is issued as a direct result, or a follow-up for a
+  // notification/tickle, the session_id from that notification.
+  string session_id = 4;
+}
+
+// Identifies Cryptauth services.
+enum TargetService {
+  // Unspecified Cryptauth service.
+  TARGET_SERVICE_UNSPECIFIED = 0;
+
+  // Cryptauth Enrollment.
+  ENROLLMENT = 1;
+
+  // Cryptauth DeviceSync.
+  DEVICE_SYNC = 2;
+}
diff --git a/chromeos/services/device_sync/proto/cryptauth_directive.proto b/chromeos/services/device_sync/proto/cryptauth_directive.proto
new file mode 100644
index 0000000..447673f
--- /dev/null
+++ b/chromeos/services/device_sync/proto/cryptauth_directive.proto
@@ -0,0 +1,113 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Contains messages sent from CryptAuth to clients who registered keys using
+// the v2 Enrollment protocol. The messages provide instructions to clients such
+// as how frequently to check in with CryptAuth via a SyncKeysRequest, how long
+// to wait between failed enrollment attempts, and what other keys are required
+// to cross-sign for a particular key.
+syntax = "proto3";
+
+package cryptauthv2;
+
+option optimize_for = LITE_RUNTIME;
+
+import "cryptauth_common.proto";
+
+// The policy to be handed down to the party which registered the public key
+// with the server. It is produced on the fly from current ServerSidePolicy
+// and PolicyConfig (PC).
+message KeyDirective {
+  // The specific policy which was used to generate this message.
+  PolicyReference policy_reference = 1;
+
+  // When rotating the current key, 'crossproof_key_name' keys should be used
+  // to cross sign. This is retrieved from PolicyConfig.crossproof_key_name .
+  repeated string crossproof_key_names = 2;
+
+  // The time when the key was enrolled/rotated (as observed by the server).
+  // This should be the same as ServerSidePolicy.enroll_time_millis .
+  int64 enroll_time_millis = 3;
+}
+
+// This contains the directives handed down to the party which registered the
+// public key with the server. These directives are aggregated from across all
+// the policies of the keys that have been registered by this first party.
+message ClientDirective {
+  // The specific policy which was used to generate this message.
+  PolicyReference policy_reference = 1;
+
+  // The first party should check in with the server after this period.
+  // The server may require the client (first party) to rotate the key
+  // (based on PolicyConfig.rotate_delay_millis from across all the policies
+  // of the registered keys).
+  // For each policy of a registered key a value is randomly drawn from
+  // [PC.checkin_delay_millis - PC.checkin_delay_millis_interval,
+  // PC.checkin_delay_millis + PC.checkin_delay_millis_interval].
+  // The minimum value from across all these values is used.
+  //
+  // Whenever such a time period is coming due, the client should check in
+  // all its keys with the server. The server tells the client which of those
+  // keys need to be rotated and the rotation process proceeds for all these
+  // keys (bundled together).
+  int64 checkin_delay_millis = 2;
+
+  // In case any call to CryptAuth v2 failed, the first party should retry
+  // at most these many times right away, without the need to wait at all.
+  // Passed in from PC.retry_attempts.
+  // For example, a value of 1 means one original request, and if failed, a
+  // single retry should follow.
+  int32 retry_attempts = 3;
+
+  // In case any call to CryptAuth v2 failed retry_attempts + 1 times, the first
+  // party should retry the call again after this time period. If this latter
+  // retry fails, the first party should wait this time period again then retry
+  // and repeat until the request succeeds.
+  // For each policy of a registered key a value is randomly drawn from
+  // [PC.retry_period_millis - PC.retry_period_millis_interval,
+  // PC.retry_period_millis + PC.retry_period_millis_interval].
+  // The maximum value from across all these values is used.
+  int64 retry_period_millis = 4;
+
+  // The timestamp when this policy was minted.
+  // This can help the client sync with the server's time.
+  // checkin_delay_millis and retry_period_millis are relative to this time.
+  // Without this timestamp, the client should act right away with regard to
+  // the *_millis fields (ie, schedule something at NOW + *_millis).
+  // With this timestamp (considering the times of both server and client are
+  // in sync), the client would have all the required information for
+  // a later action.
+  int64 create_time_millis = 5;
+
+  // Which other services should be invoked after this interaction is complete.
+  repeated InvokeNext invoke_next = 6;
+}
+
+// Instructing the client to invoke a specific service.
+message InvokeNext {
+  // Target service to be involved next.
+  TargetService service = 1;
+
+  // Key name to be processed for target service.
+  string key_name = 2;
+}
+
+// The policy to be handed down to a third party along with the corresponding
+// public key of the device it asked for. It is produced on the fly from current
+// PolicyConfig and ServerSidePolicy (defined in
+// java/com/google/security/cryptauth/v2/backend/common/policy/policy.proto).
+message ThirdPartyKeyDirective {
+  // The specific policy which was used to generate this message.
+  PolicyReference policy_reference = 1;
+
+  // The third party should not use this key after this timestamp.
+  // It should sync with CryptAuth for getting a fresh one after this timestamp.
+  // This should be consistent with what the latest first party directive states
+  // (in its create_time_millis field), combined with
+  // PolicyConfig.rotate_delay_millis .
+  int64 expire_time_millis = 2;
+
+  // The timestamp when this policy was distributed to the third party.
+  int64 distribute_time_millis = 3;
+}
diff --git a/chromeos/services/device_sync/proto/cryptauth_enrollment.proto b/chromeos/services/device_sync/proto/cryptauth_enrollment.proto
new file mode 100644
index 0000000..1448b2a
--- /dev/null
+++ b/chromeos/services/device_sync/proto/cryptauth_enrollment.proto
@@ -0,0 +1,297 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Contains the request and response messages used in the CryptAuth v2
+// Enrollment protocol. Specifically,
+//   (1) SyncKeysRequest: The client submits information about their current set
+//       of keys to the CryptAuth server. The client can update their
+//       client-specific or key-specific metadata at this time as well.
+//   (2) SyncKeysResponse: The CryptAuth server responds with instructions such
+//       as what existing keys to (de)active, what new keys to create, and the
+//       time of the next check-in.
+//   (3) EnrollKeysRequest: If new keys were requested in the SyncKeysResponse,
+//       the client sends the new key information to CryptAuth in this request.
+//   (4) EnrollKeysResponse: If a certificate was generated, it will be provided
+//       here; otherwise, this can simply be a signal of a successful
+//       enrollment.
+syntax = "proto3";
+
+package cryptauthv2;
+
+option optimize_for = LITE_RUNTIME;
+
+import "cryptauth_common.proto";
+import "cryptauth_directive.proto";
+
+// The first request in the enrollment protocol. The request contains the
+// information including the keys currently held by the client, the latest
+// policies received from the server, and the metadata associated with the
+// client and keys.
+message SyncKeysRequest {
+  // The unique name of the application.
+  string application_name = 1;
+  // The version of the CryptAuth client library.
+  string client_version = 2;
+
+  // The request to enroll a key or update the info related to a key.
+  message SyncSingleKeyRequest {
+    // The purpose/application of the key.
+    string key_name = 1;
+    // Identifiers of keys currently held by the client.
+    repeated bytes key_handles = 2;
+
+    // The policy_reference received in the last KeyDirective.
+    PolicyReference policy_reference = 3;
+    // Key-specific metadata.
+    KeyMetadata key_metadata = 4;
+    // A key-specific opaque blob provided by the application.
+    bytes key_app_metadata = 5;
+  }
+  // Per key sync data.
+  repeated SyncSingleKeyRequest sync_single_key_requests = 3;
+
+  // The policy_reference received in the last ClientDirective.
+  PolicyReference policy_reference = 4;
+  // Client-specific metadata.
+  ClientMetadata client_metadata = 5;
+  // A client-specific opaque blob provided by the application.
+  bytes client_app_metadata = 6;
+}
+
+// The response to SyncKeysRequest. The response instructs how the client should
+// manage existing keys and whether to create a new key.
+message SyncKeysResponse {
+  // The session indentifer generated by the server, which must be
+  // cryptographically random.
+  bytes random_session_id = 1;
+  // The ephemeral DH public key generated by the server.
+  bytes server_ephemeral_dh = 2;
+
+  // The response corresponding to the SyncSingleKeyRequest message.
+  message SyncSingleKeyResponse {
+    // The actions corresponding to the key handles in SyncKeysRequest.
+    enum KeyAction {
+      // Default value. A client receiving this should treat it as a noop.
+      // (-- But, be wary of b/119886258. --)
+      KEY_ACTION_UNSPECIFIED = 0;
+
+      // Keep the key and make it the "active" key.
+      ACTIVATE = 1;
+      // Keep the key. When enrollment is complete, ensure the key is not
+      // "active".
+      // (-- But, be wary of b/119886258 and a noop on iOS. --)
+      DEACTIVATE = 2;
+      // Delete the key.
+      DELETE = 3;
+    }
+    // Key actions with one entry per key handle and in the same order as in the
+    // request.
+    repeated KeyAction key_actions = 1;
+
+    // The instruction for the client to create a new key.
+    enum KeyCreation {
+      // Do not create a new key.
+      NONE = 0;
+      // Create a new key, and then use it as the "active" key.
+      ACTIVE = 1;
+      // Create a new key, but do not use it as the "active" key.
+      // (-- Beware of b/119889101. This doesn't work on Android or iOS. --)
+      INACTIVE = 2;
+    }
+    // Instruction for key creation.
+    KeyCreation key_creation = 2;
+
+    // The type of the cryptographic key.
+    KeyType key_type = 3;
+    // The updated key-specific directives.
+    KeyDirective key_directive = 4;
+    // A key-specific opaque blob given to the application.
+    bytes key_app_directive = 5;
+
+    // The storage level where the key is created and stored.
+    enum KeyStorageLevel {
+      // Default value. The client is free to decide where to create the key.
+      KEY_STORAGE_LEVEL_UNSPECIFIED = 0;
+
+      // The key should be created and stored in software store. E.g. the
+      // client may create a key using a crypto library and store it in a
+      // file.
+      SOFTWARE = 1;
+
+      // The key should be created in a Trusted Execution Environment (TEE).
+      // E.g., TrustZone from ARM chips.
+      TRUSTED_EXECUTION_ENVIRONMENT = 2;
+
+      // The key should be created in a dedicated hardware that is separate from
+      // the main processor. E.g., StrongBox chips in Android devices and Secure
+      // Enclave in iOS devices.
+      DEDICATED_SECURE_ELEMENT = 3;
+    };
+    // The storage level to create the key.
+    KeyStorageLevel key_storage_level = 6;
+    // The newly created key should require hardware backed user presence when
+    // using the key.
+    bool hardware_user_presence_required = 7;
+    // The newly created key should require user verification when using the
+    // key.
+    bool user_verification_required = 8;
+  }
+  // Per key sync response.
+  repeated SyncSingleKeyResponse sync_single_key_responses = 3;
+
+  // The updated client-specific directives.
+  ClientDirective client_directive = 4;
+  // A client-specific opaque blob given to the application.
+  bytes client_app_directive = 5;
+
+  // The state of the server.
+  enum ServerStatus {
+    // The server is fine; the rest of SyncKeysResponse should be processed.
+    SERVER_OK = 0;
+    // The server is overloaded; client_directive should be followed.
+    SERVER_OVERLOADED = 1;
+  }
+  // The status of the server.
+  ServerStatus server_status = 6;
+}
+
+// The second request in the enrollment protocol. The second request is
+// necessary if the client wants to enroll a new key. The request contains the
+// information such as the material of the new key, and necessary proofs for
+// verifying the key.
+message EnrollKeysRequest {
+  // The session identifier copied from the SyncKeysResponse message.
+  bytes random_session_id = 1;
+  // The ephemeral DH public key generated by the client.
+  bytes client_ephemeral_dh = 2;
+
+  // The request to enroll a key, e.g., create a new key or rotate an old one.
+  message EnrollSingleKeyRequest {
+    // The key_name copied from SyncKeysRequest.
+    string key_name = 1;
+    // The identifier of the new key.
+    bytes new_key_handle = 2;
+    // The raw bytes of the new public key or custom data.
+    bytes key_material = 3;
+    // The public-key signature or MAC tag that shows the client indeed
+    // possesses the private or secret key.
+    bytes key_proof = 4;
+
+    // Cross-signatures or MAC tags by other keys.
+    message KeyCrossproof {
+      // The key_name of the cross-signing key.
+      string other_key_name = 1;
+      // The computed cross-signatures or MAC tags.
+      bytes other_key_proof = 2;
+    }
+    // Cross proofs.
+    repeated KeyCrossproof key_crossproofs = 5;
+
+    // Subject to certify.
+    repeated CertificateRequest certificate_requests = 6;
+
+    // Attestation of the key.
+    message KeyAttestation {
+      // The type of the key attestation.
+      enum KeyAttestationType {
+        // Default value.
+        KEY_ATTESTATION_TYPE_UNSPECIFIED = 0;
+
+        // Attestation generated by Android KeyStore API.
+        // See
+        // https://developer.android.com/training/articles/security-key-attestation
+        // The payload should be the concatenation of the X.509
+        // certificates returned by KeyStore attestation API encoded in ASN.1
+        // DER.
+        ANDROID_KEYSTORE_ATTESTATION = 1;
+      }
+      // The attestation type.
+      KeyAttestationType type = 1;
+
+      // The payload of the key attestation. The content of the payload is
+      // dependent on the attestation type.
+      bytes payload = 2;
+    }
+    // The attestation of the key if the key supports one.
+    KeyAttestation key_attestation = 7;
+  }
+  // Per key enroll data.
+  repeated EnrollSingleKeyRequest enroll_single_key_requests = 3;
+}
+
+// The response to EnrollKeysRequest. The response can contain a public-key
+// certificate for the client to perform offline authentications.
+message EnrollKeysResponse {
+  // The response corresponding to the EnrollSingleKeyRequest message.
+  message EnrollSingleKeyResponse {
+    // The server may produce a certificate and send it to the client.
+    repeated Certificate certificate = 1;
+  }
+  // Per key enroll response.
+  repeated EnrollSingleKeyResponse enroll_single_key_responses = 1;
+}
+
+// Subject to certify.
+message CertificateRequest {
+  // The type of subject to certify.
+  enum CommonNameType {
+    // Reserved.
+    UNKNOWN_COMMON_NAME_TYPE = 0;
+    // Indicates a phone number needs to be signed.
+    PHONE_NUMBER = 1;
+  }
+  // Type of content to be signed.
+  CommonNameType type = 1;
+  // Raw data of the content.
+  bytes data = 2;
+  // Bytes used to verify the validation of data.
+  bytes token = 3;
+  // Additional data used to help verify data. (e.g. audience)
+  bytes additional_data = 4;
+}
+
+// The key-specific metadata contained in SyncKeysRequest.
+message KeyMetadata {}
+
+// This generic rpc is used by MagicShare, BetterTogether and possibly other
+// features in the future to obtain enrollment information from the server.
+// This method’s behavior shall be based on the key_name which is supplied.
+// The client and server shall set and expect specific data in
+// request_key_metadata and response_key_metadata, based on the application_name
+// and key_name.
+message GetMetadataRequest {
+  // The unique name of the application
+  string application_name = 1;
+  // The version of the CryptAuth client library
+  string client_version = 2;
+
+  // The request to get key metadata related to a key name.
+  message GetSingleKeyMetadataRequest {
+    // The purpose/application of the key.
+    string key_name = 1;
+    // key specific metadata
+    bytes request_key_metadata = 2;
+  }
+  // Per key request
+  repeated GetSingleKeyMetadataRequest get_single_key_metadata_request = 3;
+
+  // InvocationReason, retry count, etc. (same as SyncKeys).
+  ClientMetadata client_metadata = 4;
+
+  // A client-specific opaque blob provided by the application.
+  bytes app_metadata = 5;
+}
+
+// The response to GetMetadataRequest. The response contains key metadata based
+// on the application name_and key_name in GetMetadataRequest.
+message GetMetadataResponse {
+  // The response of GetKeyMetadataRequest.
+  message GetSingleKeyMetadataResponse {
+    // Key specific response metadtata.
+    bytes response_key_metadata = 1;
+  }
+
+  // A response for every key_metadata_request above.
+  repeated GetSingleKeyMetadataResponse get_single_skey_metadata_response = 1;
+}
diff --git a/components/about_ui/resources/about_credits.js b/components/about_ui/resources/about_credits.js
index 6d7ffc5d..b350e56 100644
--- a/components/about_ui/resources/about_credits.js
+++ b/components/about_ui/resources/about_credits.js
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 /* eslint-disable no-restricted-properties */
-function $(id) { return document.getElementById(id); }
+function $(id) {
+  return document.getElementById(id);
+}
 /* eslint-enable no-restricted-properties */
 
 document.addEventListener('DOMContentLoaded', function() {
@@ -21,7 +23,8 @@
 
   document.addEventListener('keypress', function(e) {
     // Make the license show/hide toggle when the Enter is pressed.
-    if (e.keyCode == 0x0d && e.target.tagName == 'LABEL')
+    if (e.keyCode == 0x0d && e.target.tagName == 'LABEL') {
       e.target.previousElementSibling.click();
+    }
   });
 });
diff --git a/components/autofill/core/browser/autofill_metrics.cc b/components/autofill/core/browser/autofill_metrics.cc
index f865dcc..a914a8f 100644
--- a/components/autofill/core/browser/autofill_metrics.cc
+++ b/components/autofill/core/browser/autofill_metrics.cc
@@ -850,13 +850,15 @@
 // static
 void AutofillMetrics::LogLocalCardMigrationDialogUserInteractionMetric(
     const base::TimeDelta& duration,
-    const int selected,
-    const int total,
     LocalCardMigrationDialogUserInteractionMetric metric) {
   DCHECK_LT(metric, NUM_LOCAL_CARD_MIGRATION_DIALOG_USER_INTERACTION_METRICS);
   base::UmaHistogramEnumeration(
       "Autofill.LocalCardMigrationDialogUserInteraction", metric,
       NUM_LOCAL_CARD_MIGRATION_DIALOG_USER_INTERACTION_METRICS);
+
+  // Do not log duration metrics for
+  // LOCAL_CARD_MIGRATION_DIALOG_DELETE_CARD_ICON_CLICKED, as it can happen
+  // multiple times in one dialog.
   std::string suffix;
   switch (metric) {
     case LOCAL_CARD_MIGRATION_DIALOG_CLOSED_SAVE_BUTTON_CLICKED:
@@ -865,13 +867,22 @@
     case LOCAL_CARD_MIGRATION_DIALOG_CLOSED_CANCEL_BUTTON_CLICKED:
       suffix = "Denied";
       break;
+    case LOCAL_CARD_MIGRATION_DIALOG_CLOSED_VIEW_CARDS_BUTTON_CLICKED:
+    case LOCAL_CARD_MIGRATION_DIALOG_CLOSED_DONE_BUTTON_CLICKED:
+      suffix = "Closed";
+      break;
     default:
       return;
   }
-  base::UmaHistogramLongTimes("Autofill.LocalCardMigrationDialogActiveDuration",
-                              duration);
+
   base::UmaHistogramLongTimes(
       "Autofill.LocalCardMigrationDialogActiveDuration." + suffix, duration);
+}
+
+// static
+void AutofillMetrics::LogLocalCardMigrationDialogUserSelectionPercentageMetric(
+    int selected,
+    int total) {
   UMA_HISTOGRAM_PERCENTAGE(
       "Autofill.LocalCardMigrationDialogUserSelectionPercentage",
       100 * selected / total);
diff --git a/components/autofill/core/browser/autofill_metrics.h b/components/autofill/core/browser/autofill_metrics.h
index 66ba7b2..9d151e1 100644
--- a/components/autofill/core/browser/autofill_metrics.h
+++ b/components/autofill/core/browser/autofill_metrics.h
@@ -498,6 +498,10 @@
     LOCAL_CARD_MIGRATION_DIALOG_SHOWN = 0,
     // The dialog is not shown due to legal message being invalid.
     LOCAL_CARD_MIGRATION_DIALOG_NOT_SHOWN_INVALID_LEGAL_MESSAGE = 1,
+    // The dialog is shown when migration feedback is available.
+    LOCAL_CARD_MIGRATION_DIALOG_FEEDBACK_SHOWN = 2,
+    // The dialog is shown when migration fails due to server error.
+    LOCAL_CARD_MIGRATION_DIALOG_FEEDBACK_SERVER_ERROR_SHOWN = 3,
     NUM_LOCAL_CARD_MIGRATION_DIALOG_OFFER_METRICS,
   };
 
@@ -509,6 +513,12 @@
     LOCAL_CARD_MIGRATION_DIALOG_CLOSED_CANCEL_BUTTON_CLICKED = 1,
     // The user clicks the legal message.
     LOCAL_CARD_MIGRATION_DIALOG_LEGAL_MESSAGE_CLICKED = 2,
+    // The user clicks the view card button after successfully migrated cards.
+    LOCAL_CARD_MIGRATION_DIALOG_CLOSED_VIEW_CARDS_BUTTON_CLICKED = 3,
+    // The user clicks the done button to close dialog after migration.
+    LOCAL_CARD_MIGRATION_DIALOG_CLOSED_DONE_BUTTON_CLICKED = 4,
+    // The user clicks the trash icon to delete invalid card.
+    LOCAL_CARD_MIGRATION_DIALOG_DELETE_CARD_ICON_CLICKED = 5,
     NUM_LOCAL_CARD_MIGRATION_DIALOG_USER_INTERACTION_METRICS,
   };
 
@@ -997,9 +1007,10 @@
       LocalCardMigrationDialogOfferMetric metric);
   static void LogLocalCardMigrationDialogUserInteractionMetric(
       const base::TimeDelta& duration,
-      const int selected,
-      const int total,
       LocalCardMigrationDialogUserInteractionMetric metric);
+  static void LogLocalCardMigrationDialogUserSelectionPercentageMetric(
+      int selected,
+      int total);
   static void LogLocalCardMigrationPromptMetric(
       LocalCardMigrationOrigin local_card_migration_origin,
       LocalCardMigrationPromptMetric metric);
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java
index d1ca29e..95b34c0 100644
--- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java
+++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUma.java
@@ -35,9 +35,8 @@
     static final int BACKGROUND_TASK_COMPONENT_UPDATE = 15;
     static final int BACKGROUND_TASK_DEPRECATED_EXPLORE_SITES_REFRESH = 16;
     static final int BACKGROUND_TASK_EXPLORE_SITES_REFRESH = 17;
-    static final int BACKGROUND_TASK_DOWNLOAD_AUTO_RESUMPTION = 18;
     // Keep this one at the end and increment appropriately when adding new tasks.
-    static final int BACKGROUND_TASK_COUNT = 19;
+    static final int BACKGROUND_TASK_COUNT = 18;
 
     static final String KEY_CACHED_UMA = "bts_cached_uma";
 
@@ -244,8 +243,6 @@
                 return BACKGROUND_TASK_DOWNLOAD_SERVICE;
             case TaskIds.DOWNLOAD_CLEANUP_JOB_ID:
                 return BACKGROUND_TASK_DOWNLOAD_CLEANUP;
-            case TaskIds.DOWNLOAD_AUTO_RESUMPTION_JOB_ID:
-                return BACKGROUND_TASK_DOWNLOAD_AUTO_RESUMPTION;
             case TaskIds.WEBVIEW_VARIATIONS_SEED_FETCH_JOB_ID:
                 return BACKGROUND_TASK_WEBVIEW_VARIATIONS;
             case TaskIds.OFFLINE_PAGES_PREFETCH_NOTIFICATION_JOB_ID:
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java
index 959377ad..a885f1c 100644
--- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java
+++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java
@@ -26,7 +26,6 @@
     public static final int WEBVIEW_VARIATIONS_SEED_FETCH_JOB_ID = 83;
     public static final int WEBAPK_UPDATE_JOB_ID = 91;
     public static final int DOWNLOAD_RESUMPTION_JOB_ID = 55;
-    public static final int DOWNLOAD_AUTO_RESUMPTION_JOB_ID = 56;
     public static final int FEED_REFRESH_JOB_ID = 22;
     public static final int COMPONENT_UPDATE_JOB_ID = 2;
     public static final int DEPRECATED_EXPLORE_SITES_REFRESH_JOB_ID = 100;
diff --git a/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java b/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java
index 905c63d..8a9dc167 100644
--- a/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java
+++ b/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java
@@ -71,9 +71,6 @@
         assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_DOWNLOAD_CLEANUP,
                 BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId(
                         TaskIds.DOWNLOAD_CLEANUP_JOB_ID));
-        assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_DOWNLOAD_AUTO_RESUMPTION,
-                BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId(
-                        TaskIds.DOWNLOAD_AUTO_RESUMPTION_JOB_ID));
         assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_WEBVIEW_VARIATIONS,
                 BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId(
                         TaskIds.WEBVIEW_VARIATIONS_SEED_FETCH_JOB_ID));
@@ -96,7 +93,7 @@
         assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_DEPRECATED_EXPLORE_SITES_REFRESH,
                 BackgroundTaskSchedulerUma.toUmaEnumValueFromTaskId(
                         TaskIds.DEPRECATED_EXPLORE_SITES_REFRESH_JOB_ID));
-        assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_COUNT, 19);
+        assertEquals(BackgroundTaskSchedulerUma.BACKGROUND_TASK_COUNT, 18);
     }
 
     @Test
diff --git a/components/discardable_memory/common/discardable_shared_memory_heap.h b/components/discardable_memory/common/discardable_shared_memory_heap.h
index 8c9ff8d..bdff97e 100644
--- a/components/discardable_memory/common/discardable_shared_memory_heap.h
+++ b/components/discardable_memory/common/discardable_shared_memory_heap.h
@@ -9,6 +9,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <unordered_map>
 #include <vector>
 
 #include "base/callback.h"
@@ -163,7 +164,7 @@
   std::vector<std::unique_ptr<ScopedMemorySegment>> memory_segments_;
 
   // Mapping from first/last block of span to Span instance.
-  typedef base::hash_map<size_t, Span*> SpanMap;
+  typedef std::unordered_map<size_t, Span*> SpanMap;
   SpanMap spans_;
 
   // Array of linked-lists with free discardable memory regions. For i < 256,
diff --git a/components/discardable_memory/service/discardable_shared_memory_manager.h b/components/discardable_memory/service/discardable_shared_memory_manager.h
index c3b1d06..ebc8e670 100644
--- a/components/discardable_memory/service/discardable_shared_memory_manager.h
+++ b/components/discardable_memory/service/discardable_shared_memory_manager.h
@@ -9,6 +9,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <unordered_map>
 #include <vector>
 
 #include "base/callback.h"
@@ -141,8 +142,8 @@
 
   base::Lock lock_;
   using MemorySegmentMap =
-      base::hash_map<int32_t, scoped_refptr<MemorySegment>>;
-  using ClientMap = base::hash_map<int, MemorySegmentMap>;
+      std::unordered_map<int32_t, scoped_refptr<MemorySegment>>;
+  using ClientMap = std::unordered_map<int, MemorySegmentMap>;
   ClientMap clients_;
   // Note: The elements in |segments_| are arranged in such a way that they form
   // a heap. The LRU memory segment always first.
diff --git a/components/dom_distiller/core/distilled_content_store.h b/components/dom_distiller/core/distilled_content_store.h
index bbee232..ed8272d7 100644
--- a/components/dom_distiller/core/distilled_content_store.h
+++ b/components/dom_distiller/core/distilled_content_store.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
 
 #include "base/bind.h"
 #include "base/containers/hash_tables.h"
@@ -81,7 +82,7 @@
                          std::unique_ptr<DistilledArticleProto, CacheDeletor>>
 
       ContentMap;
-  typedef base::hash_map<std::string, std::string> UrlMap;
+  typedef std::unordered_map<std::string, std::string> UrlMap;
 
   ContentMap cache_;
   UrlMap url_to_id_;
diff --git a/components/dom_distiller/core/distiller.h b/components/dom_distiller/core/distiller.h
index 77465ab..7801c01 100644
--- a/components/dom_distiller/core/distiller.h
+++ b/components/dom_distiller/core/distiller.h
@@ -10,6 +10,7 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/callback.h"
@@ -172,7 +173,7 @@
   // Maps page numbers of pages under distillation to the indices in |pages_|.
   // If a page is |started_pages_| that means it is still waiting for an action
   // (distillation or image fetch) to finish.
-  base::hash_map<int, size_t> started_pages_index_;
+  std::unordered_map<int, size_t> started_pages_index_;
 
   // The list of pages that are still waiting for distillation to start.
   // This is a map, to make distiller prefer distilling lower page numbers
diff --git a/components/dom_distiller/core/dom_distiller_model.h b/components/dom_distiller/core/dom_distiller_model.h
index cff3820c..6c357bb 100644
--- a/components/dom_distiller/core/dom_distiller_model.h
+++ b/components/dom_distiller/core/dom_distiller_model.h
@@ -9,6 +9,7 @@
 #include <stdint.h>
 
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -62,8 +63,8 @@
 
  private:
   typedef int32_t KeyType;
-  typedef base::hash_map<KeyType, ArticleEntry> EntryMap;
-  typedef base::hash_map<std::string, KeyType> StringToKeyMap;
+  typedef std::unordered_map<KeyType, ArticleEntry> EntryMap;
+  typedef std::unordered_map<std::string, KeyType> StringToKeyMap;
 
   void AddEntry(const ArticleEntry& entry);
   void RemoveEntry(const ArticleEntry& entry);
diff --git a/components/dom_distiller/standalone/content_extractor_browsertest.cc b/components/dom_distiller/standalone/content_extractor_browsertest.cc
index 999d6e4..aa71a04b 100644
--- a/components/dom_distiller/standalone/content_extractor_browsertest.cc
+++ b/components/dom_distiller/standalone/content_extractor_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include <stddef.h>
 #include <sstream>
+#include <unordered_map>
 #include <utility>
 
 #include "base/command_line.h"
@@ -48,8 +49,7 @@
 
 namespace {
 
-typedef base::hash_map<std::string, std::string> FileToUrlMap;
-
+typedef std::unordered_map<std::string, std::string> FileToUrlMap;
 }
 
 // Factory for creating a Distiller that creates different DomDistillerOptions
diff --git a/components/download/internal/background_service/controller_impl.cc b/components/download/internal/background_service/controller_impl.cc
index eae2dc6..717d390 100644
--- a/components/download/internal/background_service/controller_impl.cc
+++ b/components/download/internal/background_service/controller_impl.cc
@@ -401,8 +401,6 @@
     case DownloadTaskType::CLEANUP_TASK:
       ScheduleCleanupTask();
       break;
-    case DownloadTaskType::DOWNLOAD_AUTO_RESUMPTION_TASK:
-      NOTREACHED();
   }
 }
 
diff --git a/components/download/internal/background_service/stats.cc b/components/download/internal/background_service/stats.cc
index 88aebf4a..ef8cf015 100644
--- a/components/download/internal/background_service/stats.cc
+++ b/components/download/internal/background_service/stats.cc
@@ -48,8 +48,6 @@
       return "DownloadTask";
     case DownloadTaskType::CLEANUP_TASK:
       return "CleanUpTask";
-    case DownloadTaskType::DOWNLOAD_AUTO_RESUMPTION_TASK:
-      return "DownloadAutoResumptionTask";
   }
   NOTREACHED();
   return std::string();
diff --git a/components/download/internal/common/download_item_impl.cc b/components/download/internal/common/download_item_impl.cc
index 4b536f5..a39e308 100644
--- a/components/download/internal/common/download_item_impl.cc
+++ b/components/download/internal/common/download_item_impl.cc
@@ -588,12 +588,10 @@
 }
 
 void DownloadItemImpl::UpdateResumptionInfo(bool user_resume) {
-  if (user_resume) {
+  if (user_resume)
     allow_metered_ |= delegate_->IsActiveNetworkMetered();
-    bytes_wasted_ = 0;
-  }
 
-  auto_resume_count_ = user_resume ? 0 : ++auto_resume_count_;
+  auto_resume_count_ = user_resume ? 0 : auto_resume_count_++;
 }
 
 void DownloadItemImpl::Cancel(bool user_cancel) {
diff --git a/components/download/internal/common/download_item_impl_delegate.cc b/components/download/internal/common/download_item_impl_delegate.cc
index cc3fb6af5d..973248a 100644
--- a/components/download/internal/common/download_item_impl_delegate.cc
+++ b/components/download/internal/common/download_item_impl_delegate.cc
@@ -5,9 +5,7 @@
 #include "components/download/public/common/download_item_impl_delegate.h"
 
 #include "base/logging.h"
-#include "build/build_config.h"
 #include "components/download/database/in_progress/download_entry.h"
-#include "components/download/public/common/auto_resumption_handler.h"
 #include "components/download/public/common/download_danger_type.h"
 #include "components/download/public/common/download_item_impl.h"
 
@@ -96,9 +94,7 @@
 }
 
 bool DownloadItemImplDelegate::IsActiveNetworkMetered() const {
-  return download::AutoResumptionHandler::Get()
-             ? download::AutoResumptionHandler::Get()->IsActiveNetworkMetered()
-             : false;
+  return false;
 }
 
 void DownloadItemImplDelegate::ReportBytesWasted(DownloadItemImpl* download) {}
diff --git a/components/download/internal/common/in_progress_download_manager.cc b/components/download/internal/common/in_progress_download_manager.cc
index abecb58..276d856 100644
--- a/components/download/internal/common/in_progress_download_manager.cc
+++ b/components/download/internal/common/in_progress_download_manager.cc
@@ -409,12 +409,6 @@
   on_initialized_callbacks_.clear();
 }
 
-void InProgressDownloadManager::GetAllDownloads(
-    std::vector<download::DownloadItem*>* downloads) const {
-  for (auto& item : in_progress_downloads_)
-    downloads->push_back(item.get());
-}
-
 DownloadItemImpl* InProgressDownloadManager::GetInProgressDownload(
     const std::string& guid) {
   for (auto& item : in_progress_downloads_) {
diff --git a/components/download/public/background_service/download_task_types.h b/components/download/public/background_service/download_task_types.h
index 5140f520..48fcfb35 100644
--- a/components/download/public/background_service/download_task_types.h
+++ b/components/download/public/background_service/download_task_types.h
@@ -15,9 +15,6 @@
 
   // Task to remove unnecessary files from the system.
   CLEANUP_TASK = 1,
-
-  // Task to invoke the download auto-resumption handler.
-  DOWNLOAD_AUTO_RESUMPTION_TASK = 2,
 };
 
 }  // namespace download
diff --git a/components/download/public/common/BUILD.gn b/components/download/public/common/BUILD.gn
index e14fe35..8de5574 100644
--- a/components/download/public/common/BUILD.gn
+++ b/components/download/public/common/BUILD.gn
@@ -15,8 +15,6 @@
 
 component("public") {
   sources = [
-    "auto_resumption_handler.cc",
-    "auto_resumption_handler.h",
     "base_file.h",
     "download_content.h",
     "download_create_info.h",
@@ -67,9 +65,6 @@
 
   public_deps = [
     ":interfaces",
-    "//components/download/network",
-    "//components/download/public/background_service:public",
-    "//services/network/public/cpp",
   ]
 
   deps = [
diff --git a/components/download/public/common/auto_resumption_handler.cc b/components/download/public/common/auto_resumption_handler.cc
deleted file mode 100644
index 3e6dfa5..0000000
--- a/components/download/public/common/auto_resumption_handler.cc
+++ /dev/null
@@ -1,295 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/download/public/common/auto_resumption_handler.h"
-
-#include <vector>
-
-#include "base/feature_list.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "components/download/public/background_service/task_scheduler.h"
-#include "url/gurl.h"
-
-namespace {
-
-static download::AutoResumptionHandler* g_auto_resumption_handler = nullptr;
-
-// The delay to wait for after a chrome restart before resuming all pending
-// downloads so that tab loading doesn't get impacted.
-const base::TimeDelta kAutoResumeStartupDelay =
-    base::TimeDelta::FromSeconds(10);
-
-// The interval at which various download updates are grouped together for
-// computing the params for the task scheduler.
-const base::TimeDelta kBatchDownloadUpdatesInterval =
-    base::TimeDelta::FromSeconds(1);
-
-// The delay to wait for before immediately retrying a download after it got
-// interrupted due to network reasons.
-const base::TimeDelta kDownloadImmediateRetryDelay =
-    base::TimeDelta::FromSeconds(1);
-
-// The task type to use for scheduling a task.
-const download::DownloadTaskType kResumptionTaskType =
-    download::DownloadTaskType::DOWNLOAD_AUTO_RESUMPTION_TASK;
-
-// The window start time after which the system should fire the task.
-const int64_t kWindowStartTimeSeconds = 0;
-
-// The window end time before which the system should fire the task.
-const int64_t kWindowEndTimeSeconds = 24 * 60 * 60;
-
-bool IsMetered(network::mojom::ConnectionType type) {
-  switch (type) {
-    case network::mojom::ConnectionType::CONNECTION_2G:
-    case network::mojom::ConnectionType::CONNECTION_3G:
-    case network::mojom::ConnectionType::CONNECTION_4G:
-      return true;
-    case network::mojom::ConnectionType::CONNECTION_ETHERNET:
-    case network::mojom::ConnectionType::CONNECTION_WIFI:
-    case network::mojom::ConnectionType::CONNECTION_UNKNOWN:
-    case network::mojom::ConnectionType::CONNECTION_NONE:
-    case network::mojom::ConnectionType::CONNECTION_BLUETOOTH:
-      return false;
-  }
-  NOTREACHED();
-  return false;
-}
-
-bool IsConnected(network::mojom::ConnectionType type) {
-  switch (type) {
-    case network::mojom::ConnectionType::CONNECTION_UNKNOWN:
-    case network::mojom::ConnectionType::CONNECTION_NONE:
-    case network::mojom::ConnectionType::CONNECTION_BLUETOOTH:
-      return false;
-    default:
-      return true;
-  }
-}
-
-bool IsInterruptedDownloadAutoResumable(download::DownloadItem* download_item,
-                                        int auto_resumption_size_limit) {
-  if (!download_item->GetURL().SchemeIsHTTPOrHTTPS())
-    return false;
-
-  if (download_item->GetBytesWasted() > auto_resumption_size_limit)
-    return false;
-
-  int interrupt_reason = download_item->GetLastReason();
-  DCHECK_NE(interrupt_reason, download::DOWNLOAD_INTERRUPT_REASON_NONE);
-  return interrupt_reason ==
-             download::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT ||
-         interrupt_reason ==
-             download::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED ||
-         interrupt_reason ==
-             download::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED ||
-         interrupt_reason == download::DOWNLOAD_INTERRUPT_REASON_CRASH;
-}
-
-}  // namespace
-
-namespace download {
-
-AutoResumptionHandler::Config::Config() : auto_resumption_size_limit(0) {}
-
-// static
-void AutoResumptionHandler::Create(
-    std::unique_ptr<download::NetworkStatusListener> network_listener,
-    std::unique_ptr<download::TaskManager> task_manager,
-    std::unique_ptr<Config> config) {
-  DCHECK(!g_auto_resumption_handler);
-  g_auto_resumption_handler = new AutoResumptionHandler(
-      std::move(network_listener), std::move(task_manager), std::move(config));
-}
-
-// static
-AutoResumptionHandler* AutoResumptionHandler::Get() {
-  return g_auto_resumption_handler;
-}
-
-AutoResumptionHandler::AutoResumptionHandler(
-    std::unique_ptr<download::NetworkStatusListener> network_listener,
-    std::unique_ptr<download::TaskManager> task_manager,
-    std::unique_ptr<Config> config)
-    : network_listener_(std::move(network_listener)),
-      task_manager_(std::move(task_manager)),
-      config_(std::move(config)),
-      weak_factory_(this) {
-  network_listener_->Start(this);
-}
-
-AutoResumptionHandler::~AutoResumptionHandler() {
-  network_listener_->Stop();
-}
-
-void AutoResumptionHandler::SetResumableDownloads(
-    const std::vector<download::DownloadItem*>& downloads) {
-  resumable_downloads_.clear();
-  for (auto* download : downloads) {
-    if (!IsAutoResumableDownload(download))
-      continue;
-    resumable_downloads_.insert(std::make_pair(download->GetGuid(), download));
-    download->RemoveObserver(this);
-    download->AddObserver(this);
-  }
-
-  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&AutoResumptionHandler::ResumePendingDownloads,
-                     weak_factory_.GetWeakPtr()),
-      kAutoResumeStartupDelay);
-}
-
-bool AutoResumptionHandler::IsActiveNetworkMetered() const {
-  return IsMetered(network_listener_->GetConnectionType());
-}
-
-void AutoResumptionHandler::OnNetworkChanged(
-    network::mojom::ConnectionType type) {
-  if (!IsConnected(type))
-    return;
-
-  ResumePendingDownloads();
-}
-
-void AutoResumptionHandler::OnDownloadStarted(download::DownloadItem* item) {
-  item->RemoveObserver(this);
-  item->AddObserver(this);
-
-  OnDownloadUpdated(item);
-}
-
-void AutoResumptionHandler::OnDownloadUpdated(download::DownloadItem* item) {
-  if (IsAutoResumableDownload(item))
-    resumable_downloads_[item->GetGuid()] = item;
-  else
-    resumable_downloads_.erase(item->GetGuid());
-
-  if (item->GetState() == download::DownloadItem::INTERRUPTED &&
-      IsAutoResumableDownload(item) && SatisfiesNetworkRequirements(item)) {
-    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE,
-        base::BindOnce(&AutoResumptionHandler::ResumeDownload,
-                       weak_factory_.GetWeakPtr(), item),
-        kDownloadImmediateRetryDelay);
-    return;
-  }
-  RecomputeTaskParams();
-}
-
-void AutoResumptionHandler::OnDownloadRemoved(download::DownloadItem* item) {
-  resumable_downloads_.erase(item->GetGuid());
-  RecomputeTaskParams();
-}
-
-void AutoResumptionHandler::ResumeDownload(download::DownloadItem* download) {
-  if (SatisfiesNetworkRequirements(download))
-    download->Resume(false);
-  else
-    RecomputeTaskParams();
-}
-
-void AutoResumptionHandler::OnStartScheduledTask(
-    download::TaskFinishedCallback callback) {
-  task_manager_->OnStartScheduledTask(kResumptionTaskType, std::move(callback));
-  ResumePendingDownloads();
-}
-
-bool AutoResumptionHandler::OnStopScheduledTask() {
-  task_manager_->OnStopScheduledTask(kResumptionTaskType);
-  RescheduleTaskIfNecessary();
-  return false;
-}
-
-void AutoResumptionHandler::RecomputeTaskParams() {
-  if (recompute_task_params_scheduled_)
-    return;
-
-  recompute_task_params_scheduled_ = true;
-  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&AutoResumptionHandler::RescheduleTaskIfNecessary,
-                     weak_factory_.GetWeakPtr()),
-      kBatchDownloadUpdatesInterval);
-}
-
-void AutoResumptionHandler::RescheduleTaskIfNecessary() {
-  recompute_task_params_scheduled_ = false;
-
-  bool has_resumable_downloads = false;
-  bool has_actionable_downloads = false;
-  bool can_download_on_metered = false;
-  for (auto iter = resumable_downloads_.begin();
-       iter != resumable_downloads_.end(); ++iter) {
-    download::DownloadItem* download = iter->second;
-    if (!IsAutoResumableDownload(download))
-      continue;
-
-    has_resumable_downloads = true;
-    has_actionable_downloads |= SatisfiesNetworkRequirements(download);
-    can_download_on_metered |= download->AllowMetered();
-    if (can_download_on_metered)
-      break;
-  }
-
-  if (!has_actionable_downloads)
-    task_manager_->NotifyTaskFinished(kResumptionTaskType, false);
-
-  if (!has_resumable_downloads) {
-    task_manager_->UnscheduleTask(kResumptionTaskType);
-    return;
-  }
-
-  download::TaskManager::TaskParams task_params;
-  task_params.require_unmetered_network = !can_download_on_metered;
-  task_params.window_start_time_seconds = kWindowStartTimeSeconds;
-  task_params.window_end_time_seconds = kWindowEndTimeSeconds;
-  task_manager_->ScheduleTask(kResumptionTaskType, task_params);
-}
-
-void AutoResumptionHandler::ResumePendingDownloads() {
-  for (auto iter = resumable_downloads_.begin();
-       iter != resumable_downloads_.end(); ++iter) {
-    download::DownloadItem* download = iter->second;
-    if (!IsAutoResumableDownload(download))
-      continue;
-
-    if (SatisfiesNetworkRequirements(download))
-      download->Resume(false);
-  }
-}
-
-bool AutoResumptionHandler::SatisfiesNetworkRequirements(
-    download::DownloadItem* download) {
-  if (!IsConnected(network_listener_->GetConnectionType()))
-    return false;
-
-  return download->AllowMetered() || !IsActiveNetworkMetered();
-}
-
-bool AutoResumptionHandler::IsAutoResumableDownload(
-    download::DownloadItem* item) {
-  if (item->IsDangerous())
-    return false;
-
-  switch (item->GetState()) {
-    case download::DownloadItem::IN_PROGRESS:
-      return !item->IsPaused();
-    case download::DownloadItem::COMPLETE:
-    case download::DownloadItem::CANCELLED:
-      return false;
-    case download::DownloadItem::INTERRUPTED:
-      return !item->IsPaused() &&
-             IsInterruptedDownloadAutoResumable(
-                 item, config_->auto_resumption_size_limit);
-    case download::DownloadItem::MAX_DOWNLOAD_STATE:
-      NOTREACHED();
-  }
-
-  return false;
-}
-
-}  // namespace download
diff --git a/components/download/public/common/auto_resumption_handler.h b/components/download/public/common/auto_resumption_handler.h
deleted file mode 100644
index c0bb504..0000000
--- a/components/download/public/common/auto_resumption_handler.h
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_DOWNLOAD_PUBLIC_COMMON_AUTO_RESUMPTION_HANDLER_H_
-#define COMPONENTS_DOWNLOAD_PUBLIC_COMMON_AUTO_RESUMPTION_HANDLER_H_
-
-#include <stddef.h>
-
-#include <memory>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "components/download/network/network_status_listener.h"
-#include "components/download/public/background_service/task_manager.h"
-#include "components/download/public/common/download_export.h"
-#include "components/download/public/common/download_item.h"
-
-namespace download {
-
-// Handles auto-resumptions for downloads. Listens to network changes and
-// schecules task to resume downloads accordingly.
-class COMPONENTS_DOWNLOAD_EXPORT AutoResumptionHandler
-    : public download::NetworkStatusListener::Observer,
-      public download::DownloadItem::Observer {
- public:
-  struct COMPONENTS_DOWNLOAD_EXPORT Config {
-    Config();
-    ~Config() = default;
-
-    int auto_resumption_size_limit;
-  };
-
-  // Creates the singleton instance of AutoResumptionHandler.
-  static void Create(
-      std::unique_ptr<download::NetworkStatusListener> network_listener,
-      std::unique_ptr<download::TaskManager> task_manager,
-      std::unique_ptr<Config> config);
-
-  // Returns the singleton instance of the AutoResumptionHandler.
-  static AutoResumptionHandler* Get();
-
-  AutoResumptionHandler(
-      std::unique_ptr<download::NetworkStatusListener> network_listener,
-      std::unique_ptr<download::TaskManager> task_manager,
-      std::unique_ptr<Config> config);
-  ~AutoResumptionHandler() override;
-
-  void SetResumableDownloads(
-      const std::vector<download::DownloadItem*>& downloads);
-  bool IsActiveNetworkMetered() const;
-  void OnStartScheduledTask(download::TaskFinishedCallback callback);
-  bool OnStopScheduledTask();
-
-  void OnDownloadStarted(download::DownloadItem* item);
-
-  // DownloadItem::Observer overrides.
-  void OnDownloadUpdated(download::DownloadItem* item) override;
-  void OnDownloadRemoved(download::DownloadItem* item) override;
-
- private:
-  // NetworkStatusListener::Observer implementation.
-  void OnNetworkChanged(network::mojom::ConnectionType type) override;
-
-  void ResumePendingDownloads();
-  void RecomputeTaskParams();
-  void RescheduleTaskIfNecessary();
-  void ResumeDownload(download::DownloadItem* download);
-  bool SatisfiesNetworkRequirements(download::DownloadItem* download);
-  bool IsAutoResumableDownload(download::DownloadItem* item);
-
-  std::unique_ptr<download::NetworkStatusListener> network_listener_;
-
-  std::unique_ptr<download::TaskManager> task_manager_;
-
-  std::unique_ptr<Config> config_;
-
-  std::map<std::string, download::DownloadItem*> resumable_downloads_;
-
-  bool recompute_task_params_scheduled_ = false;
-
-  base::WeakPtrFactory<AutoResumptionHandler> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(AutoResumptionHandler);
-};
-
-}  // namespace download
-
-#endif  // COMPONENTS_DOWNLOAD_PUBLIC_COMMON_AUTO_RESUMPTION_HANDLER_H_
diff --git a/components/download/public/common/in_progress_download_manager.h b/components/download/public/common/in_progress_download_manager.h
index 563d97d..d76534f 100644
--- a/components/download/public/common/in_progress_download_manager.h
+++ b/components/download/public/common/in_progress_download_manager.h
@@ -122,9 +122,6 @@
   // Called to remove an in-progress download.
   void RemoveInProgressDownload(const std::string& guid);
 
-  // Called to get all in-progress downloads.
-  void GetAllDownloads(std::vector<download::DownloadItem*>* downloads) const;
-
   // Called to retrieve an in-progress download.
   DownloadItemImpl* GetInProgressDownload(const std::string& guid);
 
diff --git a/components/drive/resource_metadata_storage.cc b/components/drive/resource_metadata_storage.cc
index 6f8f3bf..f9050ab 100644
--- a/components/drive/resource_metadata_storage.cc
+++ b/components/drive/resource_metadata_storage.cc
@@ -8,6 +8,7 @@
 
 #include <map>
 #include <set>
+#include <unordered_map>
 #include <utility>
 
 #include "base/bind.h"
@@ -1124,7 +1125,7 @@
   }
 
   // First scan. Remember relationships between IDs.
-  typedef base::hash_map<std::string, std::string> KeyToIdMapping;
+  typedef std::unordered_map<std::string, std::string> KeyToIdMapping;
   KeyToIdMapping local_id_to_resource_id_map;
   KeyToIdMapping child_key_to_local_id_map;
   std::set<std::string> resource_entries;
diff --git a/components/flags_ui/resources/flags.js b/components/flags_ui/resources/flags.js
index 4508cfa..c6f320f 100644
--- a/components/flags_ui/resources/flags.js
+++ b/components/flags_ui/resources/flags.js
@@ -89,8 +89,9 @@
     var el = document.querySelector(window.location.hash);
     if (el && !el.classList.contains('referenced')) {
       // Unhighlight whatever's highlighted.
-      if (document.querySelector('.referenced'))
+      if (document.querySelector('.referenced')) {
         document.querySelector('.referenced').classList.remove('referenced');
+      }
       // Highlight the referenced element.
       el.classList.add('referenced');
 
@@ -172,15 +173,17 @@
   var bodyContainer = $('body-container');
   renderTemplate(experimentalFeaturesData);
 
-  if (experimentalFeaturesData.showBetaChannelPromotion)
+  if (experimentalFeaturesData.showBetaChannelPromotion) {
     $('channel-promo-beta').hidden = false;
-  else if (experimentalFeaturesData.showDevChannelPromotion)
+  } else if (experimentalFeaturesData.showDevChannelPromotion) {
     $('channel-promo-dev').hidden = false;
+  }
 
   bodyContainer.style.visibility = 'visible';
   var ownerWarningDiv = $('owner-warning');
-  if (ownerWarningDiv)
+  if (ownerWarningDiv) {
     ownerWarningDiv.hidden = !experimentalFeaturesData.showOwnerWarning;
+  }
 }
 
 /**
diff --git a/components/omnibox/browser/in_memory_url_index_types.h b/components/omnibox/browser/in_memory_url_index_types.h
index c8e933547..db8e31a4 100644
--- a/components/omnibox/browser/in_memory_url_index_types.h
+++ b/components/omnibox/browser/in_memory_url_index_types.h
@@ -8,6 +8,7 @@
 #include <stddef.h>
 
 #include <map>
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/flat_set.h"
@@ -167,7 +168,7 @@
 };
 
 // A map from history_id to the history's URL and title.
-typedef base::hash_map<HistoryID, HistoryInfoMapValue> HistoryInfoMap;
+typedef std::unordered_map<HistoryID, HistoryInfoMapValue> HistoryInfoMap;
 
 // A map from history_id to URL and page title word start metrics.
 struct RowWordStarts {
diff --git a/components/prefs/pref_notifier_impl.h b/components/prefs/pref_notifier_impl.h
index 77dfaf4..d7473bfe 100644
--- a/components/prefs/pref_notifier_impl.h
+++ b/components/prefs/pref_notifier_impl.h
@@ -8,6 +8,7 @@
 #include <list>
 #include <memory>
 #include <string>
+#include <unordered_map>
 
 #include "base/callback.h"
 #include "base/compiler_specific.h"
@@ -58,7 +59,7 @@
   // order they are added. These should only be accessed externally for unit
   // testing.
   typedef base::ObserverList<PrefObserver>::Unchecked PrefObserverList;
-  typedef base::hash_map<std::string, std::unique_ptr<PrefObserverList>>
+  typedef std::unordered_map<std::string, std::unique_ptr<PrefObserverList>>
       PrefObserverMap;
 
   typedef std::list<base::OnceCallback<void(bool)>> PrefInitObserverList;
diff --git a/components/prefs/pref_registry.h b/components/prefs/pref_registry.h
index c7515aa..5deb6c3d 100644
--- a/components/prefs/pref_registry.h
+++ b/components/prefs/pref_registry.h
@@ -9,6 +9,7 @@
 
 #include <memory>
 #include <set>
+#include <unordered_map>
 
 #include "base/containers/hash_tables.h"
 #include "base/macros.h"
@@ -54,7 +55,7 @@
   };
 
   typedef PrefValueMap::const_iterator const_iterator;
-  typedef base::hash_map<std::string, uint32_t> PrefRegistrationFlagsMap;
+  typedef std::unordered_map<std::string, uint32_t> PrefRegistrationFlagsMap;
 
   PrefRegistry();
 
diff --git a/components/prefs/pref_service.h b/components/prefs/pref_service.h
index 1d7e024..9c3b950 100644
--- a/components/prefs/pref_service.h
+++ b/components/prefs/pref_service.h
@@ -16,6 +16,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/callback.h"
@@ -390,7 +391,7 @@
   // string comparisons. Order is unimportant, and deletions are rare.
   // Confirmed on Android where this speeded Chrome startup by roughly 50ms
   // vs. std::map, and by roughly 180ms vs. std::set of Preference pointers.
-  typedef base::hash_map<std::string, Preference> PreferenceMap;
+  typedef std::unordered_map<std::string, Preference> PreferenceMap;
 
   // Give access to ReportUserPrefChanged() and GetMutableUserPref().
   friend class subtle::ScopedUserPrefUpdateBase;
diff --git a/components/safe_browsing/browser/threat_details.h b/components/safe_browsing/browser/threat_details.h
index ad70b614..8d18c10 100644
--- a/components/safe_browsing/browser/threat_details.h
+++ b/components/safe_browsing/browser/threat_details.h
@@ -13,6 +13,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <unordered_set>
 #include <vector>
 
@@ -45,23 +46,25 @@
 class ThreatDetailsRedirectsCollector;
 class ThreatDetailsFactory;
 
-using ResourceMap =
-    base::hash_map<std::string,
-                   std::unique_ptr<ClientSafeBrowsingReportRequest::Resource>>;
+using ResourceMap = std::unordered_map<
+    std::string,
+    std::unique_ptr<ClientSafeBrowsingReportRequest::Resource>>;
 
 // Maps a key of an HTML element to its corresponding HTMLElement proto message.
 // HTML Element keys have the form "<frame_id>-<node_id>", where |frame_id| is
 // the FrameTreeNode ID of the frame containing the element, and
 // |node_id| is a sequential ID for the element generated by the renderer.
-using ElementMap = base::hash_map<std::string, std::unique_ptr<HTMLElement>>;
+using ElementMap =
+    std::unordered_map<std::string, std::unique_ptr<HTMLElement>>;
 
 // Maps the key of an iframe element to the FrameTreeNode ID of the frame that
 // rendered the contents of the iframe.
-using KeyToFrameTreeIdMap = base::hash_map<std::string, int>;
+using KeyToFrameTreeIdMap = std::unordered_map<std::string, int>;
 
 // Maps a FrameTreeNode ID of a frame to a set of child IDs. The child IDs are
 // the Element IDs of the top-level HTML Elements in this frame.
-using FrameTreeIdToChildIdsMap = base::hash_map<int, std::unordered_set<int>>;
+using FrameTreeIdToChildIdsMap =
+    std::unordered_map<int, std::unordered_set<int>>;
 
 // Callback used to notify a caller that ThreatDetails has finished creating and
 // sending a report.
diff --git a/components/safe_browsing/browser/threat_details_cache.h b/components/safe_browsing/browser/threat_details_cache.h
index 70181aa..7cac4b13 100644
--- a/components/safe_browsing/browser/threat_details_cache.h
+++ b/components/safe_browsing/browser/threat_details_cache.h
@@ -9,6 +9,7 @@
 // An instance of this class is generated by ThreatDetails.
 
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/callback.h"
@@ -24,7 +25,7 @@
 namespace safe_browsing {
 
 // Maps a URL to its Resource.
-typedef base::hash_map<
+typedef std::unordered_map<
     std::string,
     std::unique_ptr<ClientSafeBrowsingReportRequest::Resource>>
     ResourceMap;
diff --git a/components/safe_browsing/db/v4_database_unittest.cc b/components/safe_browsing/db/v4_database_unittest.cc
index 6de1f7517..67c7fca 100644
--- a/components/safe_browsing/db/v4_database_unittest.cc
+++ b/components/safe_browsing/db/v4_database_unittest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <unordered_map>
 #include <utility>
 
 #include "base/bind.h"
@@ -210,7 +211,7 @@
   DatabaseUpdatedCallback callback_db_updated_;
   NewDatabaseReadyCallback callback_db_ready_;
   StoreStateMap expected_store_state_map_;
-  base::hash_map<ListIdentifier, V4Store*> old_stores_map_;
+  std::unordered_map<ListIdentifier, V4Store*> old_stores_map_;
   const ListIdentifier linux_malware_id_, win_malware_id_;
 };
 
diff --git a/components/safe_browsing/password_protection/metrics_util.cc b/components/safe_browsing/password_protection/metrics_util.cc
index 6e6bfa2..3913415 100644
--- a/components/safe_browsing/password_protection/metrics_util.cc
+++ b/components/safe_browsing/password_protection/metrics_util.cc
@@ -36,8 +36,6 @@
     "PasswordProtection.PageInfoAction.GSuiteSyncPasswordEntry";
 const char kGSuiteSyncPasswordWarningDialogHistogram[] =
     "PasswordProtection.ModalWarningDialogAction.GSuiteSyncPasswordEntry";
-const char kInterstitialActionByUserNavigationHistogram[] =
-    "PasswordProtection.InterstitialActionByUserNavigation";
 const char kPasswordOnFocusRequestOutcomeHistogram[] =
     "PasswordProtection.RequestOutcome.PasswordFieldOnFocus";
 const char kPasswordOnFocusVerdictHistogram[] =
@@ -179,8 +177,6 @@
   // chrome://reset-password page. In this case, do not record user action.
   if (password_type == PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN &&
       ui_type == WarningUIType::INTERSTITIAL) {
-    UMA_HISTOGRAM_ENUMERATION(
-        "PasswordProtection.InterstitialActionByUserNavigation", action);
     return;
   }
   bool is_sign_in_password =
diff --git a/components/safe_browsing/password_protection/metrics_util.h b/components/safe_browsing/password_protection/metrics_util.h
index 4ffbc45..3bf886b 100644
--- a/components/safe_browsing/password_protection/metrics_util.h
+++ b/components/safe_browsing/password_protection/metrics_util.h
@@ -28,7 +28,6 @@
 extern const char kGSuiteSyncPasswordInterstitialHistogram[];
 extern const char kGSuiteSyncPasswordPageInfoHistogram[];
 extern const char kGSuiteSyncPasswordWarningDialogHistogram[];
-extern const char kInterstitialActionByUserNavigationHistogram[];
 extern const char kPasswordOnFocusRequestOutcomeHistogram[];
 extern const char kPasswordOnFocusVerdictHistogram[];
 extern const char kProtectedPasswordEntryRequestOutcomeHistogram[];
diff --git a/components/services/font/public/cpp/font_loader.h b/components/services/font/public/cpp/font_loader.h
index 34695af..633931d6 100644
--- a/components/services/font/public/cpp/font_loader.h
+++ b/components/services/font/public/cpp/font_loader.h
@@ -99,7 +99,7 @@
   base::Lock lock_;
 
   // Maps font identity ID to the memory-mapped file with font data.
-  base::hash_map<uint32_t, internal::MappedFontFile*> mapped_font_files_;
+  std::unordered_map<uint32_t, internal::MappedFontFile*> mapped_font_files_;
 
   DISALLOW_COPY_AND_ASSIGN(FontLoader);
 };
diff --git a/components/sync_preferences/pref_model_associator.h b/components/sync_preferences/pref_model_associator.h
index 46c4b5a..b5698dc 100644
--- a/components/sync_preferences/pref_model_associator.h
+++ b/components/sync_preferences/pref_model_associator.h
@@ -9,6 +9,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/callback.h"
@@ -197,7 +198,7 @@
   // from sync.
   using SyncedPrefObserverList =
       base::ObserverList<SyncedPrefObserver>::Unchecked;
-  base::hash_map<std::string, std::unique_ptr<SyncedPrefObserverList>>
+  std::unordered_map<std::string, std::unique_ptr<SyncedPrefObserverList>>
       synced_pref_observers_;
 
   const PrefModelAssociatorClient* client_;  // Weak.
diff --git a/components/ukm/debug/ukm_internals.js b/components/ukm/debug/ukm_internals.js
index 55434b2..e6f910ed 100644
--- a/components/ukm/debug/ukm_internals.js
+++ b/components/ukm/debug/ukm_internals.js
@@ -88,8 +88,9 @@
  * @param {!NodeList<!Element>} collection Collection of Elements.
  */
 function setDisplayStyle(collection, display_value) {
-  for (const el of collection)
+  for (const el of collection) {
     el.style.display = display_value;
+  }
 }
 
 /**
@@ -113,8 +114,9 @@
 function createUrlCard(sourcesForUrl, url, sourcesDiv, displayState) {
   const sourceDiv = createElementWithClassName('div', 'url_card');
   sourcesDiv.appendChild(sourceDiv);
-  if (!sourcesForUrl || sourcesForUrl.length === 0)
+  if (!sourcesForUrl || sourcesForUrl.length === 0) {
     return;
+  }
   for (const source of sourcesForUrl) {
     // This div allows hiding of the metrics per URL.
     const sourceContainer = /** @type {!Element} */ (createElementWithClassName(
@@ -171,10 +173,11 @@
   if (displayState) {
     metricElement.style.display = displayState;
   } else {
-    if ($('toggle_expand').textContent === 'Collapse')
+    if ($('toggle_expand').textContent === 'Collapse') {
       metricElement.style.display = 'block';
-    else
+    } else {
       metricElement.style.display = 'none';
+    }
   }
 }
 
@@ -262,8 +265,9 @@
     // Note it won't be able to clear if UKM logs got cut during this call.
     cr.sendWithPromise('requestUkmData').then((/** @type {UkmData} */ data) => {
       updateUkmCache(data);
-      for (const s of CachedSources.values())
+      for (const s of CachedSources.values()) {
         ClearedSources.set(as64Bit(s.id), s.entries.length);
+      }
     });
     $('toggle_expand').textContent = 'Expand';
     updateUkmData();
@@ -308,8 +312,9 @@
     const key = as64Bit(source.id);
     if (!CachedSources.has(key)) {
       const mergedSource = {id: source.id, entries: source.entries};
-      if (source.url)
+      if (source.url) {
         mergedSource.url = source.url;
+      }
       CachedSources.set(key, mergedSource);
     } else {
       // Merge distinct entries from the source.
@@ -422,12 +427,14 @@
   $('thread_ids').addEventListener('click', updateUkmData);
   $('include_cache').addEventListener('click', updateUkmData);
   $('metrics_select').addEventListener('keyup', e => {
-    if (e.key === 'Enter')
+    if (e.key === 'Enter') {
       updateUkmData();
+    }
   });
   $('url_select').addEventListener('keyup', e => {
-    if (e.key === 'Enter')
+    if (e.key === 'Enter') {
       updateUkmData();
+    }
   });
 }
 
diff --git a/components/variations/service/variations_field_trial_creator.h b/components/variations/service/variations_field_trial_creator.h
index ea807e5e..38a16ff 100644
--- a/components/variations/service/variations_field_trial_creator.h
+++ b/components/variations/service/variations_field_trial_creator.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/compiler_specific.h"
@@ -174,7 +175,7 @@
 
   // Caches the UI strings which need to be overridden in the resource bundle.
   // These strings are cached before the resource bundle is initialized.
-  base::hash_map<int, base::string16> overridden_strings_map_;
+  std::unordered_map<int, base::string16> overridden_strings_map_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/components/version_ui/resources/about_version.js b/components/version_ui/resources/about_version.js
index 54b6b00..27a7f48 100644
--- a/components/version_ui/resources/about_version.js
+++ b/components/version_ui/resources/about_version.js
@@ -73,8 +73,9 @@
  * @param {!{customizationId: string}} response
  */
 function returnCustomizationId(response) {
-  if (!response.customizationId)
+  if (!response.customizationId) {
     return;
+  }
   $('customization_id_holder').hidden = false;
   $('customization_id').textContent = response.customizationId;
 }
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index 2eb5ea3..82e2bc6 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -107,7 +107,7 @@
   return new BrowserAccessibilityAndroid();
 }
 
-using UniqueIdMap = base::hash_map<int32_t, BrowserAccessibilityAndroid*>;
+using UniqueIdMap = std::unordered_map<int32_t, BrowserAccessibilityAndroid*>;
 // Map from each AXPlatformNode's unique id to its instance.
 base::LazyInstance<UniqueIdMap>::Leaky g_unique_id_map =
     LAZY_INSTANCE_INITIALIZER;
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h
index c1e9c2d..43f2aee 100644
--- a/content/browser/accessibility/browser_accessibility_manager.h
+++ b/content/browser/accessibility/browser_accessibility_manager.h
@@ -9,6 +9,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/callback_forward.h"
@@ -416,7 +417,7 @@
   std::unique_ptr<ui::AXSerializableTree> tree_;
 
   // A mapping from a node id to its wrapper of type BrowserAccessibility.
-  base::hash_map<int32_t, BrowserAccessibility*> id_wrapper_map_;
+  std::unordered_map<int32_t, BrowserAccessibility*> id_wrapper_map_;
 
   // True if the user has initiated a navigation to another page.
   bool user_is_navigating_away_;
diff --git a/content/browser/accessibility/web_contents_accessibility_android.cc b/content/browser/accessibility/web_contents_accessibility_android.cc
index 91902e59..8bbfa86a7 100644
--- a/content/browser/accessibility/web_contents_accessibility_android.cc
+++ b/content/browser/accessibility/web_contents_accessibility_android.cc
@@ -186,7 +186,7 @@
                             UMA_ACCESSIBILITYSERVICEINFO_MAX)
 
 using SearchKeyToPredicateMap =
-    base::hash_map<base::string16, AccessibilityMatchPredicate>;
+    std::unordered_map<base::string16, AccessibilityMatchPredicate>;
 base::LazyInstance<SearchKeyToPredicateMap>::Leaky
     g_search_key_to_predicate_map = LAZY_INSTANCE_INITIALIZER;
 base::LazyInstance<base::string16>::Leaky g_all_search_keys =
diff --git a/content/browser/appcache/appcache_backend_impl.h b/content/browser/appcache/appcache_backend_impl.h
index cf5b8e8..8f45e728 100644
--- a/content/browser/appcache/appcache_backend_impl.h
+++ b/content/browser/appcache/appcache_backend_impl.h
@@ -56,7 +56,7 @@
     return (it != hosts_.end()) ? (it->second.get()) : nullptr;
   }
 
-  using HostMap = base::hash_map<int, std::unique_ptr<AppCacheHost>>;
+  using HostMap = std::unordered_map<int, std::unique_ptr<AppCacheHost>>;
   const HostMap& hosts() { return hosts_; }
 
   // The AppCacheHost is precreated by the AppCacheNavigationHandleCore class
diff --git a/content/browser/appcache/appcache_working_set.h b/content/browser/appcache/appcache_working_set.h
index 4d8fa0a..246ae77 100644
--- a/content/browser/appcache/appcache_working_set.h
+++ b/content/browser/appcache/appcache_working_set.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <map>
+#include <unordered_map>
 
 #include "base/containers/hash_tables.h"
 #include "content/common/content_export.h"
@@ -58,9 +59,9 @@
   }
 
  private:
-  using CacheMap = base::hash_map<int64_t, AppCache*>;
+  using CacheMap = std::unordered_map<int64_t, AppCache*>;
   using GroupsByOriginMap = std::map<url::Origin, GroupMap>;
-  using ResponseInfoMap = base::hash_map<int64_t, AppCacheResponseInfo*>;
+  using ResponseInfoMap = std::unordered_map<int64_t, AppCacheResponseInfo*>;
 
   GroupMap* GetMutableGroupsInOrigin(const url::Origin& origin) {
     GroupsByOriginMap::iterator it = groups_by_origin_.find(origin);
diff --git a/content/browser/appcache/mock_appcache_storage.h b/content/browser/appcache/mock_appcache_storage.h
index 80cc4bad..03d80e4 100644
--- a/content/browser/appcache/mock_appcache_storage.h
+++ b/content/browser/appcache/mock_appcache_storage.h
@@ -9,6 +9,7 @@
 
 #include <map>
 #include <memory>
+#include <unordered_map>
 #include <vector>
 
 #include "base/callback.h"
@@ -94,7 +95,7 @@
   friend class appcache_update_job_unittest::AppCacheUpdateJobTest;
   friend class MockAppCacheStorageTest;
 
-  using StoredCacheMap = base::hash_map<int64_t, scoped_refptr<AppCache>>;
+  using StoredCacheMap = std::unordered_map<int64_t, scoped_refptr<AppCache>>;
   using StoredGroupMap = std::map<GURL, scoped_refptr<AppCacheGroup>>;
   using DoomedResponseIds = std::set<int64_t>;
   using StoredEvictionTimesMap =
diff --git a/content/browser/browsing_instance.h b/content/browser/browsing_instance.h
index 118f1781b..879409c 100644
--- a/content/browser/browsing_instance.h
+++ b/content/browser/browsing_instance.h
@@ -8,6 +8,7 @@
 #include <stddef.h>
 
 #include <string>
+#include <unordered_map>
 
 #include "base/containers/hash_tables.h"
 #include "base/gtest_prod_util.h"
@@ -107,7 +108,7 @@
 
   // Map of site to SiteInstance, to ensure we only have one SiteInstance per
   // site.
-  typedef base::hash_map<std::string, SiteInstanceImpl*> SiteInstanceMap;
+  typedef std::unordered_map<std::string, SiteInstanceImpl*> SiteInstanceMap;
 
   // Common browser context to which all SiteInstances in this BrowsingInstance
   // must belong.
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index 6cdb9cd..70fc8ad 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -2146,27 +2146,27 @@
 
   parameters.injected_errors.pop();
   TestDownloadHttpResponse::StartServing(parameters, server_url2);
-  download->Resume(true);
+  download->Resume(false);
   WaitForInterrupt(download);
 
   parameters.injected_errors.pop();
   TestDownloadHttpResponse::StartServing(parameters, server_url2);
-  download->Resume(true);
+  download->Resume(false);
   WaitForInterrupt(download);
 
   parameters.injected_errors.pop();
   TestDownloadHttpResponse::StartServing(parameters, server_url2);
-  download->Resume(true);
+  download->Resume(false);
   WaitForInterrupt(download);
 
   parameters.injected_errors.pop();
   TestDownloadHttpResponse::StartServing(parameters, server_url2);
-  download->Resume(true);
+  download->Resume(false);
   WaitForInterrupt(download);
 
   parameters.injected_errors.pop();
   TestDownloadHttpResponse::StartServing(parameters, server_url2);
-  download->Resume(true);
+  download->Resume(false);
   WaitForCompletion(download);
 
   EXPECT_EQ(expected_hash, download->GetHash());
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index 163932d..644ddcb9 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -774,6 +774,11 @@
   return browser_context_->IsOffTheRecord();
 }
 
+bool DownloadManagerImpl::IsActiveNetworkMetered() const {
+  // TODO(shaktisahu): Call ChromeDownloadManagerDelegate to get this.
+  return false;
+}
+
 void DownloadManagerImpl::ReportBytesWasted(
     download::DownloadItemImpl* download) {
   in_progress_manager_->ReportBytesWasted(download);
diff --git a/content/browser/download/download_manager_impl.h b/content/browser/download/download_manager_impl.h
index b94e85cf..d596137 100644
--- a/content/browser/download/download_manager_impl.h
+++ b/content/browser/download/download_manager_impl.h
@@ -257,6 +257,7 @@
   base::Optional<download::DownloadEntry> GetInProgressEntry(
       download::DownloadItemImpl* download) override;
   bool IsOffTheRecord() const override;
+  bool IsActiveNetworkMetered() const override;
   void ReportBytesWasted(download::DownloadItemImpl* download) override;
 
   // Drops a download before it is created.
diff --git a/content/browser/frame_host/frame_tree.h b/content/browser/frame_host/frame_tree.h
index 7e74912..03b4f49 100644
--- a/content/browser/frame_host/frame_tree.h
+++ b/content/browser/frame_host/frame_tree.h
@@ -10,6 +10,7 @@
 #include <iterator>
 #include <memory>
 #include <string>
+#include <unordered_map>
 
 #include "base/callback.h"
 #include "base/containers/queue.h"
@@ -247,7 +248,7 @@
  private:
   friend class FrameTreeTest;
   FRIEND_TEST_ALL_PREFIXES(RenderFrameHostImplBrowserTest, RemoveFocusedFrame);
-  typedef base::hash_map<int, RenderViewHostImpl*> RenderViewHostMap;
+  typedef std::unordered_map<int, RenderViewHostImpl*> RenderViewHostMap;
 
   // Returns a range to iterate over all FrameTreeNodes in the frame tree in
   // breadth-first traversal order, skipping the subtree rooted at
diff --git a/content/browser/frame_host/frame_tree_node.cc b/content/browser/frame_host/frame_tree_node.cc
index 413bfeb..4647d212 100644
--- a/content/browser/frame_host/frame_tree_node.cc
+++ b/content/browser/frame_host/frame_tree_node.cc
@@ -7,6 +7,7 @@
 #include <math.h>
 
 #include <queue>
+#include <unordered_map>
 #include <utility>
 
 #include "base/feature_list.h"
@@ -34,7 +35,7 @@
 
 // This is a global map between frame_tree_node_ids and pointers to
 // FrameTreeNodes.
-typedef base::hash_map<int, FrameTreeNode*> FrameTreeNodeIdMap;
+typedef std::unordered_map<int, FrameTreeNode*> FrameTreeNodeIdMap;
 
 base::LazyInstance<FrameTreeNodeIdMap>::DestructorAtExit
     g_frame_tree_node_id_map = LAZY_INSTANCE_INITIALIZER;
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc
index 395a89ce..2cd6997f 100644
--- a/content/browser/frame_host/navigation_handle_impl.cc
+++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -920,27 +920,27 @@
         frame_tree_node_->current_frame_host()->GetProcess()->GetID();
     LogIsSameProcess(transition_, is_same_process_);
 
-    // Don't log process-priority-specific UMAs for TimeToReadyToCommit metric
+    // Don't log process-priority-specific UMAs for TimeToReadyToCommit2 metric
     // (which shouldn't be influenced by renderer priority).
     constexpr base::Optional<bool> kIsBackground = base::nullopt;
 
     base::TimeDelta delta = ready_to_commit_time_ - navigation_start_;
-    LOG_NAVIGATION_TIMING_HISTOGRAM("TimeToReadyToCommit", transition_,
+    LOG_NAVIGATION_TIMING_HISTOGRAM("TimeToReadyToCommit2", transition_,
                                     kIsBackground, delta);
 
     if (IsInMainFrame()) {
-      LOG_NAVIGATION_TIMING_HISTOGRAM("TimeToReadyToCommit.MainFrame",
+      LOG_NAVIGATION_TIMING_HISTOGRAM("TimeToReadyToCommit2.MainFrame",
                                       transition_, kIsBackground, delta);
     } else {
-      LOG_NAVIGATION_TIMING_HISTOGRAM("TimeToReadyToCommit.Subframe",
+      LOG_NAVIGATION_TIMING_HISTOGRAM("TimeToReadyToCommit2.Subframe",
                                       transition_, kIsBackground, delta);
     }
 
     if (is_same_process_) {
-      LOG_NAVIGATION_TIMING_HISTOGRAM("TimeToReadyToCommit.SameProcess",
+      LOG_NAVIGATION_TIMING_HISTOGRAM("TimeToReadyToCommit2.SameProcess",
                                       transition_, kIsBackground, delta);
     } else {
-      LOG_NAVIGATION_TIMING_HISTOGRAM("TimeToReadyToCommit.CrossProcess",
+      LOG_NAVIGATION_TIMING_HISTOGRAM("TimeToReadyToCommit2.CrossProcess",
                                       transition_, kIsBackground, delta);
     }
   }
@@ -1027,7 +1027,7 @@
     }
 
     if (!ready_to_commit_time_.is_null()) {
-      LOG_NAVIGATION_TIMING_HISTOGRAM("ReadyToCommitUntilCommit", transition_,
+      LOG_NAVIGATION_TIMING_HISTOGRAM("ReadyToCommitUntilCommit2", transition_,
                                       is_background,
                                       now - ready_to_commit_time_);
     }
diff --git a/content/browser/frame_host/navigation_handle_impl_browsertest.cc b/content/browser/frame_host/navigation_handle_impl_browsertest.cc
index 8e4710d3..aaba4ec 100644
--- a/content/browser/frame_host/navigation_handle_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_handle_impl_browsertest.cc
@@ -2206,7 +2206,7 @@
   }
 }
 
-// Verify that the TimeToReadyToCommit metrics are correctly logged for
+// Verify that the TimeToReadyToCommit2 metrics are correctly logged for
 // SameProcess vs CrossProcess as well as MainFrame vs Subframe cases.
 IN_PROC_BROWSER_TEST_F(NavigationHandleImplBrowserTest,
                        TimeToReadyToCommitMetrics) {
@@ -2220,13 +2220,13 @@
     EXPECT_TRUE(NavigateToURL(shell(), url));
 
     base::HistogramTester::CountsMap expected_counts = {
-        {"Navigation.TimeToReadyToCommit.MainFrame", 1},
-        {"Navigation.TimeToReadyToCommit.MainFrame.NewNavigation", 1},
-        {"Navigation.TimeToReadyToCommit.NewNavigation", 1},
-        {"Navigation.TimeToReadyToCommit.SameProcess", 1},
-        {"Navigation.TimeToReadyToCommit.SameProcess.NewNavigation", 1}};
+        {"Navigation.TimeToReadyToCommit2.MainFrame", 1},
+        {"Navigation.TimeToReadyToCommit2.MainFrame.NewNavigation", 1},
+        {"Navigation.TimeToReadyToCommit2.NewNavigation", 1},
+        {"Navigation.TimeToReadyToCommit2.SameProcess", 1},
+        {"Navigation.TimeToReadyToCommit2.SameProcess.NewNavigation", 1}};
     EXPECT_THAT(
-        histograms.GetTotalCountsForPrefix("Navigation.TimeToReadyToCommit."),
+        histograms.GetTotalCountsForPrefix("Navigation.TimeToReadyToCommit2."),
         testing::ContainerEq(expected_counts));
   }
 
@@ -2237,13 +2237,13 @@
     EXPECT_TRUE(NavigateToURL(shell(), url));
 
     base::HistogramTester::CountsMap expected_counts = {
-        {"Navigation.TimeToReadyToCommit.MainFrame", 1},
-        {"Navigation.TimeToReadyToCommit.MainFrame.NewNavigation", 1},
-        {"Navigation.TimeToReadyToCommit.NewNavigation", 1},
-        {"Navigation.TimeToReadyToCommit.CrossProcess", 1},
-        {"Navigation.TimeToReadyToCommit.CrossProcess.NewNavigation", 1}};
+        {"Navigation.TimeToReadyToCommit2.MainFrame", 1},
+        {"Navigation.TimeToReadyToCommit2.MainFrame.NewNavigation", 1},
+        {"Navigation.TimeToReadyToCommit2.NewNavigation", 1},
+        {"Navigation.TimeToReadyToCommit2.CrossProcess", 1},
+        {"Navigation.TimeToReadyToCommit2.CrossProcess.NewNavigation", 1}};
     EXPECT_THAT(
-        histograms.GetTotalCountsForPrefix("Navigation.TimeToReadyToCommit."),
+        histograms.GetTotalCountsForPrefix("Navigation.TimeToReadyToCommit2."),
         testing::ContainerEq(expected_counts));
   }
 
@@ -2263,17 +2263,17 @@
     std::string navigation_type =
         AreAllSitesIsolatedForTesting() ? "CrossProcess" : "SameProcess";
     base::HistogramTester::CountsMap expected_counts = {
-        {"Navigation.TimeToReadyToCommit.Subframe", 1},
-        {"Navigation.TimeToReadyToCommit.Subframe.NewNavigation", 1},
-        {"Navigation.TimeToReadyToCommit.NewNavigation", 1},
-        {base::StringPrintf("Navigation.TimeToReadyToCommit.%s",
+        {"Navigation.TimeToReadyToCommit2.Subframe", 1},
+        {"Navigation.TimeToReadyToCommit2.Subframe.NewNavigation", 1},
+        {"Navigation.TimeToReadyToCommit2.NewNavigation", 1},
+        {base::StringPrintf("Navigation.TimeToReadyToCommit2.%s",
                             navigation_type.c_str()),
          1},
-        {base::StringPrintf("Navigation.TimeToReadyToCommit.%s.NewNavigation",
+        {base::StringPrintf("Navigation.TimeToReadyToCommit2.%s.NewNavigation",
                             navigation_type.c_str()),
          1}};
     EXPECT_THAT(
-        histograms.GetTotalCountsForPrefix("Navigation.TimeToReadyToCommit."),
+        histograms.GetTotalCountsForPrefix("Navigation.TimeToReadyToCommit2."),
         testing::ContainerEq(expected_counts));
   }
 }
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 40e3e2f..b2cc9269 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -5,6 +5,7 @@
 #include "content/browser/frame_host/render_frame_host_impl.h"
 
 #include <algorithm>
+#include <unordered_map>
 #include <utility>
 
 #include "base/bind.h"
@@ -12,6 +13,7 @@
 #include "base/containers/hash_tables.h"
 #include "base/containers/queue.h"
 #include "base/debug/alias.h"
+#include "base/hash.h"
 #include "base/lazy_instance.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
@@ -224,7 +226,9 @@
 
 // The (process id, routing id) pair that identifies one RenderFrame.
 typedef std::pair<int32_t, int32_t> RenderFrameHostID;
-typedef base::hash_map<RenderFrameHostID, RenderFrameHostImpl*>
+typedef std::unordered_map<RenderFrameHostID,
+                           RenderFrameHostImpl*,
+                           base::IntPairHash<RenderFrameHostID>>
     RoutingIDFrameMap;
 base::LazyInstance<RoutingIDFrameMap>::DestructorAtExit g_routing_id_frame_map =
     LAZY_INSTANCE_INITIALIZER;
@@ -232,9 +236,9 @@
 base::LazyInstance<RenderFrameHostImpl::CreateNetworkFactoryCallback>::Leaky
     g_create_network_factory_callback_for_test = LAZY_INSTANCE_INITIALIZER;
 
-using TokenFrameMap = base::hash_map<base::UnguessableToken,
-                                     RenderFrameHostImpl*,
-                                     base::UnguessableTokenHash>;
+using TokenFrameMap = std::unordered_map<base::UnguessableToken,
+                                         RenderFrameHostImpl*,
+                                         base::UnguessableTokenHash>;
 base::LazyInstance<TokenFrameMap>::Leaky g_token_frame_map =
     LAZY_INSTANCE_INITIALIZER;
 
@@ -418,8 +422,19 @@
   // TODO(lukasza, nasko): https://crbug.com/888079: Use exact origin, instead
   // of falling back to site URL for about:blank and about:srcdoc.
   if (target_url.SchemeIs(url::kAboutScheme)) {
+    // |site_instance|'s site URL cannot be used as
+    // |request_initiator_site_lock| unless the site requires a dedicated
+    // process.  Otherwise b.com may share a process associated with a.com, in
+    // a SiteInstance with |site_url| set to "http://a.com" (and/or
+    // "http://nonisolated.invalid" in the future) and in that scenario
+    // |request_initiator| for requests from b.com should NOT be locked to
+    // a.com.
+    if (!SiteInstanceImpl::ShouldLockToOrigin(
+            site_instance->GetBrowserContext(), site_instance->GetSiteURL()))
+      return base::nullopt;
+
     return SiteInstanceImpl::GetRequestInitiatorSiteLock(
-        site_instance->GetBrowserContext(), site_instance->GetSiteURL());
+        site_instance->GetSiteURL());
   }
 
   // In cases not covered above, URLLoaderFactory should be associated with the
diff --git a/content/browser/frame_host/render_frame_proxy_host.cc b/content/browser/frame_host/render_frame_proxy_host.cc
index 46c6f8f..07e4c96 100644
--- a/content/browser/frame_host/render_frame_proxy_host.cc
+++ b/content/browser/frame_host/render_frame_proxy_host.cc
@@ -4,10 +4,12 @@
 
 #include "content/browser/frame_host/render_frame_proxy_host.h"
 
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
 #include "base/callback.h"
+#include "base/hash.h"
 #include "base/lazy_instance.h"
 #include "content/browser/bad_message.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
@@ -33,7 +35,9 @@
 
 // The (process id, routing id) pair that identifies one RenderFrameProxy.
 typedef std::pair<int32_t, int32_t> RenderFrameProxyHostID;
-typedef base::hash_map<RenderFrameProxyHostID, RenderFrameProxyHost*>
+typedef std::unordered_map<RenderFrameProxyHostID,
+                           RenderFrameProxyHost*,
+                           base::IntPairHash<RenderFrameProxyHostID>>
     RoutingIDFrameProxyMap;
 base::LazyInstance<RoutingIDFrameProxyMap>::DestructorAtExit
     g_routing_id_frame_proxy_map = LAZY_INSTANCE_INITIALIZER;
diff --git a/content/browser/loader/cross_site_document_blocking_browsertest.cc b/content/browser/loader/cross_site_document_blocking_browsertest.cc
index 334cec5b..6741de1 100644
--- a/content/browser/loader/cross_site_document_blocking_browsertest.cc
+++ b/content/browser/loader/cross_site_document_blocking_browsertest.cc
@@ -1234,8 +1234,10 @@
   // Skip this test when servicification of service workers (S13nServiceWorker)
   // is enabled because the browser process doesn't see the request or response
   // when the request is handled entirely within the service worker.
-  if (blink::ServiceWorkerUtils::IsServicificationEnabled())
+  if (blink::ServiceWorkerUtils::IsServicificationEnabled() ||
+      base::FeatureList::IsEnabled(network::features::kNetworkService)) {
     return;
+  }
 
   SetUpServiceWorker();
 
@@ -1364,6 +1366,7 @@
 IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingIsolatedOriginTest,
                        BlockDocumentsFromIsolatedOrigin) {
   embedded_test_server()->StartAcceptingConnections();
+
   if (AreAllSitesIsolatedForTesting())
     return;
 
diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc
index 3c720de3..374c25e 100644
--- a/content/browser/media/session/media_session_impl.cc
+++ b/content/browser/media/session/media_session_impl.cc
@@ -492,12 +492,19 @@
 }
 
 bool MediaSessionImpl::IsControllable() const {
-  // Only media session having focus Gain can be controllable unless it is
-  // inactive. Also, the session will be uncontrollable if it contains one-shot
-  // players.
-  return audio_focus_state_ != State::INACTIVE &&
-         desired_audio_focus_type_ == AudioFocusType::kGain &&
-         one_shot_players_.empty();
+  // If the session does not have audio focus or it has one shot players then it
+  // cannot be controllable.
+  if (audio_focus_state_ == State::INACTIVE || !one_shot_players_.empty())
+    return false;
+
+#if !defined(OS_ANDROID)
+  if (routed_service_ && routed_service_->playback_state() !=
+                             blink::mojom::MediaSessionPlaybackState::NONE) {
+    return true;
+  }
+#endif
+
+  return desired_audio_focus_type_ == AudioFocusType::kGain;
 }
 
 bool MediaSessionImpl::IsActuallyPaused() const {
diff --git a/content/browser/media/session/media_session_impl.h b/content/browser/media/session/media_session_impl.h
index ce0f41c..561dacd 100644
--- a/content/browser/media/session/media_session_impl.h
+++ b/content/browser/media/session/media_session_impl.h
@@ -9,6 +9,7 @@
 
 #include <map>
 #include <set>
+#include <unordered_map>
 
 #include "base/callback_list.h"
 #include "base/containers/id_map.h"
@@ -269,7 +270,7 @@
     bool operator==(const PlayerIdentifier& player_identifier) const;
     bool operator<(const PlayerIdentifier&) const;
 
-    // Hash operator for base::hash_map<>.
+    // Hash operator for std::unordered_map<>.
     struct Hash {
       size_t operator()(const PlayerIdentifier& player_identifier) const;
     };
diff --git a/content/browser/media/session/media_session_impl_browsertest.cc b/content/browser/media/session/media_session_impl_browsertest.cc
index 894ea08..b2b48f3f 100644
--- a/content/browser/media/session/media_session_impl_browsertest.cc
+++ b/content/browser/media/session/media_session_impl_browsertest.cc
@@ -756,6 +756,112 @@
   EXPECT_TRUE(IsActive());
 }
 
+// This behaviour is specific to desktop.
+#if !defined(OS_ANDROID)
+
+IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest,
+                       ControlsNoShowForTransientAndRoutedService) {
+  EnsureMediaSessionService();
+  auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(
+      shell()->web_contents()->GetMainFrame());
+
+  EXPECT_CALL(*mock_media_session_observer(),
+              MediaSessionStateChanged(false, false));
+
+  // Starting a player with a transient type should not show the media controls.
+  StartNewPlayer(player_observer.get(), media::MediaContentType::Transient);
+  ResolveAudioFocusSuccess();
+
+  EXPECT_FALSE(IsControllable());
+  EXPECT_TRUE(IsActive());
+}
+
+IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest,
+                       ControlsNoShowForTransientAndPlaybackStateNone) {
+  EnsureMediaSessionService();
+  auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(
+      shell()->web_contents()->GetMainFrame());
+
+  ::testing::Sequence s;
+  EXPECT_CALL(*mock_media_session_observer(),
+              MediaSessionStateChanged(false, false))
+      .InSequence(s);
+  EXPECT_CALL(*mock_media_session_observer(),
+              MediaSessionStateChanged(false, _))
+      .InSequence(s);
+
+  // Starting a player with a transient type should not show the media controls.
+  StartNewPlayer(player_observer.get(), media::MediaContentType::Transient);
+  ResolveAudioFocusSuccess();
+
+  SetPlaybackState(blink::mojom::MediaSessionPlaybackState::NONE);
+
+  EXPECT_FALSE(IsControllable());
+  EXPECT_TRUE(IsActive());
+
+  // Verify before test exists. Otherwise the sequence will expire and cause
+  // weird problems.
+  ::testing::Mock::VerifyAndClear(mock_media_session_observer());
+}
+
+IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest,
+                       ControlsShowForTransientAndPlaybackStatePaused) {
+  EnsureMediaSessionService();
+  auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(
+      shell()->web_contents()->GetMainFrame());
+
+  ::testing::Sequence s;
+  EXPECT_CALL(*mock_media_session_observer(),
+              MediaSessionStateChanged(false, false))
+      .InSequence(s);
+  EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, _))
+      .InSequence(s);
+
+  // Starting a player with a transient type should show the media controls if
+  // we have a playback state from the service.
+  StartNewPlayer(player_observer.get(), media::MediaContentType::Transient);
+  ResolveAudioFocusSuccess();
+
+  SetPlaybackState(blink::mojom::MediaSessionPlaybackState::PAUSED);
+
+  EXPECT_TRUE(IsControllable());
+  EXPECT_TRUE(IsActive());
+
+  // Verify before test exists. Otherwise the sequence will expire and cause
+  // weird problems.
+  ::testing::Mock::VerifyAndClear(mock_media_session_observer());
+}
+
+IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest,
+                       ControlsShowForTransientAndPlaybackStatePlaying) {
+  EnsureMediaSessionService();
+  auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(
+      shell()->web_contents()->GetMainFrame());
+
+  ::testing::Sequence s;
+  EXPECT_CALL(*mock_media_session_observer(),
+              MediaSessionStateChanged(false, false))
+      .InSequence(s);
+  EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, _))
+      .InSequence(s);
+
+  // Starting a player with a transient type should show the media controls if
+  // we have a playback state from the service.
+  StartNewPlayer(player_observer.get(), media::MediaContentType::Transient);
+  ResolveAudioFocusSuccess();
+
+  SetPlaybackState(blink::mojom::MediaSessionPlaybackState::PLAYING);
+
+  EXPECT_TRUE(IsControllable());
+  EXPECT_TRUE(IsActive());
+
+  // Verify before test exists. Otherwise the sequence will expire and cause
+  // weird problems.
+  ::testing::Mock::VerifyAndClear(mock_media_session_observer());
+}
+
+#endif  // !defined(OS_ANDROID)
+
 IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest,
                        ControlsHideWhenStopped) {
   Expectation showControls = EXPECT_CALL(*mock_media_session_observer(),
diff --git a/content/browser/presentation/presentation_service_impl.h b/content/browser/presentation/presentation_service_impl.h
index cd66064f..2b08658 100644
--- a/content/browser/presentation/presentation_service_impl.h
+++ b/content/browser/presentation/presentation_service_impl.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/callback.h"
@@ -267,7 +268,7 @@
       pending_start_presentation_cb_;
 
   // For ReconnectPresentation requests.
-  base::hash_map<int, std::unique_ptr<NewPresentationCallbackWrapper>>
+  std::unordered_map<int, std::unique_ptr<NewPresentationCallbackWrapper>>
       pending_reconnect_presentation_cbs_;
 
   // RAII binding of |this| to PresentationService request.
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 724f727..aac3f05 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -11,6 +11,7 @@
 #include <limits>
 #include <map>
 #include <set>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -408,7 +409,7 @@
 // site in process-per-site mode.  Each map is specific to a BrowserContext.
 class SiteProcessMap : public base::SupportsUserData::Data {
  public:
-  typedef base::hash_map<std::string, RenderProcessHost*> SiteToProcessMap;
+  typedef std::unordered_map<std::string, RenderProcessHost*> SiteToProcessMap;
   SiteProcessMap() {}
 
   void RegisterProcess(const std::string& site, RenderProcessHost* process) {
@@ -2540,8 +2541,8 @@
   GURL process_lock =
       ChildProcessSecurityPolicyImpl::GetInstance()->GetOriginLock(GetID());
   if (process_lock.is_valid()) {
-    request_initiator_site_lock = SiteInstanceImpl::GetRequestInitiatorSiteLock(
-        GetBrowserContext(), process_lock);
+    request_initiator_site_lock =
+        SiteInstanceImpl::GetRequestInitiatorSiteLock(process_lock);
   }
 
   CreateURLLoaderFactory(request_initiator_site_lock,
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index fc94c28..7d01cc8a 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -6,6 +6,7 @@
 
 #include <set>
 #include <string>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -13,6 +14,7 @@
 #include "base/command_line.h"
 #include "base/debug/dump_without_crashing.h"
 #include "base/feature_list.h"
+#include "base/hash.h"
 #include "base/i18n/rtl.h"
 #include "base/json/json_reader.h"
 #include "base/metrics/field_trial.h"
@@ -116,7 +118,10 @@
 
 // <process id, routing id>
 using RenderViewHostID = std::pair<int32_t, int32_t>;
-using RoutingIDViewMap = base::hash_map<RenderViewHostID, RenderViewHostImpl*>;
+using RoutingIDViewMap =
+    std::unordered_map<RenderViewHostID,
+                       RenderViewHostImpl*,
+                       base::IntPairHash<RenderViewHostID>>;
 base::LazyInstance<RoutingIDViewMap>::Leaky g_routing_id_view_map =
     LAZY_INSTANCE_INITIALIZER;
 
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index f9ffebf..b866ca37 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -9,6 +9,7 @@
 #include <algorithm>
 #include <set>
 #include <tuple>
+#include <unordered_map>
 #include <utility>
 
 #include "base/auto_reset.h"
@@ -16,6 +17,7 @@
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/containers/hash_tables.h"
+#include "base/hash.h"
 #include "base/i18n/rtl.h"
 #include "base/lazy_instance.h"
 #include "base/location.h"
@@ -149,7 +151,9 @@
 // <process id, routing id>
 using RenderWidgetHostID = std::pair<int32_t, int32_t>;
 using RoutingIDWidgetMap =
-    base::hash_map<RenderWidgetHostID, RenderWidgetHostImpl*>;
+    std::unordered_map<RenderWidgetHostID,
+                       RenderWidgetHostImpl*,
+                       base::IntPairHash<RenderWidgetHostID>>;
 base::LazyInstance<RoutingIDWidgetMap>::DestructorAtExit
     g_routing_id_widget_map = LAZY_INSTANCE_INITIALIZER;
 
diff --git a/content/browser/resources/gpu/browser_bridge.js b/content/browser/resources/gpu/browser_bridge.js
index ee63b97..cf165707 100644
--- a/content/browser/resources/gpu/browser_bridge.js
+++ b/content/browser/resources/gpu/browser_bridge.js
@@ -148,8 +148,9 @@
     isSandboxedForTesting: function() {
       for (i = 0; i < this.gpuInfo_.basicInfo.length; ++i) {
         var info = this.gpuInfo_.basicInfo[i];
-        if (info.description == 'Sandboxed')
+        if (info.description == 'Sandboxed') {
           return info.value;
+        }
       }
       return false;
     }
diff --git a/content/browser/resources/gpu/info_view.js b/content/browser/resources/gpu/info_view.js
index e11e06b..01bdf10 100644
--- a/content/browser/resources/gpu/info_view.js
+++ b/content/browser/resources/gpu/info_view.js
@@ -150,25 +150,29 @@
           workaroundsForHardwareGpuDiv.hidden = true;
         }
 
-        if (gpuInfo.basicInfo)
+        if (gpuInfo.basicInfo) {
           this.setTable_('basic-info', gpuInfo.basicInfo);
-        else
+        } else {
           this.setTable_('basic-info', []);
+        }
 
-        if (gpuInfo.compositorInfo)
+        if (gpuInfo.compositorInfo) {
           this.setTable_('compositor-info', gpuInfo.compositorInfo);
-        else
+        } else {
           this.setTable_('compositor-info', []);
+        }
 
-        if (gpuInfo.gpuMemoryBufferInfo)
+        if (gpuInfo.gpuMemoryBufferInfo) {
           this.setTable_('gpu-memory-buffer-info', gpuInfo.gpuMemoryBufferInfo);
-        else
+        } else {
           this.setTable_('gpu-memory-buffer-info', []);
+        }
 
-        if (gpuInfo.displayInfo)
+        if (gpuInfo.displayInfo) {
           this.setTable_('display-info', gpuInfo.displayInfo);
-        else
+        } else {
           this.setTable_('display-info', []);
+        }
 
         if (gpuInfo.videoAcceleratorsInfo) {
           this.setTable_(
@@ -265,8 +269,9 @@
         var featureEl = document.createElement('li');
 
         var nameEl = document.createElement('span');
-        if (!featureLabelMap[featureName])
+        if (!featureLabelMap[featureName]) {
           console.log('Missing featureLabel for', featureName);
+        }
         nameEl.textContent = featureLabelMap[featureName] + ': ';
         featureEl.appendChild(nameEl);
 
@@ -355,10 +360,11 @@
         problemEl.appendChild(iNode);
 
         var headNode = document.createElement('span');
-        if (problem.tag == 'disabledFeatures')
+        if (problem.tag == 'disabledFeatures') {
           headNode.textContent = 'Disabled Features: ';
-        else  // problem.tag == 'workarounds'
+        } else {  // problem.tag == 'workarounds'
           headNode.textContent = 'Applied Workarounds: ';
+        }
         iNode.appendChild(headNode);
         for (j = 0; j < problem.affectedGpuSettings.length; ++j) {
           if (j > 0) {
@@ -367,10 +373,11 @@
             iNode.appendChild(separateNode);
           }
           var nameNode = document.createElement('span');
-          if (problem.tag == 'disabledFeatures')
+          if (problem.tag == 'disabledFeatures') {
             nameNode.classList.add('feature-red');
-          else  // problem.tag == 'workarounds'
+          } else {  // problem.tag == 'workarounds'
             nameNode.classList.add('feature-yellow');
+          }
           nameNode.textContent = problem.affectedGpuSettings[j];
           iNode.appendChild(nameNode);
         }
@@ -389,8 +396,9 @@
       jstProcess(new JsEvalContext({value: inputData}), template);
 
       var peg = $(outputElementId);
-      if (!peg)
+      if (!peg) {
         throw new Error('Node ' + outputElementId + ' not found');
+      }
 
       peg.innerHTML = '';
       peg.appendChild(template);
diff --git a/content/browser/resources/histograms/histograms_internals.js b/content/browser/resources/histograms/histograms_internals.js
index 428fc36..4be9ac7 100644
--- a/content/browser/resources/histograms/histograms_internals.js
+++ b/content/browser/resources/histograms/histograms_internals.js
@@ -7,8 +7,9 @@
  */
 function requestHistograms() {
   let query = '';
-  if (document.location.pathname)
+  if (document.location.pathname) {
     query = document.location.pathname.substring(1);
+  }
   cr.sendWithPromise('requestHistograms', query).then(addHistograms);
 }
 
@@ -19,8 +20,9 @@
  */
 function addHistograms(histograms) {
   let htmlOutput = '';
-  for (let histogram of histograms)
+  for (let histogram of histograms) {
     htmlOutput += histogram;
+  }
 
   // NOTE: This is generally unsafe due to XSS attacks. Make sure |htmlOutput|
   // cannot be modified by an external party.
diff --git a/content/browser/resources/media/client_renderer.js b/content/browser/resources/media/client_renderer.js
index 53ebfbee..56b8b58 100644
--- a/content/browser/resources/media/client_renderer.js
+++ b/content/browser/resources/media/client_renderer.js
@@ -6,14 +6,17 @@
   var ClientRenderer = function() {
     this.playerListElement = $('player-list');
     var audioTableElement = $('audio-property-table');
-    if (audioTableElement)
+    if (audioTableElement) {
       this.audioPropertiesTable = audioTableElement.querySelector('tbody');
+    }
     var playerTableElement = $('player-property-table');
-    if (playerTableElement)
+    if (playerTableElement) {
       this.playerPropertiesTable = playerTableElement.querySelector('tbody');
+    }
     var logElement = $('log');
-    if (logElement)
+    if (logElement) {
       this.logTable = logElement.querySelector('tbody');
+    }
     this.graphElement = $('graphs');
     this.audioPropertyName = $('audio-property-name');
     this.audioFocusSessionListElement_ = $('audio-focus-session-list');
@@ -35,13 +38,15 @@
       return true;
     };
     this.filterText = $('filter-text');
-    if (this.filterText)
+    if (this.filterText) {
       this.filterText.onkeyup = this.onTextChange_.bind(this);
+    }
     this.clipboardDialog = $('clipboard-dialog');
 
     this.clipboardTextarea = $('clipboard-textarea');
-    if (this.clipboardTextarea)
+    if (this.clipboardTextarea) {
       this.clipboardTextarea.onblur = this.hideClipboard_.bind(this);
+    }
     var clipboardButtons = document.getElementsByClassName('copy-button');
     if (clipboardButtons) {
       for (var i = 0; i < clipboardButtons.length; i++) {
@@ -50,8 +55,9 @@
     }
 
     this.saveLogButton = $('save-log-button');
-    if (this.saveLogButton)
+    if (this.saveLogButton) {
       this.saveLogButton.onclick = this.saveLog_.bind(this);
+    }
 
     this.hiddenKeys = ['component_id', 'component_type', 'owner_id'];
 
@@ -87,8 +93,9 @@
     radioButton.name = groupName;
 
     buttonLabel.classList.add(ClientRenderer.Css_.SELECTABLE_BUTTON);
-    if (isDestructed)
+    if (isDestructed) {
       buttonLabel.classList.add(ClientRenderer.Css_.DESTRUCTED_PLAYER);
+    }
     buttonLabel.setAttribute('for', radioButton.id);
 
     var fragment = document.createDocumentFragment();
@@ -213,8 +220,9 @@
         this.drawProperties_(player.properties, this.playerPropertiesTable);
         this.drawLog_();
       }
-      if (key === 'event' && value === 'WEBMEDIAPLAYER_DESTROYED')
+      if (key === 'event' && value === 'WEBMEDIAPLAYER_DESTROYED') {
         player.destructed = true;
+      }
       if ([
             'url', 'frame_url', 'frame_title', 'audio_codec_name',
             'video_codec_name', 'width', 'height', 'event'
@@ -224,8 +232,9 @@
     },
 
     createVideoCaptureFormatTable: function(formats) {
-      if (!formats || formats.length == 0)
+      if (!formats || formats.length == 0) {
         return document.createTextNode('No formats');
+      }
 
       var table = document.createElement('table');
       var thead = document.createElement('thead');
@@ -390,10 +399,12 @@
         label.appendChild(nameNode);
 
         var frame = [];
-        if (p.frame_title)
+        if (p.frame_title) {
           frame.push(p.frame_title);
-        if (p.frame_url)
+        }
+        if (p.frame_url) {
           frame.push(p.frame_url);
+        }
         var frameText = frame.join(' - ');
         if (frameText) {
           var frameNode = document.createElement('div');
@@ -403,16 +414,21 @@
         }
 
         var desc = [];
-        if (p.width && p.height)
+        if (p.width && p.height) {
           desc.push(p.width + 'x' + p.height);
-        if (p.video_codec_name)
+        }
+        if (p.video_codec_name) {
           desc.push(p.video_codec_name);
-        if (p.video_codec_name && p.audio_codec_name)
+        }
+        if (p.video_codec_name && p.audio_codec_name) {
           desc.push('+');
-        if (p.audio_codec_name)
+        }
+        if (p.audio_codec_name) {
           desc.push(p.audio_codec_name);
-        if (p.event)
+        }
+        if (p.event) {
           desc.push('(' + p.event + ')');
+        }
         var descText = desc.join(' ');
         if (descText) {
           var descNode = document.createElement('div');
@@ -458,8 +474,9 @@
       var sortedKeys = Object.keys(propertyMap).sort();
       for (var i = 0; i < sortedKeys.length; ++i) {
         var key = sortedKeys[i];
-        if (this.hiddenKeys.indexOf(key) >= 0)
+        if (this.hiddenKeys.indexOf(key) >= 0) {
           continue;
+        }
 
         var value = propertyMap[key];
         var row = propertiesTable.insertRow(-1);
@@ -508,8 +525,9 @@
     },
 
     hideClipboard_: function() {
-      if (this.clipboardDialog.open)
+      if (this.clipboardDialog.open) {
         this.clipboardDialog.close();
+      }
     },
 
     copyToClipboard_: function() {
diff --git a/content/browser/resources/media/data_series.js b/content/browser/resources/media/data_series.js
index adf4c2a..83e714b 100644
--- a/content/browser/resources/media/data_series.js
+++ b/content/browser/resources/media/data_series.js
@@ -34,8 +34,9 @@
      * @override
      */
     toJSON: function() {
-      if (this.dataPoints_.length < 1)
+      if (this.dataPoints_.length < 1) {
         return {};
+      }
 
       var values = [];
       for (var i = 0; i < this.dataPoints_.length; ++i) {
@@ -56,8 +57,9 @@
       var time = new Date(timeTicks);
       this.dataPoints_.push(new DataPoint(time, value));
 
-      if (this.dataPoints_.length > MAX_STATS_DATA_POINT_BUFFER_SIZE)
+      if (this.dataPoints_.length > MAX_STATS_DATA_POINT_BUFFER_SIZE) {
         this.dataPoints_.shift();
+      }
     },
 
     isVisible: function() {
diff --git a/content/browser/resources/media/main.js b/content/browser/resources/media/main.js
index 952e711..4f5287d 100644
--- a/content/browser/resources/media/main.js
+++ b/content/browser/resources/media/main.js
@@ -32,8 +32,9 @@
   };
 
   media.onReceiveAudioFocusState = function(audioFocusState) {
-    if (!audioFocusState)
+    if (!audioFocusState) {
       return;
+    }
 
     manager.updateAudioFocusSessions(audioFocusState.sessions);
   };
diff --git a/content/browser/resources/media/manager.js b/content/browser/resources/media/manager.js
index 2b6c72d..4ab6051 100644
--- a/content/browser/resources/media/manager.js
+++ b/content/browser/resources/media/manager.js
@@ -67,8 +67,9 @@
      * @param componentData The actual component data dictionary.
      */
     updateAudioComponent: function(componentType, componentId, componentData) {
-      if (!(componentType in this.audioComponents_))
+      if (!(componentType in this.audioComponents_)) {
         this.audioComponents_[componentType] = {};
+      }
       if (!(componentId in this.audioComponents_[componentType])) {
         this.audioComponents_[componentType][componentId] = componentData;
       } else {
diff --git a/content/browser/resources/media/media_internals.js b/content/browser/resources/media/media_internals.js
index 51f884e..cea2556d 100644
--- a/content/browser/resources/media/media_internals.js
+++ b/content/browser/resources/media/media_internals.js
@@ -11,5 +11,6 @@
 // <include src="client_renderer.js">
 
 media.initialize(new Manager(new ClientRenderer()));
-if (cr.ui)
+if (cr.ui) {
   cr.ui.decorate('tabbox', cr.ui.TabBox);
+}
diff --git a/content/browser/resources/media/ssrc_info_manager.js b/content/browser/resources/media/ssrc_info_manager.js
index b86c145..a3038de 100644
--- a/content/browser/resources/media/ssrc_info_manager.js
+++ b/content/browser/resources/media/ssrc_info_manager.js
@@ -98,26 +98,30 @@
       var attributes = sdp.split(this.ATTRIBUTE_SEPARATOR_);
       for (var i = 0; i < attributes.length; ++i) {
         // Check if this is a ssrc attribute.
-        if (attributes[i].indexOf(this.SSRC_ATTRIBUTE_PREFIX_) != 0)
+        if (attributes[i].indexOf(this.SSRC_ATTRIBUTE_PREFIX_) != 0) {
           continue;
+        }
 
         var nextFieldIndex = attributes[i].search(this.FIELD_SEPARATOR_REGEX_);
 
-        if (nextFieldIndex == -1)
+        if (nextFieldIndex == -1) {
           continue;
+        }
 
         var ssrc = attributes[i].substring(
             this.SSRC_ATTRIBUTE_PREFIX_.length, nextFieldIndex);
-        if (!this.streamInfoContainer_[ssrc])
+        if (!this.streamInfoContainer_[ssrc]) {
           this.streamInfoContainer_[ssrc] = {};
+        }
 
         // Make |rest| starting at the next field.
         var rest = attributes[i].substring(nextFieldIndex + 1);
         var name, value;
         while (rest.length > 0) {
           nextFieldIndex = rest.search(this.FIELD_SEPARATOR_REGEX_);
-          if (nextFieldIndex == -1)
+          if (nextFieldIndex == -1) {
             nextFieldIndex = rest.length;
+          }
 
           // The field name is the string before the colon.
           name = rest.substring(0, rest.indexOf(':'));
@@ -147,8 +151,9 @@
      * @param {string} ssrc The ssrc id.
      */
     populateSsrcInfo: function(parentElement, ssrc) {
-      if (!this.streamInfoContainer_[ssrc])
+      if (!this.streamInfoContainer_[ssrc]) {
         return;
+      }
 
       parentElement.className = this.SSRC_INFO_BLOCK_CLASS;
 
diff --git a/content/browser/resources/media/stats_graph_helper.js b/content/browser/resources/media/stats_graph_helper.js
index 7ff12e3..14f9fc7 100644
--- a/content/browser/resources/media/stats_graph_helper.js
+++ b/content/browser/resources/media/stats_graph_helper.js
@@ -77,8 +77,9 @@
     convertFunction: function(srcDataSeries) {
       var length = srcDataSeries.dataPoints_.length;
       var lastDataPoint = srcDataSeries.dataPoints_[length - 1];
-      if (lastDataPoint.value < 5000)
+      if (lastDataPoint.value < 5000) {
         return lastDataPoint.value * 1000;
+      }
       return lastDataPoint.value;
     }
   }
@@ -100,8 +101,9 @@
 
 // Returns number parsed from |value|, or NaN if the stats name is black-listed.
 function getNumberFromValue(name, value) {
-  if (statsNameBlackList[name])
+  if (statsNameBlackList[name]) {
     return NaN;
+  }
   return parseFloat(value);
 }
 
@@ -111,8 +113,9 @@
   var reportType = report.type;
   var reportId = report.id;
   var stats = report.stats;
-  if (!stats || !stats.values)
+  if (!stats || !stats.values) {
     return;
+  }
 
   for (var i = 0; i < stats.values.length - 1; i = i + 2) {
     var rawLabel = stats.values[i];
@@ -174,8 +177,9 @@
     var dataSeries =
         peerConnectionDataStore[peerConnectionElement.id].getDataSeries(
             finalDataSeriesId);
-    if (!graphViews[graphViewId].hasDataSeries(dataSeries))
+    if (!graphViews[graphViewId].hasDataSeries(dataSeries)) {
       graphViews[graphViewId].addDataSeries(dataSeries);
+    }
     graphViews[graphViewId].updateEndDate();
   }
 }
@@ -196,8 +200,9 @@
       dataSeries.setColor(bweCompoundGraphConfig[label].color);
     }
   }
-  for (var i = 0; i < times.length; ++i)
+  for (var i = 0; i < times.length; ++i) {
     dataSeries.addPoint(times[i], values[i]);
+  }
 }
 
 // Draws the received propagation deltas using the packet group arrival time as
@@ -216,8 +221,9 @@
     }
   }
   // Unexpected.
-  if (times == null)
+  if (times == null) {
     return;
+  }
 
   // Convert |deltas| and |times| from strings to arrays of numbers.
   try {
@@ -254,8 +260,9 @@
 // can be deduced from existing stats labels. Otherwise empty string for
 // non-SSRC reports or where type (audio/video) can't be deduced.
 function getSsrcReportType(report) {
-  if (report.type != 'ssrc')
+  if (report.type != 'ssrc') {
     return '';
+  }
   if (report.stats && report.stats.values) {
     // Known stats keys for audio send/receive streams.
     if (report.stats.values.indexOf('audioOutputLevel') != -1 ||
@@ -291,8 +298,9 @@
     container.firstChild.firstChild.textContent =
         'Stats graphs for ' + report.id + ' (' + report.type + ')';
     var statsType = getSsrcReportType(report);
-    if (statsType != '')
+    if (statsType != '') {
       container.firstChild.firstChild.textContent += ' (' + statsType + ')';
+    }
 
     if (report.type == 'ssrc') {
       var ssrcInfoElement = document.createElement('div');
diff --git a/content/browser/resources/media/tab_view.js b/content/browser/resources/media/tab_view.js
index 84897f5..b68b8f2 100644
--- a/content/browser/resources/media/tab_view.js
+++ b/content/browser/resources/media/tab_view.js
@@ -47,8 +47,9 @@
      * @return {!Element} The tab body element.
      */
     addTab: function(id, title) {
-      if (this.tabElements_[id])
+      if (this.tabElements_[id]) {
         throw 'Tab already exists: ' + id;
+      }
 
       var head = document.createElement('span');
       head.className = this.TAB_HEAD_CLASS_;
@@ -72,8 +73,9 @@
 
     /** Removes the tab. @param {string} id */
     removeTab: function(id) {
-      if (!this.tabElements_[id])
+      if (!this.tabElements_[id]) {
         return;
+      }
       this.tabElements_[id].head.parentNode.removeChild(
           this.tabElements_[id].head);
       this.tabElements_[id].body.parentNode.removeChild(
diff --git a/content/browser/resources/media/timeline_graph_view.js b/content/browser/resources/media/timeline_graph_view.js
index 298e273..186957c 100644
--- a/content/browser/resources/media/timeline_graph_view.js
+++ b/content/browser/resources/media/timeline_graph_view.js
@@ -82,13 +82,15 @@
      */
     updateScrollbarRange_: function(resetPosition) {
       var scrollbarRange = this.getLength_() - this.canvas_.width;
-      if (scrollbarRange < 0)
+      if (scrollbarRange < 0) {
         scrollbarRange = 0;
+      }
 
       // If we've decreased the range to less than the current scroll position,
       // we need to move the scroll position.
-      if (this.scrollbar_.position_ > scrollbarRange)
+      if (this.scrollbar_.position_ > scrollbarRange) {
         resetPosition = true;
+      }
 
       this.scrollbar_.range_ = scrollbarRange;
       if (resetPosition) {
@@ -106,8 +108,9 @@
       this.endTime_ = endDate.getTime();
 
       // Safety check.
-      if (this.endTime_ <= this.startTime_)
+      if (this.endTime_ <= this.startTime_) {
         this.startTime_ = this.endTime_ - 1;
+      }
 
       this.updateScrollbarRange_(true);
     },
@@ -133,8 +136,9 @@
     setDataSeries: function(dataSeries) {
       // Simply recreates the Graph.
       this.graph_ = new Graph();
-      for (var i = 0; i < dataSeries.length; ++i)
+      for (var i = 0; i < dataSeries.length; ++i) {
         this.graph_.addDataSeries(dataSeries[i]);
+      }
       this.repaint();
     },
 
@@ -142,8 +146,9 @@
      * Adds |dataSeries| to the current graph.
      */
     addDataSeries: function(dataSeries) {
-      if (!this.graph_)
+      if (!this.graph_) {
         this.graph_ = new Graph();
+      }
       this.graph_.addDataSeries(dataSeries);
       this.repaint();
     },
@@ -188,8 +193,9 @@
       var position = this.scrollbar_.position_;
       // If the entire time range is being displayed, align the right edge of
       // the graph to the end of the time range.
-      if (this.scrollbar_.range_ == 0)
+      if (this.scrollbar_.range_ == 0) {
         position = this.getLength_() - this.canvas_.width;
+      }
       var visibleStartTime = this.startTime_ + position * this.scale_;
 
       // Make space at the bottom of the graph for the time labels, and then
@@ -238,8 +244,9 @@
       // Draw labels and vertical grid lines.
       while (true) {
         var x = Math.round((time - startTime) / this.scale_);
-        if (x >= width)
+        if (x >= width) {
           break;
+        }
         var text = (new Date(time)).toLocaleTimeString();
         context.fillText(text, x, textHeight);
         context.beginPath();
@@ -251,14 +258,16 @@
     },
 
     getDataSeriesCount: function() {
-      if (this.graph_)
+      if (this.graph_) {
         return this.graph_.dataSeries_.length;
+      }
       return 0;
     },
 
     hasDataSeries: function(dataSeries) {
-      if (this.graph_)
+      if (this.graph_) {
         return this.graph_.hasDataSeries(dataSeries);
+      }
       return false;
     },
 
@@ -309,8 +318,9 @@
 
       hasDataSeries: function(dataSeries) {
         for (var i = 0; i < this.dataSeries_.length; ++i) {
-          if (this.dataSeries_[i] == dataSeries)
+          if (this.dataSeries_[i] == dataSeries) {
             return true;
+          }
         }
         return false;
       },
@@ -320,8 +330,9 @@
        * data series, using the current graph layout.
        */
       getValues: function(dataSeries) {
-        if (!dataSeries.isVisible())
+        if (!dataSeries.isVisible()) {
           return null;
+        }
         return dataSeries.getValues(this.startTime_, this.scale_, this.width_);
       },
 
@@ -341,13 +352,15 @@
         var max = 0, min = 0;
         for (var i = 0; i < this.dataSeries_.length; ++i) {
           var values = this.getValues(this.dataSeries_[i]);
-          if (!values)
+          if (!values) {
             continue;
+          }
           for (var j = 0; j < values.length; ++j) {
-            if (values[j] > max)
+            if (values[j] > max) {
               max = values[j];
-            else if (values[j] < min)
+            } else if (values[j] < min) {
               min = values[j];
+            }
           }
         }
 
@@ -383,8 +396,9 @@
         this.layoutLabelsBasic_(minValue, maxValue, MAX_DECIMAL_PRECISION);
 
         // Append units to labels.
-        for (var i = 0; i < this.labels_.length; ++i)
+        for (var i = 0; i < this.labels_.length; ++i) {
           this.labels_[i] += ' ' + units[unit];
+        }
 
         // Convert |min_|/|max_| back to unit '1'.
         this.min_ *= Math.pow(1024, unit);
@@ -434,8 +448,9 @@
           // the top of the graph.
 
           // Check if we can use steps of size |stepSize|.
-          if (Math.ceil(range / stepSize) + 1 <= maxLabels)
+          if (Math.ceil(range / stepSize) + 1 <= maxLabels) {
             break;
+          }
           // Check |stepSize| * 2.
           if (Math.ceil(range / (stepSize * 2)) + 1 <= maxLabels) {
             stepSize *= 2;
@@ -447,8 +462,9 @@
             break;
           }
           stepSize *= 10;
-          if (stepSizeDecimalDigits > 0)
+          if (stepSizeDecimalDigits > 0) {
             --stepSizeDecimalDigits;
+          }
         }
 
         // Set the min/max so it's an exact multiple of the chosen step size.
@@ -456,8 +472,9 @@
         this.min_ = Math.floor(minValue / stepSize) * stepSize;
 
         // Create labels.
-        for (var label = this.max_; label >= this.min_; label -= stepSize)
+        for (var label = this.max_; label >= this.min_; label -= stepSize) {
           this.labels_.push(label.toFixed(stepSizeDecimalDigits));
+        }
       },
 
       /**
@@ -489,15 +506,17 @@
         // 0 to height - 1.
         var scale = 0;
         var bottom = this.height_ - 1;
-        if (this.max_)
+        if (this.max_) {
           scale = bottom / (this.max_ - this.min_);
+        }
 
         // Draw in reverse order, so earlier data series are drawn on top of
         // subsequent ones.
         for (var i = this.dataSeries_.length - 1; i >= 0; --i) {
           var values = this.getValues(this.dataSeries_[i]);
-          if (!values)
+          if (!values) {
             continue;
+          }
           context.strokeStyle = this.dataSeries_[i].getColor();
           context.beginPath();
           for (var x = 0; x < values.length; ++x) {
@@ -514,8 +533,9 @@
        * Draw labels in |labels_|.
        */
       drawLabels: function(context) {
-        if (this.labels_.length == 0)
+        if (this.labels_.length == 0) {
           return;
+        }
         var x = this.width_ - LABEL_HORIZONTAL_SPACING;
 
         // Set up the context.
@@ -530,8 +550,9 @@
         // Draw all the other labels.
         context.textBaseline = 'bottom';
         var step = (this.height_ - 1) / (this.labels_.length - 1);
-        for (var i = 1; i < this.labels_.length; ++i)
+        for (var i = 1; i < this.labels_.length; ++i) {
           context.fillText(this.labels_[i], x, step * i);
+        }
       }
     };
 
diff --git a/content/browser/resources/media/webrtc_internals.js b/content/browser/resources/media/webrtc_internals.js
index 2ed3b3b0..2b420e5 100644
--- a/content/browser/resources/media/webrtc_internals.js
+++ b/content/browser/resources/media/webrtc_internals.js
@@ -109,8 +109,9 @@
 
 /** Sends a request to the browser to get peer connection statistics. */
 function requestStats() {
-  if (Object.keys(peerConnectionDataStore).length > 0)
+  if (Object.keys(peerConnectionDataStore).length > 0) {
     chrome.send('getAllStats');
+  }
 }
 
 
@@ -238,8 +239,9 @@
     var peerConnection = addPeerConnection(data[i]);
 
     var log = data[i].log;
-    if (!log)
+    if (!log) {
       continue;
+    }
     for (var j = 0; j < log.length; ++j) {
       addPeerConnectionUpdate(peerConnection, log[j]);
     }
@@ -259,8 +261,9 @@
  */
 function addStats(data) {
   var peerConnectionElement = $(getPeerConnectionId(data));
-  if (!peerConnectionElement)
+  if (!peerConnectionElement) {
     return;
+  }
 
   for (var i = 0; i < data.reports.length; ++i) {
     var report = data.reports[i];
@@ -307,17 +310,20 @@
  */
 function removeGetUserMediaForRenderer(data) {
   for (var i = userMediaRequests.length - 1; i >= 0; --i) {
-    if (userMediaRequests[i].rid == data.rid)
+    if (userMediaRequests[i].rid == data.rid) {
       userMediaRequests.splice(i, 1);
+    }
   }
 
   var requests = $(USER_MEDIA_TAB_ID).childNodes;
   for (var i = 0; i < requests.length; ++i) {
-    if (requests[i].rid == data.rid)
+    if (requests[i].rid == data.rid) {
       $(USER_MEDIA_TAB_ID).removeChild(requests[i]);
+    }
   }
-  if ($(USER_MEDIA_TAB_ID).childNodes.length == 0)
+  if ($(USER_MEDIA_TAB_ID).childNodes.length == 0) {
     tabView.removeTab(USER_MEDIA_TAB_ID);
+  }
 }
 
 
diff --git a/content/browser/resources/process/process_internals.js b/content/browser/resources/process/process_internals.js
index 0315df8..788028e 100644
--- a/content/browser/resources/process/process_internals.js
+++ b/content/browser/resources/process/process_internals.js
@@ -28,16 +28,18 @@
     tabContent.classList.toggle('selected', isTargetTab);
     tabHeader.classList.toggle('selected', isTargetTab);
   }
-  if (!found)
+  if (!found) {
     return false;
+  }
   window.location.hash = id;
   return true;
 }
 
 function onHashChange() {
   let hash = window.location.hash.slice(1).toLowerCase();
-  if (!selectTab(hash))
+  if (!selectTab(hash)) {
     selectTab('general');
+  }
 }
 
 function setupTabs() {
@@ -87,12 +89,15 @@
   // Compose the string which will appear in the entry for this frame.
   let itemLabel = `Frame[${frame.processId}:${frame.routingId}]:`;
   itemLabel += ` SI:${frame.siteInstance.id}`;
-  if (frame.siteInstance.locked)
+  if (frame.siteInstance.locked) {
     itemLabel += ', locked';
-  if (frame.siteInstance.siteUrl)
+  }
+  if (frame.siteInstance.siteUrl) {
     itemLabel += `, site:${frame.siteInstance.siteUrl.url}`;
-  if (frame.lastCommittedUrl)
+  }
+  if (frame.lastCommittedUrl) {
     itemLabel += ` | url: ${frame.lastCommittedUrl.url}`;
+  }
 
   let item = new cr.ui.TreeItem(
       {label: itemLabel, detail: {payload: {}, children: {}}});
@@ -121,8 +126,9 @@
  */
 function webContentsToTreeItem(webContents) {
   let itemLabel = 'WebContents: ';
-  if (webContents.title.length > 0)
+  if (webContents.title.length > 0) {
     itemLabel += webContents.title + ', ';
+  }
 
   let item = new cr.ui.TreeItem(
       {label: itemLabel, detail: {payload: {}, children: {}}});
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
index 99ef195..bb8a3bad 100644
--- a/content/browser/site_instance_impl.cc
+++ b/content/browser/site_instance_impl.cc
@@ -629,19 +629,7 @@
 
 // static
 base::Optional<url::Origin> SiteInstanceImpl::GetRequestInitiatorSiteLock(
-    BrowserContext* browser_context,
     GURL site_url) {
-  DCHECK(browser_context);
-
-  // |site_url| cannot be used as |request_initiator_site_lock| unless the site
-  // requires a dedicated process.  Otherwise b.com may share a process
-  // associated with a.com, in a SiteInstance with |site_url| set to
-  // "http://a.com" (and/or "http://nonisolated.invalid" in the future) and in
-  // that scenario |request_initiator| for requests from b.com should NOT be
-  // locked to a.com
-  if (!ShouldLockToOrigin(browser_context, site_url))
-    return base::nullopt;
-
   // The following schemes are safe for sites that require a process lock:
   // - data: - locking |request_initiator| to an opaque origin
   // - http/https - requiring |request_initiator| to match |site_url| with
diff --git a/content/browser/site_instance_impl.h b/content/browser/site_instance_impl.h
index 80394515..992034a6 100644
--- a/content/browser/site_instance_impl.h
+++ b/content/browser/site_instance_impl.h
@@ -244,10 +244,8 @@
   //
   // base::nullopt is returned if |site_url| cannot be used as a
   // |request_initiator_site_lock| (e.g. in case of site_url =
-  // chrome-guest://... OR if |site_url| doesn't require a dedicated process).
-  static base::Optional<url::Origin> GetRequestInitiatorSiteLock(
-      BrowserContext* browser_context,
-      GURL site_url);
+  // chrome-guest://...).
+  static base::Optional<url::Origin> GetRequestInitiatorSiteLock(GURL site_url);
 
  private:
   friend class BrowsingInstance;
diff --git a/content/browser/webui/shared_resources_data_source.cc b/content/browser/webui/shared_resources_data_source.cc
index a4265860..79bbb9a 100644
--- a/content/browser/webui/shared_resources_data_source.cc
+++ b/content/browser/webui/shared_resources_data_source.cc
@@ -44,7 +44,7 @@
   int idr;
   bool gzipped;
 };
-using ResourcesMap = base::hash_map<std::string, IdrGzipped>;
+using ResourcesMap = std::unordered_map<std::string, IdrGzipped>;
 
 const std::map<std::string, std::string> CreatePathPrefixAliasesMap() {
   // TODO(rkc): Once we have a separate source for apps, remove '*/apps/'
diff --git a/content/common/content_ipc_logging.cc b/content/common/content_ipc_logging.cc
index 9d2a27d..438ea7b 100644
--- a/content/common/content_ipc_logging.cc
+++ b/content/common/content_ipc_logging.cc
@@ -21,7 +21,7 @@
 
 namespace {
 
-base::LazyInstance<base::hash_map<uint32_t, LogFunction>>::Leaky
+base::LazyInstance<std::unordered_map<uint32_t, LogFunction>>::Leaky
     g_log_function_mapping = LAZY_INSTANCE_INITIALIZER;
 
 }  // namespace
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/LoadUrlParams.java b/content/public/android/java/src/org/chromium/content_public/browser/LoadUrlParams.java
index 7d8b82f..0805b7b 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/LoadUrlParams.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/LoadUrlParams.java
@@ -4,6 +4,8 @@
 
 package org.chromium.content_public.browser;
 
+import android.support.annotation.Nullable;
+
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.content_public.browser.navigation_controller.LoadURLType;
@@ -27,6 +29,9 @@
     // native code. Should not be accessed directly anywhere else outside of
     // this class.
     String mUrl;
+    // TODO(nasko,tedchoc): Don't use String to store initiator origin, as it
+    // is lossy format.
+    String mInitiatorOrigin;
     int mLoadUrlType;
     int mTransitionType;
     Referrer mReferrer;
@@ -200,6 +205,20 @@
     }
 
     /**
+     * Sets the initiator origin.
+     */
+    public void setInitiatorOrigin(String initiatorOrigin) {
+        mInitiatorOrigin = initiatorOrigin;
+    }
+
+    /**
+     * Return the initiator origin.
+     */
+    public @Nullable String getInitiatorOrigin() {
+        return mInitiatorOrigin;
+    }
+
+    /**
      * Return the base url for a data url, otherwise null.
      */
     public String getBaseUrl() {
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index d2830b6..5485aa4c 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -4,7 +4,6 @@
 
 import("//build/config/jumbo.gni")
 import("//build/config/ui.gni")
-import("//third_party/webrtc/webrtc.gni")
 
 # See //content/BUILD.gn for how this works.
 group("browser") {
@@ -336,11 +335,6 @@
     "//content:content_implementation",
   ]
 
-  if (rtc_use_pipewire) {
-    configs +=
-        [ "//third_party/webrtc/modules/desktop_capture:pipewire_config" ]
-  }
-
   public_deps = [
     "//components/download/public/common:public",
     "//content/public/common:common_sources",
diff --git a/content/public/browser/desktop_capture.cc b/content/public/browser/desktop_capture.cc
index 95bc55c..2d41880 100644
--- a/content/public/browser/desktop_capture.cc
+++ b/content/public/browser/desktop_capture.cc
@@ -30,11 +30,6 @@
     options.set_allow_iosurface(true);
   }
 #endif
-#if defined(WEBRTC_USE_PIPEWIRE)
-  if (base::FeatureList::IsEnabled(features::kWebRtcPipeWireCapturer)) {
-    options.set_allow_pipewire(true);
-  }
-#endif  // defined(WEBRTC_USE_PIPEWIRE)
   return options;
 }
 
diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn
index 0287c73..11b8632 100644
--- a/content/public/common/BUILD.gn
+++ b/content/public/common/BUILD.gn
@@ -297,11 +297,6 @@
     public_deps += [ "//media/capture/video/chromeos/public" ]
   }
 
-  if (rtc_use_pipewire) {
-    configs +=
-        [ "//third_party/webrtc/modules/desktop_capture:pipewire_config" ]
-  }
-
   # //content/common needs to include public headers.
   allow_circular_includes_from = [
     ":interfaces",
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index c90becd..10bd4f4 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -697,13 +697,6 @@
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 #endif  // defined(OS_MACOSX)
 
-#if defined(WEBRTC_USE_PIPEWIRE)
-// Controls whether the PipeWire support for screen capturing is enabled on the
-// Wayland display server.
-const base::Feature kWebRtcPipeWireCapturer{"WebRTCPipeWireCapturer",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
-#endif  // defined(WEBRTC_USE_PIPEWIRE)
-
 enum class VideoCaptureServiceConfiguration {
   kEnabledForOutOfProcess,
   kEnabledForBrowserProcess,
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index d14d7d2..10fbe0aa9 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -167,10 +167,6 @@
 CONTENT_EXPORT extern const base::Feature kTextSuggestionsTouchBar;
 #endif  // defined(OS_MACOSX)
 
-#if defined(WEBRTC_USE_PIPEWIRE)
-CONTENT_EXPORT extern const base::Feature kWebRtcPipeWireCapturer;
-#endif  // defined(WEBRTC_USE_PIPEWIRE)
-
 // DON'T ADD RANDOM STUFF HERE. Put it in the main section above in
 // alphabetical order, or in one of the ifdefs (also in order in each section).
 
diff --git a/content/public/common/navigation_policy.cc b/content/public/common/navigation_policy.cc
index a8b4e7c..2bfec2c1 100644
--- a/content/public/common/navigation_policy.cc
+++ b/content/public/common/navigation_policy.cc
@@ -11,10 +11,6 @@
 
 namespace content {
 
-bool IsBrowserSideNavigationEnabled() {
-  return true;
-}
-
 bool IsPerNavigationMojoInterfaceEnabled() {
   return base::FeatureList::IsEnabled(features::kPerNavigationMojoInterface);
 }
diff --git a/content/public/common/navigation_policy.h b/content/public/common/navigation_policy.h
index 9795775..87fc81b 100644
--- a/content/public/common/navigation_policy.h
+++ b/content/public/common/navigation_policy.h
@@ -12,7 +12,6 @@
 
 namespace content {
 
-CONTENT_EXPORT bool IsBrowserSideNavigationEnabled();
 CONTENT_EXPORT bool IsPerNavigationMojoInterfaceEnabled();
 CONTENT_EXPORT bool IsBackForwardCacheEnabled();
 
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc
index f9d571c..f3b2d3d 100644
--- a/content/renderer/accessibility/render_accessibility_impl.cc
+++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -551,7 +551,7 @@
     return;
 
   // Do a breadth-first explore of the whole blink AX tree.
-  base::hash_map<int, ui::AXRelativeBounds> new_locations;
+  std::unordered_map<int, ui::AXRelativeBounds> new_locations;
   base::queue<WebAXObject> objs_to_explore;
   objs_to_explore.push(root);
   while (objs_to_explore.size()) {
diff --git a/content/renderer/accessibility/render_accessibility_impl.h b/content/renderer/accessibility/render_accessibility_impl.h
index b1af65d..95ee2f0 100644
--- a/content/renderer/accessibility/render_accessibility_impl.h
+++ b/content/renderer/accessibility/render_accessibility_impl.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_RENDERER_ACCESSIBILITY_RENDER_ACCESSIBILITY_IMPL_H_
 #define CONTENT_RENDERER_ACCESSIBILITY_RENDER_ACCESSIBILITY_IMPL_H_
 
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -171,7 +172,7 @@
   PluginAXTreeSource* plugin_tree_source_;
 
   // Current location of every object, so we can detect when it moves.
-  base::hash_map<int, ui::AXRelativeBounds> locations_;
+  std::unordered_map<int, ui::AXRelativeBounds> locations_;
 
   // The most recently observed scroll offset of the root document element.
   // TODO(dmazzoni): remove once https://bugs.webkit.org/show_bug.cgi?id=73460
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index 9f1dcdd9..8edd55e 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -421,7 +421,7 @@
   // MediaPlayerRendererClientFactory setup.
   auto mojo_media_player_renderer_factory =
       std::make_unique<media::MojoRendererFactory>(
-          media::mojom::HostedRendererType::kMediaPlayer,
+          media_log, media::mojom::HostedRendererType::kMediaPlayer,
           media::MojoRendererFactory::GetGpuFactoriesCB(),
           GetMediaInterfaceFactory());
 
@@ -441,7 +441,7 @@
 
   // FlingingRendererClientFactory (FRCF) setup.
   auto mojo_flinging_factory = std::make_unique<media::MojoRendererFactory>(
-      media::mojom::HostedRendererType::kFlinging,
+      media_log, media::mojom::HostedRendererType::kFlinging,
       media::MojoRendererFactory::GetGpuFactoriesCB(),
       GetMediaInterfaceFactory());
 
@@ -485,7 +485,7 @@
     factory_selector->AddFactory(
         media::RendererFactorySelector::FactoryType::MOJO,
         std::make_unique<media::MojoRendererFactory>(
-            media::mojom::HostedRendererType::kDefault,
+            media_log, media::mojom::HostedRendererType::kDefault,
             base::Bind(&RenderThreadImpl::GetGpuFactories,
                        base::Unretained(render_thread)),
             GetMediaInterfaceFactory()));
diff --git a/content/renderer/pepper/v8_var_converter.cc b/content/renderer/pepper/v8_var_converter.cc
index d39d0db0..580c2a4 100644
--- a/content/renderer/pepper/v8_var_converter.cc
+++ b/content/renderer/pepper/v8_var_converter.cc
@@ -10,6 +10,7 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <unordered_map>
 
 #include "base/bind.h"
 #include "base/containers/circular_deque.h"
@@ -56,23 +57,23 @@
 
 }  // namespace
 
-namespace BASE_HASH_NAMESPACE {
+namespace std {
 template <>
 struct hash<HashedHandle> {
   size_t operator()(const HashedHandle& handle) const { return handle.hash(); }
 };
-}  // namespace BASE_HASH_NAMESPACE
+}  // namespace std
 
 namespace content {
 
 namespace {
 
 // Maps PP_Var IDs to the V8 value handle they correspond to.
-typedef base::hash_map<int64_t, v8::Local<v8::Value> > VarHandleMap;
+typedef std::unordered_map<int64_t, v8::Local<v8::Value>> VarHandleMap;
 typedef base::hash_set<int64_t> ParentVarSet;
 
 // Maps V8 value handles to the PP_Var they correspond to.
-typedef base::hash_map<HashedHandle, ScopedPPVar> HandleVarMap;
+typedef std::unordered_map<HashedHandle, ScopedPPVar> HandleVarMap;
 typedef base::hash_set<HashedHandle> ParentHandleSet;
 
 // Returns a V8 value which corresponds to a given PP_Var. If |var| is a
diff --git a/content/renderer/pepper/v8_var_converter_unittest.cc b/content/renderer/pepper/v8_var_converter_unittest.cc
index 4787d1d..69eed42 100644
--- a/content/renderer/pepper/v8_var_converter_unittest.cc
+++ b/content/renderer/pepper/v8_var_converter_unittest.cc
@@ -9,6 +9,7 @@
 
 #include <cmath>
 #include <memory>
+#include <unordered_map>
 
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
@@ -74,7 +75,7 @@
 };
 
 // Maps PP_Var IDs to the V8 value handle they correspond to.
-typedef base::hash_map<int64_t, v8::Local<v8::Value> > VarHandleMap;
+typedef std::unordered_map<int64_t, v8::Local<v8::Value>> VarHandleMap;
 
 bool Equals(const PP_Var& var,
             v8::Local<v8::Value> val,
diff --git a/content/renderer/pepper/video_decoder_shim.h b/content/renderer/pepper/video_decoder_shim.h
index 40b69d5..e5f640d 100644
--- a/content/renderer/pepper/video_decoder_shim.h
+++ b/content/renderer/pepper/video_decoder_shim.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -84,7 +85,7 @@
   // The current decoded frame size.
   gfx::Size texture_size_;
   // Map that takes the plugin's GL texture id to the renderer's GL texture id.
-  using TextureIdMap = base::hash_map<uint32_t, uint32_t>;
+  using TextureIdMap = std::unordered_map<uint32_t, uint32_t>;
   TextureIdMap texture_id_map_;
   // Available textures (these are plugin ids.)
   using TextureIdSet = base::hash_set<uint32_t>;
diff --git a/content/shell/browser/web_test/web_test_permission_manager.h b/content/shell/browser/web_test/web_test_permission_manager.h
index c777605..279bb2e 100644
--- a/content/shell/browser/web_test/web_test_permission_manager.h
+++ b/content/shell/browser/web_test/web_test_permission_manager.h
@@ -85,9 +85,9 @@
 
   struct Subscription;
   using SubscriptionsMap = base::IDMap<std::unique_ptr<Subscription>>;
-  using PermissionsMap = base::hash_map<PermissionDescription,
-                                        blink::mojom::PermissionStatus,
-                                        PermissionDescription::Hash>;
+  using PermissionsMap = std::unordered_map<PermissionDescription,
+                                            blink::mojom::PermissionStatus,
+                                            PermissionDescription::Hash>;
 
   void OnPermissionChanged(const PermissionDescription& permission,
                            blink::mojom::PermissionStatus status);
diff --git a/content/shell/test_runner/web_frame_test_proxy.cc b/content/shell/test_runner/web_frame_test_proxy.cc
index 31a2c5e..e46390c 100644
--- a/content/shell/test_runner/web_frame_test_proxy.cc
+++ b/content/shell/test_runner/web_frame_test_proxy.cc
@@ -45,13 +45,6 @@
 
   void DidStartProvisionalLoad(blink::WebDocumentLoader* document_loader,
                                bool is_content_initiated) override {
-    // A provisional load notification is received when a frame navigation is
-    // sent to the browser. We don't want to log it again during commit.
-    if (delegate()->IsNavigationInitiatedByRenderer(
-            document_loader->GetRequest())) {
-      return;
-    }
-
     if (test_runner()->shouldDumpFrameLoadCallbacks()) {
       WebFrameTestClient::PrintFrameDescription(delegate(),
                                                 render_frame()->GetWebFrame());
diff --git a/device/BUILD.gn b/device/BUILD.gn
index 09dbbaf..d94ffdcb 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -143,7 +143,7 @@
     "//url",
   ]
 
-  # U2F and Serial:
+  # U2F:
   # Android doesn't compile.
   # Linux, requires udev.
   if (!is_linux_without_udev && !is_android) {
@@ -158,15 +158,6 @@
       "//services/service_manager/public/cpp",
       "//services/service_manager/public/mojom",
     ]
-
-    # Serial is supported in below platforms. See //device/servial/BUILD.gn
-    if (is_win || is_linux || is_mac) {
-      sources += [ "serial/serial_device_enumerator_unittest.cc" ]
-      deps += [ "//device/serial" ]
-      if (is_linux || is_mac) {
-        sources += [ "serial/serial_io_handler_posix_unittest.cc" ]
-      }
-    }
   }
 
   if (use_udev) {
diff --git a/device/serial/BUILD.gn b/device/serial/BUILD.gn
deleted file mode 100644
index 0ab6e1a..0000000
--- a/device/serial/BUILD.gn
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/features.gni")
-import("//mojo/public/tools/bindings/mojom.gni")
-
-# Library works only on desktop platforms.
-if (is_win || is_linux || is_mac) {
-  config("platform_support") {
-    visibility = [ ":serial" ]
-    if (is_win) {
-      libs = [ "setupapi.lib" ]
-      ldflags = [ "/DELAYLOAD:setupapi.dll" ]
-    }
-  }
-
-  static_library("serial") {
-    visibility = [
-      ":test_support",
-      "//device:device_unittests",
-      "//services/device/serial",
-    ]
-
-    output_name = "device_serial"
-
-    sources = [
-      "buffer.cc",
-      "buffer.h",
-      "serial_device_enumerator.cc",
-      "serial_device_enumerator.h",
-      "serial_device_enumerator_linux.cc",
-      "serial_device_enumerator_linux.h",
-      "serial_device_enumerator_mac.cc",
-      "serial_device_enumerator_mac.h",
-      "serial_device_enumerator_win.cc",
-      "serial_device_enumerator_win.h",
-      "serial_io_handler.cc",
-      "serial_io_handler.h",
-      "serial_io_handler_win.cc",
-      "serial_io_handler_win.h",
-    ]
-
-    public_configs = [ ":platform_support" ]
-
-    public_deps = [
-      "//base",
-      "//device/base",
-      "//services/device/public/mojom",
-    ]
-
-    deps = [
-      "//mojo/public/cpp/system",
-      "//net",
-      "//third_party/re2",
-    ]
-
-    if (is_posix) {
-      sources += [
-        "serial_io_handler_posix.cc",
-        "serial_io_handler_posix.h",
-      ]
-    }
-    if (use_udev) {
-      deps += [ "//device/udev_linux" ]
-    }
-    if (is_chromeos) {
-      deps += [
-        "//chromeos",
-        "//dbus",
-      ]
-    }
-    if (is_mac) {
-      libs = [
-        "Foundation.framework",
-        "IOKit.framework",
-      ]
-    }
-  }
-}
diff --git a/device/serial/DEPS b/device/serial/DEPS
deleted file mode 100644
index 3afb112..0000000
--- a/device/serial/DEPS
+++ /dev/null
@@ -1,6 +0,0 @@
-include_rules = [
-  "+dbus",
-  "+net/base",
-  "+services/device/public/mojom",
-  "+third_party/re2",
-]
diff --git a/device/serial/OWNERS b/device/serial/OWNERS
deleted file mode 100644
index 08850f4..0000000
--- a/device/serial/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file *.mojom=set noparent
-per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/extensions/browser/api/declarative/deduping_factory.h b/extensions/browser/api/declarative/deduping_factory.h
index 53c75c4..b3d9658 100644
--- a/extensions/browser/api/declarative/deduping_factory.h
+++ b/extensions/browser/api/declarative/deduping_factory.h
@@ -9,6 +9,7 @@
 
 #include <list>
 #include <string>
+#include <unordered_map>
 
 #include "base/compiler_specific.h"
 #include "base/containers/hash_tables.h"
@@ -82,8 +83,8 @@
   // Cache of previous prototypes in most-recently-used order. Most recently
   // used objects are at the end.
   typedef std::list<scoped_refptr<const BaseClassT> > PrototypeList;
-  typedef base::hash_map<InstanceType, PrototypeList> ExistingPrototypes;
-  typedef base::hash_map<InstanceType, FactoryMethod> FactoryMethods;
+  typedef std::unordered_map<InstanceType, PrototypeList> ExistingPrototypes;
+  typedef std::unordered_map<InstanceType, FactoryMethod> FactoryMethods;
   typedef base::hash_set<InstanceType> ParameterizedTypes;
 
   const size_t max_number_prototypes_;
diff --git a/gpu/command_buffer/client/buffer_tracker.h b/gpu/command_buffer/client/buffer_tracker.h
index 62508b3..58b4b03 100644
--- a/gpu/command_buffer/client/buffer_tracker.h
+++ b/gpu/command_buffer/client/buffer_tracker.h
@@ -105,7 +105,7 @@
   void Free(Buffer* buffer);
 
  private:
-  typedef base::hash_map<GLuint, Buffer*> BufferMap;
+  typedef std::unordered_map<GLuint, Buffer*> BufferMap;
 
   MappedMemoryManager* mapped_memory_;
   BufferMap buffers_;
diff --git a/gpu/command_buffer/client/buffer_tracker_unittest.cc b/gpu/command_buffer/client/buffer_tracker_unittest.cc
index 2125fc6..1b85662c 100644
--- a/gpu/command_buffer/client/buffer_tracker_unittest.cc
+++ b/gpu/command_buffer/client/buffer_tracker_unittest.cc
@@ -30,7 +30,7 @@
         context_lost_(false) {}
   ~MockClientCommandBufferImpl() override = default;
 
-  scoped_refptr<gpu::Buffer> CreateTransferBuffer(size_t size,
+  scoped_refptr<gpu::Buffer> CreateTransferBuffer(uint32_t size,
                                                   int32_t* id) override {
     if (context_lost_) {
       *id = -1;
diff --git a/gpu/command_buffer/client/client_discardable_manager.cc b/gpu/command_buffer/client/client_discardable_manager.cc
index 7cc2230..ec946ca 100644
--- a/gpu/command_buffer/client/client_discardable_manager.cc
+++ b/gpu/command_buffer/client/client_discardable_manager.cc
@@ -6,6 +6,7 @@
 
 #include "base/atomic_sequence_num.h"
 #include "base/containers/flat_set.h"
+#include "base/numerics/checked_math.h"
 #include "base/system/sys_info.h"
 
 namespace gpu {
@@ -108,17 +109,17 @@
 // Returns the size of the allocation which ClientDiscardableManager will
 // sub-allocate from. This should be at least as big as the minimum shared
 // memory allocation size.
-size_t AllocationSize() {
+uint32_t AllocationSize() {
 #if defined(OS_NACL)
   // base::SysInfo isn't available under NaCl.
-  size_t allocation_size = getpagesize();
+  size_t system_allocation_size = getpagesize();
 #else
-  size_t allocation_size = base::SysInfo::VMAllocationGranularity();
+  size_t system_allocation_size = base::SysInfo::VMAllocationGranularity();
 #endif
+  DCHECK(base::CheckedNumeric<uint32_t>(system_allocation_size).IsValid());
 
   // If the allocation is small (less than 2K), round it up to at least 2K.
-  allocation_size = std::max(static_cast<size_t>(2048), allocation_size);
-  return allocation_size;
+  return std::max(2048u, static_cast<uint32_t>(system_allocation_size));
 }
 
 ClientDiscardableHandle::Id GetNextHandleId() {
diff --git a/gpu/command_buffer/client/client_discardable_manager.h b/gpu/command_buffer/client/client_discardable_manager.h
index a5331085..21888fc 100644
--- a/gpu/command_buffer/client/client_discardable_manager.h
+++ b/gpu/command_buffer/client/client_discardable_manager.h
@@ -62,10 +62,9 @@
   bool CreateNewAllocation(CommandBuffer* command_buffer);
 
  private:
-  size_t allocation_size_;
+  uint32_t allocation_size_;
   size_t element_size_ = sizeof(base::subtle::Atomic32);
-  uint32_t elements_per_allocation_ =
-      static_cast<uint32_t>(allocation_size_ / element_size_);
+  uint32_t elements_per_allocation_ = allocation_size_ / element_size_;
 
   struct Allocation;
   std::vector<std::unique_ptr<Allocation>> allocations_;
diff --git a/gpu/command_buffer/client/client_discardable_manager_unittest.cc b/gpu/command_buffer/client/client_discardable_manager_unittest.cc
index 0c967daf..2c811c66 100644
--- a/gpu/command_buffer/client/client_discardable_manager_unittest.cc
+++ b/gpu/command_buffer/client/client_discardable_manager_unittest.cc
@@ -31,15 +31,11 @@
     return State();
   }
   void SetGetBuffer(int32_t transfer_buffer_id) override { NOTREACHED(); }
-  scoped_refptr<gpu::Buffer> CreateTransferBuffer(size_t size,
+  scoped_refptr<gpu::Buffer> CreateTransferBuffer(uint32_t size,
                                                   int32_t* id) override {
     *id = next_id_++;
     active_ids_.insert(*id);
-    base::UnsafeSharedMemoryRegion shmem_region =
-        base::UnsafeSharedMemoryRegion::Create(size);
-    base::WritableSharedMemoryMapping shmem_mapping = shmem_region.Map();
-    return MakeBufferFromSharedMemory(std::move(shmem_region),
-                                      std::move(shmem_mapping));
+    return MakeMemoryBuffer(size);
   }
   void DestroyTransferBuffer(int32_t id) override {
     auto found = active_ids_.find(id);
diff --git a/gpu/command_buffer/client/client_test_helper.cc b/gpu/command_buffer/client/client_test_helper.cc
index afa81d1..89570ea 100644
--- a/gpu/command_buffer/client/client_test_helper.cc
+++ b/gpu/command_buffer/client/client_test_helper.cc
@@ -51,7 +51,7 @@
 }
 
 scoped_refptr<gpu::Buffer>
-FakeCommandBufferServiceBase::CreateTransferBufferHelper(size_t size,
+FakeCommandBufferServiceBase::CreateTransferBufferHelper(uint32_t size,
                                                          int32_t* id) {
   *id = GetNextFreeTransferBufferId();
   if (*id >= 0) {
@@ -138,7 +138,7 @@
 }
 
 scoped_refptr<gpu::Buffer> MockClientCommandBuffer::CreateTransferBuffer(
-    size_t size,
+    uint32_t size,
     int32_t* id) {
   return CreateTransferBufferHelper(size, id);
 }
diff --git a/gpu/command_buffer/client/client_test_helper.h b/gpu/command_buffer/client/client_test_helper.h
index 462298fa..a293fe6 100644
--- a/gpu/command_buffer/client/client_test_helper.h
+++ b/gpu/command_buffer/client/client_test_helper.h
@@ -45,7 +45,7 @@
 
   void FlushHelper(int32_t put_offset);
   void SetGetBufferHelper(int transfer_buffer_id, int32_t token);
-  scoped_refptr<gpu::Buffer> CreateTransferBufferHelper(size_t size,
+  scoped_refptr<gpu::Buffer> CreateTransferBufferHelper(uint32_t size,
                                                         int32_t* id);
   void DestroyTransferBufferHelper(int32_t id);
 
@@ -66,7 +66,7 @@
                                 int32_t start,
                                 int32_t end) override;
   void SetGetBuffer(int transfer_buffer_id) override;
-  scoped_refptr<gpu::Buffer> CreateTransferBuffer(size_t size,
+  scoped_refptr<gpu::Buffer> CreateTransferBuffer(uint32_t size,
                                                   int32_t* id) override;
 
   // This is so we can use all the gmock functions when Flush is called.
diff --git a/gpu/command_buffer/client/cmd_buffer_helper.cc b/gpu/command_buffer/client/cmd_buffer_helper.cc
index 8d95a805..5a764e4 100644
--- a/gpu/command_buffer/client/cmd_buffer_helper.cc
+++ b/gpu/command_buffer/client/cmd_buffer_helper.cc
@@ -133,7 +133,7 @@
   }
 }
 
-gpu::ContextResult CommandBufferHelper::Initialize(int32_t ring_buffer_size) {
+gpu::ContextResult CommandBufferHelper::Initialize(uint32_t ring_buffer_size) {
   ring_buffer_size_ = ring_buffer_size;
   if (!AllocateRingBuffer()) {
     // This would fail if CreateTransferBuffer fails, which will not fail for
diff --git a/gpu/command_buffer/client/cmd_buffer_helper.h b/gpu/command_buffer/client/cmd_buffer_helper.h
index e1d9c0e7..d6ff4d05 100644
--- a/gpu/command_buffer/client/cmd_buffer_helper.h
+++ b/gpu/command_buffer/client/cmd_buffer_helper.h
@@ -60,7 +60,7 @@
   // Parameters:
   //   ring_buffer_size: The size of the ring buffer portion of the command
   //       buffer.
-  gpu::ContextResult Initialize(int32_t ring_buffer_size);
+  gpu::ContextResult Initialize(uint32_t ring_buffer_size);
 
   // Sets whether the command buffer should automatically flush periodically
   // to try to increase performance. Defaults to true.
@@ -293,7 +293,7 @@
 
   CommandBuffer* const command_buffer_;
   int32_t ring_buffer_id_ = -1;
-  int32_t ring_buffer_size_ = 0;
+  uint32_t ring_buffer_size_ = 0;
   scoped_refptr<gpu::Buffer> ring_buffer_;
   CommandBufferEntry* entries_ = nullptr;
   int32_t total_entry_count_ = 0;  // the total number of entries
diff --git a/gpu/command_buffer/client/command_buffer_direct_locked.cc b/gpu/command_buffer/client/command_buffer_direct_locked.cc
index f51210e..1d47fd2 100644
--- a/gpu/command_buffer/client/command_buffer_direct_locked.cc
+++ b/gpu/command_buffer/client/command_buffer_direct_locked.cc
@@ -42,7 +42,7 @@
 }
 
 scoped_refptr<Buffer> CommandBufferDirectLocked::CreateTransferBuffer(
-    size_t size,
+    uint32_t size,
     int32_t* id) {
   if (fail_create_transfer_buffer_) {
     *id = -1;
diff --git a/gpu/command_buffer/client/command_buffer_direct_locked.h b/gpu/command_buffer/client/command_buffer_direct_locked.h
index 0d55f11..c7d32cdf 100644
--- a/gpu/command_buffer/client/command_buffer_direct_locked.h
+++ b/gpu/command_buffer/client/command_buffer_direct_locked.h
@@ -24,7 +24,8 @@
   CommandBuffer::State WaitForGetOffsetInRange(uint32_t set_get_buffer_count,
                                                int32_t start,
                                                int32_t end) override;
-  scoped_refptr<Buffer> CreateTransferBuffer(size_t size, int32_t* id) override;
+  scoped_refptr<Buffer> CreateTransferBuffer(uint32_t size,
+                                             int32_t* id) override;
 
   void LockFlush() { flush_locked_ = true; }
 
diff --git a/gpu/command_buffer/client/gles2_implementation.h b/gpu/command_buffer/client/gles2_implementation.h
index 4f298554..0858127 100644
--- a/gpu/command_buffer/client/gles2_implementation.h
+++ b/gpu/command_buffer/client/gles2_implementation.h
@@ -13,6 +13,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -777,7 +778,7 @@
   MappedBufferMap mapped_buffers_;
 
   // TODO(zmo): Consolidate |mapped_buffers_| and |mapped_buffer_range_map_|.
-  typedef base::hash_map<GLuint, MappedBuffer> MappedBufferRangeMap;
+  typedef std::unordered_map<GLuint, MappedBuffer> MappedBufferRangeMap;
   MappedBufferRangeMap mapped_buffer_range_map_;
 
   typedef std::map<const void*, MappedTexture> MappedTextureMap;
diff --git a/gpu/command_buffer/client/mapped_memory.cc b/gpu/command_buffer/client/mapped_memory.cc
index bbb0f8a..ebb7b79 100644
--- a/gpu/command_buffer/client/mapped_memory.cc
+++ b/gpu/command_buffer/client/mapped_memory.cc
@@ -13,6 +13,7 @@
 #include "base/atomic_sequence_num.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "base/numerics/checked_math.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -105,12 +106,15 @@
 
   // Make a new chunk to satisfy the request.
   CommandBuffer* cmd_buf = helper_->command_buffer();
-  unsigned int chunk_size =
-      ((size + chunk_size_multiple_ - 1) / chunk_size_multiple_) *
-      chunk_size_multiple_;
+  base::CheckedNumeric<uint32_t> chunk_size = size;
+  chunk_size = (size + chunk_size_multiple_ - 1) & ~(chunk_size_multiple_ - 1);
+  uint32_t safe_chunk_size = 0;
+  if (!chunk_size.AssignIfValid(&safe_chunk_size))
+    return nullptr;
+
   int32_t id = -1;
   scoped_refptr<gpu::Buffer> shm =
-      cmd_buf->CreateTransferBuffer(chunk_size, &id);
+      cmd_buf->CreateTransferBuffer(safe_chunk_size, &id);
   if (id  < 0)
     return nullptr;
   DCHECK(shm.get());
diff --git a/gpu/command_buffer/client/mapped_memory.h b/gpu/command_buffer/client/mapped_memory.h
index 7eeee65..6dca2bd 100644
--- a/gpu/command_buffer/client/mapped_memory.h
+++ b/gpu/command_buffer/client/mapped_memory.h
@@ -11,6 +11,7 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/bits.h"
 #include "base/macros.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "gpu/command_buffer/client/fenced_allocator.h"
@@ -143,7 +144,8 @@
   }
 
   void set_chunk_size_multiple(unsigned int multiple) {
-    DCHECK(multiple % FencedAllocator::kAllocAlignment == 0);
+    DCHECK(base::bits::IsPowerOfTwo(multiple));
+    DCHECK_GE(multiple, FencedAllocator::kAllocAlignment);
     chunk_size_multiple_ = multiple;
   }
 
diff --git a/gpu/command_buffer/client/mock_transfer_buffer.cc b/gpu/command_buffer/client/mock_transfer_buffer.cc
index 2621daf..5b65cfa 100644
--- a/gpu/command_buffer/client/mock_transfer_buffer.cc
+++ b/gpu/command_buffer/client/mock_transfer_buffer.cc
@@ -86,7 +86,7 @@
   // reallocated.
   actual_buffer_index_ = (actual_buffer_index_ + 1) % kNumBuffers;
 
-  size = std::min(static_cast<size_t>(size), MaxTransferBufferSize());
+  size = std::min(size, MaxTransferBufferSize());
   if (actual_offset_ + size > size_) {
     actual_offset_ = result_size_;
   }
@@ -136,7 +136,7 @@
 
 void MockTransferBuffer::ShrinkLastBlock(unsigned int new_size) {}
 
-size_t MockTransferBuffer::MaxTransferBufferSize() {
+uint32_t MockTransferBuffer::MaxTransferBufferSize() {
   return size_ - result_size_;
 }
 
@@ -150,7 +150,7 @@
 }
 
 MockTransferBuffer::ExpectedMemoryInfo MockTransferBuffer::GetExpectedMemory(
-    size_t size) {
+    uint32_t size) {
   ExpectedMemoryInfo mem;
   mem.offset = AllocateExpectedTransferBuffer(size);
   mem.id = GetExpectedTransferBufferId();
@@ -160,7 +160,7 @@
 }
 
 MockTransferBuffer::ExpectedMemoryInfo
-MockTransferBuffer::GetExpectedResultMemory(size_t size) {
+MockTransferBuffer::GetExpectedResultMemory(uint32_t size) {
   ExpectedMemoryInfo mem;
   mem.offset = GetExpectedResultBufferOffset();
   mem.id = GetExpectedResultBufferId();
@@ -169,7 +169,7 @@
   return mem;
 }
 
-uint32_t MockTransferBuffer::AllocateExpectedTransferBuffer(size_t size) {
+uint32_t MockTransferBuffer::AllocateExpectedTransferBuffer(uint32_t size) {
   EXPECT_LE(size, MaxTransferBufferSize());
 
   // Toggle which buffer we get each time to simulate the buffer being
@@ -187,7 +187,7 @@
 }
 
 void* MockTransferBuffer::GetExpectedTransferAddressFromOffset(uint32_t offset,
-                                                               size_t size) {
+                                                               uint32_t size) {
   EXPECT_GE(offset, expected_buffer_index_ * alignment_);
   EXPECT_LE(offset + size, size_ + expected_buffer_index_ * alignment_);
   return expected_buffer() + offset;
diff --git a/gpu/command_buffer/client/mock_transfer_buffer.h b/gpu/command_buffer/client/mock_transfer_buffer.h
index 7b159b1..995bc78 100644
--- a/gpu/command_buffer/client/mock_transfer_buffer.h
+++ b/gpu/command_buffer/client/mock_transfer_buffer.h
@@ -51,11 +51,11 @@
   unsigned int GetFragmentedFreeSize() const override;
   void ShrinkLastBlock(unsigned int new_size) override;
 
-  size_t MaxTransferBufferSize();
+  uint32_t MaxTransferBufferSize();
   unsigned int RoundToAlignment(unsigned int size);
   bool InSync();
-  ExpectedMemoryInfo GetExpectedMemory(size_t size);
-  ExpectedMemoryInfo GetExpectedResultMemory(size_t size);
+  ExpectedMemoryInfo GetExpectedMemory(uint32_t size);
+  ExpectedMemoryInfo GetExpectedResultMemory(uint32_t size);
 
  private:
   static const int kNumBuffers = 2;
@@ -68,15 +68,15 @@
     return static_cast<uint8_t*>(buffers_[expected_buffer_index_]->memory());
   }
 
-  uint32_t AllocateExpectedTransferBuffer(size_t size);
-  void* GetExpectedTransferAddressFromOffset(uint32_t offset, size_t size);
+  uint32_t AllocateExpectedTransferBuffer(uint32_t size);
+  void* GetExpectedTransferAddressFromOffset(uint32_t offset, uint32_t size);
   int GetExpectedResultBufferId();
   uint32_t GetExpectedResultBufferOffset();
   int GetExpectedTransferBufferId();
 
   CommandBuffer* command_buffer_;
-  size_t size_;
-  size_t result_size_;
+  uint32_t size_;
+  uint32_t result_size_;
   uint32_t alignment_;
   int buffer_ids_[kNumBuffers];
   scoped_refptr<Buffer> buffers_[kNumBuffers];
diff --git a/gpu/command_buffer/client/program_info_manager.cc b/gpu/command_buffer/client/program_info_manager.cc
index 5c79059..222e5b4a 100644
--- a/gpu/command_buffer/client/program_info_manager.cc
+++ b/gpu/command_buffer/client/program_info_manager.cc
@@ -184,7 +184,7 @@
 
 GLint ProgramInfoManager::Program::GetFragDataLocation(
     const std::string& name) const {
-  base::hash_map<std::string, GLint>::const_iterator iter =
+  std::unordered_map<std::string, GLint>::const_iterator iter =
       frag_data_locations_.find(name);
   if (iter == frag_data_locations_.end())
     return -1;
diff --git a/gpu/command_buffer/client/program_info_manager.h b/gpu/command_buffer/client/program_info_manager.h
index fc6c9677..2354bb6f 100644
--- a/gpu/command_buffer/client/program_info_manager.h
+++ b/gpu/command_buffer/client/program_info_manager.h
@@ -9,6 +9,7 @@
 #include <stdint.h>
 
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -242,14 +243,14 @@
 
     std::vector<UniformES3> uniforms_es3_;
 
-    base::hash_map<std::string, GLint> frag_data_locations_;
-    base::hash_map<std::string, GLint> frag_data_indices_;
+    std::unordered_map<std::string, GLint> frag_data_locations_;
+    std::unordered_map<std::string, GLint> frag_data_indices_;
   };
 
   Program* GetProgramInfo(
       GLES2Implementation* gl, GLuint program, ProgramInfoType type);
 
-  typedef base::hash_map<GLuint, Program> ProgramInfoMap;
+  typedef std::unordered_map<GLuint, Program> ProgramInfoMap;
 
   ProgramInfoMap program_infos_;
 
diff --git a/gpu/command_buffer/client/query_tracker.h b/gpu/command_buffer/client/query_tracker.h
index 1596e985..4feed88 100644
--- a/gpu/command_buffer/client/query_tracker.h
+++ b/gpu/command_buffer/client/query_tracker.h
@@ -13,6 +13,7 @@
 #include <bitset>
 #include <list>
 #include <memory>
+#include <unordered_map>
 
 #include "base/atomicops.h"
 #include "base/containers/circular_deque.h"
@@ -225,7 +226,7 @@
   }
 
  private:
-  typedef base::hash_map<GLuint, std::unique_ptr<Query>> QueryIdMap;
+  typedef std::unordered_map<GLuint, std::unique_ptr<Query>> QueryIdMap;
   typedef base::flat_map<GLenum, Query*> QueryTargetMap;
 
   QueryIdMap queries_;
diff --git a/gpu/command_buffer/client/shared_memory_limits.h b/gpu/command_buffer/client/shared_memory_limits.h
index 4fc1f318..bef14c3b 100644
--- a/gpu/command_buffer/client/shared_memory_limits.h
+++ b/gpu/command_buffer/client/shared_memory_limits.h
@@ -36,7 +36,7 @@
 #endif
   }
 
-  int32_t command_buffer_size = 1024 * 1024;
+  uint32_t command_buffer_size = 1024 * 1024;
   uint32_t start_transfer_buffer_size = 64 * 1024;
   uint32_t min_transfer_buffer_size = 64 * 1024;
   uint32_t max_transfer_buffer_size = 16 * 1024 * 1024;
diff --git a/gpu/command_buffer/client/transfer_buffer_unittest.cc b/gpu/command_buffer/client/transfer_buffer_unittest.cc
index b1884307..245c7ce 100644
--- a/gpu/command_buffer/client/transfer_buffer_unittest.cc
+++ b/gpu/command_buffer/client/transfer_buffer_unittest.cc
@@ -231,9 +231,9 @@
   ~MockClientCommandBufferCanFail() override = default;
 
   MOCK_METHOD2(CreateTransferBuffer,
-               scoped_refptr<Buffer>(size_t size, int32_t* id));
+               scoped_refptr<Buffer>(uint32_t size, int32_t* id));
 
-  scoped_refptr<gpu::Buffer> RealCreateTransferBuffer(size_t size,
+  scoped_refptr<gpu::Buffer> RealCreateTransferBuffer(uint32_t size,
                                                       int32_t* id) {
     return MockClientCommandBufferMockFlush::CreateTransferBuffer(size, id);
   }
diff --git a/gpu/command_buffer/client/vertex_array_object_manager.h b/gpu/command_buffer/client/vertex_array_object_manager.h
index 9efac4c..77c69c4c 100644
--- a/gpu/command_buffer/client/vertex_array_object_manager.h
+++ b/gpu/command_buffer/client/vertex_array_object_manager.h
@@ -9,6 +9,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <unordered_map>
 
 #include "base/containers/hash_tables.h"
 #include "base/macros.h"
@@ -101,7 +102,7 @@
   GLuint bound_element_array_buffer() const;
 
  private:
-  typedef base::hash_map<GLuint, VertexArrayObject*> VertexArrayObjectMap;
+  typedef std::unordered_map<GLuint, VertexArrayObject*> VertexArrayObjectMap;
 
   bool IsDefaultVAOBound() const;
 
diff --git a/gpu/command_buffer/common/command_buffer.h b/gpu/command_buffer/common/command_buffer.h
index a91610b4..f67bbda72 100644
--- a/gpu/command_buffer/common/command_buffer.h
+++ b/gpu/command_buffer/common/command_buffer.h
@@ -110,7 +110,7 @@
 
   // Create a transfer buffer of the given size. Returns its ID or -1 on
   // error.
-  virtual scoped_refptr<gpu::Buffer> CreateTransferBuffer(size_t size,
+  virtual scoped_refptr<gpu::Buffer> CreateTransferBuffer(uint32_t size,
                                                           int32_t* id) = 0;
 
   // Destroy a transfer buffer. The ID must be positive.
diff --git a/gpu/command_buffer/service/buffer_manager.h b/gpu/command_buffer/service/buffer_manager.h
index b9210a3a..cda173d 100644
--- a/gpu/command_buffer/service/buffer_manager.h
+++ b/gpu/command_buffer/service/buffer_manager.h
@@ -11,6 +11,7 @@
 
 #include <map>
 #include <memory>
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -447,7 +448,7 @@
   scoped_refptr<FeatureInfo> feature_info_;
 
   // Info for each buffer in the system.
-  typedef base::hash_map<GLuint, scoped_refptr<Buffer> > BufferMap;
+  typedef std::unordered_map<GLuint, scoped_refptr<Buffer>> BufferMap;
   BufferMap buffers_;
 
   // The maximum size of buffers.
diff --git a/gpu/command_buffer/service/command_buffer_direct.cc b/gpu/command_buffer/service/command_buffer_direct.cc
index 03fd9a5..45439dc 100644
--- a/gpu/command_buffer/service/command_buffer_direct.cc
+++ b/gpu/command_buffer/service/command_buffer_direct.cc
@@ -60,7 +60,7 @@
   service_.SetGetBuffer(transfer_buffer_id);
 }
 
-scoped_refptr<Buffer> CommandBufferDirect::CreateTransferBuffer(size_t size,
+scoped_refptr<Buffer> CommandBufferDirect::CreateTransferBuffer(uint32_t size,
                                                                 int32_t* id) {
   return service_.CreateTransferBuffer(size, id);
 }
@@ -97,7 +97,7 @@
 void CommandBufferDirect::OnSwapBuffers(uint64_t swap_id, uint32_t flags) {}
 
 scoped_refptr<Buffer> CommandBufferDirect::CreateTransferBufferWithId(
-    size_t size,
+    uint32_t size,
     int32_t id) {
   return service_.CreateTransferBufferWithId(size, id);
 }
diff --git a/gpu/command_buffer/service/command_buffer_direct.h b/gpu/command_buffer/service/command_buffer_direct.h
index 0eec8e6..0280152 100644
--- a/gpu/command_buffer/service/command_buffer_direct.h
+++ b/gpu/command_buffer/service/command_buffer_direct.h
@@ -38,7 +38,8 @@
                                                int32_t start,
                                                int32_t end) override;
   void SetGetBuffer(int32_t transfer_buffer_id) override;
-  scoped_refptr<Buffer> CreateTransferBuffer(size_t size, int32_t* id) override;
+  scoped_refptr<Buffer> CreateTransferBuffer(uint32_t size,
+                                             int32_t* id) override;
   void DestroyTransferBuffer(int32_t id) override;
 
   // CommandBufferServiceBase implementation:
@@ -54,7 +55,7 @@
   void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override;
   void ScheduleGrContextCleanup() override {}
 
-  scoped_refptr<Buffer> CreateTransferBufferWithId(size_t size, int32_t id);
+  scoped_refptr<Buffer> CreateTransferBufferWithId(uint32_t size, int32_t id);
 
   void SetGetOffsetForTest(int32_t get_offset) {
     service_.SetGetOffsetForTest(get_offset);
diff --git a/gpu/command_buffer/service/command_buffer_service.cc b/gpu/command_buffer/service/command_buffer_service.cc
index 478256d..a728519 100644
--- a/gpu/command_buffer/service/command_buffer_service.cc
+++ b/gpu/command_buffer/service/command_buffer_service.cc
@@ -145,7 +145,7 @@
   UpdateState();
 }
 
-scoped_refptr<Buffer> CommandBufferService::CreateTransferBuffer(size_t size,
+scoped_refptr<Buffer> CommandBufferService::CreateTransferBuffer(uint32_t size,
                                                                  int32_t* id) {
   *id = GetNextBufferId();
   auto result = CreateTransferBufferWithId(size, *id);
@@ -170,7 +170,7 @@
 }
 
 scoped_refptr<Buffer> CommandBufferService::CreateTransferBufferWithId(
-    size_t size,
+    uint32_t size,
     int32_t id) {
   scoped_refptr<Buffer> buffer = MakeMemoryBuffer(size);
   if (!RegisterTransferBuffer(id, buffer)) {
diff --git a/gpu/command_buffer/service/command_buffer_service.h b/gpu/command_buffer/service/command_buffer_service.h
index b6bfb5c..42e1716 100644
--- a/gpu/command_buffer/service/command_buffer_service.h
+++ b/gpu/command_buffer/service/command_buffer_service.h
@@ -106,10 +106,10 @@
 
   // Creates an in-process transfer buffer and register it with a newly created
   // id.
-  scoped_refptr<Buffer> CreateTransferBuffer(size_t size, int32_t* id);
+  scoped_refptr<Buffer> CreateTransferBuffer(uint32_t size, int32_t* id);
 
   // Creates an in-process transfer buffer and register it with a given id.
-  scoped_refptr<Buffer> CreateTransferBufferWithId(size_t size, int32_t id);
+  scoped_refptr<Buffer> CreateTransferBufferWithId(uint32_t size, int32_t id);
 
   // Sets whether commands should be processed by this scheduler. Setting to
   // false unschedules. Setting to true reschedules.
diff --git a/gpu/command_buffer/service/context_group.h b/gpu/command_buffer/service/context_group.h
index 026bca1..f7736ce 100644
--- a/gpu/command_buffer/service/context_group.h
+++ b/gpu/command_buffer/service/context_group.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -228,7 +229,7 @@
   }
 
   bool GetSyncServiceId(GLuint client_id, GLsync* service_id) const {
-    base::hash_map<GLuint, GLsync>::const_iterator iter =
+    std::unordered_map<GLuint, GLsync>::const_iterator iter =
         syncs_id_map_.find(client_id);
     if (iter == syncs_id_map_.end())
       return false;
@@ -324,7 +325,7 @@
   std::vector<base::WeakPtr<DecoderContext>> decoders_;
 
   // Mappings from client side IDs to service side IDs.
-  base::hash_map<GLuint, GLsync> syncs_id_map_;
+  std::unordered_map<GLuint, GLsync> syncs_id_map_;
 
   bool use_passthrough_cmd_decoder_;
   std::unique_ptr<PassthroughResources> passthrough_resources_;
diff --git a/gpu/command_buffer/service/framebuffer_manager.h b/gpu/command_buffer/service/framebuffer_manager.h
index 44fa609..f09d3db4 100644
--- a/gpu/command_buffer/service/framebuffer_manager.h
+++ b/gpu/command_buffer/service/framebuffer_manager.h
@@ -9,6 +9,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -279,7 +280,7 @@
   unsigned framebuffer_complete_state_count_id_;
 
   // A map of attachments.
-  typedef base::hash_map<GLenum, scoped_refptr<Attachment> > AttachmentMap;
+  typedef std::unordered_map<GLenum, scoped_refptr<Attachment>> AttachmentMap;
   AttachmentMap attachments_;
 
   // User's draw buffers setting through DrawBuffers() call.
@@ -370,8 +371,7 @@
   }
 
   // Info for each framebuffer in the system.
-  typedef base::hash_map<GLuint, scoped_refptr<Framebuffer> >
-      FramebufferMap;
+  typedef std::unordered_map<GLuint, scoped_refptr<Framebuffer>> FramebufferMap;
   FramebufferMap framebuffers_;
 
   // Incremented anytime anything changes that might effect framebuffer
diff --git a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
index f5fbb9d..f315142d7 100644
--- a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
+++ b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 
 #include <algorithm>
+#include <unordered_map>
 
 #include "gpu/command_buffer/service/decoder_context.h"
 #include "gpu/command_buffer/service/gl_utils.h"
@@ -1006,7 +1007,7 @@
   ShaderVector vertex_shaders_;
   ShaderVector fragment_shaders_;
   typedef int ProgramMapKey;
-  typedef base::hash_map<ProgramMapKey, ProgramInfo> ProgramMap;
+  typedef std::unordered_map<ProgramMapKey, ProgramInfo> ProgramMap;
   ProgramMap programs_;
   GLuint vertex_array_object_id_;
   GLuint buffer_id_;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 5457525..d58db93 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -16,6 +16,7 @@
 #include <map>
 #include <memory>
 #include <set>
+#include <unordered_map>
 #include <utility>
 
 #include "base/callback.h"
@@ -2707,7 +2708,7 @@
   std::vector<std::unique_ptr<gl::GLFence>> deschedule_until_finished_fences_;
 
   // Used to validate multisample renderbuffers if needed
-  typedef base::hash_map<GLenum, GLuint> TextureMap;
+  typedef std::unordered_map<GLenum, GLuint> TextureMap;
   TextureMap validation_textures_;
   GLuint validation_fbo_multisample_;
   GLuint validation_fbo_;
diff --git a/gpu/command_buffer/service/id_manager.h b/gpu/command_buffer/service/id_manager.h
index b855282..2de746b 100644
--- a/gpu/command_buffer/service/id_manager.h
+++ b/gpu/command_buffer/service/id_manager.h
@@ -38,7 +38,7 @@
   bool GetClientId(GLuint service_id, GLuint* client_id);
 
  private:
-  typedef base::hash_map<GLuint, GLuint> MapType;
+  typedef std::unordered_map<GLuint, GLuint> MapType;
   MapType id_map_;
 
   DISALLOW_COPY_AND_ASSIGN(IdManager);
diff --git a/gpu/command_buffer/service/image_manager.h b/gpu/command_buffer/service/image_manager.h
index 30ac82f..ee1ca3c 100644
--- a/gpu/command_buffer/service/image_manager.h
+++ b/gpu/command_buffer/service/image_manager.h
@@ -30,7 +30,7 @@
   gl::GLImage* LookupImage(int32_t service_id);
 
  private:
-  typedef base::hash_map<int32_t, scoped_refptr<gl::GLImage>> GLImageMap;
+  typedef std::unordered_map<int32_t, scoped_refptr<gl::GLImage>> GLImageMap;
   GLImageMap images_;
 
   DISALLOW_COPY_AND_ASSIGN(ImageManager);
diff --git a/gpu/command_buffer/service/program_cache.h b/gpu/command_buffer/service/program_cache.h
index 4232ad52..5f3f8be 100644
--- a/gpu/command_buffer/service/program_cache.h
+++ b/gpu/command_buffer/service/program_cache.h
@@ -9,6 +9,7 @@
 
 #include <map>
 #include <string>
+#include <unordered_map>
 
 #include "base/containers/hash_tables.h"
 #include "base/macros.h"
@@ -125,8 +126,7 @@
   CacheProgramCallback cache_program_callback_;
 
  private:
-  typedef base::hash_map<std::string,
-                         LinkedProgramStatus> LinkStatusMap;
+  typedef std::unordered_map<std::string, LinkedProgramStatus> LinkStatusMap;
 
   // called to clear the backend cache
   virtual void ClearBackend() = 0;
diff --git a/gpu/command_buffer/service/query_manager.h b/gpu/command_buffer/service/query_manager.h
index 1b6b8c57..300101f 100644
--- a/gpu/command_buffer/service/query_manager.h
+++ b/gpu/command_buffer/service/query_manager.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <unordered_map>
 #include <vector>
 
 #include "base/atomicops.h"
@@ -245,7 +246,7 @@
   unsigned query_count_;
 
   // Info for each query in the system.
-  using QueryMap = base::hash_map<GLuint, scoped_refptr<Query>>;
+  using QueryMap = std::unordered_map<GLuint, scoped_refptr<Query>>;
   QueryMap queries_;
 
   using GeneratedQueryIds = base::hash_set<GLuint>;
diff --git a/gpu/command_buffer/service/renderbuffer_manager.h b/gpu/command_buffer/service/renderbuffer_manager.h
index a647c5b5..e0d60b17 100644
--- a/gpu/command_buffer/service/renderbuffer_manager.h
+++ b/gpu/command_buffer/service/renderbuffer_manager.h
@@ -10,6 +10,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
 
 #include "base/containers/flat_set.h"
 #include "base/containers/hash_tables.h"
@@ -224,7 +225,8 @@
   bool have_context_;
 
   // Info for each renderbuffer in the system.
-  typedef base::hash_map<GLuint, scoped_refptr<Renderbuffer> > RenderbufferMap;
+  typedef std::unordered_map<GLuint, scoped_refptr<Renderbuffer>>
+      RenderbufferMap;
   RenderbufferMap renderbuffers_;
 
   DISALLOW_COPY_AND_ASSIGN(RenderbufferManager);
diff --git a/gpu/command_buffer/service/sampler_manager.h b/gpu/command_buffer/service/sampler_manager.h
index 05d2958..cff767b 100644
--- a/gpu/command_buffer/service/sampler_manager.h
+++ b/gpu/command_buffer/service/sampler_manager.h
@@ -5,6 +5,7 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_SAMPLER_MANAGER_H_
 #define GPU_COMMAND_BUFFER_SERVICE_SAMPLER_MANAGER_H_
 
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -160,7 +161,7 @@
   scoped_refptr<FeatureInfo> feature_info_;
 
   // Info for each sampler in the system.
-  typedef base::hash_map<GLuint, scoped_refptr<Sampler> > SamplerMap;
+  typedef std::unordered_map<GLuint, scoped_refptr<Sampler>> SamplerMap;
   SamplerMap samplers_;
 
   bool have_context_;
diff --git a/gpu/command_buffer/service/shader_manager.h b/gpu/command_buffer/service/shader_manager.h
index 579dddd..7d101e6 100644
--- a/gpu/command_buffer/service/shader_manager.h
+++ b/gpu/command_buffer/service/shader_manager.h
@@ -6,6 +6,7 @@
 #define GPU_COMMAND_BUFFER_SERVICE_SHADER_MANAGER_H_
 
 #include <string>
+#include <unordered_map>
 
 #include "base/containers/hash_tables.h"
 #include "base/logging.h"
@@ -316,7 +317,7 @@
   friend class Shader;
 
   // Info for each shader by service side shader Id.
-  typedef base::hash_map<GLuint, scoped_refptr<Shader> > ShaderMap;
+  typedef std::unordered_map<GLuint, scoped_refptr<Shader>> ShaderMap;
   ShaderMap shaders_;
 
   void RemoveShaderIfUnused(Shader* shader);
diff --git a/gpu/command_buffer/service/shader_translator.h b/gpu/command_buffer/service/shader_translator.h
index 98ccdb37..c17a5e4 100644
--- a/gpu/command_buffer/service/shader_translator.h
+++ b/gpu/command_buffer/service/shader_translator.h
@@ -6,6 +6,7 @@
 #define GPU_COMMAND_BUFFER_SERVICE_SHADER_TRANSLATOR_H_
 
 #include <string>
+#include <unordered_map>
 
 #include "base/containers/hash_tables.h"
 #include "base/macros.h"
@@ -22,11 +23,11 @@
 namespace gles2 {
 
 // Mapping between variable name and info.
-typedef base::hash_map<std::string, sh::Attribute> AttributeMap;
+typedef std::unordered_map<std::string, sh::Attribute> AttributeMap;
 typedef std::vector<sh::OutputVariable> OutputVariableList;
-typedef base::hash_map<std::string, sh::Uniform> UniformMap;
-typedef base::hash_map<std::string, sh::Varying> VaryingMap;
-typedef base::hash_map<std::string, sh::InterfaceBlock> InterfaceBlockMap;
+typedef std::unordered_map<std::string, sh::Uniform> UniformMap;
+typedef std::unordered_map<std::string, sh::Varying> VaryingMap;
+typedef std::unordered_map<std::string, sh::InterfaceBlock> InterfaceBlockMap;
 typedef base::RefCountedData<std::string> OptionsAffectingCompilationString;
 
 // Translates a GLSL ES 2.0 shader to desktop GLSL shader, or just
diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h
index c5fddd5..9ebcbf5 100644
--- a/gpu/command_buffer/service/texture_manager.h
+++ b/gpu/command_buffer/service/texture_manager.h
@@ -13,6 +13,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -1251,7 +1252,7 @@
   std::vector<FramebufferManager*> framebuffer_managers_;
 
   // Info for each texture in the system.
-  typedef base::hash_map<GLuint, scoped_refptr<TextureRef> > TextureMap;
+  typedef std::unordered_map<GLuint, scoped_refptr<TextureRef>> TextureMap;
   TextureMap textures_;
 
   GLsizei max_texture_size_;
diff --git a/gpu/command_buffer/service/transform_feedback_manager.h b/gpu/command_buffer/service/transform_feedback_manager.h
index e731368..98ff493 100644
--- a/gpu/command_buffer/service/transform_feedback_manager.h
+++ b/gpu/command_buffer/service/transform_feedback_manager.h
@@ -5,6 +5,7 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_TRANSFORM_FEEDBACK_MANAGER_H_
 #define GPU_COMMAND_BUFFER_SERVICE_TRANSFORM_FEEDBACK_MANAGER_H_
 
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -138,8 +139,8 @@
 
  private:
   // Info for each transform feedback in the system.
-  base::hash_map<GLuint,
-                 scoped_refptr<TransformFeedback> > transform_feedbacks_;
+  std::unordered_map<GLuint, scoped_refptr<TransformFeedback>>
+      transform_feedbacks_;
 
   GLuint max_transform_feedback_separate_attribs_;
 
diff --git a/gpu/command_buffer/service/vertex_array_manager.h b/gpu/command_buffer/service/vertex_array_manager.h
index 29cc3d6..406d7b2 100644
--- a/gpu/command_buffer/service/vertex_array_manager.h
+++ b/gpu/command_buffer/service/vertex_array_manager.h
@@ -56,7 +56,7 @@
   void StopTracking(VertexAttribManager* vertex_attrib_manager);
 
   // Info for each vertex array in the system.
-  typedef base::hash_map<GLuint, scoped_refptr<VertexAttribManager> >
+  typedef std::unordered_map<GLuint, scoped_refptr<VertexAttribManager>>
       VertexAttribManagerMap;
   VertexAttribManagerMap client_vertex_attrib_managers_;
 
diff --git a/gpu/command_buffer/tests/fuzzer_main.cc b/gpu/command_buffer/tests/fuzzer_main.cc
index ca13ceb1..6b818b8 100644
--- a/gpu/command_buffer/tests/fuzzer_main.cc
+++ b/gpu/command_buffer/tests/fuzzer_main.cc
@@ -51,10 +51,10 @@
 namespace gpu {
 namespace {
 
-const size_t kCommandBufferSize = 16384;
-const size_t kTransferBufferSize = 16384;
-const size_t kSmallTransferBufferSize = 16;
-const size_t kTinyTransferBufferSize = 3;
+const uint32_t kCommandBufferSize = 16384;
+const uint32_t kTransferBufferSize = 16384;
+const uint32_t kSmallTransferBufferSize = 16;
+const uint32_t kTinyTransferBufferSize = 3;
 
 #if !defined(GPU_FUZZER_USE_ANGLE) && !defined(GPU_FUZZER_USE_SWIFTSHADER)
 #define GPU_FUZZER_USE_STUB
@@ -453,7 +453,7 @@
   }
 
  private:
-  void CreateTransferBuffer(size_t size, int32_t id) {
+  void CreateTransferBuffer(uint32_t size, int32_t id) {
     scoped_refptr<Buffer> buffer =
         command_buffer_->CreateTransferBufferWithId(size, id);
     memset(buffer->memory(), 0, size);
diff --git a/gpu/config/gpu_control_list.h b/gpu/config/gpu_control_list.h
index d9dc206..0044bda 100644
--- a/gpu/config/gpu_control_list.h
+++ b/gpu/config/gpu_control_list.h
@@ -9,6 +9,7 @@
 
 #include <set>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -22,7 +23,7 @@
 
 class GPU_EXPORT GpuControlList {
  public:
-  typedef base::hash_map<int, std::string> FeatureMap;
+  typedef std::unordered_map<int, std::string> FeatureMap;
 
   enum OsType {
     kOsLinux,
diff --git a/gpu/ipc/client/command_buffer_proxy_impl.cc b/gpu/ipc/client/command_buffer_proxy_impl.cc
index 2d5cc9fe..4eb8b698 100644
--- a/gpu/ipc/client/command_buffer_proxy_impl.cc
+++ b/gpu/ipc/client/command_buffer_proxy_impl.cc
@@ -356,7 +356,7 @@
 }
 
 scoped_refptr<gpu::Buffer> CommandBufferProxyImpl::CreateTransferBuffer(
-    size_t size,
+    uint32_t size,
     int32_t* id) {
   CheckLock();
   base::AutoLock lock(last_state_lock_);
diff --git a/gpu/ipc/client/command_buffer_proxy_impl.h b/gpu/ipc/client/command_buffer_proxy_impl.h
index f4406ba..155d7c9 100644
--- a/gpu/ipc/client/command_buffer_proxy_impl.h
+++ b/gpu/ipc/client/command_buffer_proxy_impl.h
@@ -12,6 +12,7 @@
 #include <memory>
 #include <queue>
 #include <string>
+#include <unordered_map>
 
 #include "base/callback.h"
 #include "base/compiler_specific.h"
@@ -102,7 +103,7 @@
                                 int32_t start,
                                 int32_t end) override;
   void SetGetBuffer(int32_t shm_id) override;
-  scoped_refptr<gpu::Buffer> CreateTransferBuffer(size_t size,
+  scoped_refptr<gpu::Buffer> CreateTransferBuffer(uint32_t size,
                                                   int32_t* id) override;
   void DestroyTransferBuffer(int32_t id) override;
 
@@ -156,7 +157,7 @@
 
  private:
   typedef std::map<int32_t, scoped_refptr<gpu::Buffer>> TransferBufferMap;
-  typedef base::hash_map<uint32_t, base::OnceClosure> SignalTaskMap;
+  typedef std::unordered_map<uint32_t, base::OnceClosure> SignalTaskMap;
 
   void CheckLock() {
     if (lock_) {
diff --git a/gpu/ipc/client/gpu_channel_host.h b/gpu/ipc/client/gpu_channel_host.h
index 8334a0b..cc0c56ca 100644
--- a/gpu/ipc/client/gpu_channel_host.h
+++ b/gpu/ipc/client/gpu_channel_host.h
@@ -10,6 +10,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/atomic_sequence_num.h"
@@ -205,7 +206,7 @@
 
     // Threading notes: most fields are only accessed on the IO thread, except
     // for lost_ which is protected by |lock_|.
-    base::hash_map<int32_t, RouteInfo> routes_;
+    std::unordered_map<int32_t, RouteInfo> routes_;
     std::unique_ptr<IPC::ChannelMojo> channel_;
     base::flat_map<int, IPC::PendingSyncMsg*> pending_syncs_;
 
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index dfac9af0..9f378ab 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -957,7 +957,7 @@
 }
 
 scoped_refptr<Buffer> InProcessCommandBuffer::CreateTransferBuffer(
-    size_t size,
+    uint32_t size,
     int32_t* id) {
   scoped_refptr<Buffer> buffer = MakeMemoryBuffer(size);
   *id = GetNextBufferId();
diff --git a/gpu/ipc/in_process_command_buffer.h b/gpu/ipc/in_process_command_buffer.h
index 0e82c3f..bb4d41b 100644
--- a/gpu/ipc/in_process_command_buffer.h
+++ b/gpu/ipc/in_process_command_buffer.h
@@ -114,7 +114,8 @@
                                 int32_t start,
                                 int32_t end) override;
   void SetGetBuffer(int32_t shm_id) override;
-  scoped_refptr<Buffer> CreateTransferBuffer(size_t size, int32_t* id) override;
+  scoped_refptr<Buffer> CreateTransferBuffer(uint32_t size,
+                                             int32_t* id) override;
   void DestroyTransferBuffer(int32_t id) override;
 
   // GpuControl implementation (called on client thread):
diff --git a/gpu/ipc/service/direct_composition_child_surface_win.cc b/gpu/ipc/service/direct_composition_child_surface_win.cc
index 59db0719..886dff6 100644
--- a/gpu/ipc/service/direct_composition_child_surface_win.cc
+++ b/gpu/ipc/service/direct_composition_child_surface_win.cc
@@ -262,19 +262,19 @@
 bool DirectCompositionChildSurfaceWin::SetDrawRectangle(
     const gfx::Rect& rectangle) {
   if (!gfx::Rect(size_).Contains(rectangle)) {
-    DLOG(ERROR) << "Draw rectangle must be contained within size of surface";
+    VLOG(1) << "Draw rectangle must be contained within size of surface";
     return false;
   }
 
   if (draw_texture_) {
-    DLOG(ERROR) << "SetDrawRectangle must be called only once per swap buffers";
+    VLOG(1) << "SetDrawRectangle must be called only once per swap buffers";
     return false;
   }
   DCHECK(!real_surface_);
   DCHECK(!g_current_surface);
 
   if (gfx::Rect(size_) != rectangle && !swap_chain_ && !dcomp_surface_) {
-    DLOG(ERROR) << "First draw to surface must draw to everything";
+    VLOG(1) << "First draw to surface must draw to everything";
     return false;
   }
 
@@ -294,7 +294,7 @@
         size_.width(), size_.height(), output_format,
         DXGI_ALPHA_MODE_PREMULTIPLIED, dcomp_surface_.GetAddressOf());
     if (FAILED(hr)) {
-      DLOG(ERROR) << "CreateSurface failed with error " << std::hex << hr;
+      VLOG(1) << "CreateSurface failed with error " << std::hex << hr;
       return false;
     }
   } else if (!enable_dc_layers_ && !swap_chain_) {
@@ -331,8 +331,8 @@
         d3d11_device_.Get(), &desc, nullptr, swap_chain_.GetAddressOf());
     first_swap_ = true;
     if (FAILED(hr)) {
-      DLOG(ERROR) << "CreateSwapChainForComposition failed with error "
-                  << std::hex << hr;
+      VLOG(1) << "CreateSwapChainForComposition failed with error " << std::hex
+              << hr;
       return false;
     }
   }
@@ -346,7 +346,7 @@
     HRESULT hr = dcomp_surface_->BeginDraw(
         &rect, IID_PPV_ARGS(draw_texture_.GetAddressOf()), &update_offset);
     if (FAILED(hr)) {
-      DLOG(ERROR) << "BeginDraw failed with error " << std::hex << hr;
+      VLOG(1) << "BeginDraw failed with error " << std::hex << hr;
       return false;
     }
     draw_offset_ = gfx::Point(update_offset) - rectangle.origin();
@@ -373,8 +373,8 @@
       eglCreatePbufferFromClientBuffer(GetDisplay(), EGL_D3D_TEXTURE_ANGLE,
                                        buffer, GetConfig(), pbuffer_attribs);
   if (!real_surface_) {
-    DLOG(ERROR) << "eglCreatePbufferFromClientBuffer failed with error "
-                << ui::GetLastEGLErrorString();
+    VLOG(1) << "eglCreatePbufferFromClientBuffer failed with error "
+            << ui::GetLastEGLErrorString();
     return false;
   }
 
diff --git a/gpu/ipc/service/gpu_memory_buffer_factory_io_surface.h b/gpu/ipc/service/gpu_memory_buffer_factory_io_surface.h
index 1ec67a3..aafcc0d1 100644
--- a/gpu/ipc/service/gpu_memory_buffer_factory_io_surface.h
+++ b/gpu/ipc/service/gpu_memory_buffer_factory_io_surface.h
@@ -5,6 +5,7 @@
 #ifndef GPU_IPC_SERVICE_GPU_MEMORY_BUFFER_FACTORY_IO_SURFACE_H_
 #define GPU_IPC_SERVICE_GPU_MEMORY_BUFFER_FACTORY_IO_SURFACE_H_
 
+#include <unordered_map>
 #include <utility>
 
 #include <IOSurface/IOSurface.h>
@@ -63,7 +64,8 @@
 
  private:
   typedef std::pair<gfx::IOSurfaceId, int> IOSurfaceMapKey;
-  typedef base::hash_map<IOSurfaceMapKey, base::ScopedCFTypeRef<IOSurfaceRef>>
+  typedef std::unordered_map<IOSurfaceMapKey,
+                             base::ScopedCFTypeRef<IOSurfaceRef>>
       IOSurfaceMap;
   // TODO(reveman): Remove |io_surfaces_| and allow IOSurface backed GMBs to be
   // used with any GPU process by passing a mach_port to CreateImageCHROMIUM.
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index 5c66089..bc36556 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -2091,11 +2091,13 @@
   }
   builders {
     name: "buildbot/chromium.clang/ToTiOS"
+    name: "buildbucket/luci.chromium.ci/ToTiOS"
     category: "iOS"
     short_name: "sim"
   }
   builders {
     name: "buildbot/chromium.clang/ToTiOSDevice"
+    name: "buildbucket/luci.chromium.ci/ToTiOSDevice"
     category: "iOS"
     short_name: "dev"
   }
diff --git a/ios/net/cookies/cookie_creation_time_manager.h b/ios/net/cookies/cookie_creation_time_manager.h
index 120ba7a..5d1c904 100644
--- a/ios/net/cookies/cookie_creation_time_manager.h
+++ b/ios/net/cookies/cookie_creation_time_manager.h
@@ -6,6 +6,7 @@
 #define IOS_NET_COOKIES_COOKIE_CREATION_TIME_MANAGER_H_
 
 #include <set>
+#include <unordered_map>
 
 #include "base/containers/hash_tables.h"
 #include "base/memory/weak_ptr.h"
@@ -44,7 +45,7 @@
   base::WeakPtr<CookieCreationTimeManager> GetWeakPtr();
 
  private:
-  base::hash_map<std::string, base::Time> creation_times_;
+  std::unordered_map<std::string, base::Time> creation_times_;
   std::set<base::Time> unique_times_;
   base::ThreadChecker thread_checker_;
   base::WeakPtrFactory<CookieCreationTimeManager> weak_factory_;
diff --git a/ios/net/cookies/cookie_creation_time_manager.mm b/ios/net/cookies/cookie_creation_time_manager.mm
index 831a5a6..dfe1ec9 100644
--- a/ios/net/cookies/cookie_creation_time_manager.mm
+++ b/ios/net/cookies/cookie_creation_time_manager.mm
@@ -101,7 +101,7 @@
 
 base::Time CookieCreationTimeManager::GetCreationTime(NSHTTPCookie* cookie) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  base::hash_map<std::string, base::Time>::iterator it =
+  std::unordered_map<std::string, base::Time>::iterator it =
       creation_times_.find(GetCookieUniqueID(cookie));
   if (it != creation_times_.end())
     return it->second;
diff --git a/ios/web/net/request_tracker_impl.mm b/ios/web/net/request_tracker_impl.mm
index 1ca660a..92e71ac6 100644
--- a/ios/web/net/request_tracker_impl.mm
+++ b/ios/web/net/request_tracker_impl.mm
@@ -37,7 +37,7 @@
 // always access it from the main thread, the provider is accessing it from the
 // WebThread, a thread created by the UIWebView/CFURL. For this reason access to
 // this variable must always gated by |g_trackers_lock|.
-typedef base::hash_map<std::string, web::RequestTrackerImpl*> TrackerMap;
+typedef std::unordered_map<std::string, web::RequestTrackerImpl*> TrackerMap;
 
 TrackerMap* g_trackers = NULL;
 base::Lock* g_trackers_lock = NULL;
diff --git a/ipc/ipc_logging.h b/ipc/ipc_logging.h
index eee32f6..3b3b4ca 100644
--- a/ipc/ipc_logging.h
+++ b/ipc/ipc_logging.h
@@ -10,6 +10,7 @@
 #if BUILDFLAG(IPC_MESSAGE_LOG_ENABLED)
 
 #include <stdint.h>
+#include <unordered_map>
 #include <vector>
 
 #include "base/component_export.h"
@@ -25,7 +26,7 @@
                             const IPC::Message* msg,
                             std::string* params);
 
-typedef base::hash_map<uint32_t, LogFunction > LogFunctionMap;
+typedef std::unordered_map<uint32_t, LogFunction> LogFunctionMap;
 
 namespace IPC {
 
diff --git a/media/base/decryptor.cc b/media/base/decryptor.cc
index f37fda5a..8d24de8d 100644
--- a/media/base/decryptor.cc
+++ b/media/base/decryptor.cc
@@ -24,4 +24,8 @@
 
 Decryptor::~Decryptor() = default;
 
+bool Decryptor::CanAlwaysDecrypt() {
+  return false;
+}
+
 }  // namespace media
diff --git a/media/base/decryptor.h b/media/base/decryptor.h
index fca4c73..b865cab 100644
--- a/media/base/decryptor.h
+++ b/media/base/decryptor.h
@@ -158,6 +158,9 @@
   // The decoder can be reinitialized after it is uninitialized.
   virtual void DeinitializeDecoder(StreamType stream_type) = 0;
 
+  // Returns whether or not the decryptor implementation supports decrypt-only.
+  virtual bool CanAlwaysDecrypt();
+
  private:
   DISALLOW_COPY_AND_ASSIGN(Decryptor);
 };
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index b34300c..680e2ee 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -418,6 +418,7 @@
                     const VideoDecodeCB& video_decode_cb));
   MOCK_METHOD1(ResetDecoder, void(StreamType stream_type));
   MOCK_METHOD1(DeinitializeDecoder, void(StreamType stream_type));
+  MOCK_METHOD0(CanAlwaysDecrypt, bool());
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockDecryptor);
diff --git a/media/base/test_helpers.cc b/media/base/test_helpers.cc
index 8a1f79e..3b0e6dd 100644
--- a/media/base/test_helpers.cc
+++ b/media/base/test_helpers.cc
@@ -18,6 +18,7 @@
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/media_util.h"
+#include "media/base/mock_filters.h"
 #include "ui/gfx/geometry/rect.h"
 
 using ::testing::_;
@@ -372,4 +373,28 @@
           height == config.coded_size().height());
 }
 
+std::unique_ptr<StrictMock<MockDemuxerStream>> CreateMockDemuxerStream(
+    DemuxerStream::Type type,
+    bool encrypted) {
+  auto stream = std::make_unique<StrictMock<MockDemuxerStream>>(type);
+
+  switch (type) {
+    case DemuxerStream::AUDIO:
+      stream->set_audio_decoder_config(encrypted
+                                           ? TestAudioConfig::NormalEncrypted()
+                                           : TestAudioConfig::Normal());
+      break;
+    case DemuxerStream::VIDEO:
+      stream->set_video_decoder_config(encrypted
+                                           ? TestVideoConfig::NormalEncrypted()
+                                           : TestVideoConfig::Normal());
+      break;
+    default:
+      NOTREACHED();
+      break;
+  }
+
+  return stream;
+}
+
 }  // namespace media
diff --git a/media/base/test_helpers.h b/media/base/test_helpers.h
index 7bea53e8..cbc322a 100644
--- a/media/base/test_helpers.h
+++ b/media/base/test_helpers.h
@@ -33,6 +33,7 @@
 class AudioBuffer;
 class AudioBus;
 class DecoderBuffer;
+class MockDemuxerStream;
 
 // Return a callback that expects to be run once.
 base::Closure NewExpectedClosure();
@@ -194,6 +195,10 @@
 bool VerifyFakeVideoBufferForTest(const DecoderBuffer& buffer,
                                   const VideoDecoderConfig& config);
 
+// Create a MockDemuxerStream for testing purposes.
+std::unique_ptr<::testing::StrictMock<MockDemuxerStream>>
+CreateMockDemuxerStream(DemuxerStream::Type type, bool encrypted);
+
 // Compares two {Audio|Video}DecoderConfigs
 MATCHER_P(DecoderConfigEq, config, "") {
   return arg.Matches(config);
diff --git a/media/base/waiting.h b/media/base/waiting.h
index 90445998..2b0cc47 100644
--- a/media/base/waiting.h
+++ b/media/base/waiting.h
@@ -19,10 +19,20 @@
 // [2]
 // https://www.w3.org/TR/html5/semantics-embedded-content.html#eventdef-media-waiting
 
-// TODO(xhwang): Add more waiting reasons.
 enum class WaitingReason {
+  // The playback cannot proceed because some decryption key is not available.
+  // This could happen when the license exchange is delayed or failed. The
+  // playback will resume after the decryption key becomes available.
   kNoDecryptionKey,
-  kMaxValue = kNoDecryptionKey,
+
+  // The playback cannot proceed because the decoder has lost its state, e.g.
+  // information about reference frames. Usually this only happens to hardware
+  // decoders. To recover from this state, reset the decoder and start decoding
+  // from a key frame, which can typically be accomplished by a pipeline seek.
+  kDecoderStateLost,
+
+  // Must be assigned with the last enum value above.
+  kMaxValue = kDecoderStateLost,
 };
 
 // Callback to notify waiting state and the reason.
diff --git a/media/blink/cdm_session_adapter.h b/media/blink/cdm_session_adapter.h
index 0cb6cf5..6710962 100644
--- a/media/blink/cdm_session_adapter.h
+++ b/media/blink/cdm_session_adapter.h
@@ -10,6 +10,7 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -112,8 +113,9 @@
   friend class base::RefCounted<CdmSessionAdapter>;
 
   // Session ID to WebContentDecryptionModuleSessionImpl mapping.
-  typedef base::hash_map<std::string,
-                         base::WeakPtr<WebContentDecryptionModuleSessionImpl> >
+  typedef std::unordered_map<
+      std::string,
+      base::WeakPtr<WebContentDecryptionModuleSessionImpl>>
       SessionMap;
 
   ~CdmSessionAdapter();
diff --git a/media/blink/lru.h b/media/blink/lru.h
index 7ad9637..5aab6d3 100644
--- a/media/blink/lru.h
+++ b/media/blink/lru.h
@@ -8,6 +8,7 @@
 #include <stddef.h>
 
 #include <list>
+#include <unordered_map>
 
 #include "base/containers/hash_tables.h"
 #include "base/macros.h"
@@ -85,7 +86,7 @@
 
   // Maps element values to positions in the list so that we
   // can quickly remove elements.
-  base::hash_map<T, typename std::list<T>::iterator> pos_;
+  std::unordered_map<T, typename std::list<T>::iterator> pos_;
 
   DISALLOW_COPY_AND_ASSIGN(LRU);
 };
diff --git a/media/blink/multibuffer.h b/media/blink/multibuffer.h
index be1ea23..6457be4 100644
--- a/media/blink/multibuffer.h
+++ b/media/blink/multibuffer.h
@@ -12,6 +12,7 @@
 #include <map>
 #include <memory>
 #include <set>
+#include <unordered_map>
 #include <vector>
 
 #include "base/callback.h"
@@ -41,7 +42,7 @@
 
 }  // namespace media
 
-namespace BASE_HASH_NAMESPACE {
+namespace std {
 
 template <>
 struct hash<media::MultiBufferGlobalBlockId> {
@@ -50,7 +51,7 @@
   }
 };
 
-}  // namespace BASE_HASH_NAMESPACE
+}  // namespace std
 
 namespace media {
 
@@ -210,7 +211,7 @@
   // Block numbers can be calculated from byte positions as:
   // block_num = byte_pos >> block_size_shift
   typedef MultiBufferBlockId BlockId;
-  typedef base::hash_map<BlockId, scoped_refptr<DataBuffer>> DataMap;
+  typedef std::unordered_map<BlockId, scoped_refptr<DataBuffer>> DataMap;
 
   // Registers a reader at the given position.
   // If the cache does not already contain |pos|, it will activate
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index a8efb8c..81a9fbc 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -2102,9 +2102,18 @@
       // |has_additional_usable_key| = true). http://crbug.com/461903
       encrypted_client_->DidResumePlaybackBlockedForKey();
       return;
-  }
 
-  // TODO(xhwang): Handle other |reason| when added.
+    // Ideally this should be handled by PipelineController directly without
+    // being proxied here. But currently Pipeline::Client (|this|) is passed to
+    // PipelineImpl directly without going through |pipeline_controller_|,
+    // making it difficult to do.
+    // TODO(xhwang): Handle this in PipelineController when we have a clearer
+    // picture on how to refactor WebMediaPlayerImpl, PipelineController and
+    // PipelineImpl.
+    case WaitingReason::kDecoderStateLost:
+      pipeline_controller_.OnDecoderStateLost();
+      return;
+  }
 }
 
 void WebMediaPlayerImpl::OnVideoNaturalSizeChange(const gfx::Size& size) {
diff --git a/media/cdm/aes_decryptor.cc b/media/cdm/aes_decryptor.cc
index cd34336b..ff30013 100644
--- a/media/cdm/aes_decryptor.cc
+++ b/media/cdm/aes_decryptor.cc
@@ -553,6 +553,10 @@
   // nothing to be done here.
 }
 
+bool AesDecryptor::CanAlwaysDecrypt() {
+  return true;
+}
+
 bool AesDecryptor::CreateSession(const std::string& session_id,
                                  CdmSessionType session_type) {
   auto it = open_sessions_.find(session_id);
diff --git a/media/cdm/aes_decryptor.h b/media/cdm/aes_decryptor.h
index 4ff21fe..94d08fd 100644
--- a/media/cdm/aes_decryptor.h
+++ b/media/cdm/aes_decryptor.h
@@ -84,6 +84,7 @@
                              const VideoDecodeCB& video_decode_cb) override;
   void ResetDecoder(StreamType stream_type) override;
   void DeinitializeDecoder(StreamType stream_type) override;
+  bool CanAlwaysDecrypt() override;
 
  private:
   // Testing classes that needs to manipulate internal states for testing.
diff --git a/media/filters/BUILD.gn b/media/filters/BUILD.gn
index ffe1e64..aa3932a 100644
--- a/media/filters/BUILD.gn
+++ b/media/filters/BUILD.gn
@@ -35,6 +35,8 @@
     "decrypting_audio_decoder.h",
     "decrypting_demuxer_stream.cc",
     "decrypting_demuxer_stream.h",
+    "decrypting_media_resource.cc",
+    "decrypting_media_resource.h",
     "decrypting_video_decoder.cc",
     "decrypting_video_decoder.h",
     "file_data_source.cc",
@@ -266,6 +268,7 @@
     "decoder_selector_unittest.cc",
     "decrypting_audio_decoder_unittest.cc",
     "decrypting_demuxer_stream_unittest.cc",
+    "decrypting_media_resource_unittest.cc",
     "decrypting_video_decoder_unittest.cc",
     "fake_video_decoder.cc",
     "fake_video_decoder.h",
diff --git a/media/filters/decrypting_demuxer_stream_unittest.cc b/media/filters/decrypting_demuxer_stream_unittest.cc
index d4f605b7..ab865a4 100644
--- a/media/filters/decrypting_demuxer_stream_unittest.cc
+++ b/media/filters/decrypting_demuxer_stream_unittest.cc
@@ -24,8 +24,8 @@
 
 using ::testing::_;
 using ::testing::HasSubstr;
-using ::testing::IsNull;
 using ::testing::InSequence;
+using ::testing::IsNull;
 using ::testing::Return;
 using ::testing::SaveArg;
 using ::testing::StrictMock;
diff --git a/media/filters/decrypting_media_resource.cc b/media/filters/decrypting_media_resource.cc
new file mode 100644
index 0000000..882f1be
--- /dev/null
+++ b/media/filters/decrypting_media_resource.cc
@@ -0,0 +1,109 @@
+// 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 "media/filters/decrypting_media_resource.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "media/base/cdm_context.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/media_log.h"
+#include "media/base/pipeline_status.h"
+#include "media/filters/decrypting_demuxer_stream.h"
+
+namespace media {
+
+DecryptingMediaResource::DecryptingMediaResource(
+    MediaResource* media_resource,
+    CdmContext* cdm_context,
+    MediaLog* media_log,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : media_resource_(media_resource),
+      cdm_context_(cdm_context),
+      media_log_(media_log),
+      task_runner_(task_runner),
+      weak_factory_(this) {
+  DCHECK(media_resource);
+  DCHECK(cdm_context_);
+  DCHECK(cdm_context_->GetDecryptor());
+  DCHECK(cdm_context_->GetDecryptor()->CanAlwaysDecrypt());
+  DCHECK(media_log_);
+  DCHECK(task_runner->BelongsToCurrentThread());
+}
+
+DecryptingMediaResource::~DecryptingMediaResource() = default;
+
+MediaResource::Type DecryptingMediaResource::GetType() const {
+  return media_resource_->GetType();
+}
+
+std::vector<DemuxerStream*> DecryptingMediaResource::GetAllStreams() {
+  if (streams_.size())
+    return streams_;
+
+  return media_resource_->GetAllStreams();
+}
+
+MediaUrlParams DecryptingMediaResource::GetMediaUrlParams() const {
+  return media_resource_->GetMediaUrlParams();
+}
+
+void DecryptingMediaResource::Initialize(InitCB init_cb) {
+  DCHECK(init_cb);
+
+  auto streams = media_resource_->GetAllStreams();
+
+  // Save the callback so that we can invoke it when the
+  // DecryptingDemuxerStreams have finished initialization.
+  init_cb_ = std::move(init_cb);
+  num_dds_pending_init_ = streams.size();
+
+  for (auto* stream : streams) {
+    // TODO(chadduffin): Implement proper handling of the media::WaitingCB such
+    // that when the DecryptingDemuxerStream is waiting for a decryption key
+    // the firing of the callback will be bubbled up to the media pipeline.
+    auto decrypting_demuxer_stream = std::make_unique<DecryptingDemuxerStream>(
+        task_runner_, media_log_, base::DoNothing());
+
+    // DecryptingDemuxerStream always invokes the callback asynchronously so
+    // that we have no reentrancy issues. "All public APIs and callbacks are
+    // trampolined to the |task_runner_|."
+    decrypting_demuxer_stream->Initialize(
+        stream, cdm_context_,
+        base::BindRepeating(
+            &DecryptingMediaResource::OnDecryptingDemuxerInitialized,
+            weak_factory_.GetWeakPtr()));
+
+    streams_.push_back(decrypting_demuxer_stream.get());
+    owned_streams_.push_back(std::move(decrypting_demuxer_stream));
+  }
+}
+
+int DecryptingMediaResource::DecryptingDemuxerStreamCountForTesting() const {
+  return owned_streams_.size();
+}
+
+void DecryptingMediaResource::OnDecryptingDemuxerInitialized(
+    PipelineStatus status) {
+  DVLOG(2) << __func__ << ": DecryptingDemuxerStream initialization ended "
+           << "with the status: " << MediaLog::PipelineStatusToString(status);
+
+  // Decrement the count of DecryptingDemuxerStreams that need to be
+  // initialized.
+  --num_dds_pending_init_;
+
+  if (!init_cb_)
+    return;
+
+  if (status != PIPELINE_OK)
+    std::move(init_cb_).Run(false);
+  else if (num_dds_pending_init_ == 0)
+    std::move(init_cb_).Run(true);
+}
+
+}  // namespace media
diff --git a/media/filters/decrypting_media_resource.h b/media/filters/decrypting_media_resource.h
new file mode 100644
index 0000000..4a34bf0
--- /dev/null
+++ b/media/filters/decrypting_media_resource.h
@@ -0,0 +1,82 @@
+// 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 MEDIA_FILTERS_DECRYPTING_MEDIA_RESOURCE_H_
+#define MEDIA_FILTERS_DECRYPTING_MEDIA_RESOURCE_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "media/base/media_resource.h"
+#include "media/base/pipeline.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}  // namespace base
+
+namespace media {
+
+class CdmContext;
+class DemuxerStream;
+class DecryptingDemuxerStream;
+
+// DecryptingMediaResource is used as a proxy for a MediaResource
+// implementation. This wrapper is only created when the decryptor
+// implementation always supports decrypt-only and will decrypt the streams
+// that it retrieves from the internal MediaResource. These clear streams are
+// then passed downstream, allowing renderer implementations to no longer need
+// to worry about decryption.
+class MEDIA_EXPORT DecryptingMediaResource : public MediaResource {
+ public:
+  using InitCB = base::OnceCallback<void(bool success)>;
+
+  DecryptingMediaResource(
+      MediaResource* media_resource,
+      CdmContext* cdm_context,
+      MediaLog* media_log,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+  ~DecryptingMediaResource() override;
+
+  // MediaResource implementation:
+  MediaResource::Type GetType() const override;
+  std::vector<DemuxerStream*> GetAllStreams() override;
+  MediaUrlParams GetMediaUrlParams() const override;
+
+  void Initialize(InitCB init_cb);
+
+  // Returns the number of DecryptingDemuxerStreams that were created.
+  virtual int DecryptingDemuxerStreamCountForTesting() const;
+
+ private:
+  void OnDecryptingDemuxerInitialized(PipelineStatus status);
+
+  MediaResource* const media_resource_;
+  CdmContext* const cdm_context_;
+  MediaLog* const media_log_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  // Number of DecryptingDemuxerStreams that have yet to be initialized.
+  int num_dds_pending_init_ = 0;
+
+  // |streams_| is the set of streams that this implementation does not own and
+  // will be returned when GetAllStreams() is invoked. |owned_streams_| is the
+  // set of DecryptingDemuxerStreams that we have created and own (i.e.
+  // responsible for destructing).
+  std::vector<DemuxerStream*> streams_;
+  std::vector<std::unique_ptr<DecryptingDemuxerStream>> owned_streams_;
+
+  // Called when the final DecryptingDemuxerStream has been initialized *or*
+  // if one of the DecryptingDemuxerStreams failed to initialize correctly.
+  InitCB init_cb_;
+  base::WeakPtrFactory<DecryptingMediaResource> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DecryptingMediaResource);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_FILTERS_DECRYPTING_MEDIA_RESOURCE_H_
diff --git a/media/filters/decrypting_media_resource_unittest.cc b/media/filters/decrypting_media_resource_unittest.cc
new file mode 100644
index 0000000..f40b150
--- /dev/null
+++ b/media/filters/decrypting_media_resource_unittest.cc
@@ -0,0 +1,177 @@
+// 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 <memory>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/test/mock_callback.h"
+#include "base/test/scoped_task_environment.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/media_util.h"
+#include "media/base/mock_filters.h"
+#include "media/base/pipeline_status.h"
+#include "media/base/test_helpers.h"
+#include "media/filters/decrypting_demuxer_stream.h"
+#include "media/filters/decrypting_media_resource.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrictMock;
+
+namespace media {
+
+class DecryptingMediaResourceTest : public testing::Test {
+ public:
+  DecryptingMediaResourceTest() {
+    EXPECT_CALL(cdm_context_, GetDecryptor())
+        .WillRepeatedly(Return(&decryptor_));
+    EXPECT_CALL(decryptor_, CanAlwaysDecrypt()).WillRepeatedly(Return(true));
+    EXPECT_CALL(decryptor_, CancelDecrypt(_)).Times(AnyNumber());
+    EXPECT_CALL(decryptor_, RegisterNewKeyCB(_, _)).Times(AnyNumber());
+    EXPECT_CALL(demuxer_, GetAllStreams())
+        .WillRepeatedly(
+            Invoke(this, &DecryptingMediaResourceTest::GetAllStreams));
+
+    decrypting_media_resource_ = std::make_unique<DecryptingMediaResource>(
+        &demuxer_, &cdm_context_, &null_media_log_,
+        scoped_task_environment_.GetMainThreadTaskRunner());
+  }
+
+  ~DecryptingMediaResourceTest() {
+    // Ensure that the DecryptingMediaResource is destructed before other
+    // objects that it internally references but does not own.
+    decrypting_media_resource_.reset();
+  }
+
+  bool HasEncryptedStream() {
+    for (auto* stream : decrypting_media_resource_->GetAllStreams()) {
+      if ((stream->type() == DemuxerStream::AUDIO &&
+           stream->audio_decoder_config().is_encrypted()) ||
+          (stream->type() == DemuxerStream::VIDEO &&
+           stream->video_decoder_config().is_encrypted()))
+        return true;
+    }
+
+    return false;
+  }
+
+  void AddStream(DemuxerStream::Type type, bool encrypted) {
+    streams_.push_back(CreateMockDemuxerStream(type, encrypted));
+  }
+
+  std::vector<DemuxerStream*> GetAllStreams() {
+    std::vector<DemuxerStream*> streams;
+
+    for (auto& stream : streams_) {
+      streams.push_back(stream.get());
+    }
+
+    return streams;
+  }
+
+ protected:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  base::MockCallback<DecryptingMediaResource::InitCB>
+      decrypting_media_resource_init_cb_;
+  NullMediaLog null_media_log_;
+  StrictMock<MockDecryptor> decryptor_;
+  StrictMock<MockDemuxer> demuxer_;
+  StrictMock<MockCdmContext> cdm_context_;
+  std::unique_ptr<DecryptingMediaResource> decrypting_media_resource_;
+  std::vector<std::unique_ptr<StrictMock<MockDemuxerStream>>> streams_;
+};
+
+TEST_F(DecryptingMediaResourceTest,
+       CreatesDecryptingDemuxerStreamForClearStreams) {
+  AddStream(DemuxerStream::AUDIO, /* encrypted = */ false);
+  AddStream(DemuxerStream::VIDEO, /* encrypted = */ false);
+
+  EXPECT_CALL(decrypting_media_resource_init_cb_, Run(true));
+
+  decrypting_media_resource_->Initialize(
+      decrypting_media_resource_init_cb_.Get());
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_EQ(
+      decrypting_media_resource_->DecryptingDemuxerStreamCountForTesting(), 2);
+  EXPECT_FALSE(HasEncryptedStream());
+}
+
+TEST_F(DecryptingMediaResourceTest,
+       CreatesDecryptingDemuxerStreamForEncryptedStreams) {
+  AddStream(DemuxerStream::AUDIO, /* encrypted = */ true);
+  AddStream(DemuxerStream::VIDEO, /* encrypted = */ true);
+
+  EXPECT_CALL(decrypting_media_resource_init_cb_, Run(true));
+
+  decrypting_media_resource_->Initialize(
+      decrypting_media_resource_init_cb_.Get());
+  scoped_task_environment_.RunUntilIdle();
+
+  // When using an AesDecryptor we preemptively wrap our streams with a
+  // DecryptingDemuxerStream, regardless of encryption. With this in mind, we
+  // should have three DecryptingDemuxerStreams.
+  EXPECT_EQ(
+      decrypting_media_resource_->DecryptingDemuxerStreamCountForTesting(), 2);
+
+  // All of the streams that we get from our DecryptingMediaResource, NOT the
+  // internal MediaResource implementation, should be clear.
+  EXPECT_FALSE(HasEncryptedStream());
+}
+
+TEST_F(DecryptingMediaResourceTest,
+       CreatesDecryptingDemuxerStreamForMixedStreams) {
+  AddStream(DemuxerStream::AUDIO, /* encrypted = */ false);
+  AddStream(DemuxerStream::VIDEO, /* encrypted = */ true);
+
+  EXPECT_CALL(decrypting_media_resource_init_cb_, Run(true));
+
+  decrypting_media_resource_->Initialize(
+      decrypting_media_resource_init_cb_.Get());
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_EQ(
+      decrypting_media_resource_->DecryptingDemuxerStreamCountForTesting(), 2);
+  EXPECT_FALSE(HasEncryptedStream());
+}
+
+TEST_F(DecryptingMediaResourceTest,
+       OneDecryptingDemuxerStreamFailsInitialization) {
+  AddStream(DemuxerStream::AUDIO, /* encrypted = */ false);
+  AddStream(DemuxerStream::VIDEO, /* encrypted = */ true);
+
+  // The first DecryptingDemuxerStream will fail to initialize, causing the
+  // callback to be run with a value of false. The second
+  // DecryptingDemuxerStream will succeed but never invoke the callback.
+  EXPECT_CALL(cdm_context_, GetDecryptor())
+      .WillOnce(Return(nullptr))
+      .WillRepeatedly(Return(&decryptor_));
+  EXPECT_CALL(decrypting_media_resource_init_cb_, Run(false));
+
+  decrypting_media_resource_->Initialize(
+      decrypting_media_resource_init_cb_.Get());
+  scoped_task_environment_.RunUntilIdle();
+}
+
+TEST_F(DecryptingMediaResourceTest,
+       BothDecryptingDemuxerStreamsFailInitialization) {
+  AddStream(DemuxerStream::AUDIO, /* encrypted = */ false);
+  AddStream(DemuxerStream::VIDEO, /* encrypted = */ true);
+
+  // Both DecryptingDemuxerStreams will fail to initialize but the callback
+  // should still only be invoked a single time.
+  EXPECT_CALL(cdm_context_, GetDecryptor()).WillRepeatedly(Return(nullptr));
+  EXPECT_CALL(decrypting_media_resource_init_cb_, Run(false));
+
+  decrypting_media_resource_->Initialize(
+      decrypting_media_resource_init_cb_.Get());
+  scoped_task_environment_.RunUntilIdle();
+}
+
+}  // namespace media
diff --git a/media/filters/pipeline_controller.cc b/media/filters/pipeline_controller.cc
index 4627b84bc..bd05ebdc 100644
--- a/media/filters/pipeline_controller.cc
+++ b/media/filters/pipeline_controller.cc
@@ -107,6 +107,38 @@
   }
 }
 
+void PipelineController::OnDecoderStateLost() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Note: |time_updated| and |pending_seeked_cb_| are both false.
+  pending_seek_except_start_ = true;
+
+  // If we are already seeking or resuming, or if there's already a seek
+  // pending,elide the seek. This is okay for decoder state lost since it just
+  // needs one seek to recover (the decoder is reset and the next decode starts
+  // from a key frame).
+  //
+  // Note on potential race condition: When the seek is elided, it's possible
+  // that the decoder state loss happens before or after the previous seek
+  // (decoder Reset()):
+  // 1. Decoder state loss happens before Decoder::Reset() during the previous
+  // seek. In this case we are fine since we just need a Reset().
+  // 2. Decoder state loss happens after Decoder::Reset() during a previous
+  // seek:
+  // 2.1 If state loss happens before any Decode() we are still fine, since the
+  // decoder is in a clean state.
+  // 2.2 If state loss happens after a Decode(), then here we should not be in
+  // the SEEKING state.
+  if (state_ == State::SEEKING || state_ == State::RESUMING || pending_seek_)
+    return;
+
+  // Force a seek to the current time.
+  pending_seek_time_ = pipeline_->GetMediaTime();
+  pending_seek_ = true;
+
+  Dispatch();
+}
+
 bool PipelineController::IsStable() {
   DCHECK(thread_checker_.CalledOnValidThread());
   return state_ == State::PLAYING;
diff --git a/media/filters/pipeline_controller.h b/media/filters/pipeline_controller.h
index 92a420a..4821304 100644
--- a/media/filters/pipeline_controller.h
+++ b/media/filters/pipeline_controller.h
@@ -102,6 +102,10 @@
   // been suspended.
   void Resume();
 
+  // Called when a decoder in the pipeline lost its state. This requires a seek
+  // so that the decoder can start from a new key frame.
+  void OnDecoderStateLost();
+
   // Returns true if the current state is stable. This means that |state_| is
   // PLAYING and there are no pending operations. Requests are processed
   // immediately when the state is stable, otherwise they are queued.
@@ -193,7 +197,8 @@
   // issued at the next stable state.
   bool pending_seeked_cb_ = false;
 
-  // Indicates that a seek has occurred from an explicit call to Seek().
+  // Indicates that a seek has occurred from an explicit call to Seek() or
+  // OnDecoderStateLost().
   bool pending_seek_except_start_ = false;
 
   // Indicates that time has been changed by a seek, which will be reported at
diff --git a/media/filters/pipeline_controller_unittest.cc b/media/filters/pipeline_controller_unittest.cc
index 45ba532..1ce0adb 100644
--- a/media/filters/pipeline_controller_unittest.cc
+++ b/media/filters/pipeline_controller_unittest.cc
@@ -253,6 +253,40 @@
   EXPECT_TRUE(pipeline_controller_.IsStable());
 }
 
+// Makes sure OnDecoderStateLost() triggers a seek to the current media time.
+TEST_F(PipelineControllerTest, DecoderStateLost) {
+  Complete(StartPipeline());
+
+  constexpr auto kCurrentMediaTime = base::TimeDelta::FromSeconds(7);
+  EXPECT_CALL(*pipeline_, GetMediaTime())
+      .WillRepeatedly(Return(kCurrentMediaTime));
+
+  EXPECT_CALL(demuxer_, StartWaitingForSeek(kCurrentMediaTime));
+  EXPECT_CALL(*pipeline_, Seek(kCurrentMediaTime, _));
+
+  pipeline_controller_.OnDecoderStateLost();
+  base::RunLoop().RunUntilIdle();
+}
+
+// Makes sure OnDecoderStateLost() does not trigger a seek during pending seek.
+TEST_F(PipelineControllerTest, DecoderStateLost_DuringPendingSeek) {
+  Complete(StartPipeline());
+
+  // Create a pending seek.
+  base::TimeDelta kSeekTime = base::TimeDelta::FromSeconds(5);
+  EXPECT_CALL(demuxer_, StartWaitingForSeek(kSeekTime));
+  PipelineStatusCB seek_cb = SeekPipeline(kSeekTime);
+  base::RunLoop().RunUntilIdle();
+  Mock::VerifyAndClear(&demuxer_);
+
+  // OnDecoderStateLost() should not trigger another seek.
+  EXPECT_CALL(*pipeline_, GetMediaTime()).Times(0);
+  pipeline_controller_.OnDecoderStateLost();
+  base::RunLoop().RunUntilIdle();
+
+  Complete(seek_cb);
+}
+
 TEST_F(PipelineControllerTest, SuspendResumeTime) {
   Complete(StartPipeline());
   Complete(SuspendPipeline());
diff --git a/media/filters/vp9_parser.cc b/media/filters/vp9_parser.cc
index b3f455b0..c343565 100644
--- a/media/filters/vp9_parser.cc
+++ b/media/filters/vp9_parser.cc
@@ -652,10 +652,12 @@
 
     frame_info = frames_.front();
     frames_.pop_front();
-    if (frame_info.decrypt_config) {
-      *frame_decrypt_config = frame_info.decrypt_config->Clone();
-    } else {
-      *frame_decrypt_config = nullptr;
+    if (frame_decrypt_config) {
+      if (frame_info.decrypt_config) {
+        *frame_decrypt_config = frame_info.decrypt_config->Clone();
+      } else {
+        *frame_decrypt_config = nullptr;
+      }
     }
 
     if (ParseUncompressedHeader(frame_info, fhdr, &result))
diff --git a/media/gpu/windows/d3d11_video_decoder.cc b/media/gpu/windows/d3d11_video_decoder.cc
index 9abf1aa..4ab1f20 100644
--- a/media/gpu/windows/d3d11_video_decoder.cc
+++ b/media/gpu/windows/d3d11_video_decoder.cc
@@ -219,6 +219,8 @@
   DCHECK(output_cb);
   DCHECK(waiting_cb);
 
+  state_ = State::kInitializing;
+
   if (!IsPotentiallySupported(config)) {
     DVLOG(3) << "D3D11 video decoder not supported for the config.";
     init_cb.Run(false);
@@ -230,13 +232,6 @@
   output_cb_ = output_cb;
   waiting_cb_ = waiting_cb;
 
-  D3D11VideoDecoderImpl::InitCB cb = base::BindOnce(
-      &D3D11VideoDecoder::OnGpuInitComplete, weak_factory_.GetWeakPtr());
-
-  D3D11VideoDecoderImpl::ReturnPictureBufferCB return_picture_buffer_cb =
-      base::BindRepeating(&D3D11VideoDecoder::ReceivePictureBufferFromClient,
-                          weak_factory_.GetWeakPtr());
-
   // Initialize the video decoder.
 
   // Use the ANGLE device, rather than create our own.  It would be nice if we
@@ -349,12 +344,20 @@
             &D3D11VideoDecoder::OnCdmContextEvent, weak_factory_.GetWeakPtr()));
   }
 
+  auto impl_init_cb = base::BindOnce(&D3D11VideoDecoder::OnGpuInitComplete,
+                                     weak_factory_.GetWeakPtr());
+
+  auto get_picture_buffer_cb =
+      base::BindRepeating(&D3D11VideoDecoder::ReceivePictureBufferFromClient,
+                          weak_factory_.GetWeakPtr());
+
   // Initialize the gpu side.  We wait until everything else is initialized,
   // since we allow it to call us back re-entrantly to reduce latency.  Note
   // that if we're not on the same thread, then we should probably post the
   // call earlier, since re-entrancy won't be an issue.
   if (impl_task_runner_->RunsTasksInCurrentSequence()) {
-    impl_->Initialize(std::move(cb), std::move(return_picture_buffer_cb));
+    impl_->Initialize(std::move(impl_init_cb),
+                      std::move(get_picture_buffer_cb));
     return;
   }
 
@@ -365,8 +368,8 @@
   impl_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&D3D11VideoDecoderImpl::Initialize, impl_weak_,
-                     BindToCurrentLoop(std::move(cb)),
-                     BindToCurrentLoop(std::move(return_picture_buffer_cb))));
+                     BindToCurrentLoop(std::move(impl_init_cb)),
+                     BindToCurrentLoop(std::move(get_picture_buffer_cb))));
 }
 
 void D3D11VideoDecoder::ReceivePictureBufferFromClient(
@@ -424,8 +427,11 @@
 void D3D11VideoDecoder::DoDecode() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (state_ != State::kRunning)
+  if (state_ != State::kRunning) {
+    DVLOG(2) << __func__ << ": Do nothing in " << static_cast<int>(state_)
+             << " state.";
     return;
+  }
 
   if (!current_buffer_) {
     if (input_buffer_queue_.empty()) {
@@ -509,9 +515,10 @@
   // TODO(liberato): how do we signal an error?
   accelerated_video_decoder_->Reset();
 
-  // Transition from kWaitingForNewKey to kRunning upon reset since the new
-  // buffer could be clear or have a different key ID.
-  if (state_ == State::kWaitingForNewKey)
+  // Transition out of kWaitingForNewKey since the new buffer could be clear or
+  // have a different key ID. Transition out of kWaitingForReset since reset
+  // just happened.
+  if (state_ == State::kWaitingForNewKey || state_ == State::kWaitingForReset)
     state_ = State::kRunning;
 
   closure.Run();
@@ -650,6 +657,12 @@
 
 void D3D11VideoDecoder::OnCdmContextEvent(CdmContext::Event event) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DVLOG(1) << __func__ << ": event = " << static_cast<int>(event);
+
+  if (state_ == State::kInitializing || state_ == State::kError) {
+    DVLOG(1) << "Do nothing in " << static_cast<int>(state_) << " state.";
+    return;
+  }
 
   switch (event) {
     case CdmContext::Event::kHasAdditionalUsableKey:
@@ -667,7 +680,8 @@
       return;
 
     case CdmContext::Event::kHardwareContextLost:
-      // TODO(xhwang): Handle this event.
+      state_ = State::kWaitingForReset;
+      waiting_cb_.Run(WaitingReason::kDecoderStateLost);
       return;
   }
 }
diff --git a/media/gpu/windows/d3d11_video_decoder.h b/media/gpu/windows/d3d11_video_decoder.h
index 9ae3024..46fa8d3 100644
--- a/media/gpu/windows/d3d11_video_decoder.h
+++ b/media/gpu/windows/d3d11_video_decoder.h
@@ -152,15 +152,24 @@
   enum class State {
     // Initializing resources required to create a codec.
     kInitializing,
+
     // Initialization has completed and we're running. This is the only state
     // in which |codec_| might be non-null. If |codec_| is null, a codec
     // creation is pending.
     kRunning,
+
     // The decoder cannot make progress because it doesn't have the key to
     // decrypt the buffer. Waiting for a new key to be available.
     // This should only be transitioned from kRunning, and should only
-    // transition to kRunning.
+    // transition to kRunning or kWaitingForReset.
     kWaitingForNewKey,
+
+    // The decoder cannot make progress because it's waiting for a Reset(). This
+    // could happen as a result of CdmContext hardware context loss. This should
+    // only be transitioned from kRunning or kWaitingForNewKey, and should only
+    // transition to kRunning.
+    kWaitingForReset,
+
     // A fatal error occurred. A terminal state.
     kError,
   };
diff --git a/media/gpu/windows/d3d11_video_decoder_unittest.cc b/media/gpu/windows/d3d11_video_decoder_unittest.cc
index b4a389f..5e7a561 100644
--- a/media/gpu/windows/d3d11_video_decoder_unittest.cc
+++ b/media/gpu/windows/d3d11_video_decoder_unittest.cc
@@ -232,4 +232,6 @@
   EXPECT_TRUE(d3d11_decoder_raw_->IsPotentiallySupported(encrypted_config));
 }
 
+// TODO(xhwang): Add tests to cover kWaitingForNewKey and kWaitingForReset.
+
 }  // namespace media
diff --git a/media/midi/midi_manager_alsa.h b/media/midi/midi_manager_alsa.h
index 92fb8309..75cba5cc 100644
--- a/media/midi/midi_manager_alsa.h
+++ b/media/midi/midi_manager_alsa.h
@@ -10,6 +10,7 @@
 
 #include <map>
 #include <memory>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -362,8 +363,8 @@
     };
   };
 
-  using SourceMap = base::hash_map<int, uint32_t>;
-  using OutPortMap = base::hash_map<uint32_t, int>;
+  using SourceMap = std::unordered_map<int, uint32_t>;
+  using OutPortMap = std::unordered_map<uint32_t, int>;
   using ScopedSndSeqPtr = std::unique_ptr<snd_seq_t, SndSeqDeleter>;
   using ScopedSndMidiEventPtr =
       std::unique_ptr<snd_midi_event_t, SndMidiEventDeleter>;
diff --git a/media/midi/midi_manager_android.h b/media/midi/midi_manager_android.h
index f63fde6f..86da387b 100644
--- a/media/midi/midi_manager_android.h
+++ b/media/midi/midi_manager_android.h
@@ -10,6 +10,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <unordered_map>
 #include <vector>
 
 #include "base/android/scoped_java_ref.h"
@@ -79,11 +80,11 @@
   std::vector<MidiInputPortAndroid*> all_input_ports_;
   // A dictionary from a port to its index.
   // input_port_to_index_[all_input_ports_[i]] == i for each valid |i|.
-  base::hash_map<MidiInputPortAndroid*, size_t> input_port_to_index_;
+  std::unordered_map<MidiInputPortAndroid*, size_t> input_port_to_index_;
 
   // Ditto for output ports.
   std::vector<MidiOutputPortAndroid*> all_output_ports_;
-  base::hash_map<MidiOutputPortAndroid*, size_t> output_port_to_index_;
+  std::unordered_map<MidiOutputPortAndroid*, size_t> output_port_to_index_;
 
   base::android::ScopedJavaGlobalRef<jobject> raw_manager_;
 };
diff --git a/media/midi/midi_manager_usb.h b/media/midi/midi_manager_usb.h
index 90aa2d54..b47204a 100644
--- a/media/midi/midi_manager_usb.h
+++ b/media/midi/midi_manager_usb.h
@@ -9,11 +9,13 @@
 #include <stdint.h>
 
 #include <memory>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
 #include "base/compiler_specific.h"
 #include "base/containers/hash_tables.h"
+#include "base/hash.h"
 #include "base/macros.h"
 #include "base/synchronization/lock.h"
 #include "base/time/time.h"
@@ -87,7 +89,10 @@
   std::unique_ptr<UsbMidiInputStream> input_stream_;
 
   // A map from <endpoint_number, cable_number> to the index of input jacks.
-  base::hash_map<std::pair<int, int>, size_t> input_jack_dictionary_;
+  std::unordered_map<std::pair<int, int>,
+                     size_t,
+                     base::IntPairHash<std::pair<int, int>>>
+      input_jack_dictionary_;
 
   DISALLOW_COPY_AND_ASSIGN(MidiManagerUsb);
 };
diff --git a/media/mojo/clients/mojo_cdm_factory.cc b/media/mojo/clients/mojo_cdm_factory.cc
index 4250576..d1103f5 100644
--- a/media/mojo/clients/mojo_cdm_factory.cc
+++ b/media/mojo/clients/mojo_cdm_factory.cc
@@ -44,12 +44,11 @@
     return;
   }
 
-// When MojoRenderer is used, the real Renderer is running in a remote process,
-// which cannot use an AesDecryptor running locally. In this case, always
-// create the MojoCdm, giving the remote CDM a chance to handle |key_system|.
-// Note: We should not run AesDecryptor in the browser process except for
-// testing. See http://crbug.com/441957
-#if !BUILDFLAG(ENABLE_MOJO_RENDERER)
+  // If AesDecryptor can be used, always use it here in the local process.
+  // Note: We should not run AesDecryptor in the browser process except for
+  // testing. See http://crbug.com/441957.
+  // Note: Previously MojoRenderer doesn't work with local CDMs, this has
+  // been solved by using DecryptingRenderer. See http://crbug.com/913775.
   if (CanUseAesDecryptor(key_system)) {
     scoped_refptr<ContentDecryptionModule> cdm(
         new AesDecryptor(session_message_cb, session_closed_cb,
@@ -58,7 +57,6 @@
         FROM_HERE, base::BindOnce(cdm_created_cb, cdm, ""));
     return;
   }
-#endif
 
   mojom::ContentDecryptionModulePtr cdm_ptr;
   interface_factory_->CreateCdm(key_system, mojo::MakeRequest(&cdm_ptr));
diff --git a/media/mojo/clients/mojo_renderer_factory.cc b/media/mojo/clients/mojo_renderer_factory.cc
index 55be954..7dab77e 100644
--- a/media/mojo/clients/mojo_renderer_factory.cc
+++ b/media/mojo/clients/mojo_renderer_factory.cc
@@ -8,6 +8,7 @@
 
 #include "base/single_thread_task_runner.h"
 #include "media/mojo/clients/mojo_renderer.h"
+#include "media/renderers/decrypting_renderer.h"
 #include "media/renderers/video_overlay_factory.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "services/service_manager/public/cpp/connect.h"
@@ -16,10 +17,12 @@
 namespace media {
 
 MojoRendererFactory::MojoRendererFactory(
+    media::MediaLog* media_log,
     mojom::HostedRendererType type,
     const GetGpuFactoriesCB& get_gpu_factories_cb,
     media::mojom::InterfaceFactory* interface_factory)
-    : get_gpu_factories_cb_(get_gpu_factories_cb),
+    : media_log_(media_log),
+      get_gpu_factories_cb_(get_gpu_factories_cb),
       interface_factory_(interface_factory),
       hosted_renderer_type_(type) {
   DCHECK(interface_factory_);
@@ -48,9 +51,12 @@
         std::make_unique<VideoOverlayFactory>(get_gpu_factories_cb_.Run());
   }
 
-  return std::unique_ptr<Renderer>(
-      new MojoRenderer(media_task_runner, std::move(overlay_factory),
-                       video_renderer_sink, GetRendererPtr()));
+  // TODO(xhwang): use DecryptingRenderer in other renderer factories.
+  return std::make_unique<DecryptingRenderer>(
+      std::make_unique<MojoRenderer>(media_task_runner,
+                                     std::move(overlay_factory),
+                                     video_renderer_sink, GetRendererPtr()),
+      media_log_, media_task_runner);
 }
 
 mojom::RendererPtr MojoRendererFactory::GetRendererPtr() {
diff --git a/media/mojo/clients/mojo_renderer_factory.h b/media/mojo/clients/mojo_renderer_factory.h
index 5096df53..3e9a6d3 100644
--- a/media/mojo/clients/mojo_renderer_factory.h
+++ b/media/mojo/clients/mojo_renderer_factory.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "media/base/media_log.h"
 #include "media/base/renderer_factory.h"
 #include "media/mojo/interfaces/interface_factory.mojom.h"
 #include "media/mojo/interfaces/renderer.mojom.h"
@@ -26,7 +27,8 @@
   using GetGpuFactoriesCB = base::Callback<GpuVideoAcceleratorFactories*()>;
   using GetTypeSpecificIdCB = base::Callback<std::string()>;
 
-  MojoRendererFactory(mojom::HostedRendererType type,
+  MojoRendererFactory(media::MediaLog* media_log,
+                      mojom::HostedRendererType type,
                       const GetGpuFactoriesCB& get_gpu_factories_cb,
                       media::mojom::InterfaceFactory* interface_factory);
 
@@ -51,6 +53,8 @@
  private:
   mojom::RendererPtr GetRendererPtr();
 
+  media::MediaLog* const media_log_;
+
   GetGpuFactoriesCB get_gpu_factories_cb_;
   GetTypeSpecificIdCB get_type_specific_id_;
 
diff --git a/media/renderers/BUILD.gn b/media/renderers/BUILD.gn
index 56af800c..f6b1268e 100644
--- a/media/renderers/BUILD.gn
+++ b/media/renderers/BUILD.gn
@@ -13,6 +13,8 @@
   sources = [
     "audio_renderer_impl.cc",
     "audio_renderer_impl.h",
+    "decrypting_renderer.cc",
+    "decrypting_renderer.h",
     "default_decoder_factory.cc",
     "default_decoder_factory.h",
     "default_renderer_factory.cc",
@@ -64,6 +66,7 @@
   testonly = true
   sources = [
     "audio_renderer_impl_unittest.cc",
+    "decrypting_renderer_unittest.cc",
     "paint_canvas_video_renderer_unittest.cc",
     "renderer_impl_unittest.cc",
     "video_renderer_impl_unittest.cc",
diff --git a/media/renderers/decrypting_renderer.cc b/media/renderers/decrypting_renderer.cc
new file mode 100644
index 0000000..a7712f98
--- /dev/null
+++ b/media/renderers/decrypting_renderer.cc
@@ -0,0 +1,184 @@
+// 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 "media/renderers/decrypting_renderer.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/media_log.h"
+#include "media/base/media_resource.h"
+#include "media/base/renderer_client.h"
+#include "media/filters/decrypting_demuxer_stream.h"
+#include "media/filters/decrypting_media_resource.h"
+
+namespace media {
+
+DecryptingRenderer::DecryptingRenderer(
+    std::unique_ptr<Renderer> renderer,
+    MediaLog* media_log,
+    const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner)
+    : renderer_(std::move(renderer)),
+      media_log_(media_log),
+      media_task_runner_(media_task_runner),
+      client_(nullptr),
+      media_resource_(nullptr),
+      decrypting_media_resource_(nullptr),
+      weak_factory_(this) {
+  DCHECK(renderer_);
+}
+
+DecryptingRenderer::~DecryptingRenderer() {}
+
+// The behavior of Initialize():
+//
+// Streams    CdmContext    Action
+// ---------------------------------------------------------------------
+// Clear      nullptr       InitializeRenderer()
+// Clear      AesDecryptor  CreateAndInitializeDecryptingMediaResource()
+// Clear      Other         InitializeRenderer()
+// Encrypted  nullptr       Wait
+// Encrypted  AesDecryptor  CreateAndInitializeDecryptingMediaResource()
+// Encrypted  Other         InitializeRenderer()
+void DecryptingRenderer::Initialize(MediaResource* media_resource,
+                                    RendererClient* client,
+                                    const PipelineStatusCB& init_cb) {
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+  DCHECK(media_resource);
+  DCHECK(client);
+
+  media_resource_ = media_resource;
+  client_ = client;
+  init_cb_ = std::move(init_cb);
+
+  bool has_encrypted_stream = HasEncryptedStream();
+
+  // If we do not have a valid |cdm_context_| and there are encrypted streams we
+  // need to wait.
+  if (!cdm_context_ && has_encrypted_stream) {
+    waiting_for_cdm_ = true;
+    return;
+  }
+
+  if (cdm_context_ && cdm_context_->GetDecryptor() &&
+      cdm_context_->GetDecryptor()->CanAlwaysDecrypt()) {
+    CreateAndInitializeDecryptingMediaResource();
+    return;
+  }
+
+  InitializeRenderer(true);
+}
+
+void DecryptingRenderer::SetCdm(CdmContext* cdm_context,
+                                const CdmAttachedCB& cdm_attached_cb) {
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+
+  if (cdm_context_) {
+    DVLOG(1) << "Switching CDM not supported.";
+    cdm_attached_cb.Run(false);
+    return;
+  }
+
+  cdm_context_ = cdm_context;
+
+  // If we are using an AesDecryptor all decryption will be handled by the
+  // DecryptingMediaResource instead of the renderer implementation.
+  if (cdm_context_->GetDecryptor() &&
+      cdm_context_->GetDecryptor()->CanAlwaysDecrypt()) {
+    // If Initialize() was invoked prior to this function then
+    // |waiting_for_cdm_| will be true (if we reached this branch). In this
+    // scenario we want to initialize the DecryptingMediaResource here.
+    if (waiting_for_cdm_)
+      CreateAndInitializeDecryptingMediaResource();
+    cdm_attached_cb.Run(true);
+    return;
+  }
+
+  renderer_->SetCdm(cdm_context_, cdm_attached_cb);
+
+  // We only want to initialize the renderer if we were waiting for the
+  // CdmContext, otherwise it will already have been initialized.
+  if (waiting_for_cdm_)
+    InitializeRenderer(true);
+}
+
+void DecryptingRenderer::Flush(const base::Closure& flush_cb) {
+  renderer_->Flush(flush_cb);
+}
+
+void DecryptingRenderer::StartPlayingFrom(base::TimeDelta time) {
+  renderer_->StartPlayingFrom(time);
+}
+
+void DecryptingRenderer::SetPlaybackRate(double playback_rate) {
+  renderer_->SetPlaybackRate(playback_rate);
+}
+
+void DecryptingRenderer::SetVolume(float volume) {
+  renderer_->SetVolume(volume);
+}
+
+base::TimeDelta DecryptingRenderer::GetMediaTime() {
+  return renderer_->GetMediaTime();
+}
+
+void DecryptingRenderer::OnSelectedVideoTracksChanged(
+    const std::vector<DemuxerStream*>& enabled_tracks,
+    base::OnceClosure change_completed_cb) {
+  renderer_->OnSelectedVideoTracksChanged(enabled_tracks,
+                                          std::move(change_completed_cb));
+}
+
+void DecryptingRenderer::OnEnabledAudioTracksChanged(
+    const std::vector<DemuxerStream*>& enabled_tracks,
+    base::OnceClosure change_completed_cb) {
+  renderer_->OnEnabledAudioTracksChanged(enabled_tracks,
+                                         std::move(change_completed_cb));
+}
+
+void DecryptingRenderer::CreateAndInitializeDecryptingMediaResource() {
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+  DCHECK(init_cb_);
+
+  decrypting_media_resource_ = std::make_unique<DecryptingMediaResource>(
+      media_resource_, cdm_context_, media_log_, media_task_runner_);
+  decrypting_media_resource_->Initialize(base::BindOnce(
+      &DecryptingRenderer::InitializeRenderer, weak_factory_.GetWeakPtr()));
+}
+
+void DecryptingRenderer::InitializeRenderer(bool success) {
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+
+  if (!success) {
+    std::move(init_cb_).Run(PIPELINE_ERROR_INITIALIZATION_FAILED);
+    return;
+  }
+
+  MediaResource* const maybe_decrypting_media_resource =
+      decrypting_media_resource_ ? decrypting_media_resource_.get()
+                                 : media_resource_;
+  renderer_->Initialize(maybe_decrypting_media_resource, client_,
+                        std::move(init_cb_));
+}
+
+bool DecryptingRenderer::HasEncryptedStream() {
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+
+  for (auto* stream : media_resource_->GetAllStreams()) {
+    if ((stream->type() == DemuxerStream::AUDIO &&
+         stream->audio_decoder_config().is_encrypted()) ||
+        (stream->type() == DemuxerStream::VIDEO &&
+         stream->video_decoder_config().is_encrypted())) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool DecryptingRenderer::HasDecryptingMediaResourceForTesting() const {
+  return decrypting_media_resource_ != nullptr;
+}
+
+}  // namespace media
diff --git a/media/renderers/decrypting_renderer.h b/media/renderers/decrypting_renderer.h
new file mode 100644
index 0000000..4235ff3
--- /dev/null
+++ b/media/renderers/decrypting_renderer.h
@@ -0,0 +1,91 @@
+// 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 MEDIA_RENDERERS_DECRYPTING_RENDERER_H_
+#define MEDIA_RENDERERS_DECRYPTING_RENDERER_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "media/base/pipeline.h"
+#include "media/base/renderer.h"
+
+namespace media {
+
+class CdmContext;
+class DemuxerStream;
+class MediaLog;
+class MediaResource;
+class DecryptingMediaResource;
+class RendererClient;
+
+// DecryptingRenderer is used as a wrapper around a Renderer
+// implementation that decrypts streams when an AesDecryptor is available. In
+// this case only clear streams are passed on to the internally owned renderer
+// implementation.
+//
+// All methods are pass-through except Initialize() and SetCdm().
+class MEDIA_EXPORT DecryptingRenderer : public Renderer {
+ public:
+  DecryptingRenderer(
+      std::unique_ptr<Renderer> renderer,
+      MediaLog* media_log,
+      const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner);
+  ~DecryptingRenderer() override;
+
+  // Renderer implementation:
+  void Initialize(MediaResource* media_resource,
+                  RendererClient* client,
+                  const PipelineStatusCB& init_cb) override;
+  void SetCdm(CdmContext* cdm_context,
+              const CdmAttachedCB& cdm_attached_cb) override;
+
+  void Flush(const base::Closure& flush_cb) override;
+  void StartPlayingFrom(base::TimeDelta time) override;
+  void SetPlaybackRate(double playback_rate) override;
+  void SetVolume(float volume) override;
+  base::TimeDelta GetMediaTime() override;
+  void OnSelectedVideoTracksChanged(
+      const std::vector<DemuxerStream*>& enabled_tracks,
+      base::OnceClosure change_completed_cb) override;
+  void OnEnabledAudioTracksChanged(
+      const std::vector<DemuxerStream*>& enabled_tracks,
+      base::OnceClosure change_completed_cb) override;
+
+  bool HasDecryptingMediaResourceForTesting() const;
+
+ private:
+  friend class DecryptingRendererTest;
+
+  // Cannot be called before Initialize() has been called.
+  void CreateAndInitializeDecryptingMediaResource();
+
+  // Invoked as a callback after |decrypting_media_resource_| has been
+  // initialized.
+  void InitializeRenderer(bool success);
+  bool HasEncryptedStream();
+
+  const std::unique_ptr<Renderer> renderer_;
+  MediaLog* const media_log_;
+  const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
+
+  bool waiting_for_cdm_ = false;
+  CdmContext* cdm_context_ = nullptr;
+  RendererClient* client_;
+  MediaResource* media_resource_;
+  PipelineStatusCB init_cb_;
+
+  std::unique_ptr<DecryptingMediaResource> decrypting_media_resource_;
+
+  base::WeakPtrFactory<DecryptingRenderer> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DecryptingRenderer);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_RENDERERS_DECRYPTING_RENDERER_H_
diff --git a/media/renderers/decrypting_renderer_unittest.cc b/media/renderers/decrypting_renderer_unittest.cc
new file mode 100644
index 0000000..e87f7b00
--- /dev/null
+++ b/media/renderers/decrypting_renderer_unittest.cc
@@ -0,0 +1,266 @@
+// 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 "media/renderers/decrypting_renderer.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/mock_callback.h"
+#include "base/test/scoped_task_environment.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/gmock_callback_support.h"
+#include "media/base/media_util.h"
+#include "media/base/mock_filters.h"
+#include "media/base/test_helpers.h"
+#include "media/filters/decrypting_media_resource.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::ReturnPointee;
+using ::testing::StrictMock;
+
+namespace media {
+
+class CdmContext;
+class DemuxerStream;
+class MediaLog;
+
+class DecryptingRendererTest : public testing::Test {
+ public:
+  DecryptingRendererTest() {
+    auto renderer = std::make_unique<StrictMock<MockRenderer>>();
+    renderer_ = renderer.get();
+    decrypting_renderer_ = std::make_unique<DecryptingRenderer>(
+        std::move(renderer), &null_media_log_,
+        scoped_task_environment_.GetMainThreadTaskRunner());
+
+    EXPECT_CALL(cdm_context_, GetDecryptor())
+        .WillRepeatedly(Return(&decryptor_));
+    EXPECT_CALL(decryptor_, CanAlwaysDecrypt())
+        .WillRepeatedly(ReturnPointee(&use_aes_decryptor_));
+    EXPECT_CALL(decryptor_, CancelDecrypt(_)).Times(AnyNumber());
+    EXPECT_CALL(decryptor_, RegisterNewKeyCB(_, _)).Times(AnyNumber());
+    EXPECT_CALL(demuxer_, GetAllStreams())
+        .WillRepeatedly(Invoke(this, &DecryptingRendererTest::GetAllStreams));
+  }
+
+  ~DecryptingRendererTest() override {
+    // Ensure that the DecryptingRenderer is destructed before other objects
+    // that it internally references but does not own.
+    decrypting_renderer_.reset();
+  }
+
+  void AddStream(DemuxerStream::Type type, bool encrypted) {
+    streams_.push_back(CreateMockDemuxerStream(type, encrypted));
+  }
+
+  void UseAesDecryptor(bool use_aes_decryptor) {
+    use_aes_decryptor_ = use_aes_decryptor;
+  }
+
+  std::vector<DemuxerStream*> GetAllStreams() {
+    std::vector<DemuxerStream*> streams;
+
+    for (auto& stream : streams_) {
+      streams.push_back(stream.get());
+    }
+
+    return streams;
+  }
+
+ protected:
+  // Invoking InitializeRenderer(false) will cause the initialization of the
+  // DecryptingRenderer to halt and an error will be propagated to the media
+  // pipeline.
+  void InitializeDecryptingRendererWithFalse() {
+    decrypting_renderer_->InitializeRenderer(false);
+  }
+
+  bool use_aes_decryptor_ = false;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  base::MockCallback<CdmAttachedCB> set_cdm_cb_;
+  base::MockCallback<PipelineStatusCB> renderer_init_cb_;
+  NullMediaLog null_media_log_;
+  StrictMock<MockCdmContext> cdm_context_;
+  StrictMock<MockDecryptor> decryptor_;
+  StrictMock<MockDemuxer> demuxer_;
+  StrictMock<MockRendererClient> renderer_client_;
+  StrictMock<MockRenderer>* renderer_;
+  std::unique_ptr<DecryptingRenderer> decrypting_renderer_;
+  std::vector<std::unique_ptr<StrictMock<MockDemuxerStream>>> streams_;
+};
+
+TEST_F(DecryptingRendererTest, ClearStreams_NoCdm) {
+  AddStream(DemuxerStream::AUDIO, /* encrypted = */ false);
+  AddStream(DemuxerStream::VIDEO, /* encrypted = */ false);
+
+  EXPECT_CALL(*renderer_, Initialize(_, _, _))
+      .WillOnce(RunCallback<2>(PIPELINE_OK));
+  EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK));
+
+  decrypting_renderer_->Initialize(&demuxer_, &renderer_client_,
+                                   renderer_init_cb_.Get());
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_FALSE(decrypting_renderer_->HasDecryptingMediaResourceForTesting());
+}
+
+TEST_F(DecryptingRendererTest, ClearStreams_AesDecryptor) {
+  AddStream(DemuxerStream::AUDIO, /* encrypted = */ false);
+  AddStream(DemuxerStream::VIDEO, /* encrypted = */ false);
+  UseAesDecryptor(true);
+
+  EXPECT_CALL(*renderer_, Initialize(_, _, _))
+      .WillOnce(RunCallback<2>(PIPELINE_OK));
+  EXPECT_CALL(set_cdm_cb_, Run(true));
+  EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK));
+
+  decrypting_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());
+  decrypting_renderer_->Initialize(&demuxer_, &renderer_client_,
+                                   renderer_init_cb_.Get());
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_TRUE(decrypting_renderer_->HasDecryptingMediaResourceForTesting());
+}
+
+TEST_F(DecryptingRendererTest, ClearStreams_OtherCdm) {
+  AddStream(DemuxerStream::AUDIO, /* encrypted = */ false);
+  AddStream(DemuxerStream::VIDEO, /* encrypted = */ false);
+
+  EXPECT_CALL(*renderer_, Initialize(_, _, _))
+      .WillOnce(RunCallback<2>(PIPELINE_OK));
+  EXPECT_CALL(*renderer_, SetCdm(_, _)).WillOnce(RunCallback<1>(true));
+  EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK));
+  EXPECT_CALL(set_cdm_cb_, Run(true));
+
+  decrypting_renderer_->Initialize(&demuxer_, &renderer_client_,
+                                   renderer_init_cb_.Get());
+  decrypting_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_FALSE(decrypting_renderer_->HasDecryptingMediaResourceForTesting());
+}
+
+TEST_F(DecryptingRendererTest, EncryptedStreams_NoCdm) {
+  AddStream(DemuxerStream::AUDIO, /* encrypted = */ true);
+  AddStream(DemuxerStream::VIDEO, /* encrypted = */ true);
+
+  decrypting_renderer_->Initialize(&demuxer_, &renderer_client_,
+                                   renderer_init_cb_.Get());
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_FALSE(decrypting_renderer_->HasDecryptingMediaResourceForTesting());
+}
+
+TEST_F(DecryptingRendererTest, EncryptedStreams_AesDecryptor) {
+  AddStream(DemuxerStream::AUDIO, /* encrypted = */ true);
+  AddStream(DemuxerStream::VIDEO, /* encrypted = */ true);
+  UseAesDecryptor(true);
+
+  EXPECT_CALL(*renderer_, Initialize(_, _, _))
+      .WillOnce(RunCallback<2>(PIPELINE_OK));
+  EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK));
+  EXPECT_CALL(set_cdm_cb_, Run(true));
+
+  decrypting_renderer_->Initialize(&demuxer_, &renderer_client_,
+                                   renderer_init_cb_.Get());
+  decrypting_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_TRUE(decrypting_renderer_->HasDecryptingMediaResourceForTesting());
+}
+
+TEST_F(DecryptingRendererTest, EncryptedStreams_OtherCdm) {
+  AddStream(DemuxerStream::AUDIO, /* encrypted = */ true);
+  AddStream(DemuxerStream::VIDEO, /* encrypted = */ true);
+
+  EXPECT_CALL(*renderer_, Initialize(_, _, _))
+      .WillOnce(RunCallback<2>(PIPELINE_OK));
+  EXPECT_CALL(*renderer_, SetCdm(_, _)).WillOnce(RunCallback<1>(true));
+  EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK));
+  EXPECT_CALL(set_cdm_cb_, Run(true));
+
+  decrypting_renderer_->Initialize(&demuxer_, &renderer_client_,
+                                   renderer_init_cb_.Get());
+  decrypting_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_FALSE(decrypting_renderer_->HasDecryptingMediaResourceForTesting());
+}
+
+TEST_F(DecryptingRendererTest, EncryptedStreams_AesDecryptor_CdmSetBeforeInit) {
+  AddStream(DemuxerStream::AUDIO, /* encrypted = */ true);
+  AddStream(DemuxerStream::VIDEO, /* encrypted = */ true);
+  UseAesDecryptor(true);
+
+  EXPECT_CALL(*renderer_, Initialize(_, _, _))
+      .WillOnce(RunCallback<2>(PIPELINE_OK));
+  EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK));
+  EXPECT_CALL(set_cdm_cb_, Run(true));
+
+  decrypting_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());
+  decrypting_renderer_->Initialize(&demuxer_, &renderer_client_,
+                                   renderer_init_cb_.Get());
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_TRUE(decrypting_renderer_->HasDecryptingMediaResourceForTesting());
+}
+
+TEST_F(DecryptingRendererTest, EncryptedStreams_OtherCdm_CdmSetBeforeInit) {
+  AddStream(DemuxerStream::AUDIO, /* encrypted = */ true);
+  AddStream(DemuxerStream::VIDEO, /* encrypted = */ true);
+
+  EXPECT_CALL(*renderer_, Initialize(_, _, _))
+      .WillOnce(RunCallback<2>(PIPELINE_OK));
+  EXPECT_CALL(*renderer_, SetCdm(_, _)).WillOnce(RunCallback<1>(true));
+  EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK));
+  EXPECT_CALL(set_cdm_cb_, Run(true));
+
+  decrypting_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());
+  decrypting_renderer_->Initialize(&demuxer_, &renderer_client_,
+                                   renderer_init_cb_.Get());
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_FALSE(decrypting_renderer_->HasDecryptingMediaResourceForTesting());
+}
+
+TEST_F(DecryptingRendererTest, EncryptedAndClearStream_OtherCdm) {
+  AddStream(DemuxerStream::AUDIO, /* encrypted = */ false);
+  AddStream(DemuxerStream::VIDEO, /* encrypted = */ true);
+
+  EXPECT_CALL(*renderer_, Initialize(_, _, _))
+      .WillOnce(RunCallback<2>(PIPELINE_OK));
+  EXPECT_CALL(*renderer_, SetCdm(_, _)).WillOnce(RunCallback<1>(true));
+  EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK));
+  EXPECT_CALL(set_cdm_cb_, Run(true));
+
+  decrypting_renderer_->Initialize(&demuxer_, &renderer_client_,
+                                   renderer_init_cb_.Get());
+  decrypting_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_FALSE(decrypting_renderer_->HasDecryptingMediaResourceForTesting());
+}
+
+TEST_F(DecryptingRendererTest, DecryptingMediaResourceInitFails) {
+  AddStream(DemuxerStream::AUDIO, /* encrypted = */ false);
+  AddStream(DemuxerStream::VIDEO, /* encrypted = */ true);
+  UseAesDecryptor(true);
+
+  EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_ERROR_INITIALIZATION_FAILED));
+
+  decrypting_renderer_->Initialize(&demuxer_, &renderer_client_,
+                                   renderer_init_cb_.Get());
+  scoped_task_environment_.RunUntilIdle();
+
+  // Cause a PIPELINE_ERROR_INITIALIZATION_FAILED error to be passed as a
+  // parameter to the initialization callback.
+  InitializeDecryptingRendererWithFalse();
+}
+
+}  // namespace media
diff --git a/mojo/core/handle_table.h b/mojo/core/handle_table.h
index 234bdac..7e7f82f 100644
--- a/mojo/core/handle_table.h
+++ b/mojo/core/handle_table.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -74,7 +75,7 @@
     bool busy = false;
   };
 
-  using HandleMap = base::hash_map<MojoHandle, Entry>;
+  using HandleMap = std::unordered_map<MojoHandle, Entry>;
 
   HandleMap handles_;
   base::Lock lock_;
diff --git a/mojo/core/multiprocess_message_pipe_unittest.cc b/mojo/core/multiprocess_message_pipe_unittest.cc
index c6f1141..4b5d4344 100644
--- a/mojo/core/multiprocess_message_pipe_unittest.cc
+++ b/mojo/core/multiprocess_message_pipe_unittest.cc
@@ -8,6 +8,7 @@
 #include <string.h>
 
 #include <string>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -937,7 +938,7 @@
 DEFINE_TEST_CLIENT_WITH_PIPE(CommandDrivenClient,
                              MultiprocessMessagePipeTest,
                              h) {
-  base::hash_map<std::string, MojoHandle> named_pipes;
+  std::unordered_map<std::string, MojoHandle> named_pipes;
   for (;;) {
     MojoHandle p;
     auto parts = base::SplitString(ReadMessageWithOptionalHandle(h, &p), ":",
diff --git a/net/nqe/throughput_analyzer.h b/net/nqe/throughput_analyzer.h
index c02a639..73682a2 100644
--- a/net/nqe/throughput_analyzer.h
+++ b/net/nqe/throughput_analyzer.h
@@ -124,7 +124,7 @@
 
   // Mapping from URL request to the last time data was received for that
   // request.
-  typedef base::hash_map<const URLRequest*, base::TimeTicks> Requests;
+  typedef std::unordered_map<const URLRequest*, base::TimeTicks> Requests;
 
   // Set of URL requests to hold the requests that reduce the accuracy of
   // throughput computation. These requests are not used in throughput
diff --git a/net/third_party/quic/core/http/quic_spdy_session.cc b/net/third_party/quic/core/http/quic_spdy_session.cc
index b3e494c..4bbb9a39 100644
--- a/net/third_party/quic/core/http/quic_spdy_session.cc
+++ b/net/third_party/quic/core/http/quic_spdy_session.cc
@@ -514,11 +514,6 @@
   }
 }
 
-bool QuicSpdySession::ShouldBufferIncomingStream(QuicStreamId id) const {
-  DCHECK_EQ(QUIC_VERSION_99, connection()->transport_version());
-  return !QuicUtils::IsBidirectionalStreamId(id);
-}
-
 void QuicSpdySession::OnPromiseHeaderList(QuicStreamId stream_id,
                                           QuicStreamId promised_stream_id,
                                           size_t frame_len,
diff --git a/net/third_party/quic/core/http/quic_spdy_session.h b/net/third_party/quic/core/http/quic_spdy_session.h
index cf24dbca..17553f7 100644
--- a/net/third_party/quic/core/http/quic_spdy_session.h
+++ b/net/third_party/quic/core/http/quic_spdy_session.h
@@ -154,9 +154,6 @@
   virtual bool ShouldCreateOutgoingBidirectionalStream() = 0;
   virtual bool ShouldCreateOutgoingUnidirectionalStream() = 0;
 
-  // Overridden to buffer incoming streams for version 99.
-  bool ShouldBufferIncomingStream(QuicStreamId id) const override;
-
   // This was formerly QuicHeadersStream::WriteHeaders.  Needs to be
   // separate from QuicSpdySession::WriteHeaders because tests call
   // this but mock the latter.
diff --git a/net/third_party/quic/core/http/quic_spdy_session_test.cc b/net/third_party/quic/core/http/quic_spdy_session_test.cc
index a5d2716..0ca3111 100644
--- a/net/third_party/quic/core/http/quic_spdy_session_test.cc
+++ b/net/third_party/quic/core/http/quic_spdy_session_test.cc
@@ -266,7 +266,6 @@
 
   using QuicSession::closed_streams;
   using QuicSession::zombie_streams;
-  using QuicSpdySession::ShouldBufferIncomingStream;
 
  private:
   StrictMock<TestCryptoStream> crypto_stream_;
@@ -392,24 +391,6 @@
                         QuicSpdySessionTestServer,
                         ::testing::ValuesIn(AllSupportedVersions()));
 
-TEST_P(QuicSpdySessionTestServer, ShouldBufferIncomingStreamUnidirectional) {
-  if (connection_->transport_version() != QUIC_VERSION_99) {
-    return;
-  }
-  EXPECT_TRUE(session_.ShouldBufferIncomingStream(
-      QuicUtils::GetFirstUnidirectionalStreamId(
-          connection_->transport_version(), Perspective::IS_CLIENT)));
-}
-
-TEST_P(QuicSpdySessionTestServer, ShouldBufferIncomingStreamBidirectional) {
-  if (connection_->transport_version() != QUIC_VERSION_99) {
-    return;
-  }
-  EXPECT_FALSE(session_.ShouldBufferIncomingStream(
-      QuicUtils::GetFirstBidirectionalStreamId(connection_->transport_version(),
-                                               Perspective::IS_CLIENT)));
-}
-
 TEST_P(QuicSpdySessionTestServer, PeerAddress) {
   EXPECT_EQ(QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort),
             session_.peer_address());
diff --git a/net/third_party/quic/core/quic_session.cc b/net/third_party/quic/core/quic_session.cc
index 1b6ff62..020e858c 100644
--- a/net/third_party/quic/core/quic_session.cc
+++ b/net/third_party/quic/core/quic_session.cc
@@ -136,13 +136,8 @@
     return;
   }
 
-  StreamHandler handler = GetOrCreateStreamImpl(stream_id, frame.offset != 0);
-  if (handler.is_pending) {
-    handler.pending->OnStreamFrame(frame);
-    return;
-  }
-
-  if (!handler.stream) {
+  QuicStream* stream = GetOrCreateStream(stream_id);
+  if (!stream) {
     // The stream no longer exists, but we may still be interested in the
     // final stream byte offset sent by the peer. A frame with a FIN can give
     // us this offset.
@@ -152,7 +147,7 @@
     }
     return;
   }
-  handler.stream->OnStreamFrame(frame);
+  stream->OnStreamFrame(frame);
 }
 
 bool QuicSession::OnStopSendingFrame(const QuicStopSendingFrame& frame) {
@@ -250,20 +245,12 @@
     visitor_->OnRstStreamReceived(frame);
   }
 
-  // may_buffer is true here to allow subclasses to buffer streams until the
-  // first byte of payload arrives which would allow sessions to delay
-  // creation of the stream until the type is known.
-  StreamHandler handler = GetOrCreateStreamImpl(stream_id, /*may_buffer=*/true);
-  if (handler.is_pending) {
-    handler.pending->OnRstStreamFrame(frame);
-    ClosePendingStream(stream_id);
-    return;
-  }
-  if (!handler.stream) {
+  QuicStream* stream = GetOrCreateDynamicStream(stream_id);
+  if (!stream) {
     HandleRstOnValidNonexistentStream(frame);
     return;  // Errors are handled by GetOrCreateStream.
   }
-  handler.stream->OnStreamReset(frame);
+  stream->OnStreamReset(frame);
 }
 
 void QuicSession::OnGoAway(const QuicGoAwayFrame& frame) {
@@ -704,33 +691,6 @@
   }
 }
 
-void QuicSession::ClosePendingStream(QuicStreamId stream_id) {
-  QUIC_DVLOG(1) << ENDPOINT << "Closing stream " << stream_id;
-
-  if (pending_stream_map_.find(stream_id) == pending_stream_map_.end()) {
-    QUIC_BUG << ENDPOINT << "Stream is already closed: " << stream_id;
-    return;
-  }
-
-  SendRstStream(stream_id, QUIC_RST_ACKNOWLEDGEMENT, 0);
-
-  // The pending stream may have been deleted and removed during SendRstStream.
-  // Remove the stream from pending stream map iff it is still in the map.
-  if (pending_stream_map_.find(stream_id) != pending_stream_map_.end()) {
-    pending_stream_map_.erase(stream_id);
-  }
-
-  --num_dynamic_incoming_streams_;
-
-  if (connection_->transport_version() == QUIC_VERSION_99) {
-    v99_streamid_manager_.OnStreamClosed(stream_id);
-  }
-
-  // Decrease the number of streams being emulated when a new one is opened.
-  connection_->SetNumOpenStreams(dynamic_stream_map_.size());
-  OnCanCreateNewOutgoingStream();
-}
-
 void QuicSession::OnFinalByteOffsetReceived(
     QuicStreamId stream_id,
     QuicStreamOffset final_byte_offset) {
@@ -1035,20 +995,11 @@
 }
 
 QuicStream* QuicSession::GetOrCreateStream(const QuicStreamId stream_id) {
-  StreamHandler handler =
-      GetOrCreateStreamImpl(stream_id, /*may_buffer=*/false);
-  DCHECK(!handler.is_pending);
-  return handler.stream;
-}
-
-QuicSession::StreamHandler QuicSession::GetOrCreateStreamImpl(
-    QuicStreamId stream_id,
-    bool may_buffer) {
   StaticStreamMap::iterator it = static_stream_map_.find(stream_id);
   if (it != static_stream_map_.end()) {
-    return StreamHandler(it->second);
+    return it->second;
   }
-  return GetOrCreateDynamicStreamImpl(stream_id, may_buffer);
+  return GetOrCreateDynamicStream(stream_id);
 }
 
 void QuicSession::StreamDraining(QuicStreamId stream_id) {
@@ -1085,49 +1036,25 @@
 
 QuicStream* QuicSession::GetOrCreateDynamicStream(
     const QuicStreamId stream_id) {
-  StreamHandler handler =
-      GetOrCreateDynamicStreamImpl(stream_id, /*may_buffer=*/false);
-  DCHECK(!handler.is_pending);
-  return handler.stream;
-}
-
-QuicSession::StreamHandler QuicSession::GetOrCreateDynamicStreamImpl(
-    QuicStreamId stream_id,
-    bool may_buffer) {
   DCHECK(!QuicContainsKey(static_stream_map_, stream_id))
       << "Attempt to call GetOrCreateDynamicStream for a static stream";
 
   DynamicStreamMap::iterator it = dynamic_stream_map_.find(stream_id);
   if (it != dynamic_stream_map_.end()) {
-    return StreamHandler(it->second.get());
+    return it->second.get();
   }
 
   if (IsClosedStream(stream_id)) {
-    return StreamHandler();
+    return nullptr;
   }
 
   if (!IsIncomingStream(stream_id)) {
     HandleFrameOnNonexistentOutgoingStream(stream_id);
-    return StreamHandler();
-  }
-
-  auto pending_it = pending_stream_map_.find(stream_id);
-  if (pending_it != pending_stream_map_.end()) {
-    DCHECK_EQ(QUIC_VERSION_99, connection_->transport_version());
-    if (may_buffer) {
-      return StreamHandler(pending_it->second.get());
-    }
-    // The stream limit accounting has already been taken care of
-    // when the PendingStream was created, so there is no need to
-    // do so here. Now we can create the actual stream from the
-    // PendingStream.
-    StreamHandler handler(CreateIncomingStream(std::move(*pending_it->second)));
-    pending_stream_map_.erase(pending_it);
-    return handler;
+    return nullptr;
   }
 
   if (!MaybeIncreaseLargestPeerStreamId(stream_id)) {
-    return StreamHandler();
+    return nullptr;
   }
 
   if (connection_->transport_version() != QUIC_VERSION_99) {
@@ -1137,23 +1064,11 @@
             GetNumOpenIncomingStreams())) {
       // Refuse to open the stream.
       SendRstStream(stream_id, QUIC_REFUSED_STREAM, 0);
-      return StreamHandler();
+      return nullptr;
     }
   }
 
-  if (connection_->transport_version() == QUIC_VERSION_99 && may_buffer &&
-      ShouldBufferIncomingStream(stream_id)) {
-    ++num_dynamic_incoming_streams_;
-    // Since STREAM frames may arrive out of order, delay creating the
-    // stream object until the first byte arrives. Buffer the frames and
-    // handle flow control accounting in the PendingStream.
-    auto pending = QuicMakeUnique<PendingStream>(stream_id, this);
-    StreamHandler handler(pending.get());
-    pending_stream_map_[stream_id] = std::move(pending);
-    return handler;
-  }
-
-  return StreamHandler(CreateIncomingStream(stream_id));
+  return CreateIncomingStream(stream_id);
 }
 
 void QuicSession::set_largest_peer_created_stream_id(
@@ -1186,8 +1101,7 @@
   DCHECK_NE(QuicUtils::GetInvalidStreamId(connection_->transport_version()),
             id);
   if (QuicContainsKey(static_stream_map_, id) ||
-      QuicContainsKey(dynamic_stream_map_, id) ||
-      QuicContainsKey(pending_stream_map_, id)) {
+      QuicContainsKey(dynamic_stream_map_, id)) {
     // Stream is active
     return true;
   }
@@ -1242,10 +1156,8 @@
 }
 
 size_t QuicSession::GetNumDynamicOutgoingStreams() const {
-  DCHECK_GE(dynamic_stream_map_.size() + pending_stream_map_.size(),
-            num_dynamic_incoming_streams_);
-  return dynamic_stream_map_.size() + pending_stream_map_.size() -
-         num_dynamic_incoming_streams_;
+  DCHECK_GE(dynamic_stream_map_.size(), num_dynamic_incoming_streams_);
+  return dynamic_stream_map_.size() - num_dynamic_incoming_streams_;
 }
 
 size_t QuicSession::GetNumDrainingOutgoingStreams() const {
diff --git a/net/third_party/quic/core/quic_session.h b/net/third_party/quic/core/quic_session.h
index cd618fd..7a1330ac4 100644
--- a/net/third_party/quic/core/quic_session.h
+++ b/net/third_party/quic/core/quic_session.h
@@ -379,9 +379,6 @@
   using DynamicStreamMap =
       QuicSmallMap<QuicStreamId, std::unique_ptr<QuicStream>, 10>;
 
-  using PendingStreamMap =
-      QuicSmallMap<QuicStreamId, std::unique_ptr<PendingStream>, 10>;
-
   using ClosedStreams = std::vector<std::unique_ptr<QuicStream>>;
 
   using ZombieStreamMap =
@@ -434,12 +431,6 @@
   virtual void OnFinalByteOffsetReceived(QuicStreamId id,
                                          QuicStreamOffset final_byte_offset);
 
-  // Returns true if incoming streams should be buffered until the first
-  // byte of the stream arrives.
-  virtual bool ShouldBufferIncomingStream(QuicStreamId id) const {
-    return false;
-  }
-
   // Register (|id|, |stream|) with the static stream map. Override previous
   // registrations with the same id.
   void RegisterStaticStream(QuicStreamId id, QuicStream* stream);
@@ -501,29 +492,6 @@
  private:
   friend class test::QuicSessionPeer;
 
-  // A StreamHandler represents an object which can receive a STREAM or
-  // or RST_STREAM frame.
-  struct StreamHandler {
-    StreamHandler() : is_pending(false), stream(nullptr) {}
-
-    // Creates a StreamHandler wrapping a QuicStream.
-    explicit StreamHandler(QuicStream* stream)
-        : is_pending(false), stream(stream) {}
-
-    // Creates a StreamHandler wrapping a PendingStream.
-    explicit StreamHandler(PendingStream* pending)
-        : is_pending(true), pending(pending) {
-      DCHECK(pending != nullptr);
-    }
-
-    // True if this handler contains a non-null PendingStream, false otherwise.
-    bool is_pending;
-    union {
-      QuicStream* stream;
-      PendingStream* pending;
-    };
-  };
-
   // Called in OnConfigNegotiated when we receive a new stream level flow
   // control window in a negotiated config. Closes the connection if invalid.
   void OnNewStreamFlowControlWindow(QuicStreamOffset new_window);
@@ -551,17 +519,10 @@
   // closed.
   QuicStream* GetStream(QuicStreamId id) const;
 
-  StreamHandler GetOrCreateStreamImpl(QuicStreamId stream_id, bool may_buffer);
-  StreamHandler GetOrCreateDynamicStreamImpl(QuicStreamId stream_id,
-                                             bool may_buffer);
-
   // Let streams and control frame managers retransmit lost data, returns true
   // if all lost data is retransmitted. Returns false otherwise.
   bool RetransmitLostData();
 
-  // Closes the pending stream |stream_id| before it has been created.
-  void ClosePendingStream(QuicStreamId stream_id);
-
   // Keep track of highest received byte offset of locally closed streams, while
   // waiting for a definitive final highest offset from the peer.
   std::map<QuicStreamId, QuicStreamOffset>
@@ -591,10 +552,6 @@
   // Map from StreamId to pointers to streams. Owns the streams.
   DynamicStreamMap dynamic_stream_map_;
 
-  // Map from StreamId to PendingStreams for peer-created unidirectional streams
-  // which are waiting for the first byte of payload to arrive.
-  PendingStreamMap pending_stream_map_;
-
   // Set of stream ids that are "draining" -- a FIN has been sent and received,
   // but the stream object still exists because not all the received data has
   // been consumed.
diff --git a/net/third_party/quic/core/quic_session_test.cc b/net/third_party/quic/core/quic_session_test.cc
index 49bad125..1fc0427 100644
--- a/net/third_party/quic/core/quic_session_test.cc
+++ b/net/third_party/quic/core/quic_session_test.cc
@@ -141,9 +141,7 @@
                     DefaultQuicConfig(),
                     CurrentSupportedVersions()),
         crypto_stream_(this),
-        writev_consumes_all_data_(false),
-        should_buffer_incoming_streams_(false),
-        num_incoming_streams_created_(0) {
+        writev_consumes_all_data_(false) {
     Initialize();
     this->connection()->SetEncrypter(
         ENCRYPTION_FORWARD_SECURE,
@@ -189,15 +187,14 @@
           QUIC_TOO_MANY_OPEN_STREAMS, "Too many streams!",
           ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
       return nullptr;
+    } else {
+      TestStream* stream = new TestStream(
+          id, this,
+          DetermineStreamType(id, connection()->transport_version(),
+                              /*is_incoming=*/true, BIDIRECTIONAL));
+      ActivateStream(QuicWrapUnique(stream));
+      return stream;
     }
-
-    TestStream* stream = new TestStream(
-        id, this,
-        DetermineStreamType(id, connection()->transport_version(),
-                            /*is_incoming=*/true, BIDIRECTIONAL));
-    ActivateStream(QuicWrapUnique(stream));
-    ++num_incoming_streams_created_;
-    return stream;
   }
 
   TestStream* CreateIncomingStream(PendingStream pending) override {
@@ -207,7 +204,6 @@
         DetermineStreamType(id, connection()->transport_version(),
                             /*is_incoming=*/true, BIDIRECTIONAL));
     ActivateStream(QuicWrapUnique(stream));
-    ++num_incoming_streams_created_;
     return stream;
   }
 
@@ -277,18 +273,6 @@
     return WritevData(stream, stream->id(), bytes, 0, FIN);
   }
 
-  bool ShouldBufferIncomingStream(QuicStreamId id) const override {
-    return should_buffer_incoming_streams_;
-  }
-
-  void set_should_buffer_incoming_streams(bool should_buffer_incoming_streams) {
-    should_buffer_incoming_streams_ = should_buffer_incoming_streams;
-  }
-
-  int num_incoming_streams_created() const {
-    return num_incoming_streams_created_;
-  }
-
   using QuicSession::ActivateStream;
   using QuicSession::closed_streams;
   using QuicSession::zombie_streams;
@@ -297,9 +281,7 @@
   StrictMock<TestCryptoStream> crypto_stream_;
 
   bool writev_consumes_all_data_;
-  bool should_buffer_incoming_streams_;
   QuicFrame save_frame_;
-  int num_incoming_streams_created_;
 };
 
 class QuicSessionTestBase : public QuicTestWithParam<ParsedQuicVersion> {
@@ -1521,63 +1503,6 @@
   session_.StreamDraining(stream_id);
 }
 
-TEST_P(QuicSessionTestServer, NoPendingStreams) {
-  session_.set_should_buffer_incoming_streams(false);
-
-  QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId(
-      transport_version(), Perspective::IS_CLIENT);
-  QuicStreamFrame data1(stream_id, true, 10, QuicStringPiece("HT"));
-  session_.OnStreamFrame(data1);
-  EXPECT_EQ(1, session_.num_incoming_streams_created());
-
-  QuicStreamFrame data2(stream_id, false, 0, QuicStringPiece("HT"));
-  session_.OnStreamFrame(data2);
-  EXPECT_EQ(1, session_.num_incoming_streams_created());
-}
-
-TEST_P(QuicSessionTestServer, PendingStreams) {
-  if (connection_->transport_version() != QUIC_VERSION_99) {
-    return;
-  }
-  session_.set_should_buffer_incoming_streams(true);
-
-  QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId(
-      transport_version(), Perspective::IS_CLIENT);
-  QuicStreamFrame data1(stream_id, true, 10, QuicStringPiece("HT"));
-  session_.OnStreamFrame(data1);
-  EXPECT_EQ(0, session_.num_incoming_streams_created());
-
-  QuicStreamFrame data2(stream_id, false, 0, QuicStringPiece("HT"));
-  session_.OnStreamFrame(data2);
-  EXPECT_EQ(1, session_.num_incoming_streams_created());
-}
-
-TEST_P(QuicSessionTestServer, RstPendingStreams) {
-  if (connection_->transport_version() != QUIC_VERSION_99) {
-    return;
-  }
-  session_.set_should_buffer_incoming_streams(true);
-
-  QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId(
-      transport_version(), Perspective::IS_CLIENT);
-  QuicStreamFrame data1(stream_id, true, 10, QuicStringPiece("HT"));
-  session_.OnStreamFrame(data1);
-  EXPECT_EQ(0, session_.num_incoming_streams_created());
-
-  EXPECT_CALL(session_, OnCanCreateNewOutgoingStream()).Times(1);
-  EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
-  EXPECT_CALL(*connection_, OnStreamReset(stream_id, QUIC_RST_ACKNOWLEDGEMENT))
-      .Times(1);
-  QuicRstStreamFrame rst1(kInvalidControlFrameId, stream_id,
-                          QUIC_ERROR_PROCESSING_STREAM, 12);
-  session_.OnRstStream(rst1);
-  EXPECT_EQ(0, session_.num_incoming_streams_created());
-
-  QuicStreamFrame data2(stream_id, false, 0, QuicStringPiece("HT"));
-  session_.OnStreamFrame(data2);
-  EXPECT_EQ(0, session_.num_incoming_streams_created());
-}
-
 TEST_P(QuicSessionTestServer, DrainingStreamsDoNotCountAsOpened) {
   // Verify that a draining stream (which has received a FIN but not consumed
   // it) does not count against the open quota (because it is closed from the
diff --git a/ppapi/c/pp_macros.h b/ppapi/c/pp_macros.h
index c232374..c92adcd 100644
--- a/ppapi/c/pp_macros.h
+++ b/ppapi/c/pp_macros.h
@@ -3,13 +3,12 @@
  * found in the LICENSE file.
  */
 
-/* From pp_macros.idl modified Fri Jun 13 10:40:42 2014. */
+/* From pp_macros.idl modified Tue Jun 10 17:37:13 2014. */
 
 #ifndef PPAPI_C_PP_MACROS_H_
 #define PPAPI_C_PP_MACROS_H_
 
-
-#define PPAPI_RELEASE 62
+#define PPAPI_RELEASE 60
 
 /**
  * @file
diff --git a/ppapi/generators/idl_c_header.py b/ppapi/generators/idl_c_header.py
index 9b4513ac..7577682 100755
--- a/ppapi/generators/idl_c_header.py
+++ b/ppapi/generators/idl_c_header.py
@@ -276,7 +276,7 @@
         release_numbers = re.findall('[\d\_]+', releasestr)
         release = re.findall('\d+', release_numbers[-1])[0]
         if release:
-          out.Write('\n#define PPAPI_RELEASE %s\n' % release)
+          out.Write('#define PPAPI_RELEASE %s\n' % release)
 
     # Generate all interface defines
     out.Write('\n')
diff --git a/ppapi/proxy/host_dispatcher.h b/ppapi/proxy/host_dispatcher.h
index 036b2386..47d1f34 100644
--- a/ppapi/proxy/host_dispatcher.h
+++ b/ppapi/proxy/host_dispatcher.h
@@ -7,6 +7,7 @@
 
 #include <map>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/compiler_specific.h"
@@ -130,7 +131,7 @@
 
   // Maps interface name to whether that interface is supported. If an interface
   // name is not in the map, that implies that we haven't queried for it yet.
-  typedef base::hash_map<std::string, bool> PluginSupportedMap;
+  typedef std::unordered_map<std::string, bool> PluginSupportedMap;
   PluginSupportedMap plugin_supported_;
 
   // Guaranteed non-NULL.
diff --git a/ppapi/proxy/plugin_dispatcher.h b/ppapi/proxy/plugin_dispatcher.h
index 7a3fe87..613edc9 100644
--- a/ppapi/proxy/plugin_dispatcher.h
+++ b/ppapi/proxy/plugin_dispatcher.h
@@ -235,7 +235,7 @@
   // be the pointer to the interface pointer supplied by the plugin if it's
   // supported, or NULL if it's not supported. This allows us to cache failures
   // and not req-query if a plugin doesn't support the interface.
-  typedef base::hash_map<std::string, const void*> InterfaceMap;
+  typedef std::unordered_map<std::string, const void*> InterfaceMap;
   InterfaceMap plugin_interfaces_;
 
   typedef std::unordered_map<PP_Instance, std::unique_ptr<InstanceData>>
diff --git a/ppapi/proxy/ppapi_command_buffer_proxy.cc b/ppapi/proxy/ppapi_command_buffer_proxy.cc
index 85440525..e6160b6 100644
--- a/ppapi/proxy/ppapi_command_buffer_proxy.cc
+++ b/ppapi/proxy/ppapi_command_buffer_proxy.cc
@@ -117,7 +117,7 @@
 }
 
 scoped_refptr<gpu::Buffer> PpapiCommandBufferProxy::CreateTransferBuffer(
-    size_t size,
+    uint32_t size,
     int32_t* id) {
   *id = -1;
 
@@ -129,8 +129,7 @@
   ppapi::proxy::SerializedHandle handle(
       ppapi::proxy::SerializedHandle::SHARED_MEMORY_REGION);
   if (!Send(new PpapiHostMsg_PPBGraphics3D_CreateTransferBuffer(
-          ppapi::API_ID_PPB_GRAPHICS_3D, resource_,
-          base::checked_cast<uint32_t>(size), id, &handle))) {
+          ppapi::API_ID_PPB_GRAPHICS_3D, resource_, size, id, &handle))) {
     if (last_state_.error == gpu::error::kNoError)
       last_state_.error = gpu::error::kLostContext;
     return NULL;
diff --git a/ppapi/proxy/ppapi_command_buffer_proxy.h b/ppapi/proxy/ppapi_command_buffer_proxy.h
index 44db7cae..4dd7c31 100644
--- a/ppapi/proxy/ppapi_command_buffer_proxy.h
+++ b/ppapi/proxy/ppapi_command_buffer_proxy.h
@@ -50,7 +50,7 @@
                                 int32_t start,
                                 int32_t end) override;
   void SetGetBuffer(int32_t transfer_buffer_id) override;
-  scoped_refptr<gpu::Buffer> CreateTransferBuffer(size_t size,
+  scoped_refptr<gpu::Buffer> CreateTransferBuffer(uint32_t size,
                                                   int32_t* id) override;
   void DestroyTransferBuffer(int32_t id) override;
 
diff --git a/ppapi/proxy/raw_var_data.cc b/ppapi/proxy/raw_var_data.cc
index 510a25cc..dfa5626d 100644
--- a/ppapi/proxy/raw_var_data.cc
+++ b/ppapi/proxy/raw_var_data.cc
@@ -43,11 +43,11 @@
 // is newly created. The index into |data| pointing to the result is returned.
 // |visited_map| keeps track of RawVarDatas that have already been created.
 size_t GetOrCreateRawVarData(const PP_Var& var,
-                             base::hash_map<int64_t, size_t>* visited_map,
+                             std::unordered_map<int64_t, size_t>* visited_map,
                              std::vector<std::unique_ptr<RawVarData>>* data) {
   if (VarTracker::IsVarTypeRefcounted(var.type)) {
-    base::hash_map<int64_t, size_t>::iterator it = visited_map->find(
-        var.value.as_id);
+    std::unordered_map<int64_t, size_t>::iterator it =
+        visited_map->find(var.value.as_id);
     if (it != visited_map->end()) {
       return it->second;
     } else {
@@ -87,7 +87,7 @@
                                                          PP_Instance instance) {
   std::unique_ptr<RawVarDataGraph> graph(new RawVarDataGraph);
   // Map of |var.value.as_id| to a RawVarData index in RawVarDataGraph.
-  base::hash_map<int64_t, size_t> visited_map;
+  std::unordered_map<int64_t, size_t> visited_map;
   base::hash_set<int64_t> parent_ids;
 
   base::stack<StackEntry> stack;
diff --git a/ppapi/proxy/video_decoder_resource.h b/ppapi/proxy/video_decoder_resource.h
index 2f67587..88db77fe 100644
--- a/ppapi/proxy/video_decoder_resource.h
+++ b/ppapi/proxy/video_decoder_resource.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -149,7 +150,7 @@
   ShmBufferList available_shm_buffers_;
 
   // Map of GL texture id to texture info.
-  using TextureMap = base::hash_map<uint32_t, Texture>;
+  using TextureMap = std::unordered_map<uint32_t, Texture>;
   TextureMap textures_;
 
   // Queue of received pictures.
diff --git a/ppapi/shared_impl/resource_tracker.h b/ppapi/shared_impl/resource_tracker.h
index 38a7a835..9ee9a3c 100644
--- a/ppapi/shared_impl/resource_tracker.h
+++ b/ppapi/shared_impl/resource_tracker.h
@@ -9,6 +9,7 @@
 
 #include <memory>
 #include <set>
+#include <unordered_map>
 
 #include "base/containers/hash_tables.h"
 #include "base/macros.h"
@@ -106,7 +107,7 @@
     // going away (otherwise, they may crash if they outlive the instance).
     ResourceSet resources;
   };
-  typedef base::hash_map<PP_Instance, std::unique_ptr<InstanceData>>
+  typedef std::unordered_map<PP_Instance, std::unique_ptr<InstanceData>>
       InstanceMap;
 
   InstanceMap instance_map_;
@@ -120,7 +121,7 @@
   //
   // A resource will be in this list as long as the object is alive.
   typedef std::pair<Resource*, int> ResourceAndRefCount;
-  typedef base::hash_map<PP_Resource, ResourceAndRefCount> ResourceMap;
+  typedef std::unordered_map<PP_Resource, ResourceAndRefCount> ResourceMap;
   ResourceMap live_resources_;
 
   int32_t last_resource_value_;
diff --git a/ppapi/shared_impl/test_utils.cc b/ppapi/shared_impl/test_utils.cc
index 18bc9e3..d94c20ce 100644
--- a/ppapi/shared_impl/test_utils.cc
+++ b/ppapi/shared_impl/test_utils.cc
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <cmath>
+#include <unordered_map>
 
 #include "base/containers/hash_tables.h"
 #include "base/logging.h"
@@ -29,14 +30,14 @@
 bool Equals(const PP_Var& expected,
             const PP_Var& actual,
             bool test_string_references,
-            base::hash_map<int64_t, int64_t>* visited_map) {
+            std::unordered_map<int64_t, int64_t>* visited_map) {
   if (expected.type != actual.type) {
     LOG(ERROR) << "expected type: " << expected.type
                << " actual type: " << actual.type;
     return false;
   }
   if (VarTracker::IsVarTypeRefcounted(expected.type)) {
-    base::hash_map<int64_t, int64_t>::iterator it =
+    std::unordered_map<int64_t, int64_t>::iterator it =
         visited_map->find(expected.value.as_id);
     if (it != visited_map->end()) {
       if (it->second != actual.value.as_id) {
@@ -208,7 +209,7 @@
 bool TestEqual(const PP_Var& expected,
                const PP_Var& actual,
                bool test_string_references) {
-  base::hash_map<int64_t, int64_t> visited_map;
+  std::unordered_map<int64_t, int64_t> visited_map;
   return Equals(expected, actual, test_string_references, &visited_map);
 }
 
diff --git a/ppapi/shared_impl/var_tracker.h b/ppapi/shared_impl/var_tracker.h
index 9e5439f97..1203ab1 100644
--- a/ppapi/shared_impl/var_tracker.h
+++ b/ppapi/shared_impl/var_tracker.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -173,7 +174,7 @@
     // we know when we can stop tracking this object.
     int track_with_no_reference_count;
   };
-  typedef base::hash_map<int32_t, VarInfo> VarMap;
+  typedef std::unordered_map<int32_t, VarInfo> VarMap;
 
   // Specifies what should happen with the refcount when calling AddVarInternal.
   enum AddVarRefMode {
diff --git a/services/device/BUILD.gn b/services/device/BUILD.gn
index d82aa46e..406a60c89 100644
--- a/services/device/BUILD.gn
+++ b/services/device/BUILD.gn
@@ -198,9 +198,15 @@
 
   if (is_serial_enabled_platform) {
     sources += [
+      "serial/serial_device_enumerator_unittest.cc",
       "serial/serial_port_impl_unittest.cc",
       "serial/serial_port_manager_impl_unittest.cc",
     ]
+
+    if (is_posix) {
+      sources += [ "serial/serial_io_handler_posix_unittest.cc" ]
+    }
+
     deps += [ "//services/device/serial" ]
   }
 
diff --git a/services/device/serial/BUILD.gn b/services/device/serial/BUILD.gn
index 724a0a90..c64a10ee 100644
--- a/services/device/serial/BUILD.gn
+++ b/services/device/serial/BUILD.gn
@@ -5,6 +5,14 @@
 import("//build/config/features.gni")
 
 if (is_win || (is_linux && use_udev) || is_mac) {
+  config("platform_support") {
+    visibility = [ ":serial" ]
+    if (is_win) {
+      libs = [ "setupapi.lib" ]
+      ldflags = [ "/DELAYLOAD:setupapi.dll" ]
+    }
+  }
+
   source_set("serial") {
     visibility = [
       "//services/device:lib",
@@ -12,20 +20,68 @@
     ]
 
     sources = [
+      "buffer.cc",
+      "buffer.h",
+      "serial_device_enumerator.cc",
+      "serial_device_enumerator.h",
+      "serial_device_enumerator_linux.cc",
+      "serial_device_enumerator_linux.h",
+      "serial_device_enumerator_mac.cc",
+      "serial_device_enumerator_mac.h",
+      "serial_device_enumerator_win.cc",
+      "serial_device_enumerator_win.h",
+      "serial_io_handler.cc",
+      "serial_io_handler.h",
+      "serial_io_handler_win.cc",
+      "serial_io_handler_win.h",
       "serial_port_impl.cc",
       "serial_port_impl.h",
       "serial_port_manager_impl.cc",
       "serial_port_manager_impl.h",
     ]
 
+    public_configs = [ ":platform_support" ]
+
     public_deps = [
       "//services/device/public/mojom",
     ]
 
     deps = [
       "//base",
-      "//device/serial",
       "//mojo/public/cpp/bindings",
+      "//net",
     ]
+
+    if (is_posix) {
+      sources += [
+        "serial_io_handler_posix.cc",
+        "serial_io_handler_posix.h",
+      ]
+    }
+
+    if (use_udev) {
+      deps += [ "//device/udev_linux" ]
+    }
+
+    if (is_chromeos) {
+      deps += [
+        "//chromeos",
+        "//dbus",
+      ]
+    }
+
+    if (is_mac) {
+      libs = [
+        "Foundation.framework",
+        "IOKit.framework",
+      ]
+    }
+
+    if (is_win) {
+      deps += [
+        "//device/base",
+        "//third_party/re2",
+      ]
+    }
   }
 }
diff --git a/services/device/serial/DEPS b/services/device/serial/DEPS
index e273c39..96812db 100644
--- a/services/device/serial/DEPS
+++ b/services/device/serial/DEPS
@@ -1,3 +1,6 @@
 include_rules = [
-  "+device/serial",
+  "+chromeos/dbus",
+  "+net/base",
+  "+services/device/public/mojom",
+  "+third_party/re2",
 ]
diff --git a/device/serial/buffer.cc b/services/device/serial/buffer.cc
similarity index 97%
rename from device/serial/buffer.cc
rename to services/device/serial/buffer.cc
index f1c3743b..0ab176b 100644
--- a/device/serial/buffer.cc
+++ b/services/device/serial/buffer.cc
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "services/device/serial/buffer.h"
+
 #include "base/numerics/safe_conversions.h"
-#include "device/serial/buffer.h"
 #include "net/base/io_buffer.h"
 
 namespace device {
diff --git a/device/serial/buffer.h b/services/device/serial/buffer.h
similarity index 95%
rename from device/serial/buffer.h
rename to services/device/serial/buffer.h
index d4ef869..d70775f 100644
--- a/device/serial/buffer.h
+++ b/services/device/serial/buffer.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 DEVICE_SERIAL_BUFFER_H_
-#define DEVICE_SERIAL_BUFFER_H_
+#ifndef SERVICES_DEVICE_SERIAL_BUFFER_H_
+#define SERVICES_DEVICE_SERIAL_BUFFER_H_
 
 #include <stdint.h>
 
@@ -82,4 +82,4 @@
 
 }  // namespace device
 
-#endif  // DEVICE_SERIAL_BUFFER_H_
+#endif  // SERVICES_DEVICE_SERIAL_BUFFER_H_
diff --git a/device/serial/serial_device_enumerator.cc b/services/device/serial/serial_device_enumerator.cc
similarity index 84%
rename from device/serial/serial_device_enumerator.cc
rename to services/device/serial/serial_device_enumerator.cc
index b2815a1d..a34cc556 100644
--- a/device/serial/serial_device_enumerator.cc
+++ b/services/device/serial/serial_device_enumerator.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 "device/serial/serial_device_enumerator.h"
+#include "services/device/serial/serial_device_enumerator.h"
 
 namespace device {
 
diff --git a/device/serial/serial_device_enumerator.h b/services/device/serial/serial_device_enumerator.h
similarity index 76%
rename from device/serial/serial_device_enumerator.h
rename to services/device/serial/serial_device_enumerator.h
index 689b257..0f105c0d 100644
--- a/device/serial/serial_device_enumerator.h
+++ b/services/device/serial/serial_device_enumerator.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 DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_H_
-#define DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_H_
+#ifndef SERVICES_DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_H_
+#define SERVICES_DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_H_
 
 #include <memory>
 
@@ -24,4 +24,4 @@
 
 }  // namespace device
 
-#endif  // DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_H_
+#endif  // SERVICES_DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_H_
diff --git a/device/serial/serial_device_enumerator_linux.cc b/services/device/serial/serial_device_enumerator_linux.cc
similarity index 95%
rename from device/serial/serial_device_enumerator_linux.cc
rename to services/device/serial/serial_device_enumerator_linux.cc
index 980f232..a59e4ab 100644
--- a/device/serial/serial_device_enumerator_linux.cc
+++ b/services/device/serial/serial_device_enumerator_linux.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 "device/serial/serial_device_enumerator_linux.h"
+#include "services/device/serial/serial_device_enumerator_linux.h"
 
 #include <stdint.h>
 
@@ -29,8 +29,7 @@
 
 // static
 std::unique_ptr<SerialDeviceEnumerator> SerialDeviceEnumerator::Create() {
-  return std::unique_ptr<SerialDeviceEnumerator>(
-      new SerialDeviceEnumeratorLinux());
+  return std::make_unique<SerialDeviceEnumeratorLinux>();
 }
 
 SerialDeviceEnumeratorLinux::SerialDeviceEnumeratorLinux() {
diff --git a/device/serial/serial_device_enumerator_linux.h b/services/device/serial/serial_device_enumerator_linux.h
similarity index 73%
rename from device/serial/serial_device_enumerator_linux.h
rename to services/device/serial/serial_device_enumerator_linux.h
index 1c00098..edcb9fa 100644
--- a/device/serial/serial_device_enumerator_linux.h
+++ b/services/device/serial/serial_device_enumerator_linux.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_LINUX_H_
-#define DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_LINUX_H_
+#ifndef SERVICES_DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_LINUX_H_
+#define SERVICES_DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_LINUX_H_
 
 #include "base/macros.h"
-#include "device/serial/serial_device_enumerator.h"
 #include "device/udev_linux/scoped_udev.h"
+#include "services/device/serial/serial_device_enumerator.h"
 
 namespace device {
 
@@ -28,4 +28,4 @@
 
 }  // namespace device
 
-#endif  // DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_LINUX_H_
+#endif  // SERVICES_DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_LINUX_H_
diff --git a/device/serial/serial_device_enumerator_mac.cc b/services/device/serial/serial_device_enumerator_mac.cc
similarity index 98%
rename from device/serial/serial_device_enumerator_mac.cc
rename to services/device/serial/serial_device_enumerator_mac.cc
index 7c01b4a..d3c557e4 100644
--- a/device/serial/serial_device_enumerator_mac.cc
+++ b/services/device/serial/serial_device_enumerator_mac.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 "device/serial/serial_device_enumerator_mac.h"
+#include "services/device/serial/serial_device_enumerator_mac.h"
 
 #include <IOKit/serial/IOSerialKeys.h>
 #include <IOKit/usb/IOUSBLib.h>
@@ -209,8 +209,7 @@
 
 // static
 std::unique_ptr<SerialDeviceEnumerator> SerialDeviceEnumerator::Create() {
-  return std::unique_ptr<SerialDeviceEnumerator>(
-      new SerialDeviceEnumeratorMac());
+  return std::make_unique<SerialDeviceEnumeratorMac>();
 }
 
 SerialDeviceEnumeratorMac::SerialDeviceEnumeratorMac() {}
diff --git a/device/serial/serial_device_enumerator_mac.h b/services/device/serial/serial_device_enumerator_mac.h
similarity index 71%
rename from device/serial/serial_device_enumerator_mac.h
rename to services/device/serial/serial_device_enumerator_mac.h
index 939a7f9b3..e9c79513 100644
--- a/device/serial/serial_device_enumerator_mac.h
+++ b/services/device/serial/serial_device_enumerator_mac.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_MAC_H_
-#define DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_MAC_H_
+#ifndef SERVICES_DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_MAC_H_
+#define SERVICES_DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_MAC_H_
 
 #include "base/macros.h"
-#include "device/serial/serial_device_enumerator.h"
+#include "services/device/serial/serial_device_enumerator.h"
 
 namespace device {
 
@@ -25,4 +25,4 @@
 
 }  // namespace device
 
-#endif  // DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_MAC_H_
+#endif  // SERVICES_DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_MAC_H_
diff --git a/device/serial/serial_device_enumerator_unittest.cc b/services/device/serial/serial_device_enumerator_unittest.cc
similarity index 91%
rename from device/serial/serial_device_enumerator_unittest.cc
rename to services/device/serial/serial_device_enumerator_unittest.cc
index c277c417..899f1c1 100644
--- a/device/serial/serial_device_enumerator_unittest.cc
+++ b/services/device/serial/serial_device_enumerator_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 "device/serial/serial_device_enumerator.h"
+#include "services/device/serial/serial_device_enumerator.h"
 
 #include <memory>
 #include <vector>
diff --git a/device/serial/serial_device_enumerator_win.cc b/services/device/serial/serial_device_enumerator_win.cc
similarity index 97%
rename from device/serial/serial_device_enumerator_win.cc
rename to services/device/serial/serial_device_enumerator_win.cc
index 9a4d69b..5d317381 100644
--- a/device/serial/serial_device_enumerator_win.cc
+++ b/services/device/serial/serial_device_enumerator_win.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 "device/serial/serial_device_enumerator_win.h"
+#include "services/device/serial/serial_device_enumerator_win.h"
 
 #include <windows.h>  // Must be in front of other Windows header files.
 
@@ -160,8 +160,7 @@
 
 // static
 std::unique_ptr<SerialDeviceEnumerator> SerialDeviceEnumerator::Create() {
-  return std::unique_ptr<SerialDeviceEnumerator>(
-      new SerialDeviceEnumeratorWin());
+  return std::make_unique<SerialDeviceEnumeratorWin>();
 }
 
 SerialDeviceEnumeratorWin::SerialDeviceEnumeratorWin() {}
diff --git a/device/serial/serial_device_enumerator_win.h b/services/device/serial/serial_device_enumerator_win.h
similarity index 71%
rename from device/serial/serial_device_enumerator_win.h
rename to services/device/serial/serial_device_enumerator_win.h
index 64ce93b7..9b76a5f 100644
--- a/device/serial/serial_device_enumerator_win.h
+++ b/services/device/serial/serial_device_enumerator_win.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_WIN_H_
-#define DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_WIN_H_
+#ifndef SERVICES_DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_WIN_H_
+#define SERVICES_DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_WIN_H_
 
 #include "base/macros.h"
-#include "device/serial/serial_device_enumerator.h"
+#include "services/device/serial/serial_device_enumerator.h"
 
 namespace device {
 
@@ -25,4 +25,4 @@
 
 }  // namespace device
 
-#endif  // DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_WIN_H_
+#endif  // SERVICES_DEVICE_SERIAL_SERIAL_DEVICE_ENUMERATOR_WIN_H_
diff --git a/device/serial/serial_io_handler.cc b/services/device/serial/serial_io_handler.cc
similarity index 94%
rename from device/serial/serial_io_handler.cc
rename to services/device/serial/serial_io_handler.cc
index e8219537..94542ef 100644
--- a/device/serial/serial_io_handler.cc
+++ b/services/device/serial/serial_io_handler.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 "device/serial/serial_io_handler.h"
+#include "services/device/serial/serial_io_handler.h"
 
 #include <memory>
 #include <utility>
@@ -57,10 +57,12 @@
       base::ThreadTaskRunnerHandle::Get();
   ui_thread_task_runner_->PostTask(
       FROM_HERE,
-      base::BindOnce(
-          &chromeos::PermissionBrokerClient::OpenPath, base::Unretained(client),
-          port, base::Bind(&SerialIoHandler::OnPathOpened, this, task_runner),
-          base::Bind(&SerialIoHandler::OnPathOpenError, this, task_runner)));
+      base::BindOnce(&chromeos::PermissionBrokerClient::OpenPath,
+                     base::Unretained(client), port,
+                     base::BindRepeating(&SerialIoHandler::OnPathOpened, this,
+                                         task_runner),
+                     base::BindRepeating(&SerialIoHandler::OnPathOpenError,
+                                         this, task_runner)));
 #else
   base::PostTaskWithTraits(
       FROM_HERE,
diff --git a/device/serial/serial_io_handler.h b/services/device/serial/serial_io_handler.h
similarity index 97%
rename from device/serial/serial_io_handler.h
rename to services/device/serial/serial_io_handler.h
index ac833fe..ce75809 100644
--- a/device/serial/serial_io_handler.h
+++ b/services/device/serial/serial_io_handler.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 DEVICE_SERIAL_SERIAL_IO_HANDLER_H_
-#define DEVICE_SERIAL_SERIAL_IO_HANDLER_H_
+#ifndef SERVICES_DEVICE_SERIAL_SERIAL_IO_HANDLER_H_
+#define SERVICES_DEVICE_SERIAL_SERIAL_IO_HANDLER_H_
 
 #include <stdint.h>
 
@@ -17,8 +17,8 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
-#include "device/serial/buffer.h"
 #include "services/device/public/mojom/serial.mojom.h"
+#include "services/device/serial/buffer.h"
 
 namespace device {
 
@@ -248,4 +248,4 @@
 
 }  // namespace device
 
-#endif  // DEVICE_SERIAL_SERIAL_IO_HANDLER_H_
+#endif  // SERVICES_DEVICE_SERIAL_SERIAL_IO_HANDLER_H_
diff --git a/device/serial/serial_io_handler_posix.cc b/services/device/serial/serial_io_handler_posix.cc
similarity index 96%
rename from device/serial/serial_io_handler_posix.cc
rename to services/device/serial/serial_io_handler_posix.cc
index 2cbe3bf..cfbe691 100644
--- a/device/serial/serial_io_handler_posix.cc
+++ b/services/device/serial/serial_io_handler_posix.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 "device/serial/serial_io_handler_posix.h"
+#include "services/device/serial/serial_io_handler_posix.h"
 
 #include <sys/ioctl.h>
 #include <termios.h>
@@ -300,9 +300,9 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (pending_read_buffer()) {
-    int bytes_read = HANDLE_EINTR(read(file().GetPlatformFile(),
-                                       pending_read_buffer(),
-                                       pending_read_buffer_len()));
+    int bytes_read =
+        HANDLE_EINTR(read(file().GetPlatformFile(), pending_read_buffer(),
+                          pending_read_buffer_len()));
     if (bytes_read < 0) {
       if (errno == EAGAIN) {
         // The fd does not have data to read yet so continue waiting.
@@ -359,9 +359,9 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (pending_write_buffer()) {
-    int bytes_written = HANDLE_EINTR(write(file().GetPlatformFile(),
-                                           pending_write_buffer(),
-                                           pending_write_buffer_len()));
+    int bytes_written =
+        HANDLE_EINTR(write(file().GetPlatformFile(), pending_write_buffer(),
+                           pending_write_buffer_len()));
     if (bytes_written < 0) {
       WriteCompleted(0, mojom::SerialSendError::SYSTEM_ERROR);
     } else {
@@ -379,8 +379,9 @@
   DCHECK(file().IsValid());
   if (!file_read_watcher_) {
     file_read_watcher_ = base::FileDescriptorWatcher::WatchReadable(
-        file().GetPlatformFile(), base::Bind(&SerialIoHandlerPosix::AttemptRead,
-                                             base::Unretained(this), false));
+        file().GetPlatformFile(),
+        base::BindRepeating(&SerialIoHandlerPosix::AttemptRead,
+                            base::Unretained(this), false));
   }
 }
 
@@ -390,8 +391,9 @@
   if (!file_write_watcher_) {
     file_write_watcher_ = base::FileDescriptorWatcher::WatchWritable(
         file().GetPlatformFile(),
-        base::Bind(&SerialIoHandlerPosix::OnFileCanWriteWithoutBlocking,
-                   base::Unretained(this)));
+        base::BindRepeating(
+            &SerialIoHandlerPosix::OnFileCanWriteWithoutBlocking,
+            base::Unretained(this)));
   }
 }
 
diff --git a/device/serial/serial_io_handler_posix.h b/services/device/serial/serial_io_handler_posix.h
similarity index 90%
rename from device/serial/serial_io_handler_posix.h
rename to services/device/serial/serial_io_handler_posix.h
index cd8bf41..d46d021 100644
--- a/device/serial/serial_io_handler_posix.h
+++ b/services/device/serial/serial_io_handler_posix.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 DEVICE_SERIAL_SERIAL_IO_HANDLER_POSIX_H_
-#define DEVICE_SERIAL_SERIAL_IO_HANDLER_POSIX_H_
+#ifndef SERVICES_DEVICE_SERIAL_SERIAL_IO_HANDLER_POSIX_H_
+#define SERVICES_DEVICE_SERIAL_SERIAL_IO_HANDLER_POSIX_H_
 
 #include <memory>
 
@@ -11,7 +11,7 @@
 #include "base/macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "device/serial/serial_io_handler.h"
+#include "services/device/serial/serial_io_handler.h"
 
 namespace device {
 
@@ -74,4 +74,4 @@
 
 }  // namespace device
 
-#endif  // DEVICE_SERIAL_SERIAL_IO_HANDLER_POSIX_H_
+#endif  // SERVICES_DEVICE_SERIAL_SERIAL_IO_HANDLER_POSIX_H_
diff --git a/device/serial/serial_io_handler_posix_unittest.cc b/services/device/serial/serial_io_handler_posix_unittest.cc
similarity index 99%
rename from device/serial/serial_io_handler_posix_unittest.cc
rename to services/device/serial/serial_io_handler_posix_unittest.cc
index 45b05c02..b9ff041c 100644
--- a/device/serial/serial_io_handler_posix_unittest.cc
+++ b/services/device/serial/serial_io_handler_posix_unittest.cc
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "services/device/serial/serial_io_handler_posix.h"
+
 #include "base/macros.h"
-#include "device/serial/serial_io_handler_posix.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace device {
diff --git a/device/serial/serial_io_handler_win.cc b/services/device/serial/serial_io_handler_win.cc
similarity index 94%
rename from device/serial/serial_io_handler_win.cc
rename to services/device/serial/serial_io_handler_win.cc
index c68b256..74e1cec8 100644
--- a/device/serial/serial_io_handler_win.cc
+++ b/services/device/serial/serial_io_handler_win.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 "device/serial/serial_io_handler_win.h"
+#include "services/device/serial/serial_io_handler_win.h"
 
 #define INITGUID
 #include <devpkey.h>
@@ -166,10 +166,10 @@
         io_handler_(io_handler),
         io_thread_task_runner_(io_thread_task_runner) {}
 
-  ~UiThreadHelper() { DCHECK(thread_checker_.CalledOnValidThread()); }
+  ~UiThreadHelper() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); }
 
   static void Start(UiThreadHelper* self) {
-    self->thread_checker_.DetachFromThread();
+    DETACH_FROM_THREAD(self->thread_checker_);
     DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces();
     if (device_monitor)
       self->device_observer_.Add(device_monitor);
@@ -179,13 +179,13 @@
   // DeviceMonitorWin::Observer
   void OnDeviceRemoved(const GUID& class_guid,
                        const std::string& device_path) override {
-    DCHECK(thread_checker_.CalledOnValidThread());
+    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
     io_thread_task_runner_->PostTask(
-        FROM_HERE, base::Bind(&SerialIoHandlerWin::OnDeviceRemoved, io_handler_,
-                              device_path));
+        FROM_HERE, base::BindOnce(&SerialIoHandlerWin::OnDeviceRemoved,
+                                  io_handler_, device_path));
   }
 
-  base::ThreadChecker thread_checker_;
+  THREAD_CHECKER(thread_checker_);
   ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_;
 
   // This weak pointer is only valid when checked on this task runner.
@@ -249,7 +249,7 @@
   helper_ =
       new UiThreadHelper(weak_factory_.GetWeakPtr(), io_thread_task_runner);
   ui_thread_task_runner()->PostTask(
-      FROM_HERE, base::Bind(&UiThreadHelper::Start, helper_));
+      FROM_HERE, base::BindOnce(&UiThreadHelper::Start, helper_));
 
   // A ReadIntervalTimeout of MAXDWORD will cause async reads to complete
   // immediately with any data that's available, even if there is none.
@@ -275,8 +275,8 @@
   }
 
   event_mask_ = 0;
-  BOOL ok = ::WaitCommEvent(
-      file().GetPlatformFile(), &event_mask_, &comm_context_->overlapped);
+  BOOL ok = ::WaitCommEvent(file().GetPlatformFile(), &event_mask_,
+                            &comm_context_->overlapped);
   if (!ok && GetLastError() != ERROR_IO_PENDING) {
     VPLOG(1) << "Failed to receive serial event";
     QueueReadCompleted(0, mojom::SerialReceiveError::SYSTEM_ERROR);
@@ -289,10 +289,8 @@
   DCHECK(pending_write_buffer());
   DCHECK(file().IsValid());
 
-  BOOL ok = ::WriteFile(file().GetPlatformFile(),
-                        pending_write_buffer(),
-                        pending_write_buffer_len(),
-                        NULL,
+  BOOL ok = ::WriteFile(file().GetPlatformFile(), pending_write_buffer(),
+                        pending_write_buffer_len(), NULL,
                         &write_context_->overlapped);
   if (!ok && GetLastError() != ERROR_IO_PENDING) {
     VPLOG(1) << "Write failed";
@@ -401,10 +399,8 @@
     } else if (error != ERROR_SUCCESS && error != ERROR_OPERATION_ABORTED) {
       ReadCompleted(0, mojom::SerialReceiveError::SYSTEM_ERROR);
     } else if (pending_read_buffer()) {
-      BOOL ok = ::ReadFile(file().GetPlatformFile(),
-                           pending_read_buffer(),
-                           pending_read_buffer_len(),
-                           NULL,
+      BOOL ok = ::ReadFile(file().GetPlatformFile(), pending_read_buffer(),
+                           pending_read_buffer_len(), NULL,
                            &read_context_->overlapped);
       if (!ok && GetLastError() != ERROR_IO_PENDING) {
         VPLOG(1) << "Read failed";
diff --git a/device/serial/serial_io_handler_win.h b/services/device/serial/serial_io_handler_win.h
similarity index 91%
rename from device/serial/serial_io_handler_win.h
rename to services/device/serial/serial_io_handler_win.h
index a0ec69e3..4c34ca7a 100644
--- a/device/serial/serial_io_handler_win.h
+++ b/services/device/serial/serial_io_handler_win.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 DEVICE_SERIAL_SERIAL_IO_HANDLER_WIN_H_
-#define DEVICE_SERIAL_SERIAL_IO_HANDLER_WIN_H_
+#ifndef SERVICES_DEVICE_SERIAL_SERIAL_IO_HANDLER_WIN_H_
+#define SERVICES_DEVICE_SERIAL_SERIAL_IO_HANDLER_WIN_H_
 
 #include <memory>
 
@@ -11,7 +11,7 @@
 #include "base/message_loop/message_pump_for_io.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "device/serial/serial_io_handler.h"
+#include "services/device/serial/serial_io_handler.h"
 
 namespace device {
 
@@ -75,4 +75,4 @@
 
 }  // namespace device
 
-#endif  // DEVICE_SERIAL_SERIAL_IO_HANDLER_WIN_H_
+#endif  // SERVICES_DEVICE_SERIAL_SERIAL_IO_HANDLER_WIN_H_
diff --git a/services/device/serial/serial_port_impl.cc b/services/device/serial/serial_port_impl.cc
index fd971dbb..ca74814 100644
--- a/services/device/serial/serial_port_impl.cc
+++ b/services/device/serial/serial_port_impl.cc
@@ -6,8 +6,9 @@
 
 #include "base/single_thread_task_runner.h"
 #include "base/task/post_task.h"
-#include "device/serial/buffer.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
+#include "services/device/serial/buffer.h"
+#include "services/device/serial/serial_io_handler.h"
 
 namespace device {
 
diff --git a/services/device/serial/serial_port_impl.h b/services/device/serial/serial_port_impl.h
index ee35310..5c82e23 100644
--- a/services/device/serial/serial_port_impl.h
+++ b/services/device/serial/serial_port_impl.h
@@ -7,7 +7,6 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "device/serial/serial_io_handler.h"
 #include "services/device/public/mojom/serial.mojom.h"
 
 namespace base {
@@ -16,6 +15,8 @@
 
 namespace device {
 
+class SerialIoHandler;
+
 // TODO(leonhsl): Merge this class with SerialIoHandler if/once
 // SerialIoHandler is exposed only via the Device Service.
 // crbug.com/748505
@@ -51,7 +52,7 @@
   void ClearBreak(ClearBreakCallback callback) override;
 
   std::string path_;
-  scoped_refptr<device::SerialIoHandler> io_handler_;
+  scoped_refptr<SerialIoHandler> io_handler_;
 
   DISALLOW_COPY_AND_ASSIGN(SerialPortImpl);
 };
diff --git a/services/device/serial/serial_port_manager_impl.cc b/services/device/serial/serial_port_manager_impl.cc
index ca3add12..120a108 100644
--- a/services/device/serial/serial_port_manager_impl.cc
+++ b/services/device/serial/serial_port_manager_impl.cc
@@ -6,8 +6,8 @@
 
 #include "base/sequenced_task_runner.h"
 #include "base/task/post_task.h"
-#include "device/serial/serial_device_enumerator.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
+#include "services/device/serial/serial_device_enumerator.h"
 #include "services/device/serial/serial_port_impl.h"
 
 namespace device {
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 0c16b3e..fe109e6 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -431,9 +431,9 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash"
+          "--enable-features=SingleProcessMash"
         ],
-        "name": "non_single_process_mash_ash_unittests",
+        "name": "single_process_mash_ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -510,10 +510,10 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
+          "--enable-features=SingleProcessMash",
           "--override-use-software-gl-for-tests"
         ],
-        "name": "non_single_process_mash_browser_tests",
+        "name": "single_process_mash_browser_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 20
@@ -621,18 +621,6 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
-          "--override-use-software-gl-for-tests"
-        ],
-        "name": "non_single_process_mash_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "shards": 5
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=TracingPerfettoBackend",
           "--gtest_filter=TracingControllerTest.*"
         ],
@@ -644,6 +632,18 @@
       },
       {
         "args": [
+          "--enable-features=SingleProcessMash",
+          "--override-use-software-gl-for-tests"
+        ],
+        "name": "single_process_mash_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 5
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
           "--enable-features=VizDisplayCompositor"
         ],
         "name": "viz_content_browsertests",
@@ -661,9 +661,9 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash"
+          "--enable-features=SingleProcessMash"
         ],
-        "name": "non_single_process_mash_content_unittests",
+        "name": "single_process_mash_content_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -717,9 +717,9 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash"
+          "--enable-features=SingleProcessMash"
         ],
-        "name": "non_single_process_mash_exo_unittests",
+        "name": "single_process_mash_exo_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -788,9 +788,9 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash"
+          "--enable-features=SingleProcessMash"
         ],
-        "name": "non_single_process_mash_interactive_ui_tests",
+        "name": "single_process_mash_interactive_ui_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 3
@@ -1042,9 +1042,9 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash"
+          "--enable-features=SingleProcessMash"
         ],
-        "name": "non_single_process_mash_unit_tests",
+        "name": "single_process_mash_unit_tests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1137,9 +1137,9 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash"
+          "--enable-features=SingleProcessMash"
         ],
-        "name": "non_single_process_mash_ash_unittests",
+        "name": "single_process_mash_ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1216,10 +1216,10 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
+          "--enable-features=SingleProcessMash",
           "--override-use-software-gl-for-tests"
         ],
-        "name": "non_single_process_mash_browser_tests",
+        "name": "single_process_mash_browser_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 10
@@ -1327,18 +1327,6 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
-          "--override-use-software-gl-for-tests"
-        ],
-        "name": "non_single_process_mash_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "shards": 5
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=TracingPerfettoBackend",
           "--gtest_filter=TracingControllerTest.*"
         ],
@@ -1350,6 +1338,18 @@
       },
       {
         "args": [
+          "--enable-features=SingleProcessMash",
+          "--override-use-software-gl-for-tests"
+        ],
+        "name": "single_process_mash_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 5
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
           "--enable-features=VizDisplayCompositor"
         ],
         "name": "viz_content_browsertests",
@@ -1367,9 +1367,9 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash"
+          "--enable-features=SingleProcessMash"
         ],
-        "name": "non_single_process_mash_content_unittests",
+        "name": "single_process_mash_content_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1423,9 +1423,9 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash"
+          "--enable-features=SingleProcessMash"
         ],
-        "name": "non_single_process_mash_exo_unittests",
+        "name": "single_process_mash_exo_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1494,9 +1494,9 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash"
+          "--enable-features=SingleProcessMash"
         ],
-        "name": "non_single_process_mash_interactive_ui_tests",
+        "name": "single_process_mash_interactive_ui_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 3
@@ -1747,9 +1747,9 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash"
+          "--enable-features=SingleProcessMash"
         ],
-        "name": "non_single_process_mash_unit_tests",
+        "name": "single_process_mash_unit_tests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index ff628a2..7ccb3cf 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -175,8 +175,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -941,8 +940,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -2116,8 +2114,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -3279,8 +3276,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -9553,8 +9549,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -10225,7 +10220,6 @@
       {
         "args": [
           "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter",
           "--test-launcher-print-test-stdio=always"
         ],
         "name": "network_service_content_browsertests",
@@ -10995,8 +10989,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -12133,8 +12126,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -12727,8 +12719,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -13309,8 +13300,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -14687,8 +14677,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -15358,8 +15347,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -16029,8 +16017,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -16700,8 +16687,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -17371,8 +17357,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -18042,8 +18027,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -18713,8 +18697,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -19384,8 +19367,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -20055,8 +20037,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -20726,8 +20707,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -21379,8 +21359,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -22116,8 +22095,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index dac1895..b95e959 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -1513,7 +1513,6 @@
       {
         "args": [
           "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -1561,7 +1560,6 @@
       {
         "args": [
           "--enable-features=NetworkService,NetworkServiceInProcess",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -1911,8 +1909,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -2053,8 +2050,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -2066,8 +2062,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService,NetworkServiceInProcess",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService,NetworkServiceInProcess"
         ],
         "name": "network_service_in_process_content_browsertests",
         "swarming": {
@@ -2176,8 +2171,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -2188,8 +2182,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService,NetworkServiceInProcess",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService,NetworkServiceInProcess"
         ],
         "name": "network_service_in_process_content_browsertests",
         "swarming": {
@@ -2696,8 +2689,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -3459,7 +3451,6 @@
       {
         "args": [
           "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -3506,7 +3497,6 @@
       {
         "args": [
           "--enable-features=NetworkService,NetworkServiceInProcess",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -5971,8 +5961,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -6725,8 +6714,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "isolate_coverage_data": true,
         "name": "network_service_content_browsertests",
@@ -10116,8 +10104,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -10732,8 +10719,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -11155,8 +11141,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 9ccca6c..3033032 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -370,7 +370,16 @@
           ],
           "shards": 4
         },
-        "test": "angle_end2end_tests"
+        "test": "angle_end2end_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -386,7 +395,16 @@
             }
           ]
         },
-        "test": "angle_gles1_conformance_tests"
+        "test": "angle_gles1_conformance_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -404,7 +422,16 @@
             }
           ]
         },
-        "test": "angle_unittests"
+        "test": "angle_unittests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -421,7 +448,16 @@
             }
           ]
         },
-        "test": "angle_white_box_tests"
+        "test": "angle_white_box_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -439,7 +475,16 @@
             }
           ]
         },
-        "test": "gl_tests"
+        "test": "gl_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -457,7 +502,16 @@
             }
           ]
         },
-        "test": "gles2_conform_test"
+        "test": "gles2_conform_test",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -476,7 +530,16 @@
             }
           ]
         },
-        "test": "gles2_conform_test"
+        "test": "gles2_conform_test",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -495,7 +558,16 @@
             }
           ]
         },
-        "test": "video_decode_accelerator_unittest"
+        "test": "video_decode_accelerator_unittest",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -514,7 +586,16 @@
             }
           ]
         },
-        "test": "video_decode_accelerator_unittest"
+        "test": "video_decode_accelerator_unittest",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       }
     ],
     "isolated_scripts": [
@@ -534,6 +615,15 @@
               "pool": "Chrome-GPU"
             }
           ]
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -555,6 +645,15 @@
               "pool": "Chrome-GPU"
             }
           ]
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -576,6 +675,15 @@
               "pool": "Chrome-GPU"
             }
           ]
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -603,6 +711,15 @@
           ],
           "idempotent": false,
           "shards": 20
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -630,6 +747,15 @@
           ],
           "idempotent": false,
           "shards": 20
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -657,6 +783,15 @@
           ],
           "idempotent": false,
           "shards": 20
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -682,6 +817,15 @@
           ],
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -707,6 +851,15 @@
           ],
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -732,6 +885,15 @@
           ],
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -757,6 +919,15 @@
           ],
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       }
     ]
@@ -4553,7 +4724,16 @@
           ],
           "shards": 4
         },
-        "test": "dawn_end2end_tests"
+        "test": "dawn_end2end_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       }
     ]
   },
@@ -15294,7 +15474,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15319,7 +15499,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15345,7 +15525,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15372,7 +15552,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15399,7 +15579,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15424,7 +15604,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15451,7 +15631,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15479,7 +15659,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15501,7 +15681,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15523,7 +15703,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15551,7 +15731,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15579,7 +15759,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15602,7 +15782,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15631,7 +15811,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15668,7 +15848,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15698,7 +15878,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15728,7 +15908,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15764,7 +15944,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15798,7 +15978,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15832,7 +16012,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15866,7 +16046,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15900,7 +16080,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15934,7 +16114,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -15962,7 +16142,16 @@
           ],
           "shards": 4
         },
-        "test": "angle_end2end_tests"
+        "test": "angle_end2end_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -15978,7 +16167,16 @@
             }
           ]
         },
-        "test": "angle_gles1_conformance_tests"
+        "test": "angle_gles1_conformance_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -15996,7 +16194,16 @@
             }
           ]
         },
-        "test": "angle_unittests"
+        "test": "angle_unittests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -16013,7 +16220,16 @@
             }
           ]
         },
-        "test": "angle_white_box_tests"
+        "test": "angle_white_box_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -16031,7 +16247,16 @@
           ],
           "shards": 4
         },
-        "test": "dawn_end2end_tests"
+        "test": "dawn_end2end_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -16048,7 +16273,16 @@
             }
           ]
         },
-        "test": "gl_tests"
+        "test": "gl_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -16066,7 +16300,16 @@
             }
           ]
         },
-        "test": "gl_tests"
+        "test": "gl_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -16082,7 +16325,16 @@
             }
           ]
         },
-        "test": "gl_unittests"
+        "test": "gl_unittests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -16098,7 +16350,16 @@
             }
           ]
         },
-        "test": "gles2_conform_test"
+        "test": "gles2_conform_test",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -16116,7 +16377,16 @@
             }
           ]
         },
-        "test": "gles2_conform_test"
+        "test": "gles2_conform_test",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -16135,7 +16405,16 @@
             }
           ]
         },
-        "test": "gles2_conform_test"
+        "test": "gles2_conform_test",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "swarming": {
@@ -16148,7 +16427,16 @@
             }
           ]
         },
-        "test": "gpu_unittests"
+        "test": "gpu_unittests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "swarming": {
@@ -16161,7 +16449,16 @@
             }
           ]
         },
-        "test": "swiftshader_unittests"
+        "test": "swiftshader_unittests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -16180,7 +16477,16 @@
             }
           ]
         },
-        "test": "video_decode_accelerator_unittest"
+        "test": "video_decode_accelerator_unittest",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -16199,7 +16505,16 @@
             }
           ]
         },
-        "test": "video_decode_accelerator_unittest"
+        "test": "video_decode_accelerator_unittest",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -16218,7 +16533,16 @@
             }
           ]
         },
-        "test": "video_decode_accelerator_unittest"
+        "test": "video_decode_accelerator_unittest",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "name": "xr_browser_tests",
@@ -16232,7 +16556,16 @@
             }
           ]
         },
-        "test": "xr_browser_tests"
+        "test": "xr_browser_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       }
     ],
     "isolated_scripts": [
@@ -16258,6 +16591,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -16282,6 +16624,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -16306,6 +16657,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -16330,6 +16690,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -16358,6 +16727,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -16389,6 +16767,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -16428,6 +16815,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -16453,6 +16849,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -16477,6 +16882,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -16502,6 +16916,15 @@
           ],
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -16527,6 +16950,15 @@
           ],
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -16552,6 +16984,15 @@
           ],
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -16577,6 +17018,15 @@
           ],
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -16602,6 +17052,15 @@
           ],
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -16627,6 +17086,15 @@
           ],
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       }
     ]
@@ -19117,7 +19585,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19142,7 +19610,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19169,7 +19637,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19195,7 +19663,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19224,7 +19692,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19251,7 +19719,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19277,7 +19745,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19304,7 +19772,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19329,7 +19797,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19354,7 +19822,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19381,7 +19849,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19409,7 +19877,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19431,7 +19899,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19453,7 +19921,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19481,7 +19949,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19509,7 +19977,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19537,7 +20005,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19560,7 +20028,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19589,7 +20057,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19622,7 +20090,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19655,7 +20123,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19688,7 +20156,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19721,7 +20189,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19758,7 +20226,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19798,7 +20266,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19828,7 +20296,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19876,7 +20344,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19910,7 +20378,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19943,7 +20411,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -19973,7 +20441,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20009,7 +20477,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20045,7 +20513,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20081,7 +20549,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20115,7 +20583,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20149,7 +20617,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20183,7 +20651,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20217,7 +20685,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20251,7 +20719,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20285,7 +20753,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -20338,7 +20806,16 @@
           ],
           "shards": 4
         },
-        "test": "angle_deqp_egl_tests"
+        "test": "angle_deqp_egl_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -20358,7 +20835,16 @@
           ],
           "shards": 4
         },
-        "test": "angle_deqp_egl_tests"
+        "test": "angle_deqp_egl_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -20378,7 +20864,16 @@
           ],
           "shards": 4
         },
-        "test": "angle_deqp_egl_tests"
+        "test": "angle_deqp_egl_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -20398,7 +20893,16 @@
           ],
           "shards": 4
         },
-        "test": "angle_deqp_gles2_tests"
+        "test": "angle_deqp_gles2_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -20418,7 +20922,16 @@
           ],
           "shards": 4
         },
-        "test": "angle_deqp_gles2_tests"
+        "test": "angle_deqp_gles2_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -20438,7 +20951,16 @@
           ],
           "shards": 4
         },
-        "test": "angle_deqp_gles2_tests"
+        "test": "angle_deqp_gles2_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -20458,7 +20980,16 @@
           ],
           "shards": 6
         },
-        "test": "angle_deqp_gles31_tests"
+        "test": "angle_deqp_gles31_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -20478,7 +21009,16 @@
           ],
           "shards": 6
         },
-        "test": "angle_deqp_gles31_tests"
+        "test": "angle_deqp_gles31_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -20498,7 +21038,16 @@
           ],
           "shards": 12
         },
-        "test": "angle_deqp_gles3_tests"
+        "test": "angle_deqp_gles3_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -20518,7 +21067,16 @@
           ],
           "shards": 12
         },
-        "test": "angle_deqp_gles3_tests"
+        "test": "angle_deqp_gles3_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       }
     ]
   },
diff --git a/testing/buildbot/chromium.gpu.json b/testing/buildbot/chromium.gpu.json
index fe7d4f2a3..e80d194 100644
--- a/testing/buildbot/chromium.gpu.json
+++ b/testing/buildbot/chromium.gpu.json
@@ -2435,7 +2435,16 @@
             }
           ]
         },
-        "test": "angle_unittests"
+        "test": "angle_unittests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -2452,7 +2461,16 @@
             }
           ]
         },
-        "test": "gl_tests"
+        "test": "gl_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -2470,7 +2488,16 @@
             }
           ]
         },
-        "test": "gl_tests"
+        "test": "gl_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -2486,7 +2513,16 @@
             }
           ]
         },
-        "test": "gl_unittests"
+        "test": "gl_unittests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -2505,7 +2541,16 @@
             }
           ]
         },
-        "test": "video_decode_accelerator_unittest"
+        "test": "video_decode_accelerator_unittest",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "name": "xr_browser_tests",
@@ -2519,7 +2564,16 @@
             }
           ]
         },
-        "test": "xr_browser_tests"
+        "test": "xr_browser_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       }
     ],
     "isolated_scripts": [
@@ -2545,6 +2599,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -2569,6 +2632,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -2593,6 +2665,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -2617,6 +2698,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -2645,6 +2735,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -2676,6 +2775,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -2715,6 +2823,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -2740,6 +2857,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -2764,6 +2890,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -2789,6 +2924,15 @@
           ],
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       }
     ]
@@ -2815,7 +2959,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -2844,7 +2988,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -2870,7 +3014,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -2897,7 +3041,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -2922,7 +3066,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -2950,7 +3094,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -2973,7 +3117,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3008,7 +3152,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3041,7 +3185,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3074,7 +3218,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3107,7 +3251,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3144,7 +3288,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3184,7 +3328,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3232,7 +3376,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3266,7 +3410,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3299,7 +3443,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -3333,7 +3477,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 6c6c41f6..b557da5 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -1136,8 +1136,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -1888,8 +1887,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -2582,8 +2580,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -3393,8 +3390,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index 595be1f..a38c5cd 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -2565,8 +2565,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 8f4a868..df2995c 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -3668,8 +3668,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -4169,10 +4168,10 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
+          "--enable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "non_single_process_mash_ash_unittests",
+        "name": "single_process_mash_ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -4277,11 +4276,11 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
+          "--enable-features=SingleProcessMash",
           "--override-use-software-gl-for-tests",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "non_single_process_mash_browser_tests",
+        "name": "single_process_mash_browser_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 30
@@ -4426,19 +4425,6 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
-          "--override-use-software-gl-for-tests",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "name": "non_single_process_mash_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "shards": 5
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=TracingPerfettoBackend",
           "--gtest_filter=TracingControllerTest.*",
           "--test-launcher-print-test-stdio=always"
@@ -4451,6 +4437,19 @@
       },
       {
         "args": [
+          "--enable-features=SingleProcessMash",
+          "--override-use-software-gl-for-tests",
+          "--test-launcher-print-test-stdio=always"
+        ],
+        "name": "single_process_mash_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 5
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
           "--enable-features=VizDisplayCompositor",
           "--test-launcher-print-test-stdio=always"
         ],
@@ -4472,10 +4471,10 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
+          "--enable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "non_single_process_mash_content_unittests",
+        "name": "single_process_mash_content_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -4548,10 +4547,10 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
+          "--enable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "non_single_process_mash_exo_unittests",
+        "name": "single_process_mash_exo_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -4641,10 +4640,10 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
+          "--enable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "non_single_process_mash_interactive_ui_tests",
+        "name": "single_process_mash_interactive_ui_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 3
@@ -5010,10 +5009,10 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
+          "--enable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "non_single_process_mash_unit_tests",
+        "name": "single_process_mash_unit_tests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -5181,10 +5180,10 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
+          "--enable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "non_single_process_mash_ash_unittests",
+        "name": "single_process_mash_ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -5355,11 +5354,11 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
+          "--enable-features=SingleProcessMash",
           "--override-use-software-gl-for-tests",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "non_single_process_mash_browser_tests",
+        "name": "single_process_mash_browser_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -5592,25 +5591,6 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
-          "--override-use-software-gl-for-tests",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "name": "non_single_process_mash_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
-            }
-          ],
-          "shards": 5
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=TracingPerfettoBackend",
           "--gtest_filter=TracingControllerTest.*",
           "--test-launcher-print-test-stdio=always"
@@ -5629,6 +5609,25 @@
       },
       {
         "args": [
+          "--enable-features=SingleProcessMash",
+          "--override-use-software-gl-for-tests",
+          "--test-launcher-print-test-stdio=always"
+        ],
+        "name": "single_process_mash_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Ubuntu-14.04"
+            }
+          ],
+          "shards": 5
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
           "--enable-features=VizDisplayCompositor",
           "--test-launcher-print-test-stdio=always"
         ],
@@ -5663,10 +5662,10 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
+          "--enable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "non_single_process_mash_content_unittests",
+        "name": "single_process_mash_content_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -5787,10 +5786,10 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
+          "--enable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "non_single_process_mash_exo_unittests",
+        "name": "single_process_mash_exo_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -5940,10 +5939,10 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
+          "--enable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "non_single_process_mash_interactive_ui_tests",
+        "name": "single_process_mash_interactive_ui_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -6534,10 +6533,10 @@
       },
       {
         "args": [
-          "--disable-features=SingleProcessMash",
+          "--enable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "non_single_process_mash_unit_tests",
+        "name": "single_process_mash_unit_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -7078,7 +7077,6 @@
       {
         "args": [
           "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter",
           "--test-launcher-print-test-stdio=always"
         ],
         "name": "network_service_content_browsertests",
@@ -9989,8 +9987,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index fb361be..fe7d15d 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -220,8 +220,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -1174,8 +1173,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
@@ -4394,8 +4392,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "name": "network_service_content_browsertests",
         "swarming": {
diff --git a/testing/buildbot/client.v8.fyi.json b/testing/buildbot/client.v8.fyi.json
index fa87f4ed..8dba211 100644
--- a/testing/buildbot/client.v8.fyi.json
+++ b/testing/buildbot/client.v8.fyi.json
@@ -1154,6 +1154,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -1178,6 +1187,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -1202,6 +1220,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -1226,6 +1253,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -1257,6 +1293,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -1296,6 +1341,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -1321,6 +1375,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -1345,6 +1408,15 @@
             }
           ],
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -1372,6 +1444,15 @@
           ],
           "idempotent": false,
           "shards": 20
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -1397,6 +1478,15 @@
           ],
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8816\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-24.21.14.1195\", \"os\": \"Windows-10\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       }
     ]
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index 915471b..6956db4 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -58,7 +58,6 @@
 
   data = [
     "//testing/buildbot/filters/cast-linux.content_browsertests.filter",
-    "//testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter",
     "//testing/buildbot/filters/site_isolation_android.content_browsertests.filter",
   ]
 }
diff --git a/testing/buildbot/filters/OWNERS b/testing/buildbot/filters/OWNERS
index 386f093..238827e 100644
--- a/testing/buildbot/filters/OWNERS
+++ b/testing/buildbot/filters/OWNERS
@@ -3,7 +3,6 @@
 
 # For changes to:
 #   mojo.fyi.network_browser_tests.filter
-#   mojo.fyi.network_content_browsertests.filter
 # If adding an exclusion for an existing failure (e.g. additional test for
 # feature X that is already not working), please add it beside the existing
 # failures. Otherwise please reach out to network-service-dev@.
diff --git a/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter b/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter
index 1007632..27652a22 100644
--- a/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter
+++ b/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter
@@ -37,7 +37,7 @@
 -SwitchAccessPredicateTest.IsActionable
 -SwitchAccessPredicateTest.IsGroup
 -SwitchAccessPredicateTest.IsInterestingSubtree
--SwitchAccessPredicateTest.IsSubtreeLeaf
+-SwitchAccessPredicateTest.IsInteresting
 -SwitchAccessPredicateTest.LeafPredicate
 -SwitchAccessPredicateTest.RootPredicate
 -SwitchAccessPredicateTest.VisitPredicate
@@ -333,4 +333,7 @@
 # https://crbug.com/918876
 -SwitchAccessPredicateTest.IsActionableFocusableElements
 
+# Flaky. https://crbug.com/919256
+-PDFAnnotationsTest.AnnotationsFeatureEnabled
+
 # See comment at top of file regarding adding test exclusions.
diff --git a/testing/buildbot/filters/mojo.fyi.chromeos.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.chromeos.network_browser_tests.filter
index e04919fc..9e5025ab 100644
--- a/testing/buildbot/filters/mojo.fyi.chromeos.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.chromeos.network_browser_tests.filter
@@ -19,6 +19,9 @@
 # Flaky with error: `picture_in_picture_window_controller_impl.cc(167)] Check failed: media_player_id_.has_value()`.
 -PictureInPictureWindowControllerBrowserTest.TabIconUpdated
 
+# Crashing on Mash. https://crbug.com/919108
+-ChromeBrowserMainBrowserTest.VariationsServiceStartsRequestOnNetworkChange
+
 # NOTE: if adding an exclusion for an existing failure (e.g. additional test for
 # feature X that is already not working), please add it beside the existing
 # failures. Otherwise please reach out to network-service-dev@.
diff --git a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
index 3f46a3e..66038eaf 100644
--- a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
@@ -15,15 +15,6 @@
 # https://crbug.com/816684 Track Page Load Metrics.
 -PageLoadMetricsBrowserTest.LoadingMetricsFailed
 
-# https://crbug.com/810329 DnsProbe browsertests that rely on delaying requests:
--DnsProbeBrowserTest.NxdomainProbeResultWithWorkingSlowCorrections
-# Addendum to the above: These three were previously marked as flaky. After
-# resolving https://crbug.com/810329, they will need to be monitored to see
-# if flake is still an issue.
--DnsProbeBrowserTest.CorrectionsLoadStoppedSlowProbe
--DnsProbeBrowserTest.CorrectionsLoadStopped
--DnsProbeBrowserTest.NoInternetProbeResultWithSlowBrokenCorrections
-
 # https://crbug.com/773295
 # Remove this test when there are no more clients left that use URLFetcher.
 -VariationsHttpHeadersBrowserTest.TestStrippingHeadersFromInternalRequest
diff --git a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
deleted file mode 100644
index 9028e3c..0000000
--- a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
+++ /dev/null
@@ -1,14 +0,0 @@
-# NOTE: if adding an exclusion for an existing failure (e.g. additional test for
-# feature X that is already not working), please add it beside the existing
-# failures. Otherwise please reach out to network-service-dev@.
-
-# https://crbug.com/846352: CORB/NetworkService: Remove
-# CrossSiteDocumentResourceHandler while preserving test coverage - the tests
-# below can be probably removed altogether once NetworkService ships and the
-# CrossSiteDocumentResourceHandler is removed.  See the bug for details.
--CrossSiteDocumentBlockingIsolatedOriginTest.BlockDocumentsFromIsolatedOrigin
--CrossSiteDocumentBlockingServiceWorkerTest.NoNetwork
-
-# NOTE: if adding an exclusion for an existing failure (e.g. additional test for
-# feature X that is already not working), please add it beside the existing
-# failures. Otherwise please reach out to network-service-dev@.
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 288dd67..35435621 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -958,29 +958,6 @@
       },
     },
   },
-  'non_single_process_mash_browser_tests': {
-    'modifications': {
-      # chromium.chromiumos
-      'linux-chromeos-dbg': {
-        'swarming': {
-          'shards': 20,
-        },
-      },
-      # chromium.memory
-      'Linux Chromium OS ASan LSan Tests (1)': {
-        # These are very slow on the ASAN trybot for some reason.
-        # crbug.com/794372
-        'swarming': {
-          'shards': 30,
-        },
-      },
-      'Linux ChromiumOS MSan Tests': {
-        'swarming': {
-          'shards': 20,
-        },
-      },
-    },
-  },
   'not_site_per_process_webkit_layout_tests': {
     'remove_from': [
       # chromium.linux
@@ -1048,6 +1025,29 @@
       'Linux MSan Tests',  # https://crbug.com/831676
     ],
   },
+  'single_process_mash_browser_tests': {
+    'modifications': {
+      # chromium.chromiumos
+      'linux-chromeos-dbg': {
+        'swarming': {
+          'shards': 20,
+        },
+      },
+      # chromium.memory
+      'Linux Chromium OS ASan LSan Tests (1)': {
+        # These are very slow on the ASAN trybot for some reason.
+        # crbug.com/794372
+        'swarming': {
+          'shards': 30,
+        },
+      },
+      'Linux ChromiumOS MSan Tests': {
+        'swarming': {
+          'shards': 20,
+        },
+      },
+    },
+  },
   'sizes': {
     'remove_from': [
       'win32-dbg',
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 966c4a75..485ab449 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3529,57 +3529,57 @@
       },
       'ozone_unittests': {},
       'ozone_x11_unittests': {},
-      'non_single_process_mash_ash_unittests': {
+      'single_process_mash_ash_unittests': {
         'test': 'ash_unittests',
         'args': [
-          '--disable-features=SingleProcessMash',
+          '--enable-features=SingleProcessMash',
         ],
       },
-      'non_single_process_mash_browser_tests': {
+      'single_process_mash_browser_tests': {
         'test': 'browser_tests',
         'args': [
-          '--disable-features=SingleProcessMash',
+          '--enable-features=SingleProcessMash',
           '--override-use-software-gl-for-tests'
         ],
         'swarming': {
           'shards': 10,
         },
       },
-      'non_single_process_mash_content_unittests': {
+      'single_process_mash_content_unittests': {
         'test': 'content_unittests',
         'args': [
-          '--disable-features=SingleProcessMash',
+          '--enable-features=SingleProcessMash',
         ],
       },
-      'non_single_process_mash_content_browsertests': {
+      'single_process_mash_content_browsertests': {
         'test': 'content_browsertests',
         'args': [
-          '--disable-features=SingleProcessMash',
+          '--enable-features=SingleProcessMash',
           '--override-use-software-gl-for-tests'
         ],
         'swarming': {
           'shards': 5,
         },
       },
-      'non_single_process_mash_exo_unittests': {
+      'single_process_mash_exo_unittests': {
         'test': 'exo_unittests',
         'args': [
-          '--disable-features=SingleProcessMash',
+          '--enable-features=SingleProcessMash',
         ],
       },
-      'non_single_process_mash_interactive_ui_tests': {
+      'single_process_mash_interactive_ui_tests': {
         'test': 'interactive_ui_tests',
         'args': [
-          '--disable-features=SingleProcessMash'
+          '--enable-features=SingleProcessMash'
         ],
         'swarming': {
           'shards': 3,
         },
       },
-      'non_single_process_mash_unit_tests': {
+      'single_process_mash_unit_tests': {
         'test': 'unit_tests',
         'args': [
-          '--disable-features=SingleProcessMash'
+          '--enable-features=SingleProcessMash'
         ],
       },
       'ui_chromeos_unittests': {},
@@ -3728,7 +3728,6 @@
       'network_service_content_browsertests': {
         'args': [
           '--enable-features=NetworkService',
-          '--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter',
         ],
         'swarming': {
           'shards': 5,
@@ -3756,7 +3755,6 @@
       'network_service_in_process_content_browsertests': {
         'args': [
           '--enable-features=NetworkService,NetworkServiceInProcess',
-          '--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter',
         ],
         'swarming': {
           'shards': 5,
@@ -3834,7 +3832,6 @@
       'network_service_content_browsertests': {
         'args': [
           '--enable-features=NetworkService',
-          '--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter',
         ],
         'swarming': {
           'shards': 2,
@@ -3879,7 +3876,6 @@
       'network_service_content_browsertests': {
         'args': [
           '--enable-features=NetworkService',
-          '--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter',
         ],
         'swarming': {
           'shards': 2,
@@ -3917,7 +3913,6 @@
       'network_service_in_process_content_browsertests': {
         'args': [
           '--enable-features=NetworkService,NetworkServiceInProcess',
-          '--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter',
         ],
         'swarming': {
           'shards': 2,
diff --git a/testing/buildbot/tryserver.chromium.linux.json b/testing/buildbot/tryserver.chromium.linux.json
index b021a3f4e..24801df 100644
--- a/testing/buildbot/tryserver.chromium.linux.json
+++ b/testing/buildbot/tryserver.chromium.linux.json
@@ -200,8 +200,7 @@
       },
       {
         "args": [
-          "--enable-features=NetworkService",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+          "--enable-features=NetworkService"
         ],
         "isolate_coverage_data": true,
         "name": "network_service_content_browsertests",
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 9bdd1c11e..c6284969 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -1892,6 +1892,14 @@
           'gtest_tests': 'gpu_win_gtests',
           'gpu_telemetry_tests': 'gpu_common_win_and_linux_telemetry_tests',
         },
+        'use_multi_dimension_trigger_script': True,
+        'alternate_swarming_dimensions': [
+          {
+            'gpu': '10de:1cb3-24.21.14.1195',
+            'os': 'Windows-10',
+            'pool': 'Chrome-GPU',
+          },
+        ],
       },
       'Win10 Release (NVIDIA)': {
         'browser_config': 'release',
@@ -1904,6 +1912,13 @@
           'gpu_telemetry_tests': 'gpu_common_win_and_linux_telemetry_tests',
         },
         'use_multi_dimension_trigger_script': True,
+        'alternate_swarming_dimensions': [
+          {
+            'gpu': '10de:1cb3-24.21.14.1195',
+            'os': 'Windows-10',
+            'pool': 'Chrome-GPU',
+          },
+        ],
       },
     },
   },
@@ -1933,6 +1948,14 @@
           'isolated_scripts': 'gpu_fyi_and_optional_win_isolated_scripts',
           'gpu_telemetry_tests': 'gpu_angle_win_nvidia_telemetry_tests',
         },
+        'use_multi_dimension_trigger_script': True,
+        'alternate_swarming_dimensions': [
+          {
+            'gpu': '10de:1cb3-24.21.14.1195',
+            'os': 'Windows-10',
+            'pool': 'Chrome-GPU',
+          },
+        ],
       },
       # END Fake builder used as mirror targets for ANGLE's GPU tryservers
       'Android FYI 32 Vk Release (Nexus 5X)': {
@@ -2255,6 +2278,14 @@
         'test_suites': {
           'gtest_tests': 'gpu_dawn_end2end_tests',
         },
+        'use_multi_dimension_trigger_script': True,
+        'alternate_swarming_dimensions': [
+          {
+            'gpu': '10de:1cb3-24.21.14.1195',
+            'os': 'Windows-10',
+            'pool': 'Chrome-GPU',
+          },
+        ],
       },
       # END Fake builder used as mirror targets for Dawn's GPU tryservers
       'GPU FYI Linux Builder' : {},
@@ -2671,6 +2702,13 @@
           'gpu_telemetry_tests': 'gpu_optional_win_telemetry_tests',
         },
         'use_multi_dimension_trigger_script': True,
+        'alternate_swarming_dimensions': [
+          {
+            'gpu': '10de:1cb3-24.21.14.1195',
+            'os': 'Windows-10',
+            'pool': 'Chrome-GPU',
+          },
+        ],
       },
       # END Fake builder used as mirror targets for Optional GPU tryservers
       'Win10 FYI Debug (NVIDIA)': {
@@ -2683,6 +2721,14 @@
           'gtest_tests': 'gpu_fyi_win_gtests',
           'gpu_telemetry_tests': 'gpu_fyi_win_debug_telemetry_tests',
         },
+        'use_multi_dimension_trigger_script': True,
+        'alternate_swarming_dimensions': [
+          {
+            'gpu': '10de:1cb3-24.21.14.1195',
+            'os': 'Windows-10',
+            'pool': 'Chrome-GPU',
+          },
+        ],
       },
       'Win10 FYI Exp Release (Intel HD 630)': {
         'os_type': 'win',
@@ -2734,6 +2780,13 @@
           'gpu_telemetry_tests': 'gpu_fyi_win_release_telemetry_tests',
         },
         'use_multi_dimension_trigger_script': True,
+        'alternate_swarming_dimensions': [
+          {
+            'gpu': '10de:1cb3-24.21.14.1195',
+            'os': 'Windows-10',
+            'pool': 'Chrome-GPU',
+          },
+        ],
       },
       'Win10 FYI dEQP Release (Intel HD 630)': {
         'os_type': 'win',
@@ -2752,6 +2805,14 @@
         'test_suites': {
           'gtest_tests': 'gpu_angle_deqp_win_nvidia_gtests',
         },
+        'use_multi_dimension_trigger_script': True,
+        'alternate_swarming_dimensions': [
+          {
+            'gpu': '10de:1cb3-24.21.14.1195',
+            'os': 'Windows-10',
+            'pool': 'Chrome-GPU',
+          },
+        ],
       },
       # This tryserver doesn't actually exist; it's a separate
       # configuration from the Win AMD bot on this waterfall because we
@@ -3778,6 +3839,14 @@
         'test_suites': {
           'gpu_telemetry_tests': 'gpu_v8_desktop_telemetry_tests',
         },
+        'use_multi_dimension_trigger_script': True,
+        'alternate_swarming_dimensions': [
+          {
+            'gpu': '10de:1cb3-24.21.14.1195',
+            'os': 'Windows-10',
+            'pool': 'Chrome-GPU',
+          },
+        ],
       },
     },
   },
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index a4af77c1..5560fc9 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -150,6 +150,7 @@
 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
 #include "third_party/blink/renderer/platform/keyboard_codes.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/testing/histogram_tester.h"
 #include "third_party/blink/renderer/platform/testing/scoped_fake_plugin_registry.h"
@@ -12806,6 +12807,18 @@
             frame->GetDocument().CanonicalUrlForSharing());
 }
 
+TEST_F(WebFrameTest, NavigationTimingInfo) {
+  RegisterMockedHttpURLLoad("foo.html");
+  frame_test_helpers::WebViewHelper web_view_helper;
+  web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
+  ResourceTimingInfo* navigation_timing_info = web_view_helper.LocalMainFrame()
+                                                   ->GetFrame()
+                                                   ->Loader()
+                                                   .GetDocumentLoader()
+                                                   ->GetNavigationTimingInfo();
+  EXPECT_EQ(navigation_timing_info->TransferSize(), 34);
+}
+
 TEST_F(WebFrameSimTest, EnterFullscreenResetScrollAndScaleState) {
   UseAndroidSettings();
   WebView().MainFrameWidget()->Resize(WebSize(490, 500));
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.cc b/third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.cc
index 0e22e2b..1f87cdea 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.cc
@@ -13,8 +13,4 @@
 
 CanvasContextCreationAttributesCore::~CanvasContextCreationAttributesCore() {}
 
-void CanvasContextCreationAttributesCore::Trace(blink::Visitor* visitor) {
-  visitor->Trace(compatible_xr_device);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.h b/third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.h
index a6f46ed..614e4e3 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.h
@@ -31,11 +31,7 @@
   bool premultiplied_alpha = true;
   bool preserve_drawing_buffer = false;
   bool stencil = false;
-
-  // This attribute is of type XRDevice, defined in modules/xr/xr_device.h
-  Member<ScriptWrappable> compatible_xr_device;
-
-  void Trace(blink::Visitor*);
+  bool xr_compatible = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
index 57e2833..6229fc1 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
@@ -169,7 +169,6 @@
 
 void CanvasRenderingContext::Trace(blink::Visitor* visitor) {
   visitor->Trace(host_);
-  visitor->Trace(creation_attributes_);
   ScriptWrappable::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 4bc21ee..e1f2c629 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -96,7 +96,7 @@
   LayoutUnit intrinsic_content_logical_height;
   LayoutRectOutsets margin_box_outsets;
   LayoutUnit preferred_logical_width[2];
-  void* pointers[4];
+  void* pointers[3];
 };
 
 static_assert(sizeof(LayoutBox) == sizeof(SameSizeAsLayoutBox),
@@ -763,7 +763,7 @@
 }
 
 LayoutUnit LayoutBox::LogicalHeightWithVisibleOverflow() const {
-  if (!layout_overflow_ || HasOverflowClip())
+  if (!LayoutOverflowIsSet() || HasOverflowClip())
     return LogicalHeight();
   LayoutRect overflow = LayoutOverflowRect();
   if (StyleRef().IsHorizontalWritingMode())
@@ -5223,8 +5223,8 @@
   visual_effect_overflow.Expand(outsets);
   AddSelfVisualOverflow(visual_effect_overflow);
 
-  if (visual_overflow_) {
-    visual_overflow_->SetHasSubpixelVisualEffectOutsets(
+  if (VisualOverflowIsSet()) {
+    overflow_->visual_overflow->SetHasSubpixelVisualEffectOutsets(
         !IsIntegerValue(outsets.Top()) || !IsIntegerValue(outsets.Right()) ||
         !IsIntegerValue(outsets.Bottom()) || !IsIntegerValue(outsets.Left()));
   }
@@ -5292,13 +5292,14 @@
 }
 
 void LayoutBox::SetLayoutClientAfterEdge(LayoutUnit client_after_edge) {
-  if (layout_overflow_)
-    layout_overflow_->SetLayoutClientAfterEdge(client_after_edge);
+  if (LayoutOverflowIsSet())
+    overflow_->layout_overflow->SetLayoutClientAfterEdge(client_after_edge);
 }
 
 LayoutUnit LayoutBox::LayoutClientAfterEdge() const {
-  return layout_overflow_ ? layout_overflow_->LayoutClientAfterEdge()
-                          : ClientLogicalBottom();
+  return LayoutOverflowIsSet()
+             ? overflow_->layout_overflow->LayoutClientAfterEdge()
+             : ClientLogicalBottom();
 }
 
 LayoutRect LayoutBox::VisualOverflowRectIncludingFilters() const {
@@ -5356,11 +5357,13 @@
       return;
   }
 
-  if (!layout_overflow_) {
-    layout_overflow_ = std::make_unique<BoxLayoutOverflowModel>(client_box);
+  if (!LayoutOverflowIsSet()) {
+    if (!overflow_)
+      overflow_ = std::make_unique<BoxOverflowModel>();
+    overflow_->layout_overflow.emplace(client_box);
   }
 
-  layout_overflow_->AddLayoutOverflow(overflow_rect);
+  overflow_->layout_overflow->AddLayoutOverflow(overflow_rect);
 }
 
 void LayoutBox::AddSelfVisualOverflow(const LayoutRect& rect) {
@@ -5371,11 +5374,14 @@
   if (border_box.Contains(rect))
     return;
 
-  if (!visual_overflow_) {
-    visual_overflow_ = std::make_unique<BoxVisualOverflowModel>(border_box);
+  if (!VisualOverflowIsSet()) {
+    if (!overflow_)
+      overflow_ = std::make_unique<BoxOverflowModel>();
+
+    overflow_->visual_overflow.emplace(border_box);
   }
 
-  visual_overflow_->AddSelfVisualOverflow(rect);
+  overflow_->visual_overflow->AddSelfVisualOverflow(rect);
 }
 
 void LayoutBox::AddContentsVisualOverflow(const LayoutRect& rect) {
@@ -5391,18 +5397,29 @@
   if (!HasOverflowClip() && border_box.Contains(rect))
     return;
 
-  if (!visual_overflow_) {
-    visual_overflow_ = std::make_unique<BoxVisualOverflowModel>(border_box);
+  if (!VisualOverflowIsSet()) {
+    if (!overflow_)
+      overflow_ = std::make_unique<BoxOverflowModel>();
+
+    overflow_->visual_overflow.emplace(border_box);
   }
-  visual_overflow_->AddContentsVisualOverflow(rect);
+  overflow_->visual_overflow->AddContentsVisualOverflow(rect);
 }
 
 void LayoutBox::ClearLayoutOverflow() {
-  layout_overflow_.reset();
+  if (!overflow_)
+    return;
+  overflow_->layout_overflow.reset();
+  if (!overflow_->visual_overflow)
+    overflow_.reset();
 }
 
 void LayoutBox::ClearVisualOverflow() {
-  visual_overflow_.reset();
+  if (!overflow_)
+    return;
+  overflow_->visual_overflow.reset();
+  if (!overflow_->layout_overflow)
+    overflow_.reset();
 }
 
 bool LayoutBox::PercentageLogicalHeightIsResolvable() const {
@@ -5587,12 +5604,12 @@
 }
 
 LayoutRect LayoutBox::VisualOverflowRect() const {
-  if (!visual_overflow_)
+  if (!VisualOverflowIsSet())
     return BorderBoxRect();
   if (HasOverflowClip() || HasMask())
-    return visual_overflow_->SelfVisualOverflowRect();
-  return UnionRect(visual_overflow_->SelfVisualOverflowRect(),
-                   visual_overflow_->ContentsVisualOverflowRect());
+    return overflow_->visual_overflow->SelfVisualOverflowRect();
+  return UnionRect(overflow_->visual_overflow->SelfVisualOverflowRect(),
+                   overflow_->visual_overflow->ContentsVisualOverflowRect());
 }
 
 LayoutPoint LayoutBox::OffsetPoint(const Element* parent) const {
@@ -6044,7 +6061,8 @@
   // painted along the pixel-snapped border box, the pixels on the anti-aliased
   // edge of the effect may overflow the calculated visual rect. Expand visual
   // rect by one pixel in the case.
-  return visual_overflow_ && visual_overflow_->HasSubpixelVisualEffectOutsets()
+  return VisualOverflowIsSet() &&
+                 overflow_->visual_overflow->HasSubpixelVisualEffectOutsets()
              ? 1
              : 0;
 }
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index 05feb87d..eeadf12 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -480,8 +480,9 @@
   // in the "physical coordinates in flipped block-flow direction" of the box.
   LayoutRect NoOverflowRect() const;
   LayoutRect LayoutOverflowRect() const {
-    return layout_overflow_ ? layout_overflow_->LayoutOverflowRect()
-                            : NoOverflowRect();
+    return LayoutOverflowIsSet()
+               ? overflow_->layout_overflow->LayoutOverflowRect()
+               : NoOverflowRect();
   }
   LayoutRect PhysicalLayoutOverflowRect() const {
     LayoutRect overflow_rect = LayoutOverflowRect();
@@ -519,12 +520,14 @@
   }
 
   LayoutRect SelfVisualOverflowRect() const {
-    return visual_overflow_ ? visual_overflow_->SelfVisualOverflowRect()
-                            : BorderBoxRect();
+    return VisualOverflowIsSet()
+               ? overflow_->visual_overflow->SelfVisualOverflowRect()
+               : BorderBoxRect();
   }
   LayoutRect ContentsVisualOverflowRect() const {
-    return visual_overflow_ ? visual_overflow_->ContentsVisualOverflowRect()
-                            : LayoutRect();
+    return VisualOverflowIsSet()
+               ? overflow_->visual_overflow->ContentsVisualOverflowRect()
+               : LayoutRect();
   }
 
   // Returns the visual overflow rect, expanded to the area affected by any
@@ -1312,18 +1315,19 @@
   LayoutRect LayoutOverflowRectForPropagation(LayoutObject* container) const;
 
   // TODO(chrishtr): delete callsites of this.
-  bool HasOverflowModel() const {
-    return layout_overflow_.get() || visual_overflow_.get();
-  }
+  bool HasOverflowModel() const { return overflow_.get(); }
   bool HasSelfVisualOverflow() const {
-    return visual_overflow_ && !BorderBoxRect().Contains(
-                                   visual_overflow_->SelfVisualOverflowRect());
+    return VisualOverflowIsSet() &&
+           !BorderBoxRect().Contains(
+               overflow_->visual_overflow->SelfVisualOverflowRect());
   }
   bool HasVisualOverflow() const {
-    return visual_overflow_ && !BorderBoxRect().Contains(VisualOverflowRect());
+    return VisualOverflowIsSet() &&
+           !BorderBoxRect().Contains(VisualOverflowRect());
   }
   bool HasLayoutOverflow() const {
-    return layout_overflow_ && !BorderBoxRect().Contains(LayoutOverflowRect());
+    return LayoutOverflowIsSet() &&
+           !BorderBoxRect().Contains(LayoutOverflowRect());
   }
 
   // Return true if re-laying out the containing block of this object means that
@@ -1380,20 +1384,22 @@
   bool HasRelativeLogicalHeight() const;
 
   bool HasHorizontalLayoutOverflow() const {
-    if (!layout_overflow_)
+    if (!LayoutOverflowIsSet())
       return false;
 
-    LayoutRect layout_overflow_rect = layout_overflow_->LayoutOverflowRect();
+    LayoutRect layout_overflow_rect =
+        overflow_->layout_overflow->LayoutOverflowRect();
     LayoutRect no_overflow_rect = NoOverflowRect();
     return layout_overflow_rect.X() < no_overflow_rect.X() ||
            layout_overflow_rect.MaxX() > no_overflow_rect.MaxX();
   }
 
   bool HasVerticalLayoutOverflow() const {
-    if (!layout_overflow_)
+    if (!LayoutOverflowIsSet())
       return false;
 
-    LayoutRect layout_overflow_rect = layout_overflow_->LayoutOverflowRect();
+    LayoutRect layout_overflow_rect =
+        overflow_->layout_overflow->LayoutOverflowRect();
     LayoutRect no_overflow_rect = NoOverflowRect();
     return layout_overflow_rect.Y() < no_overflow_rect.Y() ||
            layout_overflow_rect.MaxY() > no_overflow_rect.MaxY();
@@ -1673,6 +1679,13 @@
   TextDirection ResolvedDirection() const;
 
  private:
+  inline bool LayoutOverflowIsSet() const {
+    return overflow_ && overflow_->layout_overflow;
+  }
+  inline bool VisualOverflowIsSet() const {
+    return overflow_ && overflow_->visual_overflow;
+  }
+
   void UpdateShapeOutsideInfoAfterStyleChange(const ComputedStyle&,
                                               const ComputedStyle* old_style);
   void UpdateGridPositionAfterStyleChange(const ComputedStyle*);
@@ -1802,8 +1815,7 @@
         &LayoutBox::SetMarginBottom, &LayoutBox::SetMarginLeft);
   }
 
-  std::unique_ptr<BoxLayoutOverflowModel> layout_overflow_;
-  std::unique_ptr<BoxVisualOverflowModel> visual_overflow_;
+  std::unique_ptr<BoxOverflowModel> overflow_;
 
   union {
     // The inline box containing this LayoutBox, for atomic inline elements.
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object.h b/third_party/blink/renderer/core/layout/layout_box_model_object.h
index 3ef81509..242c5fe2 100644
--- a/third_party/blink/renderer/core/layout/layout_box_model_object.h
+++ b/third_party/blink/renderer/core/layout/layout_box_model_object.h
@@ -455,6 +455,17 @@
   virtual bool HasOverrideContainingBlockContentWidth() const { return false; }
   virtual bool HasOverrideContainingBlockContentHeight() const { return false; }
 
+  // Returns the continuation associated with |this|.
+  // Returns nullptr if no continuation is associated with |this|.
+  //
+  // See the section about CONTINUATIONS AND ANONYMOUS LAYOUTBLOCKFLOWS in
+  // LayoutInline for more details about them.
+  //
+  // Our implementation uses a HashMap to store them to avoid paying the cost
+  // for each LayoutBoxModelObject (|continuationMap| in the cpp file).
+  // public only for NGOutOfFlowLayoutPart, otherwise protected.
+  LayoutBoxModelObject* Continuation() const;
+
  protected:
   // Compute absolute quads for |this|, but not any continuations. May only be
   // called for objects which can be or have continuations, i.e. LayoutInline or
@@ -467,16 +478,6 @@
   LayoutPoint AdjustedPositionRelativeTo(const LayoutPoint&,
                                          const Element*) const;
 
-  // Returns the continuation associated with |this|.
-  // Returns nullptr if no continuation is associated with |this|.
-  //
-  // See the section about CONTINUATIONS AND ANONYMOUS LAYOUTBLOCKFLOWS in
-  // LayoutInline for more details about them.
-  //
-  // Our implementation uses a HashMap to store them to avoid paying the cost
-  // for each LayoutBoxModelObject (|continuationMap| in the cpp file).
-  LayoutBoxModelObject* Continuation() const;
-
   // Set the next link in the continuation chain.
   //
   // See continuation above for more details.
diff --git a/third_party/blink/renderer/core/layout/layout_image_resource.cc b/third_party/blink/renderer/core/layout/layout_image_resource.cc
index ad5af39..c2c301c 100644
--- a/third_party/blink/renderer/core/layout/layout_image_resource.cc
+++ b/third_party/blink/renderer/core/layout/layout_image_resource.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/core/layout/layout_image.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h"
+#include "third_party/blink/renderer/platform/graphics/placeholder_image.h"
 
 namespace blink {
 
@@ -159,6 +160,11 @@
     return Image::NullImage();
 
   Image* image = cached_image_->GetImage();
+  if (image->IsPlaceholderImage()) {
+    static_cast<PlaceholderImage*>(image)->SetIconAndTextScaleFactor(
+        layout_object_->StyleRef().EffectiveZoom());
+  }
+
   if (!image->IsSVGImage())
     return image;
 
diff --git a/third_party/blink/renderer/core/layout/layout_replaced.cc b/third_party/blink/renderer/core/layout/layout_replaced.cc
index 8bd259f..0cbb932 100644
--- a/third_party/blink/renderer/core/layout/layout_replaced.cc
+++ b/third_party/blink/renderer/core/layout/layout_replaced.cc
@@ -153,7 +153,6 @@
 }
 
 void LayoutReplaced::RecalcVisualOverflow() {
-  // Add in overflow from children.
   LayoutObject::RecalcVisualOverflow();
   ClearVisualOverflow();
   AddVisualEffectOverflow();
diff --git a/third_party/blink/renderer/core/layout/layout_table_row.cc b/third_party/blink/renderer/core/layout/layout_table_row.cc
index 74fd2978..e5c441b 100644
--- a/third_party/blink/renderer/core/layout/layout_table_row.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_row.cc
@@ -359,11 +359,6 @@
     AddSelfVisualOverflow(collapsed_border_rect);
   }
 
-  // Should propagate cell's overflow to row if the cell has row span or has
-  // overflow.
-  if (cell->ResolvedRowSpan() == 1 && !cell->HasOverflowModel())
-    return;
-
   LayoutRect cell_visual_overflow_rect =
       cell->VisualOverflowRectForPropagation();
   cell_visual_overflow_rect.Move(cell_row_offset);
diff --git a/third_party/blink/renderer/core/layout/line/inline_flow_box.cc b/third_party/blink/renderer/core/layout/line/inline_flow_box.cc
index 5d73d13..e875d4b 100644
--- a/third_party/blink/renderer/core/layout/line/inline_flow_box.cc
+++ b/third_party/blink/renderer/core/layout/line/inline_flow_box.cc
@@ -44,7 +44,7 @@
 namespace blink {
 
 struct SameSizeAsInlineFlowBox : public InlineBox {
-  void* pointers[6];
+  void* pointers[5];
   uint32_t bitfields : 23;
 };
 
@@ -301,10 +301,10 @@
   }
   // FIXME: Rounding error here since overflow was pixel snapped, but nobody
   // other than list markers passes non-integral values here.
-  if (layout_overflow_)
-    layout_overflow_->Move(delta.Width(), delta.Height());
-  if (visual_overflow_)
-    visual_overflow_->Move(delta.Width(), delta.Height());
+  if (LayoutOverflowIsSet())
+    overflow_->layout_overflow->Move(delta.Width(), delta.Height());
+  if (VisualOverflowIsSet())
+    overflow_->visual_overflow->Move(delta.Width(), delta.Height());
 }
 
 LineBoxList* InlineFlowBox::LineBoxes() const {
@@ -1210,14 +1210,11 @@
     GlyphOverflowAndFallbackFontsMap& text_box_data_map) {
   // If we know we have no overflow, we can just bail.
   if (KnownToHaveNoOverflow()) {
-    DCHECK(!layout_overflow_ && !visual_overflow_);
+    DCHECK(!LayoutOverflowIsSet() && !VisualOverflowIsSet());
     return;
   }
 
-  if (layout_overflow_)
-    layout_overflow_.reset();
-  if (visual_overflow_)
-    visual_overflow_.reset();
+  overflow_.reset();
 
   // Visual overflow just includes overflow for stuff we need to issues paint
   // invalidations for ourselves. Self-painting layers are ignored.
@@ -1291,10 +1288,13 @@
   if (frame_box.Contains(rect) || rect.IsEmpty())
     return;
 
-  if (!layout_overflow_)
-    layout_overflow_ = std::make_unique<SimpleLayoutOverflowModel>(frame_box);
+  if (!LayoutOverflowIsSet()) {
+    if (!overflow_)
+      overflow_ = std::make_unique<SimpleOverflowModel>();
+    overflow_->layout_overflow.emplace(frame_box);
+  }
 
-  layout_overflow_->SetLayoutOverflow(rect);
+  overflow_->layout_overflow->SetLayoutOverflow(rect);
 }
 
 void InlineFlowBox::SetVisualOverflow(const LayoutRect& rect,
@@ -1303,10 +1303,13 @@
   if (frame_box.Contains(rect) || rect.IsEmpty())
     return;
 
-  if (!visual_overflow_)
-    visual_overflow_ = std::make_unique<SimpleVisualOverflowModel>(frame_box);
+  if (!VisualOverflowIsSet()) {
+    if (!overflow_)
+      overflow_ = std::make_unique<SimpleOverflowModel>();
+    overflow_->visual_overflow.emplace(frame_box);
+  }
 
-  visual_overflow_->SetVisualOverflow(rect);
+  overflow_->visual_overflow->SetVisualOverflow(rect);
 }
 
 void InlineFlowBox::SetVisualOverflowFromLogicalRect(
diff --git a/third_party/blink/renderer/core/layout/line/inline_flow_box.h b/third_party/blink/renderer/core/layout/line/inline_flow_box.h
index 7fd35e8f..db0237b 100644
--- a/third_party/blink/renderer/core/layout/line/inline_flow_box.h
+++ b/third_party/blink/renderer/core/layout/line/inline_flow_box.h
@@ -286,21 +286,23 @@
   // the right in vertical-rl.
   LayoutRect LayoutOverflowRect(LayoutUnit line_top,
                                 LayoutUnit line_bottom) const {
-    return layout_overflow_
-               ? layout_overflow_->LayoutOverflowRect()
+    return LayoutOverflowIsSet()
+               ? overflow_->layout_overflow->LayoutOverflowRect()
                : FrameRectIncludingLineHeight(line_top, line_bottom);
   }
   LayoutUnit LogicalTopLayoutOverflow(LayoutUnit line_top) const {
-    if (layout_overflow_) {
-      return IsHorizontal() ? layout_overflow_->LayoutOverflowRect().Y()
-                            : layout_overflow_->LayoutOverflowRect().X();
+    if (LayoutOverflowIsSet()) {
+      return IsHorizontal()
+                 ? overflow_->layout_overflow->LayoutOverflowRect().Y()
+                 : overflow_->layout_overflow->LayoutOverflowRect().X();
     }
     return line_top;
   }
   LayoutUnit LogicalBottomLayoutOverflow(LayoutUnit line_bottom) const {
-    if (layout_overflow_) {
-      return IsHorizontal() ? layout_overflow_->LayoutOverflowRect().MaxY()
-                            : layout_overflow_->LayoutOverflowRect().MaxX();
+    if (LayoutOverflowIsSet()) {
+      return IsHorizontal()
+                 ? overflow_->layout_overflow->LayoutOverflowRect().MaxY()
+                 : overflow_->layout_overflow->LayoutOverflowRect().MaxX();
     }
     return line_bottom;
   }
@@ -312,50 +314,55 @@
     return result;
   }
   LayoutUnit LogicalRightLayoutOverflow() const {
-    if (layout_overflow_) {
-      return IsHorizontal() ? layout_overflow_->LayoutOverflowRect().MaxX()
-                            : layout_overflow_->LayoutOverflowRect().MaxY();
+    if (LayoutOverflowIsSet()) {
+      return IsHorizontal()
+                 ? overflow_->layout_overflow->LayoutOverflowRect().MaxX()
+                 : overflow_->layout_overflow->LayoutOverflowRect().MaxY();
     }
     return LogicalRight();
   }
   LayoutUnit LogicalLeftLayoutOverflow() const {
-    if (layout_overflow_) {
-      return IsHorizontal() ? layout_overflow_->LayoutOverflowRect().X()
-                            : layout_overflow_->LayoutOverflowRect().Y();
+    if (LayoutOverflowIsSet()) {
+      return IsHorizontal()
+                 ? overflow_->layout_overflow->LayoutOverflowRect().X()
+                 : overflow_->layout_overflow->LayoutOverflowRect().Y();
     }
     return LogicalLeft();
   }
 
   LayoutRect VisualOverflowRect(LayoutUnit line_top,
                                 LayoutUnit line_bottom) const {
-    return visual_overflow_
-               ? visual_overflow_->VisualOverflowRect()
+    return VisualOverflowIsSet()
+               ? overflow_->visual_overflow->VisualOverflowRect()
                : FrameRectIncludingLineHeight(line_top, line_bottom);
   }
   LayoutUnit LogicalLeftVisualOverflow() const {
-    return visual_overflow_
-               ? (IsHorizontal() ? visual_overflow_->VisualOverflowRect().X()
-                                 : visual_overflow_->VisualOverflowRect().Y())
+    return VisualOverflowIsSet()
+               ? (IsHorizontal()
+                      ? overflow_->visual_overflow->VisualOverflowRect().X()
+                      : overflow_->visual_overflow->VisualOverflowRect().Y())
                : LogicalLeft();
   }
   LayoutUnit LogicalRightVisualOverflow() const {
-    return visual_overflow_
+    return VisualOverflowIsSet()
                ? (IsHorizontal()
-                      ? visual_overflow_->VisualOverflowRect().MaxX()
-                      : visual_overflow_->VisualOverflowRect().MaxY())
+                      ? overflow_->visual_overflow->VisualOverflowRect().MaxX()
+                      : overflow_->visual_overflow->VisualOverflowRect().MaxY())
                : static_cast<LayoutUnit>(LogicalRight().Ceil());
   }
   LayoutUnit LogicalTopVisualOverflow(LayoutUnit line_top) const {
-    if (visual_overflow_) {
-      return IsHorizontal() ? visual_overflow_->VisualOverflowRect().Y()
-                            : visual_overflow_->VisualOverflowRect().X();
+    if (VisualOverflowIsSet()) {
+      return IsHorizontal()
+                 ? overflow_->visual_overflow->VisualOverflowRect().Y()
+                 : overflow_->visual_overflow->VisualOverflowRect().X();
     }
     return line_top;
   }
   LayoutUnit LogicalBottomVisualOverflow(LayoutUnit line_bottom) const {
-    if (visual_overflow_) {
-      return IsHorizontal() ? visual_overflow_->VisualOverflowRect().MaxY()
-                            : visual_overflow_->VisualOverflowRect().MaxX();
+    if (VisualOverflowIsSet()) {
+      return IsHorizontal()
+                 ? overflow_->visual_overflow->VisualOverflowRect().MaxY()
+                 : overflow_->visual_overflow->VisualOverflowRect().MaxX();
     }
     return line_bottom;
   }
@@ -410,6 +417,12 @@
                                           LayoutUnit current) const;
 
  private:
+  inline bool LayoutOverflowIsSet() const {
+    return overflow_ && overflow_->layout_overflow;
+  }
+  inline bool VisualOverflowIsSet() const {
+    return overflow_ && overflow_->visual_overflow;
+  }
   void PlaceBoxRangeInInlineDirection(InlineBox* first_child,
                                       InlineBox* last_child,
                                       LayoutUnit& logical_left,
@@ -454,8 +467,7 @@
       LayoutUnit line_bottom);
 
  protected:
-  std::unique_ptr<SimpleLayoutOverflowModel> layout_overflow_;
-  std::unique_ptr<SimpleVisualOverflowModel> visual_overflow_;
+  std::unique_ptr<SimpleOverflowModel> overflow_;
 
   bool IsInlineFlowBox() const final { return true; }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
index 4ac0663f..cb52509 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
@@ -20,6 +20,58 @@
 
 namespace blink {
 
+using LineBoxPair = std::pair<const NGPhysicalLineBoxFragment*,
+                              const NGPhysicalLineBoxFragment*>;
+void GatherInlineContainerFragmentsFromLinebox(
+    NGBoxFragmentBuilder::InlineContainingBlockMap* inline_containing_block_map,
+    HashMap<const LayoutObject*, LineBoxPair>* containing_linebox_map,
+    const NGPhysicalLineBoxFragment* linebox,
+    const NGPhysicalOffset linebox_offset) {
+  for (auto& descendant : NGInlineFragmentTraversal::DescendantsOf(*linebox)) {
+    if (!descendant.fragment->IsBox())
+      continue;
+    LayoutObject* key = descendant.fragment->GetLayoutObject();
+    // TODO(atotic) Is traversing continuations the right thing to do?
+    if (key->IsLayoutInline())  // key for inlines is continuation root.
+      key = key->GetNode()->GetLayoutObject();
+    auto it = inline_containing_block_map->find(key);
+    if (it == inline_containing_block_map->end()) {
+      // Default case, not one of the blocks we are looking for.
+      continue;
+    }
+    base::Optional<NGBoxFragmentBuilder::InlineContainingBlockGeometry>&
+        containing_block_geometry = it->value;
+    LineBoxPair& containing_lineboxes =
+        containing_linebox_map->insert(key, LineBoxPair{nullptr, nullptr})
+            .stored_value->value;
+    DCHECK(containing_block_geometry.has_value() ||
+           !containing_lineboxes.first);
+
+    // |DescendantsOf| returns the offset from the given fragment. Since
+    // we give it the line box, need to add the |linebox_offset|.
+    NGPhysicalOffsetRect fragment_rect(
+        linebox_offset + descendant.offset_to_container_box,
+        descendant.fragment->Size());
+    if (containing_lineboxes.first == linebox) {
+      containing_block_geometry.value().start_fragment_union_rect.Unite(
+          fragment_rect);
+    } else if (!containing_lineboxes.first) {
+      containing_lineboxes.first = linebox;
+      containing_block_geometry =
+          NGBoxFragmentBuilder::InlineContainingBlockGeometry{
+              fragment_rect, NGPhysicalOffsetRect()};
+    }
+    // Skip fragments within an empty line boxes for the end fragment.
+    if (containing_lineboxes.second == linebox) {
+      containing_block_geometry.value().end_fragment_union_rect.Unite(
+          fragment_rect);
+    } else if (!containing_lineboxes.second || !linebox->IsEmptyLineBox()) {
+      containing_lineboxes.second = linebox;
+      containing_block_geometry.value().end_fragment_union_rect = fragment_rect;
+    }
+  }
+}
+
 void NGBoxFragmentBuilder::RemoveChildren() {
   child_break_tokens_.resize(0);
   inline_break_tokens_.resize(0);
@@ -203,9 +255,8 @@
 }
 
 // Finds InlineContainingBlockGeometry that define inline containing blocks.
-// inline_containing_block_map is a map whose keys specify which
+// |inline_containing_block_map| is a map whose keys specify which
 // inline containing blocks are required.
-// Not finding a required block is an unexpected behavior (DCHECK).
 void NGBoxFragmentBuilder::ComputeInlineContainerFragments(
     InlineContainingBlockMap* inline_containing_block_map) {
   if (!inline_containing_block_map->size())
@@ -229,45 +280,37 @@
       const NGPhysicalOffset linebox_offset = offsets_[i].ConvertToPhysical(
           GetWritingMode(), Direction(),
           ToNGPhysicalSize(Size(), GetWritingMode()), linebox->Size());
+      GatherInlineContainerFragmentsFromLinebox(inline_containing_block_map,
+                                                &containing_linebox_map,
+                                                linebox, linebox_offset);
+    } else if (children_[i]->IsBox()) {
+      const NGPhysicalBoxFragment* box_fragment =
+          ToNGPhysicalBoxFragment(children_[i].get());
+      bool is_anonymous_container =
+          box_fragment->GetLayoutObject() &&
+          box_fragment->GetLayoutObject()->IsAnonymousBlock();
+      if (!is_anonymous_container)
+        continue;
+      // If child is an anonymous container, this might be a special case of
+      // split inlines. The inline container fragments might be inside
+      // anonymous boxes. To find inline container fragments, traverse
+      // lineboxes inside anonymous box.
+      // For more on this special case, see "css container is an inline,
+      // with inline splitting" comment in
+      // NGOutOfFlowLayoutPart::LayoutDescendant.
+      const NGPhysicalOffset box_offset = offsets_[i].ConvertToPhysical(
+          GetWritingMode(), Direction(),
+          ToNGPhysicalSize(Size(), GetWritingMode()), box_fragment->Size());
 
-      for (auto& descendant :
-           NGInlineFragmentTraversal::DescendantsOf(*linebox)) {
-        if (!descendant.fragment->IsBox())
-          continue;
-
-        LayoutObject* key = descendant.fragment->GetLayoutObject();
-        auto it = inline_containing_block_map->find(key);
-        if (it == inline_containing_block_map->end())
-          continue;
-        base::Optional<NGBoxFragmentBuilder::InlineContainingBlockGeometry>&
-            containing_block_geometry = it->value;
-
-        LineBoxPair* containing_lineboxes =
-            &containing_linebox_map.insert(key, LineBoxPair{nullptr, nullptr})
-                 .stored_value->value;
-
-        // |DescendantsOf| returns the offset from the given fragment. Since
-        // we give it the line box, need to add the |linebox_offset|.
-        NGPhysicalOffsetRect fragment_rect(
-            linebox_offset + descendant.offset_to_container_box,
-            descendant.fragment->Size());
-        if (containing_lineboxes->first == linebox) {
-          containing_block_geometry.value().start_fragment_union_rect.Unite(
-              fragment_rect);
-        } else if (!containing_lineboxes->first) {
-          containing_lineboxes->first = linebox;
-          containing_block_geometry = InlineContainingBlockGeometry{
-              fragment_rect, NGPhysicalOffsetRect()};
-        }
-        // Skip fragments within an empty line boxes for the end fragment.
-        if (containing_lineboxes->second == linebox) {
-          containing_block_geometry.value().end_fragment_union_rect.Unite(
-              fragment_rect);
-        } else if (!containing_lineboxes->second ||
-                   !linebox->IsEmptyLineBox()) {
-          containing_lineboxes->second = linebox;
-          containing_block_geometry.value().end_fragment_union_rect =
-              fragment_rect;
+      // Traverse lineboxes of anonymous box.
+      for (const auto& child : box_fragment->Children()) {
+        if (child->IsLineBox()) {
+          const NGPhysicalLineBoxFragment* linebox =
+              ToNGPhysicalLineBoxFragment(child.get());
+          const NGPhysicalOffset linebox_offset = child.Offset() + box_offset;
+          GatherInlineContainerFragmentsFromLinebox(inline_containing_block_map,
+                                                    &containing_linebox_map,
+                                                    linebox, linebox_offset);
         }
       }
     }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
index 60fa9fbc..22d4bf32 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
@@ -102,6 +102,16 @@
   return *this;
 }
 
+NGLogicalOffset NGContainerFragmentBuilder::GetChildOffset(
+    const LayoutObject* child) {
+  for (wtf_size_t i = 0; i < children_.size(); ++i) {
+    if (children_[i]->GetLayoutObject() == child)
+      return offsets_[i];
+  }
+  NOTREACHED();
+  return NGLogicalOffset();
+}
+
 NGContainerFragmentBuilder&
 NGContainerFragmentBuilder::AddOutOfFlowChildCandidate(
     NGBlockNode child,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
index 31ab431..63e82a7c 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
@@ -86,6 +86,10 @@
 
   const ChildrenVector& Children() const { return children_; }
 
+  // Returns offset for given child. DCHECK if child not found.
+  // Warning: Do not call unless necessary.
+  NGLogicalOffset GetChildOffset(const LayoutObject* child);
+
   // Builder has non-trivial out-of-flow descendant methods.
   // These methods are building blocks for implementation of
   // out-of-flow descendants by layout algorithms.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h
index f5da6f6..6dd3fe9 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h
@@ -52,7 +52,7 @@
   }
   void SetBlockSize(LayoutUnit block_size) { size_.block_size = block_size; }
 
-  LayoutObject* GetLayoutObject() { return layout_object_; }
+  const LayoutObject* GetLayoutObject() { return layout_object_; }
 
  protected:
   NGFragmentBuilder(scoped_refptr<const ComputedStyle> style,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index 04158c85..4c268ae 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -34,6 +34,7 @@
       contains_fixed_(contains_fixed) {
   if (!container_builder->HasOutOfFlowDescendantCandidates())
     return;
+
   NGPhysicalBoxStrut physical_border_scrollbar =
       border_scrollbar.ConvertToPhysical(container_style.GetWritingMode(),
                                          container_style.Direction());
@@ -62,8 +63,45 @@
 
 void NGOutOfFlowLayoutPart::Run(LayoutBox* only_layout) {
   Vector<NGOutOfFlowPositionedDescendant> descendant_candidates;
+  const LayoutObject* current_container = container_builder_->GetLayoutObject();
+
   container_builder_->GetAndClearOutOfFlowDescendantCandidates(
-      &descendant_candidates, container_builder_->GetLayoutObject());
+      &descendant_candidates, current_container);
+
+  // Containing block traversal copied from
+  // LayoutBox::ContainingBlockLogicalWidthForPositioned.
+  // This code can be removed once we stop inline splitting.
+
+  if (descendant_candidates.size() > 0 && current_container &&
+      current_container->IsAnonymousBlock() &&
+      current_container->IsRelPositioned()) {
+    // Comments copied from LayoutBox::ContainingBlockLogicalWidthForPositioned
+    // Ensure we compute our width based on the width of our rel-pos inline
+    // container rather than any anonymous block created to manage a block-flow
+    // ancestor of ours in the rel-pos inline's inline flow.
+    LayoutBoxModelObject* absolute_containing_block =
+        ToLayoutBox(current_container)->Continuation();
+    // There may be nested parallel inline continuations. We have now found the
+    // innermost inline (which may not be relatively positioned). Locate the
+    // inline that serves as the containing block of this box.
+    while (!absolute_containing_block->CanContainOutOfFlowPositionedElement(
+        EPosition::kAbsolute)) {
+      absolute_containing_block =
+          ToLayoutBoxModelObject(absolute_containing_block->Container());
+    }
+    DCHECK(absolute_containing_block->IsLayoutInline());
+    // Make absolute_containing_block continuation root.
+    absolute_containing_block = ToLayoutBoxModelObject(
+        absolute_containing_block->GetNode()->GetLayoutObject());
+    for (auto& candidate : descendant_candidates) {
+      if (absolute_containing_block->CanContainOutOfFlowPositionedElement(
+              candidate.node.Style().GetPosition())) {
+        candidate.inline_container = absolute_containing_block;
+        container_builder_->AddOutOfFlowDescendant(candidate);
+      }
+    }
+    return;
+  }
 
   while (descendant_candidates.size() > 0) {
     ComputeInlineContainingBlocks(descendant_candidates);
@@ -84,7 +122,7 @@
     // This happens when an absolute container has a fixed child.
     descendant_candidates.Shrink(0);
     container_builder_->GetAndClearOutOfFlowDescendantCandidates(
-        &descendant_candidates, container_builder_->GetLayoutObject());
+        &descendant_candidates, current_container);
   }
 }
 
@@ -375,6 +413,34 @@
       offset->inline_offset = y.value();
   }
 
+  // Nonobvious special case: css container is an inline, with inline splitting.
+  // If Layout tree looks like this:
+  // LayoutNGBlockFlow#container
+  //   LayoutNGBlockFlow (anonymous#1)
+  //     LayoutInline#1 (relative)
+  //   LayoutNGBlockFlow (anonymous#2 relative)
+  //     LayoutNGBlockFlow#oof (positioned)
+  //   LayoutNGBlockFlow (anonymous#3)
+  //     LayoutInline#3 (continuation)
+  //
+  // The containing block geometry is defined by LayoutInline#1, and
+  // LayoutInline#3. This breaks the assumption that Element's css container
+  // is also ELement's ascendant.
+  // Because css container anonymous#2, does not have information needed
+  // to compute containing block geometry,
+  // Therefore, #oof cannot be placed by anonymous#2. NG handles this case
+  // by placing #oof in anonymous parent, #container.
+
+  // But, PaintPropertyTreeBuilder expects #oof.Location() to be wrt
+  // css container, #anonymous2. This is why the code below adjusts
+  // the legacy offset from being wrt #container to being wrt #anonymous2.
+
+  const LayoutObject* container = descendant.node.GetLayoutBox()->Container();
+  if (container->IsAnonymousBlock()) {
+    NGLogicalOffset container_offset =
+        container_builder_->GetChildOffset(container);
+    *offset -= container_offset;
+  }
   return layout_result;
 }
 
diff --git a/third_party/blink/renderer/core/layout/overflow_model.h b/third_party/blink/renderer/core/layout/overflow_model.h
index d6d1882..467a4e4 100644
--- a/third_party/blink/renderer/core/layout/overflow_model.h
+++ b/third_party/blink/renderer/core/layout/overflow_model.h
@@ -126,6 +126,11 @@
   DISALLOW_COPY_AND_ASSIGN(SimpleVisualOverflowModel);
 };
 
+struct SimpleOverflowModel {
+  base::Optional<SimpleLayoutOverflowModel> layout_overflow;
+  base::Optional<SimpleVisualOverflowModel> visual_overflow;
+};
+
 // BoxModelOverflow tracks overflows of a LayoutBox. It separates visual
 // overflow into self visual overflow and contents visual overflow.
 //
@@ -176,7 +181,6 @@
  private:
   LayoutRect layout_overflow_;
   LayoutUnit layout_client_after_edge_;
-  bool has_subpixel_visual_effect_outsets_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(BoxLayoutOverflowModel);
 };
@@ -230,6 +234,11 @@
   DISALLOW_COPY_AND_ASSIGN(BoxVisualOverflowModel);
 };
 
+struct BoxOverflowModel {
+  base::Optional<BoxLayoutOverflowModel> layout_overflow;
+  base::Optional<BoxVisualOverflowModel> visual_overflow;
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_OVERFLOW_MODEL_H_
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
index b4f64c54..6dc21f5b 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc
@@ -301,7 +301,6 @@
 }
 
 void LayoutSVGText::RecalcVisualOverflow() {
-  // Add in overflow from children.
   LayoutObject::RecalcVisualOverflow();
   AddSelfVisualOverflow(LayoutRect(ObjectBoundingBox()));
   AddVisualEffectOverflow();
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 5896d88..f11a7adc 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -104,13 +104,6 @@
 
 namespace blink {
 
-// The MHTML mime type should be same as the one we check in the browser
-// process's IsDownload (navigation_url_loader_network_service.cc).
-static bool IsArchiveMIMEType(const String& mime_type) {
-  return DeprecatedEqualIgnoringCase("multipart/related", mime_type) ||
-         DeprecatedEqualIgnoringCase("message/rfc822", mime_type);
-}
-
 DocumentLoader::DocumentLoader(
     LocalFrame* frame,
     WebNavigationType navigation_type,
@@ -241,8 +234,7 @@
 }
 
 ResourceTimingInfo* DocumentLoader::GetNavigationTimingInfo() const {
-  DCHECK(Fetcher());
-  return Fetcher()->GetNavigationTimingInfo();
+  return navigation_timing_info_.get();
 }
 
 const KURL& DocumentLoader::OriginalUrl() const {
@@ -423,6 +415,13 @@
   DCHECK(GetResource());
 
   if (!resource->ErrorOccurred() && !resource->WasCanceled()) {
+    const ResourceResponse& response = resource->GetResponse();
+    if (response.IsHTTP()) {
+      navigation_timing_info_->SetFinalResponse(response);
+      const int64_t encoded_data_length = response.EncodedDataLength();
+      navigation_timing_info_->AddFinalTransferSize(
+          encoded_data_length == -1 ? 0 : encoded_data_length);
+    }
     FinishedLoading(resource->LoadFinishTime());
     return;
   }
@@ -526,10 +525,14 @@
   DCHECK_EQ(resource, GetResource());
   DCHECK(!redirect_response.IsNull());
   request_ = request;
+  const KURL& request_url = request_.Url();
+
+  bool cross_origin = !SecurityOrigin::AreSameSchemeHostPort(
+      redirect_response.CurrentRequestUrl(), request_url);
+  navigation_timing_info_->AddRedirect(redirect_response, cross_origin);
 
   // If the redirecting url is not allowed to display content from the target
   // origin, then block the redirect.
-  const KURL& request_url = request_.Url();
   scoped_refptr<const SecurityOrigin> redirecting_origin =
       SecurityOrigin::Create(redirect_response.CurrentRequestUrl());
   if (!redirecting_origin->CanDisplay(request_url)) {
@@ -717,9 +720,11 @@
     }
   }
 
-  if (IsArchiveMIMEType(response_.MimeType()) &&
-      resource->GetDataBufferingPolicy() != kBufferData)
-    resource->SetDataBufferingPolicy(kBufferData);
+  // The MHTML mime type should be same as the one we check in the browser
+  // process's IsDownload (navigation_url_loader_network_service.cc).
+  loading_mhtml_archive_ =
+      DeprecatedEqualIgnoringCase("multipart/related", response_.MimeType()) ||
+      DeprecatedEqualIgnoringCase("message/rfc822", response_.MimeType());
 
   if (!ShouldContinueForResponse()) {
     probe::didReceiveResourceResponse(
@@ -819,7 +824,7 @@
   DCHECK(!response_.IsNull());
   DCHECK(!frame_->GetPage()->Paused());
 
-  if (listing_ftp_directory_) {
+  if (listing_ftp_directory_ || loading_mhtml_archive_) {
     data_buffer_->Append(data, length);
     return;
   }
@@ -853,9 +858,6 @@
 void DocumentLoader::ProcessData(const char* data, size_t length) {
   application_cache_host_->MainResourceDataReceived(data, length);
   time_of_last_data_received_ = CurrentTimeTicks();
-
-  if (IsArchiveMIMEType(GetResponse().MimeType()))
-    return;
   CommitData(data, length);
 
   // If we are sending data to MediaDocument, we should stop here and cancel the
@@ -912,15 +914,15 @@
 }
 
 bool DocumentLoader::MaybeCreateArchive() {
-  // Give the archive machinery a crack at this document. If the MIME type is
-  // not an archive type, it will return 0.
-  if (!IsArchiveMIMEType(response_.MimeType()))
+  // Give the archive machinery a crack at this document.
+  if (!loading_mhtml_archive_)
     return false;
 
-  DCHECK(GetResource());
-  ArchiveResource* main_resource = fetcher_->CreateArchive(GetResource());
+  ArchiveResource* main_resource = fetcher_->CreateArchive(Url(), data_buffer_);
   if (!main_resource)
     return false;
+
+  data_buffer_ = nullptr;
   // The origin is the MHTML file, we need to set the base URL to the document
   // encoded in the MHTML so relative URLs are resolved properly.
   CommitNavigation(main_resource->MimeType(), main_resource->Url());
@@ -940,6 +942,12 @@
 bool DocumentLoader::WillLoadUrlAsEmpty(const KURL& url) {
   if (url.IsEmpty())
     return true;
+  // Usually, we load urls with about: scheme as empty.
+  // However, about:srcdoc is only used as a marker for non-existent
+  // url of iframes with srcdoc attribute, which have possibly non-empty
+  // content of the srcdoc attribute used as document's html.
+  if (url.IsAboutSrcdocURL())
+    return false;
   return SchemeRegistry::ShouldLoadURLSchemeAsEmptyDocument(url.Protocol());
 }
 
@@ -971,6 +979,14 @@
     // so we don't MarkFetchStart here.
 
     main_resource_identifier_ = CreateUniqueIdentifier();
+
+    // This buffer is created and populated for providing transferSize
+    // and redirect timing opt-in information.
+    navigation_timing_info_ = ResourceTimingInfo::Create(
+        fetch_initiator_type_names::kDocument, GetTiming().NavigationStart(),
+        true /* is_main_resource */);
+    navigation_timing_info_->SetInitialURL(request_.Url());
+
     ResourceLoaderOptions options;
     options.data_buffering_policy = kDoNotBufferData;
     options.initiator_info.name = fetch_initiator_type_names::kDocument;
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index a7202cd..0395dd14 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -420,7 +420,9 @@
   bool had_transient_activation_;
 
   bool listing_ftp_directory_ = false;
+  bool loading_mhtml_archive_ = false;
   unsigned long main_resource_identifier_ = 0;
+  scoped_refptr<ResourceTimingInfo> navigation_timing_info_;
 
   // This UseCounter tracks feature usage associated with the lifetime of the
   // document load. Features recorded prior to commit will be recorded locally.
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index ee8a02e..e8da2c83 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -1028,9 +1028,7 @@
   RecordLatestRequiredCSP();
 
   if (!CancelProvisionalLoaderForNewNavigation(
-          false /* cancel_scheduled_navigations */,
-          DocumentLoader::WillLoadUrlAsEmpty(
-              navigation_params->request.Url()))) {
+          false /* cancel_scheduled_navigations */)) {
     return;
   }
 
@@ -1124,8 +1122,7 @@
     const WebNavigationInfo& info,
     std::unique_ptr<WebDocumentLoader::ExtraData> extra_data) {
   if (!CancelProvisionalLoaderForNewNavigation(
-          true /* cancel_scheduled_navigations */,
-          false /* is_starting_blank_navigation */)) {
+          true /* cancel_scheduled_navigations */)) {
     return false;
   }
 
@@ -1568,8 +1565,7 @@
 }
 
 bool FrameLoader::CancelProvisionalLoaderForNewNavigation(
-    bool cancel_scheduled_navigations,
-    bool is_starting_blank_navigation) {
+    bool cancel_scheduled_navigations) {
   bool had_placeholder_client_document_loader =
       provisional_document_loader_ && !provisional_document_loader_->DidStart();
 
@@ -1597,22 +1593,9 @@
 
   progress_tracker_->ProgressStarted();
 
-  // If this is an about:blank navigation committing asynchronously, don't
-  // cancel scheduled navigations, so that the scheduled navigation still goes
-  // through. This handles the case where a navigation is scheduled between the
-  // about:blank navigation starting and finishing, where previously it would
-  // have happened after about:blank completed.
-  // TODO(japhet): This is an atrocious hack. Get rid of NavigationScheduler
-  // so it isn't needed.
-  bool skip_cancel_for_about_blank =
-      state_machine_.CommittedFirstRealDocumentLoad() &&
-      is_starting_blank_navigation;
   // We need to ensure that script initiated navigations are honored.
-  if (!skip_cancel_for_about_blank &&
-      (!had_placeholder_client_document_loader ||
-       cancel_scheduled_navigations)) {
+  if (!had_placeholder_client_document_loader || cancel_scheduled_navigations)
     frame_->GetNavigationScheduler().Cancel();
-  }
 
   return true;
 }
diff --git a/third_party/blink/renderer/core/loader/frame_loader.h b/third_party/blink/renderer/core/loader/frame_loader.h
index b2bb56586..3ba3153 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.h
+++ b/third_party/blink/renderer/core/loader/frame_loader.h
@@ -254,8 +254,7 @@
 
   // Returns whether we should continue with new navigation.
   bool CancelProvisionalLoaderForNewNavigation(
-      bool cancel_scheduled_navigations,
-      bool is_starting_blank_navigation);
+      bool cancel_scheduled_navigations);
 
   void ClearInitialScrollState();
 
diff --git a/third_party/blink/renderer/core/page/scrolling/scroll_into_view_test.cc b/third_party/blink/renderer/core/page/scrolling/scroll_into_view_test.cc
index c154be9..ab766f4 100644
--- a/third_party/blink/renderer/core/page/scrolling/scroll_into_view_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scroll_into_view_test.cc
@@ -621,6 +621,37 @@
   ASSERT_EQ(Window().scrollY(), content->OffsetTop());
 }
 
+TEST_F(ScrollIntoViewTest, LongDistanceSmoothScrollFinishedInThreeSeconds) {
+  v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
+  WebView().MainFrameWidget()->Resize(WebSize(800, 600));
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(
+      "<div id='space' style='height: 100000px'></div>"
+      "<div id='target' style='height: 1000px'></div>");
+
+  Compositor().BeginFrame();
+  ASSERT_EQ(Window().scrollY(), 0);
+
+  Element* target = GetDocument().getElementById("target");
+  ScrollIntoViewOptionsOrBoolean arg;
+  ScrollIntoViewOptions* options = ScrollIntoViewOptions::Create();
+  options->setBlock("start");
+  options->setBehavior("smooth");
+  arg.SetScrollIntoViewOptions(options);
+  target->scrollIntoView(arg);
+
+  // Scrolling the window
+  Compositor().BeginFrame();  // update run_state_.
+  Compositor().BeginFrame();  // Set start_time = now.
+  Compositor().BeginFrame(0.2);
+  ASSERT_EQ(Window().scrollY(), 864);
+
+  // Finish scrolling the container
+  Compositor().BeginFrame(2.8);
+  ASSERT_EQ(Window().scrollY(), target->OffsetTop());
+}
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/image_painter.cc b/third_party/blink/renderer/core/paint/image_painter.cc
index 43d7804..4170ce0ac 100644
--- a/third_party/blink/renderer/core/paint/image_painter.cc
+++ b/third_party/blink/renderer/core/paint/image_painter.cc
@@ -187,11 +187,17 @@
           ? ToHTMLImageElement(node)->GetDecodingModeForPainting(
                 image->paint_image_id())
           : Image::kUnspecifiedDecode;
+
   if (layout_image_.IsImagePolicyViolated()) {
-    // Does not an observer for the placeholder image, setting it to null.
-    image = PlaceholderImage::Create(nullptr, image->Size(),
-                                     image->Data() ? image->Data()->size() : 0);
+    // Does not set an observer for the placeholder image, setting it to null.
+    scoped_refptr<PlaceholderImage> placeholder_image =
+        PlaceholderImage::Create(nullptr, image->Size(),
+                                 image->Data() ? image->Data()->size() : 0);
+    placeholder_image->SetIconAndTextScaleFactor(
+        layout_image_.GetFrame()->PageZoomFactor());
+    image = std::move(placeholder_image);
   }
+
   context.DrawImage(
       image.get(), decode_mode, FloatRect(pixel_snapped_dest_rect), &src_rect,
       SkBlendMode::kSrcOver,
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/async-local-storage/index.js b/third_party/blink/renderer/core/script/resources/layered_api/async-local-storage/index.js
index b3971d0..7937f21 100644
--- a/third_party/blink/renderer/core/script/resources/layered_api/async-local-storage/index.js
+++ b/third_party/blink/renderer/core/script/resources/layered_api/async-local-storage/index.js
@@ -22,7 +22,11 @@
     throwForDisallowedKey(key);
 
     return performDatabaseOperation(this, 'readwrite', (transaction, store) => {
-      store.put(value, key);
+      if (value === undefined) {
+        store.delete(key);
+      } else {
+        store.put(value, key);
+      }
 
       return new Promise((resolve, reject) => {
         transaction.oncomplete = () => resolve();
@@ -45,19 +49,6 @@
     });
   }
 
-  async has(key) {
-    throwForDisallowedKey(key);
-
-    return performDatabaseOperation(this, 'readonly', (transaction, store) => {
-      const request = store.count(key);
-
-      return new Promise((resolve, reject) => {
-        request.onsuccess = () => resolve(request.result === 0 ? false : true);
-        request.onerror = () => reject(request.error);
-      });
-    });
-  }
-
   async delete(key) {
     throwForDisallowedKey(key);
 
diff --git a/third_party/blink/renderer/core/style/style_fetched_image.cc b/third_party/blink/renderer/core/style/style_fetched_image.cc
index 68a9dc3..1c450e9 100644
--- a/third_party/blink/renderer/core/style/style_fetched_image.cc
+++ b/third_party/blink/renderer/core/style/style_fetched_image.cc
@@ -31,6 +31,7 @@
 #include "third_party/blink/renderer/core/svg/graphics/svg_image.h"
 #include "third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h"
 #include "third_party/blink/renderer/platform/geometry/layout_size.h"
+#include "third_party/blink/renderer/platform/graphics/placeholder_image.h"
 
 namespace blink {
 
@@ -146,6 +147,11 @@
     const ComputedStyle& style,
     const FloatSize& target_size) const {
   Image* image = image_->GetImage();
+  if (image->IsPlaceholderImage()) {
+    static_cast<PlaceholderImage*>(image)->SetIconAndTextScaleFactor(
+        style.EffectiveZoom());
+  }
+
   if (!image->IsSVGImage())
     return image;
   return SVGImageForContainer::Create(ToSVGImage(image), target_size,
diff --git a/third_party/blink/renderer/core/style/style_fetched_image_set.cc b/third_party/blink/renderer/core/style/style_fetched_image_set.cc
index c9649cdb..933cc71 100644
--- a/third_party/blink/renderer/core/style/style_fetched_image_set.cc
+++ b/third_party/blink/renderer/core/style/style_fetched_image_set.cc
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h"
+#include "third_party/blink/renderer/platform/graphics/placeholder_image.h"
 
 namespace blink {
 
@@ -131,6 +132,11 @@
     const ComputedStyle& style,
     const FloatSize& target_size) const {
   Image* image = best_fit_image_->GetImage();
+  if (image->IsPlaceholderImage()) {
+    static_cast<PlaceholderImage*>(image)->SetIconAndTextScaleFactor(
+        style.EffectiveZoom());
+  }
+
   if (!image->IsSVGImage())
     return image;
   return SVGImageForContainer::Create(ToSVGImage(image), target_size,
diff --git a/third_party/blink/renderer/devtools/front_end/resources/ApplicationCacheItemsView.js b/third_party/blink/renderer/devtools/front_end/resources/ApplicationCacheItemsView.js
index 6c2373b7..9bee1db 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/ApplicationCacheItemsView.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/ApplicationCacheItemsView.js
@@ -105,20 +105,26 @@
 
     const statusInformation = {};
     // We should never have UNCACHED status, since we remove frames with UNCACHED application cache status from the tree.
-    statusInformation[applicationCache.UNCACHED] = {type: 'smallicon-red-ball', text: 'UNCACHED'};
-    statusInformation[applicationCache.IDLE] = {type: 'smallicon-green-ball', text: 'IDLE'};
-    statusInformation[applicationCache.CHECKING] = {type: 'smallicon-orange-ball', text: 'CHECKING'};
-    statusInformation[applicationCache.DOWNLOADING] = {type: 'smallicon-orange-ball', text: 'DOWNLOADING'};
-    statusInformation[applicationCache.UPDATEREADY] = {type: 'smallicon-green-ball', text: 'UPDATEREADY'};
-    statusInformation[applicationCache.OBSOLETE] = {type: 'smallicon-red-ball', text: 'OBSOLETE'};
+    statusInformation[Resources.ApplicationCacheModel.UNCACHED] = {type: 'smallicon-red-ball', text: 'UNCACHED'};
+    statusInformation[Resources.ApplicationCacheModel.IDLE] = {type: 'smallicon-green-ball', text: 'IDLE'};
+    statusInformation[Resources.ApplicationCacheModel.CHECKING] = {type: 'smallicon-orange-ball', text: 'CHECKING'};
+    statusInformation[Resources.ApplicationCacheModel.DOWNLOADING] = {
+      type: 'smallicon-orange-ball',
+      text: 'DOWNLOADING'
+    };
+    statusInformation[Resources.ApplicationCacheModel.UPDATEREADY] = {
+      type: 'smallicon-green-ball',
+      text: 'UPDATEREADY'
+    };
+    statusInformation[Resources.ApplicationCacheModel.OBSOLETE] = {type: 'smallicon-red-ball', text: 'OBSOLETE'};
 
-    const info = statusInformation[status] || statusInformation[applicationCache.UNCACHED];
+    const info = statusInformation[status] || statusInformation[Resources.ApplicationCacheModel.UNCACHED];
 
     this._statusIcon.type = info.type;
     this._statusIcon.textContent = info.text;
 
-    if (this.isShowing() && this._status === applicationCache.IDLE &&
-        (oldStatus === applicationCache.UPDATEREADY || !this._resources))
+    if (this.isShowing() && this._status === Resources.ApplicationCacheModel.IDLE &&
+        (oldStatus === Resources.ApplicationCacheModel.UPDATEREADY || !this._resources))
       this._markDirty();
     this._maybeUpdate();
   }
diff --git a/third_party/blink/renderer/devtools/front_end/resources/ApplicationCacheModel.js b/third_party/blink/renderer/devtools/front_end/resources/ApplicationCacheModel.js
index fafdbda5..41c076e 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/ApplicationCacheModel.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/ApplicationCacheModel.js
@@ -218,3 +218,8 @@
 };
 
 Resources.ApplicationCacheModel.UNCACHED = 0;
+Resources.ApplicationCacheModel.IDLE = 1;
+Resources.ApplicationCacheModel.CHECKING = 2;
+Resources.ApplicationCacheModel.DOWNLOADING = 3;
+Resources.ApplicationCacheModel.UPDATEREADY = 4;
+Resources.ApplicationCacheModel.OBSOLETE = 5;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_media_controls.cc b/third_party/blink/renderer/modules/accessibility/ax_media_controls.cc
index d8d69f7b..bf84406 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_media_controls.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_media_controls.cc
@@ -70,15 +70,11 @@
     case kMediaMuteButton:
     case kMediaPlayButton:
     case kMediaSliderThumb:
-    case kMediaShowClosedCaptionsButton:
-    case kMediaHideClosedCaptionsButton:
     case kMediaTextTrackList:
     case kMediaUnMuteButton:
     case kMediaPauseButton:
     case kMediaTimelineContainer:
     case kMediaTrackSelectionCheckmark:
-    case kMediaVolumeSliderContainer:
-    case kMediaVolumeSliderThumb:
     case kMediaExitFullscreenButton:
     case kMediaCastOffButton:
     case kMediaCastOnButton:
@@ -94,8 +90,8 @@
     case kMediaAnimatedArrowContainer:
       return MakeGarbageCollected<AccessibilityMediaControl>(layout_object,
                                                              ax_object_cache);
-    // Removed as a part of the a11y tree rewrite https://crbug.com/836549.
-    case kMediaVolumeSlider:
+    // Removed as a part of the a11y tree rewrite https://crbug/836549.
+    case kMediaIgnore:
       NOTREACHED();
       return MakeGarbageCollected<AccessibilityMediaControl>(layout_object,
                                                              ax_object_cache);
@@ -148,10 +144,6 @@
       return QueryString(WebLocalizedString::kAXMediaCurrentTimeDisplay);
     case kMediaTimeRemainingDisplay:
       return QueryString(WebLocalizedString::kAXMediaTimeRemainingDisplay);
-    case kMediaShowClosedCaptionsButton:
-      return QueryString(WebLocalizedString::kAXMediaShowClosedCaptionsButton);
-    case kMediaHideClosedCaptionsButton:
-      return QueryString(WebLocalizedString::kAXMediaHideClosedCaptionsButton);
     case kMediaCastOffButton:
     case kMediaOverlayCastOffButton:
       return QueryString(WebLocalizedString::kAXMediaCastOffButton);
@@ -167,8 +159,6 @@
     case kMediaTimelineContainer:
     case kMediaTrackSelectionCheckmark:
     case kMediaControlsPanel:
-    case kMediaVolumeSliderContainer:
-    case kMediaVolumeSliderThumb:
     case kMediaOverflowList:
     case kMediaScrubbingMessage:
     case kMediaAnimatedArrowContainer:
@@ -183,7 +173,8 @@
       return QueryString(
           WebLocalizedString::kAXMediaDisplayCutoutFullscreenButton);
     case kMediaSlider:
-    case kMediaVolumeSlider:
+    // Removed as a part of the a11y tree rewrite https://crbug/836549.
+    case kMediaIgnore:
       NOTREACHED();
       return QueryString(WebLocalizedString::kAXMediaDefault);
   }
@@ -213,8 +204,6 @@
     case kMediaUnMuteButton:
     case kMediaPlayButton:
     case kMediaPauseButton:
-    case kMediaShowClosedCaptionsButton:
-    case kMediaHideClosedCaptionsButton:
     case kMediaCastOffButton:
     case kMediaOverlayCastOffButton:
     case kMediaCastOnButton:
@@ -227,15 +216,14 @@
     case kMediaTimelineContainer:
     case kMediaTrackSelectionCheckmark:
     case kMediaControlsPanel:
-    case kMediaVolumeSliderContainer:
-    case kMediaVolumeSliderThumb:
     case kMediaOverflowList:
     case kMediaDownloadButton:
     case kMediaScrubbingMessage:
     case kMediaAnimatedArrowContainer:
       return QueryString(WebLocalizedString::kAXMediaDefault);
     case kMediaSlider:
-    case kMediaVolumeSlider:
+    // Removed as a part of the a11y tree rewrite https://crbug/836549.
+    case kMediaIgnore:
       NOTREACHED();
       return QueryString(WebLocalizedString::kAXMediaDefault);
   }
@@ -262,8 +250,6 @@
     case kMediaPlayButton:
     case kMediaUnMuteButton:
     case kMediaPauseButton:
-    case kMediaShowClosedCaptionsButton:
-    case kMediaHideClosedCaptionsButton:
     case kMediaOverlayCastOffButton:
     case kMediaOverlayCastOnButton:
     case kMediaOverflowButton:
@@ -276,7 +262,6 @@
       return ax::mojom::Role::kButton;
 
     case kMediaTimelineContainer:
-    case kMediaVolumeSliderContainer:
     case kMediaTextTrackList:
     case kMediaOverflowList:
       return ax::mojom::Role::kGroup;
@@ -286,13 +271,12 @@
     case kMediaTimeRemainingDisplay:
     case kMediaSliderThumb:
     case kMediaTrackSelectionCheckmark:
-    case kMediaVolumeSliderThumb:
     case kMediaScrubbingMessage:
     case kMediaAnimatedArrowContainer:
       return ax::mojom::Role::kUnknown;
 
     case kMediaSlider:
-    case kMediaVolumeSlider:
+    case kMediaIgnore:
       // Not using AccessibilityMediaControl.
       NOTREACHED();
       return ax::mojom::Role::kUnknown;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 8ceeb86..20c241af 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -312,11 +312,10 @@
     return AXList::Create(layout_object, *this);
 
   // media controls
-  // TODO(https://crbug.com/836549): Remove for the rest of the controls.
-  // kMediaVolumeSlider has already been removed.
+  // TODO(836549): Remove for the rest of the controls.
   if (node && node->IsMediaControlElement() &&
       MediaControlElementsHelper::GetMediaControlElementType(node) !=
-          kMediaVolumeSlider) {
+          kMediaIgnore) {
     return AccessibilityMediaControl::Create(layout_object, *this);
   }
 
diff --git a/third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_helpers.cc b/third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_helpers.cc
index fa11d4bf..057682e 100644
--- a/third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_helpers.cc
+++ b/third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_helpers.cc
@@ -23,7 +23,7 @@
   result.premultiplied_alpha = attrs->premultipliedAlpha();
   result.preserve_drawing_buffer = attrs->preserveDrawingBuffer();
   result.stencil = attrs->stencil();
-  result.compatible_xr_device = attrs->compatibleXRDevice();
+  result.xr_compatible = attrs->xrCompatible();
   return result;
 }
 
diff --git a/third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_module.idl b/third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_module.idl
index 98875e9..ad9f867 100644
--- a/third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_module.idl
+++ b/third_party/blink/renderer/modules/canvas/htmlcanvas/canvas_context_creation_attributes_module.idl
@@ -48,5 +48,5 @@
     boolean premultipliedAlpha = true;
     boolean preserveDrawingBuffer = false;
     boolean failIfMajorPerformanceCaveat = false;
-    [OriginTrialEnabled=WebXR] XRDevice compatibleXRDevice = null;
+    [OriginTrialEnabled=WebXR] boolean xrCompatible = false;
 };
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_element_type.h b/third_party/blink/renderer/modules/media_controls/elements/media_control_element_type.h
index 06df6aa..457c40d 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_element_type.h
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_element_type.h
@@ -14,8 +14,6 @@
   kMediaPlayButton,
   kMediaSlider,
   kMediaSliderThumb,
-  kMediaShowClosedCaptionsButton,
-  kMediaHideClosedCaptionsButton,
   kMediaTextTrackList,
   kMediaUnMuteButton,
   kMediaPauseButton,
@@ -24,9 +22,6 @@
   kMediaTimeRemainingDisplay,
   kMediaTrackSelectionCheckmark,
   kMediaControlsPanel,
-  kMediaVolumeSliderContainer,
-  kMediaVolumeSlider,
-  kMediaVolumeSliderThumb,
   kMediaExitFullscreenButton,
   kMediaCastOffButton,
   kMediaCastOnButton,
@@ -40,6 +35,7 @@
   kMediaExitPictureInPictureButton,
   kMediaDisplayCutoutFullscreenButton,
   kMediaAnimatedArrowContainer,
+  kMediaIgnore
 };
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_ELEMENTS_MEDIA_CONTROL_ELEMENT_TYPE_H_
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_input_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_input_element.cc
index 441b1ae..d40803c 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_input_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_input_element.cc
@@ -75,6 +75,8 @@
   if (MediaControlsImpl::IsModern()) {
     overflow_menu_container_ = HTMLDivElement::Create(GetDocument());
     overflow_menu_container_->ParserAppendChild(overflow_menu_text_);
+    // Having the container selectable caused aria output to be duplicated.
+    overflow_menu_container_->setTabIndex(-1);
     UpdateOverflowSubtitleElement(button->GetOverflowMenuSubtitleString());
     element->ParserAppendChild(overflow_menu_container_);
   } else {
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_toggle_closed_captions_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_toggle_closed_captions_button_element.cc
index 8261013..c1c42301 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_toggle_closed_captions_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_toggle_closed_captions_button_element.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/input_type_names.h"
 #include "third_party/blink/renderer/modules/media_controls/media_controls_impl.h"
 #include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/text/platform_locale.h"
 
 namespace blink {
 
@@ -45,7 +46,11 @@
 MediaControlToggleClosedCaptionsButtonElement::
     MediaControlToggleClosedCaptionsButtonElement(
         MediaControlsImpl& media_controls)
-    : MediaControlInputElement(media_controls, kMediaShowClosedCaptionsButton) {
+    : MediaControlInputElement(media_controls, kMediaIgnore) {
+  setAttribute(html_names::kRoleAttr, "button");
+  setAttribute(html_names::kAriaLabelAttr,
+               WTF::AtomicString(GetLocale().QueryString(
+                   WebLocalizedString::kAXMediaShowClosedCaptionsButton)));
   setType(input_type_names::kButton);
   SetShadowPseudoId(
       AtomicString("-webkit-media-controls-toggle-closed-captions-button"));
@@ -59,8 +64,6 @@
 
 void MediaControlToggleClosedCaptionsButtonElement::UpdateDisplayType() {
   bool captions_visible = MediaElement().TextTracksVisible();
-  SetDisplayType(captions_visible ? kMediaHideClosedCaptionsButton
-                                  : kMediaShowClosedCaptionsButton);
   SetClass("visible", captions_visible);
   UpdateOverflowString();
 
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_volume_slider_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_volume_slider_element.cc
index 220580d4..4c23feb 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_volume_slider_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_volume_slider_element.cc
@@ -18,7 +18,7 @@
 
 MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(
     MediaControlsImpl& media_controls)
-    : MediaControlSliderElement(media_controls, kMediaVolumeSlider) {
+    : MediaControlSliderElement(media_controls, kMediaIgnore) {
   setAttribute(html_names::kMaxAttr, "1");
   setAttribute(html_names::kAriaValuemaxAttr, "100");
   setAttribute(html_names::kAriaValueminAttr, "0");
diff --git a/third_party/blink/renderer/modules/webgl/webgl_context_attribute_helpers.cc b/third_party/blink/renderer/modules/webgl/webgl_context_attribute_helpers.cc
index 121b0fd8..f0dd4b4 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_context_attribute_helpers.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_context_attribute_helpers.cc
@@ -20,8 +20,7 @@
   result->setPreserveDrawingBuffer(attrs.preserve_drawing_buffer);
   result->setFailIfMajorPerformanceCaveat(
       attrs.fail_if_major_performance_caveat);
-  result->setCompatibleXRDevice(
-      static_cast<XRDevice*>(attrs.compatible_xr_device.Get()));
+  result->setXrCompatible(attrs.xr_compatible);
   result->setLowLatency(attrs.low_latency);
   return result;
 }
diff --git a/third_party/blink/renderer/modules/webgl/webgl_context_attributes.idl b/third_party/blink/renderer/modules/webgl/webgl_context_attributes.idl
index 39092f2a..725dbfb 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_context_attributes.idl
+++ b/third_party/blink/renderer/modules/webgl/webgl_context_attributes.idl
@@ -34,7 +34,7 @@
     boolean premultipliedAlpha = true;
     boolean preserveDrawingBuffer = false;
     boolean failIfMajorPerformanceCaveat = false;
-    [OriginTrialEnabled=WebXR] XRDevice compatibleXRDevice = null;
+    [OriginTrialEnabled=WebXR] boolean xrCompatible = false;
     // TODO(crbug.com/788439): remove OriginTrialEnabled.
     [OriginTrialEnabled=LowLatencyCanvas] boolean lowLatency = false;
 };
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index fc97003..0ed3994 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -92,7 +92,6 @@
 #include "third_party/blink/renderer/modules/webgl/webgl_uniform_location.h"
 #include "third_party/blink/renderer/modules/webgl/webgl_vertex_array_object.h"
 #include "third_party/blink/renderer/modules/webgl/webgl_vertex_array_object_oes.h"
-#include "third_party/blink/renderer/modules/xr/xr_device.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h"
 #include "third_party/blink/renderer/platform/bindings/v8_binding_macros.h"
@@ -816,38 +815,35 @@
   return resource_provider->Snapshot();
 }
 
-ScriptPromise WebGLRenderingContextBase::setCompatibleXRDevice(
-    ScriptState* script_state,
-    XRDevice* xr_device) {
-
+ScriptPromise WebGLRenderingContextBase::makeXRCompatible(
+    ScriptState* script_state) {
   if (isContextLost()) {
     return ScriptPromise::RejectWithDOMException(
         script_state, DOMException::Create(DOMExceptionCode::kInvalidStateError,
                                            "Context lost."));
   }
 
-  if (xr_device == compatible_xr_device_) {
+  if (xr_compatible_) {
     // Returns a script promise resolved with undefined.
     return ScriptPromise::CastUndefined(script_state);
   }
 
-  if (ContextCreatedOnCompatibleAdapter(xr_device)) {
-    compatible_xr_device_ = xr_device;
+  if (ContextCreatedOnXRCompatibleAdapter()) {
+    xr_compatible_ = true;
     return ScriptPromise::CastUndefined(script_state);
-  } else {
-    // TODO(http://crbug.com/876140) Trigger context loss and recreate on
-    // compatible GPU.
-    return ScriptPromise::RejectWithDOMException(
-        script_state,
-        DOMException::Create(
-            DOMExceptionCode::kNotSupportedError,
-            "Context is not compatible. Switching not yet implemented."));
   }
+
+  // TODO(http://crbug.com/876140) Trigger context loss and recreate on
+  // compatible GPU.
+  return ScriptPromise::RejectWithDOMException(
+      script_state,
+      DOMException::Create(
+          DOMExceptionCode::kNotSupportedError,
+          "Context is not compatible. Switching not yet implemented."));
 }
 
-bool WebGLRenderingContextBase::IsXRDeviceCompatible(
-    const XRDevice* xr_device) {
-  return xr_device == compatible_xr_device_;
+bool WebGLRenderingContextBase::IsXRCompatible() {
+  return xr_compatible_;
 }
 
 namespace {
@@ -1022,8 +1018,7 @@
 
   // TODO(http://crbug.com/876140) Make sure this is being created on a
   // compatible adapter.
-  compatible_xr_device_ =
-      static_cast<XRDevice*>(requested_attributes.compatible_xr_device.Get());
+  xr_compatible_ = requested_attributes.xr_compatible;
 
   context_group_->AddContext(this);
 
@@ -1576,8 +1571,7 @@
   return true;
 }
 
-bool WebGLRenderingContextBase::ContextCreatedOnCompatibleAdapter(
-    const XRDevice* device) {
+bool WebGLRenderingContextBase::ContextCreatedOnXRCompatibleAdapter() {
   // TODO(http://crbug.com/876140) Determine if device is compatible with
   // current context.
   return true;
@@ -2906,9 +2900,7 @@
   if (CreationAttributes().stencil && !GetDrawingBuffer()->HasStencilBuffer())
     result->setStencil(false);
   result->setAntialias(GetDrawingBuffer()->Multisample());
-  if (compatible_xr_device_) {
-    result->setCompatibleXRDevice(compatible_xr_device_);
-  }
+  result->setXrCompatible(xr_compatible_);
   return result;
 }
 
@@ -7998,7 +7990,6 @@
   visitor->Trace(current_program_);
   visitor->Trace(framebuffer_binding_);
   visitor->Trace(renderbuffer_binding_);
-  visitor->Trace(compatible_xr_device_);
   visitor->Trace(texture_units_);
   visitor->Trace(extensions_);
   CanvasRenderingContext::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
index 6c2b734d..7b6d15a7 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
@@ -46,7 +46,6 @@
 #include "third_party/blink/renderer/modules/webgl/webgl_extension_name.h"
 #include "third_party/blink/renderer/modules/webgl/webgl_texture.h"
 #include "third_party/blink/renderer/modules/webgl/webgl_vertex_array_object_base.h"
-#include "third_party/blink/renderer/modules/xr/xr_device.h"
 #include "third_party/blink/renderer/platform/bindings/name_client.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
@@ -614,8 +613,8 @@
   scoped_refptr<StaticBitmapImage> GetStaticBitmapImage(
       std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback);
 
-  ScriptPromise setCompatibleXRDevice(ScriptState*, XRDevice*);
-  bool IsXRDeviceCompatible(const XRDevice*);
+  ScriptPromise makeXRCompatible(ScriptState*);
+  bool IsXRCompatible();
 
  protected:
   friend class EXTDisjointTimerQuery;
@@ -760,7 +759,7 @@
   TraceWrapperMember<WebGLFramebuffer> framebuffer_binding_;
   TraceWrapperMember<WebGLRenderbuffer> renderbuffer_binding_;
 
-  Member<XRDevice> compatible_xr_device_;
+  bool xr_compatible_;
 
   HeapVector<TextureUnitState> texture_units_;
   wtf_size_t active_texture_unit_;
@@ -1714,9 +1713,9 @@
 
   bool IsPaintable() const final { return GetDrawingBuffer(); }
 
-  // Returns true if the context is compatible with the given device as defined
+  // Returns true if the context is compatible with the XR device as defined
   // by https://immersive-web.github.io/webxr/spec/latest/#contextcompatibility
-  bool ContextCreatedOnCompatibleAdapter(const XRDevice*);
+  bool ContextCreatedOnXRCompatibleAdapter();
 
   bool CopyRenderingResultsFromDrawingBuffer(CanvasResourceProvider*,
                                              SourceDrawingBuffer) const;
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.idl b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.idl
index 46b48b4..d3a2f114 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.idl
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.idl
@@ -714,5 +714,5 @@
     [RuntimeEnabled=OffscreenCanvasCommit] void commit();
 
     // WebXR Device API support
-    [OriginTrialEnabled=WebXR, SecureContext, CallWith=ScriptState] Promise<void> setCompatibleXRDevice(XRDevice device);
+    [OriginTrialEnabled=WebXR, SecureContext, CallWith=ScriptState] Promise<void> makeXRCompatible();
 };
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
index b8b36a3..a700e904 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
@@ -61,10 +61,10 @@
     return nullptr;
   }
 
-  if (!webgl_context->IsXRDeviceCompatible(session->device())) {
+  if (!webgl_context->IsXRCompatible()) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidStateError,
-        "The session's device is not the compatible device for this context.");
+        "This context is not marked as XR compatible.");
     return nullptr;
   }
 
diff --git a/third_party/blink/renderer/platform/graphics/placeholder_image.cc b/third_party/blink/renderer/platform/graphics/placeholder_image.cc
index 2d9790e5..be09c5f 100644
--- a/third_party/blink/renderer/platform/graphics/placeholder_image.cc
+++ b/third_party/blink/renderer/platform/graphics/placeholder_image.cc
@@ -50,30 +50,36 @@
 void DrawIcon(cc::PaintCanvas* canvas,
               const PaintFlags& flags,
               float x,
-              float y) {
+              float y,
+              float scale_factor) {
+  // Note that |icon_image| will be a 0x0 image when running
+  // blink_platform_unittests.
   DEFINE_STATIC_REF(Image, icon_image,
                     (Image::LoadPlatformResource("placeholderIcon")));
-  DCHECK(!icon_image->IsNull());
 
   // Note that the |icon_image| is not scaled according to dest_rect / src_rect,
   // and is always drawn at the same size. This is so that placeholder icons are
   // visible (e.g. when replacing a large image that's scaled down to a small
   // area) and so that all placeholder images on the same page look consistent.
-  canvas->drawImageRect(icon_image->PaintImageForCurrentFrame(),
-                        IntRect(IntPoint::Zero(), icon_image->Size()),
-                        FloatRect(x, y, kIconWidth, kIconHeight), &flags,
-                        cc::PaintCanvas::kFast_SrcRectConstraint);
+  canvas->drawImageRect(
+      icon_image->PaintImageForCurrentFrame(),
+      IntRect(IntPoint::Zero(), icon_image->Size()),
+      FloatRect(x, y, scale_factor * kIconWidth, scale_factor * kIconHeight),
+      &flags, cc::PaintCanvas::kFast_SrcRectConstraint);
 }
 
 void DrawCenteredIcon(cc::PaintCanvas* canvas,
                       const PaintFlags& flags,
-                      const FloatRect& dest_rect) {
-  DrawIcon(canvas, flags,
-           dest_rect.X() + (dest_rect.Width() - kIconWidth) / 2.0f,
-           dest_rect.Y() + (dest_rect.Height() - kIconHeight) / 2.0f);
+                      const FloatRect& dest_rect,
+                      float scale_factor) {
+  DrawIcon(
+      canvas, flags,
+      dest_rect.X() + (dest_rect.Width() - scale_factor * kIconWidth) / 2.0f,
+      dest_rect.Y() + (dest_rect.Height() - scale_factor * kIconHeight) / 2.0f,
+      scale_factor);
 }
 
-FontDescription CreatePlaceholderFontDescription() {
+FontDescription CreatePlaceholderFontDescription(float scale_factor) {
   FontDescription description;
   description.FirstFamily().SetFamily("Roboto");
 
@@ -89,7 +95,7 @@
   description.FirstFamily().AppendFamily(std::move(helvetica_neue));
 
   description.SetGenericFamily(FontDescription::kSansSerifFamily);
-  description.SetComputedSize(kFontSize);
+  description.SetComputedSize(scale_factor * kFontSize);
   description.SetWeight(FontSelectionValue(500));
 
   return description;
@@ -153,29 +159,47 @@
 // can share the same Font.
 class PlaceholderImage::SharedFont : public RefCounted<SharedFont> {
  public:
-  static scoped_refptr<SharedFont> GetOrCreateInstance() {
-    if (g_instance_)
-      return scoped_refptr<SharedFont>(g_instance_);
+  static scoped_refptr<SharedFont> GetOrCreateInstance(float scale_factor) {
+    if (g_instance_) {
+      scoped_refptr<SharedFont> shared_font(g_instance_);
+      shared_font->MaybeUpdateForScaleFactor(scale_factor);
+      return shared_font;
+    }
 
-    scoped_refptr<SharedFont> shared_font(base::AdoptRef(new SharedFont()));
+    scoped_refptr<SharedFont> shared_font =
+        base::MakeRefCounted<SharedFont>(scale_factor);
     g_instance_ = shared_font.get();
     return shared_font;
   }
 
+  // This constructor is public so that base::MakeRefCounted() can call it.
+  explicit SharedFont(float scale_factor)
+      : font_(CreatePlaceholderFontDescription(scale_factor)),
+        scale_factor_(scale_factor) {
+    font_.Update(nullptr);
+  }
+
   ~SharedFont() {
     DCHECK_EQ(this, g_instance_);
     g_instance_ = nullptr;
   }
 
-  const Font& font() const { return font_; }
+  void MaybeUpdateForScaleFactor(float scale_factor) {
+    if (scale_factor_ == scale_factor)
+      return;
 
- private:
-  SharedFont() : font_(CreatePlaceholderFontDescription()) {
+    scale_factor_ = scale_factor;
+    font_ = Font(CreatePlaceholderFontDescription(scale_factor_));
     font_.Update(nullptr);
   }
 
+  const Font& font() const { return font_; }
+
+ private:
   static SharedFont* g_instance_;
+
   Font font_;
+  float scale_factor_;
 };
 
 // static
@@ -238,6 +262,15 @@
       .TakePaintImage();
 }
 
+void PlaceholderImage::SetIconAndTextScaleFactor(
+    float icon_and_text_scale_factor) {
+  if (icon_and_text_scale_factor_ == icon_and_text_scale_factor)
+    return;
+  icon_and_text_scale_factor_ = icon_and_text_scale_factor;
+  cached_text_width_.reset();
+  paint_record_for_current_frame_.reset();
+}
+
 void PlaceholderImage::Draw(cc::PaintCanvas* canvas,
                             const PaintFlags& base_flags,
                             const FloatRect& dest_rect,
@@ -260,27 +293,35 @@
   flags.setColor(SkColorSetARGB(0x80, 0xD9, 0xD9, 0xD9));
   canvas->drawRect(dest_rect, flags);
 
-  if (dest_rect.Width() < kIconWidth + 2 * kFeaturePaddingX ||
-      dest_rect.Height() < kIconHeight + 2 * kIconPaddingY) {
+  if (dest_rect.Width() <
+          icon_and_text_scale_factor_ * (kIconWidth + 2 * kFeaturePaddingX) ||
+      dest_rect.Height() <
+          icon_and_text_scale_factor_ * (kIconHeight + 2 * kIconPaddingY)) {
     return;
   }
 
   if (text_.IsEmpty()) {
-    DrawCenteredIcon(canvas, base_flags, dest_rect);
+    DrawCenteredIcon(canvas, base_flags, dest_rect,
+                     icon_and_text_scale_factor_);
     return;
   }
 
   if (!shared_font_)
-    shared_font_ = SharedFont::GetOrCreateInstance();
+    shared_font_ = SharedFont::GetOrCreateInstance(icon_and_text_scale_factor_);
+  else
+    shared_font_->MaybeUpdateForScaleFactor(icon_and_text_scale_factor_);
+
   if (!cached_text_width_.has_value())
     cached_text_width_ = shared_font_->font().Width(TextRun(text_));
 
   const float icon_and_text_width =
       cached_text_width_.value() +
-      (kIconWidth + 2 * kFeaturePaddingX + kPaddingBetweenIconAndText);
+      icon_and_text_scale_factor_ *
+          (kIconWidth + 2 * kFeaturePaddingX + kPaddingBetweenIconAndText);
 
   if (dest_rect.Width() < icon_and_text_width) {
-    DrawCenteredIcon(canvas, base_flags, dest_rect);
+    DrawCenteredIcon(canvas, base_flags, dest_rect,
+                     icon_and_text_scale_factor_);
     return;
   }
 
@@ -288,25 +329,32 @@
       dest_rect.X() + (dest_rect.Width() - icon_and_text_width) / 2.0f;
   const float feature_y =
       dest_rect.Y() +
-      (dest_rect.Height() - (kIconHeight + 2 * kIconPaddingY)) / 2.0f;
+      (dest_rect.Height() -
+       icon_and_text_scale_factor_ * (kIconHeight + 2 * kIconPaddingY)) /
+          2.0f;
 
   float icon_x, text_x;
   if (Locale::DefaultLocale().IsRTL()) {
     icon_x = feature_x + cached_text_width_.value() +
-             (kFeaturePaddingX + kPaddingBetweenIconAndText);
-    text_x = feature_x + kFeaturePaddingX;
+             icon_and_text_scale_factor_ *
+                 (kFeaturePaddingX + kPaddingBetweenIconAndText);
+    text_x = feature_x + icon_and_text_scale_factor_ * kFeaturePaddingX;
   } else {
-    icon_x = feature_x + kFeaturePaddingX;
+    icon_x = feature_x + icon_and_text_scale_factor_ * kFeaturePaddingX;
     text_x = feature_x +
-             (kFeaturePaddingX + kIconWidth + kPaddingBetweenIconAndText);
+             icon_and_text_scale_factor_ *
+                 (kFeaturePaddingX + kIconWidth + kPaddingBetweenIconAndText);
   }
 
-  DrawIcon(canvas, base_flags, icon_x, feature_y + kIconPaddingY);
+  DrawIcon(canvas, base_flags, icon_x,
+           feature_y + icon_and_text_scale_factor_ * kIconPaddingY,
+           icon_and_text_scale_factor_);
 
   flags.setColor(SkColorSetARGB(0xAB, 0, 0, 0));
   shared_font_->font().DrawBidiText(
       canvas, TextRunPaintInfo(TextRun(text_)),
-      FloatPoint(text_x, feature_y + (kTextPaddingY + kFontSize)),
+      FloatPoint(text_x, feature_y + icon_and_text_scale_factor_ *
+                                         (kTextPaddingY + kFontSize)),
       Font::kUseFallbackIfFontNotReady, 1.0f, flags);
 }
 
@@ -340,4 +388,8 @@
   return Image::kSizeAvailable;
 }
 
+const Font* PlaceholderImage::GetFontForTesting() const {
+  return shared_font_ ? &shared_font_->font() : nullptr;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/placeholder_image.h b/third_party/blink/renderer/platform/graphics/placeholder_image.h
index e869f99..e6a2647 100644
--- a/third_party/blink/renderer/platform/graphics/placeholder_image.h
+++ b/third_party/blink/renderer/platform/graphics/placeholder_image.h
@@ -21,6 +21,7 @@
 class FloatPoint;
 class FloatRect;
 class FloatSize;
+class Font;
 class GraphicsContext;
 class ImageObserver;
 
@@ -62,6 +63,9 @@
   bool IsPlaceholderImage() const override;
 
   const String& GetTextForTesting() const { return text_; }
+  const Font* GetFontForTesting() const;
+
+  void SetIconAndTextScaleFactor(float icon_and_text_scale_factor);
 
  private:
   PlaceholderImage(ImageObserver*,
@@ -90,6 +94,8 @@
   // This placeholder image is used for lazyloading of images.
   bool is_lazy_image_;
 
+  float icon_and_text_scale_factor_ = 1.0f;
+
   class SharedFont;
   // Lazily initialized. All instances of PlaceholderImage will share the same
   // Font object, wrapped as a SharedFont.
diff --git a/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc b/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc
index 5df5aca..b3367ad 100644
--- a/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc
+++ b/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc
@@ -6,16 +6,204 @@
 
 #include <stdint.h>
 
+#include "base/memory/scoped_refptr.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/web_localized_string.h"
 #include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/geometry/float_rect.h"
+#include "third_party/blink/renderer/platform/geometry/int_rect.h"
 #include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/graphics/image_orientation.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+#include "third_party/blink/renderer/platform/graphics/test/mock_paint_canvas.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/text/platform_locale.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkRect.h"
 
 namespace blink {
 namespace {
 
+using testing::_;
+using testing::AllOf;
+using testing::FloatNear;
+using testing::InvokeWithoutArgs;
+using testing::Property;
+
+constexpr float kBaseIconWidth = 24.0f;
+constexpr float kBaseIconHeight = 24.0f;
+constexpr float kBaseFeaturePaddingX = 8.0f;
+constexpr float kBaseIconPaddingY = 5.0f;
+
+constexpr float kBasePaddingBetweenIconAndText = 2.0f;
+constexpr float kBaseTextPaddingY = 9.0f;
+constexpr float kBaseFontSize = 14.0f;
+
+constexpr float kBaseIconOnlyFeatureWidth =
+    kBaseFeaturePaddingX + kBaseIconWidth + kBaseFeaturePaddingX;
+constexpr float kBaseFeatureHeight =
+    kBaseIconPaddingY + kBaseIconHeight + kBaseIconPaddingY;
+
+void ExpectDrawGrayBox(MockPaintCanvas& canvas,
+                       const FloatRect& expected_rect) {
+  EXPECT_CALL(
+      canvas,
+      drawRect(AllOf(Property(&SkRect::x, FloatNear(expected_rect.X(), 0.01)),
+                     Property(&SkRect::y, FloatNear(expected_rect.Y(), 0.01)),
+                     Property(&SkRect::width,
+                              FloatNear(expected_rect.Width(), 0.01)),
+                     Property(&SkRect::height,
+                              FloatNear(expected_rect.Height(), 0.01))),
+               AllOf(Property(&PaintFlags::getStyle, PaintFlags::kFill_Style),
+                     Property(&PaintFlags::getColor,
+                              SkColorSetARGB(0x80, 0xD9, 0xD9, 0xD9)))))
+      .Times(1);
+}
+
+void DrawImageExpectingGrayBoxOnly(PlaceholderImage& image,
+                                   const FloatRect& dest_rect) {
+  MockPaintCanvas canvas;
+  ExpectDrawGrayBox(canvas, dest_rect);
+  EXPECT_CALL(canvas, drawImageRect(_, _, _, _, _)).Times(0);
+  EXPECT_CALL(canvas, drawTextBlob(_, _, _, _)).Times(0);
+
+  image.Draw(&canvas, PaintFlags(), dest_rect,
+             FloatRect(0.0f, 0.0f, 100.0f, 100.0f),
+             kDoNotRespectImageOrientation, Image::kClampImageToSourceRect,
+             Image::kUnspecifiedDecode);
+}
+
+void DrawImageExpectingIconOnly(PlaceholderImage& image,
+                                const FloatRect& dest_rect,
+                                float scale_factor) {
+  MockPaintCanvas canvas;
+  ExpectDrawGrayBox(canvas, dest_rect);
+
+  EXPECT_CALL(
+      canvas,
+      drawImageRect(
+          /*image=*/_, /*src=*/_, /*dst=*/
+          AllOf(Property(&SkRect::x,
+                         FloatNear(dest_rect.Center().X() -
+                                       scale_factor * kBaseIconWidth / 2.0f,
+                                   0.01)),
+                Property(&SkRect::y,
+                         FloatNear(dest_rect.Center().Y() -
+                                       scale_factor * kBaseIconHeight / 2.0f,
+                                   0.01)),
+                Property(&SkRect::width,
+                         FloatNear(scale_factor * kBaseIconWidth, 0.01)),
+                Property(&SkRect::height,
+                         FloatNear(scale_factor * kBaseIconHeight, 0.01))),
+          /*flags=*/_, /*constraint=*/_))
+      .Times(1);
+
+  EXPECT_CALL(canvas, drawTextBlob(_, _, _, _)).Times(0);
+
+  image.Draw(&canvas, PaintFlags(), dest_rect,
+             FloatRect(0.0f, 0.0f, 100.0f, 100.0f),
+             kDoNotRespectImageOrientation, Image::kClampImageToSourceRect,
+             Image::kUnspecifiedDecode);
+}
+
+float GetExpectedPlaceholderTextWidth(const StringView& text,
+                                      float scale_factor) {
+  FontDescription description;
+  description.FirstFamily().SetFamily("Roboto");
+
+  scoped_refptr<SharedFontFamily> helvetica_neue = SharedFontFamily::Create();
+  helvetica_neue->SetFamily("Helvetica Neue");
+  scoped_refptr<SharedFontFamily> helvetica = SharedFontFamily::Create();
+  helvetica->SetFamily("Helvetica");
+  scoped_refptr<SharedFontFamily> arial = SharedFontFamily::Create();
+  arial->SetFamily("Arial");
+
+  helvetica->AppendFamily(std::move(arial));
+  helvetica_neue->AppendFamily(std::move(helvetica));
+  description.FirstFamily().AppendFamily(std::move(helvetica_neue));
+
+  description.SetGenericFamily(FontDescription::kSansSerifFamily);
+  description.SetComputedSize(scale_factor * 14.0f);
+  description.SetWeight(FontSelectionValue(500));
+
+  Font font(description);
+  font.Update(nullptr);
+  return font.Width(TextRun(text));
+}
+
+void DrawImageExpectingIconAndTextLTR(PlaceholderImage& image,
+                                      const FloatRect& dest_rect,
+                                      float scale_factor) {
+  EXPECT_FALSE(Locale::DefaultLocale().IsRTL());
+
+  MockPaintCanvas canvas;
+  ExpectDrawGrayBox(canvas, dest_rect);
+
+  const float expected_text_width =
+      GetExpectedPlaceholderTextWidth(image.GetTextForTesting(), scale_factor);
+  const float expected_feature_width =
+      scale_factor *
+          (kBaseIconOnlyFeatureWidth + kBasePaddingBetweenIconAndText) +
+      expected_text_width;
+  const float expected_feature_x =
+      dest_rect.Center().X() - expected_feature_width / 2.0f;
+  const float expected_feature_y =
+      dest_rect.Center().Y() - scale_factor * kBaseFeatureHeight / 2.0f;
+
+  EXPECT_CALL(
+      canvas,
+      drawImageRect(
+          /*image=*/_, /*src=*/_, /*dst=*/
+          AllOf(Property(&SkRect::x,
+                         FloatNear(expected_feature_x +
+                                       scale_factor * kBaseFeaturePaddingX,
+                                   0.01)),
+                Property(&SkRect::y,
+                         FloatNear(expected_feature_y +
+                                       scale_factor * kBaseIconPaddingY,
+                                   0.01)),
+                Property(&SkRect::width,
+                         FloatNear(scale_factor * kBaseIconWidth, 0.01)),
+                Property(&SkRect::height,
+                         FloatNear(scale_factor * kBaseIconHeight, 0.01))),
+          /*flags=*/_,
+          /*constraint=*/_))
+      .Times(1);
+
+  EXPECT_CALL(
+      canvas,
+      drawTextBlob(
+          _,
+          FloatNear(expected_feature_x +
+                        scale_factor * (kBaseFeaturePaddingX + kBaseIconWidth +
+                                        kBasePaddingBetweenIconAndText),
+                    0.01),
+          FloatNear(expected_feature_y +
+                        scale_factor * (kBaseTextPaddingY + kBaseFontSize),
+                    0.01),
+          AllOf(
+              Property(&PaintFlags::getStyle, PaintFlags::kFill_Style),
+              Property(&PaintFlags::getColor, SkColorSetARGB(0xAB, 0, 0, 0)))))
+      .WillOnce(InvokeWithoutArgs([&image, scale_factor]() {
+        EXPECT_NEAR(
+            scale_factor * kBaseFontSize,
+            image.GetFontForTesting()->GetFontDescription().ComputedSize(),
+            0.01);
+      }));
+
+  image.Draw(&canvas, PaintFlags(), dest_rect,
+             FloatRect(0.0f, 0.0f, 100.0f, 100.0f),
+             kDoNotRespectImageOrientation, Image::kClampImageToSourceRect,
+             Image::kUnspecifiedDecode);
+}
+
 class TestingUnitsPlatform : public TestingPlatformSupport {
  public:
   TestingUnitsPlatform() {}
@@ -44,8 +232,19 @@
 TestingUnitsPlatform::~TestingUnitsPlatform() = default;
 
 class PlaceholderImageTest : public testing::Test {
+ public:
+  void SetUp() override {
+    old_user_preferred_languages_ = UserPreferredLanguages();
+    OverrideUserPreferredLanguagesForTesting(Vector<AtomicString>(1U, "en-US"));
+  }
+
+  void TearDown() override {
+    OverrideUserPreferredLanguagesForTesting(old_user_preferred_languages_);
+  }
+
  private:
   ScopedTestingPlatformSupport<TestingUnitsPlatform> platform_;
+  Vector<AtomicString> old_user_preferred_languages_;
 };
 
 TEST_F(PlaceholderImageTest, FormatPlaceholderText) {
@@ -84,6 +283,214 @@
   }
 }
 
+TEST_F(PlaceholderImageTest, DrawLazyImage) {
+  MockPaintCanvas canvas;
+  EXPECT_CALL(canvas, drawRect(_, _)).Times(0);
+  EXPECT_CALL(canvas, drawImageRect(_, _, _, _, _)).Times(0);
+  EXPECT_CALL(canvas, drawTextBlob(_, _, _, _)).Times(0);
+
+  PlaceholderImage::CreateForLazyImages(nullptr, IntSize(800, 600))
+      ->Draw(&canvas, PaintFlags(), FloatRect(0.0f, 0.0f, 800.0f, 600.0f),
+             FloatRect(0.0f, 0.0f, 800.0f, 600.0f),
+             kDoNotRespectImageOrientation, Image::kClampImageToSourceRect,
+             Image::kUnspecifiedDecode);
+}
+
+TEST_F(PlaceholderImageTest, DrawNonIntersectingSrcRect) {
+  MockPaintCanvas canvas;
+  EXPECT_CALL(canvas, drawRect(_, _)).Times(0);
+  EXPECT_CALL(canvas, drawImageRect(_, _, _, _, _)).Times(0);
+  EXPECT_CALL(canvas, drawTextBlob(_, _, _, _)).Times(0);
+
+  PlaceholderImage::Create(nullptr, IntSize(800, 600), 0)
+      ->Draw(&canvas, PaintFlags(), FloatRect(0.0f, 0.0f, 800.0f, 600.0f),
+             // The source rectangle is outside the 800x600 bounds of the image,
+             // so nothing should be drawn.
+             FloatRect(1000.0f, 0.0f, 800.0f, 600.0f),
+             kDoNotRespectImageOrientation, Image::kClampImageToSourceRect,
+             Image::kUnspecifiedDecode);
+}
+
+TEST_F(PlaceholderImageTest, DrawWithoutOriginalResourceSize) {
+  scoped_refptr<PlaceholderImage> image =
+      PlaceholderImage::Create(nullptr, IntSize(800, 600), 0);
+
+  constexpr float kTestScaleFactors[] = {0.5f, 1.0f, 2.0f};
+  for (const float scale_factor : kTestScaleFactors) {
+    image->SetIconAndTextScaleFactor(scale_factor);
+
+    DrawImageExpectingGrayBoxOnly(
+        *image, FloatRect(1000.0f, 2000.0f,
+                          scale_factor * kBaseIconOnlyFeatureWidth - 1.0f,
+                          scale_factor * kBaseFeatureHeight + 1.0f));
+    DrawImageExpectingGrayBoxOnly(
+        *image, FloatRect(1000.0f, 2000.0f,
+                          scale_factor * kBaseIconOnlyFeatureWidth + 1.0f,
+                          scale_factor * kBaseFeatureHeight - 1.0f));
+
+    DrawImageExpectingIconOnly(
+        *image,
+        FloatRect(1000.0f, 2000.0f,
+                  scale_factor * kBaseIconOnlyFeatureWidth + 1.0f,
+                  scale_factor * kBaseFeatureHeight + 1.0f),
+        scale_factor);
+    DrawImageExpectingIconOnly(
+        *image, FloatRect(1000.0f, 2000.0f, 800.0f, 600.0f), scale_factor);
+  }
+}
+
+TEST_F(PlaceholderImageTest, DrawWithOriginalResourceSizeLTR) {
+  scoped_refptr<PlaceholderImage> image =
+      PlaceholderImage::Create(nullptr, IntSize(800, 600), 50 * 1024);
+
+  String expected_text = "50 KB";
+  expected_text.Ensure16Bit();
+  EXPECT_EQ(expected_text, image->GetTextForTesting());
+
+  constexpr float kTestScaleFactors[] = {0.5f, 1.0f, 2.0f};
+  for (const float scale_factor : kTestScaleFactors) {
+    image->SetIconAndTextScaleFactor(scale_factor);
+
+    DrawImageExpectingGrayBoxOnly(
+        *image, FloatRect(1000.0f, 2000.0f,
+                          scale_factor * kBaseIconOnlyFeatureWidth - 1.0f,
+                          scale_factor * kBaseFeatureHeight + 1.0f));
+    DrawImageExpectingGrayBoxOnly(
+        *image, FloatRect(1000.0f, 2000.0f,
+                          scale_factor * kBaseIconOnlyFeatureWidth + 1.0f,
+                          scale_factor * kBaseFeatureHeight - 1.0f));
+    DrawImageExpectingGrayBoxOnly(
+        *image, FloatRect(1000.0f, 2000.0f, 800.0f,
+                          scale_factor * kBaseFeatureHeight - 1.0f));
+
+    const float expected_text_width = GetExpectedPlaceholderTextWidth(
+        image->GetTextForTesting(), scale_factor);
+    const float expected_icon_and_text_width =
+        scale_factor *
+            (kBaseIconOnlyFeatureWidth + kBasePaddingBetweenIconAndText) +
+        expected_text_width;
+
+    DrawImageExpectingIconOnly(
+        *image,
+        FloatRect(1000.0f, 2000.0f,
+                  scale_factor * kBaseIconOnlyFeatureWidth + 1.0f,
+                  scale_factor * kBaseFeatureHeight + 1.0f),
+        scale_factor);
+    DrawImageExpectingIconOnly(
+        *image,
+        FloatRect(1000.0f, 2000.0f, expected_icon_and_text_width - 1.0f,
+                  scale_factor * kBaseFeatureHeight + 1.0f),
+        scale_factor);
+
+    DrawImageExpectingIconAndTextLTR(
+        *image,
+        FloatRect(1000.0f, 2000.0f, expected_icon_and_text_width + 1.0f,
+                  scale_factor * kBaseFeatureHeight + 1.0f),
+        scale_factor);
+    DrawImageExpectingIconAndTextLTR(
+        *image, FloatRect(1000.0f, 2000.0f, 800.0f, 600.0f), scale_factor);
+  }
+}
+
+TEST_F(PlaceholderImageTest, DrawWithOriginalResourceSizeRTL) {
+  scoped_refptr<PlaceholderImage> image =
+      PlaceholderImage::Create(nullptr, IntSize(800, 600), 50 * 1024);
+
+  String expected_text = "50 KB";
+  expected_text.Ensure16Bit();
+  EXPECT_EQ(expected_text, image->GetTextForTesting());
+
+  OverrideUserPreferredLanguagesForTesting(Vector<AtomicString>(1U, "ar"));
+  EXPECT_TRUE(Locale::DefaultLocale().IsRTL());
+
+  static constexpr float kScaleFactor = 2.0f;
+  image->SetIconAndTextScaleFactor(kScaleFactor);
+
+  const FloatRect dest_rect(1000.0f, 2000.0f, 800.0f, 600.0f);
+
+  MockPaintCanvas canvas;
+  ExpectDrawGrayBox(canvas, dest_rect);
+
+  const float expected_text_width =
+      GetExpectedPlaceholderTextWidth(image->GetTextForTesting(), kScaleFactor);
+  const float expected_feature_width =
+      kScaleFactor *
+          (kBaseIconOnlyFeatureWidth + kBasePaddingBetweenIconAndText) +
+      expected_text_width;
+  const float expected_feature_x =
+      dest_rect.Center().X() - expected_feature_width / 2.0f;
+  const float expected_feature_y =
+      dest_rect.Center().Y() - kScaleFactor * kBaseFeatureHeight / 2.0f;
+
+  EXPECT_CALL(
+      canvas,
+      drawImageRect(
+          /*image=*/_, /*src=*/_, /*dst=*/
+          AllOf(Property(&SkRect::x,
+                         FloatNear(expected_feature_x +
+                                       kScaleFactor *
+                                           (kBaseFeaturePaddingX +
+                                            kBasePaddingBetweenIconAndText) +
+                                       expected_text_width,
+                                   0.01)),
+                Property(&SkRect::y,
+                         FloatNear(expected_feature_y +
+                                       kScaleFactor * kBaseIconPaddingY,
+                                   0.01)),
+                Property(&SkRect::width,
+                         FloatNear(kScaleFactor * kBaseIconWidth, 0.01)),
+                Property(&SkRect::height,
+                         FloatNear(kScaleFactor * kBaseIconHeight, 0.01))),
+          /*flags=*/_,
+          /*constraint=*/_))
+      .Times(1);
+
+  EXPECT_CALL(
+      canvas,
+      drawTextBlob(
+          _,
+          FloatNear(expected_feature_x + kScaleFactor * kBaseFeaturePaddingX,
+                    0.01),
+          FloatNear(expected_feature_y +
+                        kScaleFactor * (kBaseTextPaddingY + kBaseFontSize),
+                    0.01),
+          AllOf(
+              Property(&PaintFlags::getStyle, PaintFlags::kFill_Style),
+              Property(&PaintFlags::getColor, SkColorSetARGB(0xAB, 0, 0, 0)))))
+      .WillOnce(InvokeWithoutArgs([image]() {
+        EXPECT_NEAR(
+            kScaleFactor * kBaseFontSize,
+            image->GetFontForTesting()->GetFontDescription().ComputedSize(),
+            0.01);
+      }));
+
+  image->Draw(&canvas, PaintFlags(), dest_rect,
+              FloatRect(0.0f, 0.0f, 100.0f, 100.0f),
+              kDoNotRespectImageOrientation, Image::kClampImageToSourceRect,
+              Image::kUnspecifiedDecode);
+}
+
+TEST_F(PlaceholderImageTest, DrawSeparateImageWithDifferentScaleFactor) {
+  scoped_refptr<PlaceholderImage> image_1 =
+      PlaceholderImage::Create(nullptr, IntSize(800, 600), 50 * 1024);
+  constexpr float kScaleFactor1 = 0.5f;
+  image_1->SetIconAndTextScaleFactor(kScaleFactor1);
+
+  DrawImageExpectingIconAndTextLTR(
+      *image_1, FloatRect(1000.0f, 2000.0f, 800.0f, 600.0f), kScaleFactor1);
+
+  scoped_refptr<PlaceholderImage> image_2 =
+      PlaceholderImage::Create(nullptr, IntSize(800, 600), 100 * 1024);
+  constexpr float kScaleFactor2 = 2.0f;
+  image_2->SetIconAndTextScaleFactor(kScaleFactor2);
+
+  DrawImageExpectingIconAndTextLTR(
+      *image_2, FloatRect(1000.0f, 2000.0f, 800.0f, 600.0f), kScaleFactor2);
+
+  DrawImageExpectingIconAndTextLTR(
+      *image_1, FloatRect(1000.0f, 2000.0f, 1600.0f, 1200.0f), kScaleFactor1);
+}
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index 7b6de04f..14fee98b 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -1043,14 +1043,6 @@
           ? resource->GetResourceRequest().NavigationStartTime()
           : CurrentTimeTicks();
 
-  // This buffer is created and populated for providing transferSize
-  // and redirect timing opt-in information.
-  if (is_main_resource) {
-    DCHECK(!navigation_timing_info_);
-    navigation_timing_info_ = ResourceTimingInfo::Create(
-        fetch_initiator, start_time, is_main_resource);
-  }
-
   scoped_refptr<ResourceTimingInfo> info =
       ResourceTimingInfo::Create(fetch_initiator, start_time, is_main_resource);
 
@@ -1075,11 +1067,6 @@
   if (it != resource_timing_info_map_.end()) {
     it->value->AddRedirect(redirect_response, cross_origin);
   }
-
-  if (resource->GetType() == ResourceType::kMainResource) {
-    DCHECK(navigation_timing_info_);
-    navigation_timing_info_->AddRedirect(redirect_response, cross_origin);
-  }
 }
 
 static bool IsDownloadOrStreamRequest(const ResourceRequest& request) {
@@ -1546,32 +1533,30 @@
   return urls;
 }
 
-ArchiveResource* ResourceFetcher::CreateArchive(Resource* resource) {
+ArchiveResource* ResourceFetcher::CreateArchive(
+    const KURL& url,
+    scoped_refptr<const SharedBuffer> buffer) {
   // Only the top-frame can load MHTML.
   if (!Context().IsMainFrame()) {
     console_logger_->AddErrorMessage(
         ConsoleLogger::Source::kScript,
         "Attempted to load a multipart archive into an subframe: " +
-            resource->Url().GetString());
+            url.GetString());
     return nullptr;
   }
 
-  archive_ = MHTMLArchive::Create(resource->Url(), resource->ResourceBuffer());
+  archive_ = MHTMLArchive::Create(url, buffer);
   if (!archive_) {
     // Log if attempting to load an invalid archive resource.
     console_logger_->AddErrorMessage(
         ConsoleLogger::Source::kScript,
-        "Malformed multipart archive: " + resource->Url().GetString());
+        "Malformed multipart archive: " + url.GetString());
     return nullptr;
   }
 
   return archive_->MainResource();
 }
 
-ResourceTimingInfo* ResourceFetcher::GetNavigationTimingInfo() {
-  return navigation_timing_info_.get();
-}
-
 void ResourceFetcher::HandleLoadCompletion(Resource* resource) {
   Context().DidLoadResource(resource);
 
@@ -1605,14 +1590,6 @@
   const int64_t encoded_data_length =
       resource->GetResponse().EncodedDataLength();
 
-  if (resource->GetType() == ResourceType::kMainResource) {
-    DCHECK(navigation_timing_info_);
-    if (resource->GetResponse().IsHTTP()) {
-      PopulateTimingInfo(navigation_timing_info_.get(), resource);
-      navigation_timing_info_->AddFinalTransferSize(
-          encoded_data_length == -1 ? 0 : encoded_data_length);
-    }
-  }
   if (scoped_refptr<ResourceTimingInfo> info =
           resource_timing_info_map_.Take(resource)) {
     if (resource->GetResponse().IsHTTP() &&
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
index 42b277f3..bbf8a2d2 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
@@ -132,7 +132,8 @@
   Vector<KURL> GetUrlsOfUnusedPreloads();
 
   MHTMLArchive* Archive() const { return archive_.Get(); }
-  ArchiveResource* CreateArchive(Resource*);
+  ArchiveResource* CreateArchive(const KURL&,
+                                 scoped_refptr<const SharedBuffer>);
 
   void SetDefersLoading(bool);
   void StopFetching();
@@ -165,9 +166,6 @@
 
   void ReloadLoFiImages();
 
-  // Calling this method before main document resource is fetched is invalid.
-  ResourceTimingInfo* GetNavigationTimingInfo();
-
   // Returns whether the given resource is contained as a preloaded resource.
   bool ContainsAsPreload(Resource*) const;
 
@@ -309,8 +307,6 @@
       HeapHashMap<Member<Resource>, scoped_refptr<ResourceTimingInfo>>;
   ResourceTimingInfoMap resource_timing_info_map_;
 
-  scoped_refptr<ResourceTimingInfo> navigation_timing_info_;
-
   Vector<scoped_refptr<ResourceTimingInfo>> scheduled_resource_timing_reports_;
 
   HeapHashSet<Member<ResourceLoader>> loaders_;
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
index 4156ab2..4c5ecbd 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
@@ -223,44 +223,17 @@
   new_resource->Loader()->Cancel();
 }
 
-TEST_F(ResourceFetcherTest, NavigationTimingInfo) {
-  KURL url("http://127.0.0.1:8000/foo.html");
-  ResourceResponse response(url);
-  response.SetHTTPStatusCode(200);
-
-  ResourceFetcher* fetcher =
-      MakeGarbageCollected<ResourceFetcher>(CreateFetchContext());
-  ResourceRequest resource_request(url);
-  resource_request.SetFrameType(
-      network::mojom::RequestContextFrameType::kNested);
-  resource_request.SetRequestContext(mojom::RequestContextType::FORM);
-  FetchParameters fetch_params(resource_request);
-  platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), "");
-  Resource* resource = RawResource::FetchMainResource(fetch_params, fetcher,
-                                                      nullptr, SubstituteData(),
-                                                      CreateUniqueIdentifier());
-  resource->ResponseReceived(response, nullptr);
-  EXPECT_EQ(resource->GetType(), ResourceType::kMainResource);
-
-  ResourceTimingInfo* navigation_timing_info =
-      fetcher->GetNavigationTimingInfo();
-  ASSERT_TRUE(navigation_timing_info);
-  long long encoded_data_length = 123;
-  resource->Loader()->DidFinishLoading(
-      TimeTicks(), encoded_data_length, 0, 0, false,
-      std::vector<network::cors::PreflightTimingInfo>());
-  EXPECT_EQ(navigation_timing_info->TransferSize(), encoded_data_length);
-
-  // When there are redirects.
-  KURL redirect_url("http://127.0.0.1:8000/redirect.html");
-  ResourceResponse redirect_response(redirect_url);
+TEST_F(ResourceFetcherTest, ResourceTimingInfo) {
+  auto info = ResourceTimingInfo::Create(fetch_initiator_type_names::kDocument,
+                                         CurrentTimeTicks(),
+                                         true /* is_main_resource */);
+  info->AddFinalTransferSize(5);
+  EXPECT_EQ(info->TransferSize(), 5);
+  ResourceResponse redirect_response(BlankURL());
   redirect_response.SetHTTPStatusCode(200);
-  long long redirect_encoded_data_length = 123;
-  redirect_response.SetEncodedDataLength(redirect_encoded_data_length);
-  ResourceRequest redirect_resource_request(url);
-  fetcher->RecordResourceTimingOnRedirect(resource, redirect_response, false);
-  EXPECT_EQ(navigation_timing_info->TransferSize(),
-            encoded_data_length + redirect_encoded_data_length);
+  redirect_response.SetEncodedDataLength(7);
+  info->AddRedirect(redirect_response, false);
+  EXPECT_EQ(info->TransferSize(), 12);
 }
 
 TEST_F(ResourceFetcherTest, VaryOnBack) {
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
index 14f5288..215a925d 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -72,7 +72,6 @@
 crbug.com/591099 css3/selectors3/xml/css3-modsel-91.xml [ Failure Pass ]
 crbug.com/591099 css3/selectors3/xml/css3-modsel-93.xml [ Failure Pass ]
 crbug.com/591099 editing/selection/paint-hyphen.html [ Pass ]
-crbug.com/591099 external/wpt/css/CSS2/abspos/abspos-in-block-in-inline-in-relpos-inline.html [ Failure ]
 crbug.com/591099 external/wpt/css/CSS2/floats/float-nowrap-3.html [ Pass ]
 crbug.com/591099 external/wpt/css/CSS2/floats/floats-line-wrap-shifted-001.html [ Pass ]
 crbug.com/591099 external/wpt/css/CSS2/normal-flow/block-in-inline-empty-001.xht [ Pass ]
@@ -242,7 +241,6 @@
 crbug.com/845902 external/wpt/quirks/line-height-trailing-collapsable-whitespace.html [ Pass ]
 crbug.com/591099 fast/backgrounds/quirks-mode-line-box-backgrounds.html [ Failure ]
 crbug.com/591099 fast/block/float-avoids-padding-inline-ancestors.html [ Failure ]
-crbug.com/591099 fast/block/positioning/positioned-child-inside-relative-positioned-anonymous-block.html [ Failure ]
 crbug.com/591099 fast/borders/inline-mask-overlay-image-outset-vertical-rl.html [ Failure ]
 crbug.com/591099 fast/css-intrinsic-dimensions/height-positioned.html [ Pass ]
 crbug.com/591099 fast/css/absolute-inline-alignment-2.html [ Pass ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
index 7179920..c96ae9fd 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -90865,6 +90865,18 @@
      {}
     ]
    ],
+   "css/filter-effects/backdrop-filter-reference-filter.html": [
+    [
+     "/css/filter-effects/backdrop-filter-reference-filter.html",
+     [
+      [
+       "/css/filter-effects/backdrop-filter-clip-rect-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/filter-effects/blur-clip-stacking-context-001.html": [
     [
      "/css/filter-effects/blur-clip-stacking-context-001.html",
@@ -115657,6 +115669,11 @@
      {}
     ]
    ],
+   "common/sleep.py": [
+    [
+     {}
+    ]
+   ],
    "common/slow.py": [
     [
      {}
@@ -179077,11 +179094,6 @@
      {}
     ]
    ],
-   "resource-timing/iframe-setdomain.sub.html": [
-    [
-     {}
-    ]
-   ],
    "resource-timing/no-entries-for-cross-origin-css-fetched.sub-expected.txt": [
     [
      {}
@@ -179092,7 +179104,7 @@
      {}
     ]
    ],
-   "resource-timing/resource-timing.js": [
+   "resource-timing/resource-timing-level1.js": [
     [
      {}
     ]
@@ -179122,6 +179134,11 @@
      {}
     ]
    ],
+   "resource-timing/resources/document-domain-no-impact.sub.html": [
+    [
+     {}
+    ]
+   ],
    "resource-timing/resources/empty.js": [
     [
      {}
@@ -179162,6 +179179,11 @@
      {}
     ]
    ],
+   "resource-timing/resources/iframe-setdomain.sub.html": [
+    [
+     {}
+    ]
+   ],
    "resource-timing/resources/iframe_TAO_match_origin.html": [
     [
      {}
@@ -271757,6 +271779,12 @@
      {}
     ]
    ],
+   "resource-timing/document-domain-no-impact-loader.sub.html": [
+    [
+     "/resource-timing/document-domain-no-impact-loader.sub.html",
+     {}
+    ]
+   ],
    "resource-timing/idlharness.any.js": [
     [
      "/resource-timing/idlharness.any.html",
@@ -271773,20 +271801,20 @@
      {}
     ]
    ],
+   "resource-timing/resource-timing-level1.sub.html": [
+    [
+     "/resource-timing/resource-timing-level1.sub.html",
+     {
+      "timeout": "long"
+     }
+    ]
+   ],
    "resource-timing/resource-timing-tojson.html": [
     [
      "/resource-timing/resource-timing-tojson.html",
      {}
     ]
    ],
-   "resource-timing/resource-timing.html": [
-    [
-     "/resource-timing/resource-timing.html",
-     {
-      "timeout": "long"
-     }
-    ]
-   ],
    "resource-timing/resource_TAO_cross_origin_redirect_chain.html": [
     [
      "/resource-timing/resource_TAO_cross_origin_redirect_chain.html",
@@ -285373,15 +285401,15 @@
      {}
     ]
    ],
-   "webxr/webGLCanvasContext_create_with_xrdevice.https.html": [
+   "webxr/webGLCanvasContext_create_xrcompatible.https.html": [
     [
-     "/webxr/webGLCanvasContext_create_with_xrdevice.https.html",
+     "/webxr/webGLCanvasContext_create_xrcompatible.https.html",
      {}
     ]
    ],
-   "webxr/webGLCanvasContext_setdevice_contextlost.https.html": [
+   "webxr/webGLCanvasContext_makecompatible_contextlost.https.html": [
     [
-     "/webxr/webGLCanvasContext_setdevice_contextlost.https.html",
+     "/webxr/webGLCanvasContext_makecompatible_contextlost.https.html",
      {}
     ]
    ],
@@ -304380,6 +304408,10 @@
    "6805c323df5a975231648b830e33ce183c3cbbd3",
    "support"
   ],
+  "common/sleep.py": [
+   "28065eff5fc907dbfb69f4c69020d049c7daa35d",
+   "support"
+  ],
   "common/slow.py": [
    "f3b1c7e2ea61b571bd56cc1c70c5f89bb8e7e4dc",
    "support"
@@ -376956,6 +376988,10 @@
    "88531532fc33296e8c8eca5452da04d5d612662d",
    "reftest"
   ],
+  "css/filter-effects/backdrop-filter-reference-filter.html": [
+   "74c813c92a46aae10739356318a3c356c0aae3c3",
+   "reftest"
+  ],
   "css/filter-effects/blur-clip-stacking-context-001.html": [
    "a96994a8afe126e474f9ee015338749f0015dc1f",
    "reftest"
@@ -390497,7 +390533,7 @@
    "support"
   ],
   "feature-policy/experimental-features/layout-animations-disabled-violation-report-js-tentative.html": [
-   "b45d8c3d4483ae309f87d6a26552636efa44e407",
+   "e7150a91e8749473962884727fb59bb3b978bf68",
    "testharness"
   ],
   "feature-policy/experimental-features/layout-animations-disabled-violation-report-js-tentative.html.headers": [
@@ -390505,7 +390541,7 @@
    "support"
   ],
   "feature-policy/experimental-features/layout-animations-disabled-violation-report-keyframes-tentative.html": [
-   "0701c50f6cac4cef768cf94ce8cd1764a885a14e",
+   "163ccae812f129e6cc338eb548e7ace92dd65b17",
    "testharness"
   ],
   "feature-policy/experimental-features/layout-animations-disabled-violation-report-keyframes-tentative.html.headers": [
@@ -390657,7 +390693,7 @@
    "reftest"
   ],
   "feature-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html": [
-   "6392ea5bfb0ef0b65ca7917ef3053ee73c1f8254",
+   "34cf142d456626f224957c44b9297500496c18cf",
    "manual"
   ],
   "feature-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html.headers": [
@@ -390721,7 +390757,7 @@
    "support"
   ],
   "feature-policy/feature-policy-header-policy-allowed-for-all.https.sub.html": [
-   "3334b97247e5fd347eda96d00d1399bf4c9a42f4",
+   "6bf7ca65326a8a5a146b2f5c399b7adbce527805",
    "testharness"
   ],
   "feature-policy/feature-policy-header-policy-allowed-for-all.https.sub.html.sub.headers": [
@@ -390729,7 +390765,7 @@
    "support"
   ],
   "feature-policy/feature-policy-header-policy-allowed-for-self.https.sub.html": [
-   "60e22f4aec865b54305737df21cc0dee3a869f82",
+   "8641746ec8fb9db9af9f284d098b32b8663ca85c",
    "testharness"
   ],
   "feature-policy/feature-policy-header-policy-allowed-for-self.https.sub.html.sub.headers": [
@@ -390737,7 +390773,7 @@
    "support"
   ],
   "feature-policy/feature-policy-header-policy-allowed-for-some.https.sub.html": [
-   "ade5dda4d9ddb879b007c6efef22b2757ced5743",
+   "e07c51fefd9cc7002cf01667b2e7885ba19a3907",
    "testharness"
   ],
   "feature-policy/feature-policy-header-policy-allowed-for-some.https.sub.html.sub.headers": [
@@ -390745,7 +390781,7 @@
    "support"
   ],
   "feature-policy/feature-policy-header-policy-declined.https.sub.html": [
-   "531c919aa03536770d80f59f0c21a922040aaddb",
+   "d00d592d9ac4688f015d9edccc5cb683f3c496ad",
    "testharness"
   ],
   "feature-policy/feature-policy-header-policy-declined.https.sub.html.sub.headers": [
@@ -390753,7 +390789,7 @@
    "support"
   ],
   "feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html": [
-   "c025705a36b10e3830cf2f076b34e14f77737129",
+   "9a0f483ce44bc382fe12caaa6670838fa98d4209",
    "testharness"
   ],
   "feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html.sub.headers": [
@@ -390785,7 +390821,7 @@
    "support"
   ],
   "feature-policy/idlharness.window-expected.txt": [
-   "43914ea6d4e64618dfe12c697829a428cc2c3a4e",
+   "1d9622defe0e9170c5ab66105d381d9a4a178d42",
    "support"
   ],
   "feature-policy/idlharness.window.js": [
@@ -391105,7 +391141,7 @@
    "support"
   ],
   "feature-policy/resources/feature-policy-allowedfeatures.html": [
-   "9cc8e1e33a32d2d6265580a261ce761b37ec4acb",
+   "f4b020273fd394fb8691b6eeb406c7b3773cbcb3",
    "support"
   ],
   "feature-policy/resources/feature-policy-autoplay.html": [
@@ -391149,7 +391185,7 @@
    "support"
   ],
   "feature-policy/resources/featurepolicy.js": [
-   "38ae4184096b6e53f17987c81f8ad8f1ff879195",
+   "a0756e385de6534821d3c15048e922384e4610ae",
    "support"
   ],
   "feature-policy/resources/picture-in-picture.js": [
@@ -425705,7 +425741,7 @@
    "support"
   ],
   "origin-policy/origin-policy-features.https.tentative.html": [
-   "b5dbe3e6e1a69662a38a2fec7f2f6eece887bcee",
+   "e83acf1dbdef347ce93dc7a64ccdf3e9b0e77d79",
    "testharness"
   ],
   "origin-policy/origin-policy-features.https.tentative.html.headers": [
@@ -437049,7 +437085,7 @@
    "support"
   ],
   "resource-timing/SyntheticResponse.py": [
-   "528ee239c174e9bd7618288f88cc09f8c5a9b58b",
+   "26e0a8017d6c2c7b51c94998f34a79fa20eb1e12",
    "support"
   ],
   "resource-timing/buffer-full-add-after-full-event.html": [
@@ -437100,14 +437136,14 @@
    "eaf21b483ccd6ec63bcb8dc87c9e19a4305bf43d",
    "testharness"
   ],
+  "resource-timing/document-domain-no-impact-loader.sub.html": [
+   "8a1c433a5c3a7b79d92b797ee1057c624d4a9866",
+   "testharness"
+  ],
   "resource-timing/idlharness.any.js": [
    "a7542f191c10ea7b42110b5fc80e7abf43174bb5",
    "testharness"
   ],
-  "resource-timing/iframe-setdomain.sub.html": [
-   "944ee10c44259ac84281cd802144068583d539db",
-   "support"
-  ],
   "resource-timing/no-entries-for-cross-origin-css-fetched.sub-expected.txt": [
    "a256bb2dc766a40f21115049572246bf003499ba",
    "support"
@@ -437120,18 +437156,18 @@
    "e8973024c18b7b05f08107fd2ac414e3d6c4e1b2",
    "support"
   ],
+  "resource-timing/resource-timing-level1.js": [
+   "95b5cdfb1ed0ca2bbfd6b692ad565512218dd7a4",
+   "support"
+  ],
+  "resource-timing/resource-timing-level1.sub.html": [
+   "093d2542218fbe48b134230b4d9d65e1ef1d4c10",
+   "testharness"
+  ],
   "resource-timing/resource-timing-tojson.html": [
    "77094f4b843a43fb30aeca48c505337b4322ca81",
    "testharness"
   ],
-  "resource-timing/resource-timing.html": [
-   "ad97044a6291912880046a4e784af13d28ab2f71",
-   "testharness"
-  ],
-  "resource-timing/resource-timing.js": [
-   "8d97b2f26871e3a492cc6d3d253808e0cf18b0f2",
-   "support"
-  ],
   "resource-timing/resource_TAO_cross_origin_redirect_chain.html": [
    "522188279c3ab81ec27cd7e83f014ecf747b21a8",
    "testharness"
@@ -437272,6 +437308,10 @@
    "b143da7b325f0ad24a2f9ad67db7ba31d0aa912c",
    "support"
   ],
+  "resource-timing/resources/document-domain-no-impact.sub.html": [
+   "fbd7bc3b6e21ee39478c8a63780bb673dafe96a4",
+   "support"
+  ],
   "resource-timing/resources/empty.js": [
    "3b44754e301ded90e559f6343df641e476803542",
    "support"
@@ -437304,6 +437344,10 @@
    "31a769eb3666492a3804f1c3deca73df526c0a4f",
    "support"
   ],
+  "resource-timing/resources/iframe-setdomain.sub.html": [
+   "4a2f609aa469f14d7efe64baaeb739efdb2fdc75",
+   "support"
+  ],
   "resource-timing/resources/iframe_TAO_match_origin.html": [
    "cf68aade7954e609087d34f21e437c285eb73a58",
    "support"
@@ -454472,11 +454516,11 @@
    "ee6e67f4c3d090015ead8b8718c2a836871a68ba",
    "support"
   ],
-  "webxr/webGLCanvasContext_create_with_xrdevice.https.html": [
+  "webxr/webGLCanvasContext_create_xrcompatible.https.html": [
    "94731ccfbb4937cabe175de5acce4423fbf8de43",
    "testharness"
   ],
-  "webxr/webGLCanvasContext_setdevice_contextlost.https.html": [
+  "webxr/webGLCanvasContext_makecompatible_contextlost.https.html": [
    "25f9869b70e27ca563df2860dd01a679e140d221",
    "testharness"
   ],
diff --git a/third_party/blink/web_tests/external/wpt/async-local-storage/api-surface.tentative.https.html b/third_party/blink/web_tests/external/wpt/async-local-storage/api-surface.tentative.https.html
index eea51ab..927871a 100644
--- a/third_party/blink/web_tests/external/wpt/async-local-storage/api-surface.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/async-local-storage/api-surface.tentative.https.html
@@ -21,14 +21,13 @@
 
 test(() => {
   classAssert.propertyKeys(StorageArea.prototype, [
-    "constructor", "set", "get", "has", "delete", "clear",
+    "constructor", "set", "get", "delete", "clear",
     "keys", "values", "entries", "backingStore"
   ], []);
 
   classAssert.methods(StorageArea.prototype, {
     set: 2,
     get: 1,
-    has: 1,
     delete: 1,
     clear: 0,
     keys: 0,
@@ -43,7 +42,7 @@
 
 testWithArea(async area => {
   classAssert.propertyKeys(area, [], []);
-}, "Instances don't have any properties")
+}, "Instances don't have any properties");
 
 test(() => {
   assert_equals(storage instanceof StorageArea, true, "instanceof");
diff --git a/third_party/blink/web_tests/external/wpt/async-local-storage/helpers/als-tests.js b/third_party/blink/web_tests/external/wpt/async-local-storage/helpers/als-tests.js
index fd6d6844..28087abf 100644
--- a/third_party/blink/web_tests/external/wpt/async-local-storage/helpers/als-tests.js
+++ b/third_party/blink/web_tests/external/wpt/async-local-storage/helpers/als-tests.js
@@ -19,7 +19,7 @@
 }
 
 // These two functions take a key/value and use them to test
-// set()/get()/delete()/has()/keys()/values()/entries(). The keyEqualityAsserter should be a
+// set()/get()/delete()/keys()/values()/entries(). The keyEqualityAsserter should be a
 // function from ./equality-asserters.js.
 
 export function testVariousMethodsWithDefaultArea(label, key, value, keyEqualityAsserter) {
@@ -35,7 +35,6 @@
     await assertPromiseEquals(area.set(key, value), undefined, "set()", "undefined");
 
     await assertPromiseEquals(area.get(key), value, "get()", "the set value");
-    await assertPromiseEquals(area.has(key), true, "has()", "true");
 
     const keysPromise = area.keys();
     assertIsPromise(keysPromise, "keys()");
@@ -58,7 +57,6 @@
     await assertPromiseEquals(area.delete(key), undefined, "delete()", "undefined");
 
     await assertPromiseEquals(area.get(key), undefined, "get()", "undefined after deleting");
-    await assertPromiseEquals(area.has(key), false, "has()", "false after deleting");
   };
 }
 
diff --git a/third_party/blink/web_tests/external/wpt/async-local-storage/key-types.tentative.https.html b/third_party/blink/web_tests/external/wpt/async-local-storage/key-types.tentative.https.html
index 771ee2f..c3985b7 100644
--- a/third_party/blink/web_tests/external/wpt/async-local-storage/key-types.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/async-local-storage/key-types.tentative.https.html
@@ -32,7 +32,7 @@
   "an ArrayBuffer": [new Uint8Array([5, 6]).buffer, assertEqualArrayBuffers]
 };
 
-const methods = ["set", "get", "has", "delete"];
+const methods = ["delete", "get", "set"];
 
 for (const method of methods) {
   testWithArea(async (area, t) => {
diff --git a/third_party/blink/web_tests/external/wpt/async-local-storage/undefined-value.https.html b/third_party/blink/web_tests/external/wpt/async-local-storage/undefined-value.https.html
new file mode 100644
index 0000000..c76c32f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/async-local-storage/undefined-value.https.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Async local storage: undefined keys</title>
+<!-- https://github.com/domenic/async-local-storage/commit/5bf31109f37d1371f619ea33d0e2391f10e8b8f5 -->
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script type="module">
+import { StorageArea } from "std:async-local-storage";
+import { testWithArea } from "./helpers/als-tests.js";
+
+testWithArea(async (area) => {
+  assert_equals(await area.get("key"), undefined);
+}, "Get on a non-existant key returns undefined");
+
+testWithArea(async (area) => {
+  await area.set("key", undefined);
+  assert_equals(await area.get("key"), undefined);
+
+  assert_equals((await area.keys()).length, 0, "number of keys");
+  assert_equals((await area.values()).length, 0, "number of values");
+  assert_equals((await area.entries()).length, 0, "number of entries");
+}, "Setting undefined as a value when nothing was present is a no-op");
+
+testWithArea(async (area) => {
+  await area.set("key", "value");
+  await area.set("key", undefined);
+
+  assert_equals(await area.get("key"), undefined);
+
+  assert_equals((await area.keys()).length, 0, "number of keys");
+  assert_equals((await area.values()).length, 0, "number of values");
+  assert_equals((await area.entries()).length, 0, "number of entries");
+}, "Setting undefined as a value deletes what was previously there");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/common/sleep.py b/third_party/blink/web_tests/external/wpt/common/sleep.py
new file mode 100644
index 0000000..28065eff
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/common/sleep.py
@@ -0,0 +1,8 @@
+import time
+
+# sleep can be lower than requested value in some platforms: https://bugs.python.org/issue31539
+# We add padding here to compensate for that.
+sleep_padding = 15.0
+
+def sleep_at_least(sleep_in_ms):
+    time.sleep((sleep_in_ms + sleep_padding) / 1E3);
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/SyntheticResponse.py b/third_party/blink/web_tests/external/wpt/resource-timing/SyntheticResponse.py
index 528ee23..26e0a80 100644
--- a/third_party/blink/web_tests/external/wpt/resource-timing/SyntheticResponse.py
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/SyntheticResponse.py
@@ -1,5 +1,7 @@
 import urllib
-import time
+import sys, os
+sys.path.append(os.path.join(os.path.dirname(__file__), "../common/"))
+import sleep
 
 def main(request, response):
     index = request.request_path.index("?")
@@ -11,7 +13,7 @@
         if arg.startswith("ignored"):
             continue
         elif arg.endswith("ms"):
-            time.sleep(float(arg[0:-2]) / 1E3);
+            sleep.sleep_at_least(float(arg[0:-2]))
         elif arg.startswith("redirect:"):
             return (302, "WEBPERF MARKETING"), [("Location", urllib.unquote(arg[9:]))], "TEST"
         elif arg.startswith("mime:"):
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/document-domain-no-impact-loader.sub.html b/third_party/blink/web_tests/external/wpt/resource-timing/document-domain-no-impact-loader.sub.html
new file mode 100644
index 0000000..8a1c433
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/document-domain-no-impact-loader.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+    const t = async_test("Finite resource timing entries buffer size");
+    addEventListener("message", t.step_func_done(e => {
+        assert_equals(e.data, "PASS", "Document domain had no impact on the timing-allow check");
+    }));
+window.open("//{{domains[www]}}:{{ports[http][1]}}/resource-timing/resources/document-domain-no-impact.sub.html");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/iframe-setdomain.sub.html b/third_party/blink/web_tests/external/wpt/resource-timing/iframe-setdomain.sub.html
deleted file mode 100644
index 944ee10..0000000
--- a/third_party/blink/web_tests/external/wpt/resource-timing/iframe-setdomain.sub.html
+++ /dev/null
@@ -1,14 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <title>domain: {{domains[www]}}</title>
-</head>
-<body>
-  <script>
-    // The purpose of this IFrame is to change the 'document.domain'
-    document.domain = "{{domains[www]}}";
-  </script>
-  The resource-timings.html test loads this document into an IFrame to vet that setting
-  'document.domain' does not effect the timing allowed.
-</body>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing.js b/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing-level1.js
similarity index 93%
rename from third_party/blink/web_tests/external/wpt/resource-timing/resource-timing.js
rename to third_party/blink/web_tests/external/wpt/resource-timing/resource-timing-level1.js
index 8d97b2f..95b5cdfb 100644
--- a/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing.js
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing-level1.js
@@ -53,34 +53,6 @@
                             "'Src' of new <iframe> must be 'about:blank'.");
                     }
             },
-            {
-                description: "Setting 'document.domain' does not effect same-origin checks",
-                test:
-                    function (test) {
-                        initiateFetch(
-                            test,
-                            "iframe",
-                            canonicalize("iframe-setdomain.sub.html"),
-                            function (initiator, entry) {
-                                // Ensure that the script inside the IFrame has successfully changed the IFrame's domain.
-                                assert_throws(
-                                    null,
-                                    function () {
-                                        assert_not_equals(frame.contentWindow.document, null);
-                                    },
-                                    "Test Error: IFrame is not recognized as cross-domain.");
-
-                                // To verify that setting 'document.domain' did not change the results of the timing allow check,
-                                // verify that the following non-zero properties return their value.
-                                ["domainLookupStart", "domainLookupEnd", "connectStart", "connectEnd"]
-                                    .forEach(function(property) {
-                                        assert_greater_than(entry.connectEnd, 0,
-                                            "Property should be non-zero because timing allow check ignores 'document.domain'.");
-                                    });
-                                test.done();
-                            });
-                    }
-            }
         ];
 
         // Create cached/uncached tests from the following array of templates.  For each template entry,
@@ -324,7 +296,7 @@
             // Multiple browsers seem to cheat a bit and race img.onLoad and setting responseEnd.  Microsoft https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/2379187
             // Yield for 100ms to workaround a suspected race where window.onload fires before
             //     script visible side-effects from the wininet/urlmon thread have finished.
-            window.setTimeout(
+            test.step_timeout(
                 test.step_func(
                     function () {
                         performance
@@ -468,7 +440,7 @@
             when invoked. */
         function createOnloadCallbackFn(test, initiator, url, onloadCallback) {
             // Remember the number of entries on the timeline prior to initiating the fetch:
-            var beforeEntryCount = performance.getEntries().length;
+            var beforeEntryCount = performance.getEntriesByType("resource").length;
 
             return test.step_func(
                 function() {
@@ -480,7 +452,7 @@
                         }
                     }
 
-                    var entries = performance.getEntries();
+                    var entries = performance.getEntriesByType("resource");
                     var candidateEntry = entries[entries.length - 1];
 
                     switch (entries.length - beforeEntryCount)
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing.html b/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing-level1.sub.html
similarity index 91%
rename from third_party/blink/web_tests/external/wpt/resource-timing/resource-timing.html
rename to third_party/blink/web_tests/external/wpt/resource-timing/resource-timing-level1.sub.html
index ad97044..093d254 100644
--- a/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing.html
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/resource-timing-level1.sub.html
@@ -14,6 +14,6 @@
 <body>
   <div id="log"></div>
   <pre id="output"></pre>
-  <script src="resource-timing.js"></script>
+  <script src="resource-timing-level1.js"></script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/resources/document-domain-no-impact.sub.html b/third_party/blink/web_tests/external/wpt/resource-timing/resources/document-domain-no-impact.sub.html
new file mode 100644
index 0000000..fbd7bc3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/resources/document-domain-no-impact.sub.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<iframe id="frame" src="//{{domains[www]}}:{{ports[http][1]}}/resource-timing/resources/iframe-setdomain.sub.html"></iframe>
+<script>
+    let opener = window.opener;
+    window.addEventListener("load", () => {
+        try {
+            let frameDoc = document.getElementById("frame").contentWindow.document;
+            opener.postMessage("FAIL - iframe document.domain was not set", "*");
+            return;
+        } catch(error) {
+            if (error.name != "SecurityError") {
+                opener.postMessage("FAIL - error is " + error.name, "*");
+                return;
+            }
+            let entry = performance.getEntriesByName(window.location.protocol + "//{{domains[www]}}:{{ports[http][1]}}/resource-timing/resources/iframe-setdomain.sub.html");
+            // To verify that setting 'document.domain' did not change the results of the timing allow check,
+            // verify that the following non-zero properties return their value.
+            ["domainLookupStart", "domainLookupEnd", "connectStart", "connectEnd"].forEach(property => {
+                if (entry[property] == 0) {
+                    opener.postMessage("FAIL - " + property + " is 0 but timing allow check should ignore document.domain", "*");
+                }
+            });
+            opener.postMessage("PASS", "*");
+        }
+        opener.postMessage("FAIL - unknown", "*");
+    });
+</script>
+
+
+
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/resources/iframe-setdomain.sub.html b/third_party/blink/web_tests/external/wpt/resource-timing/resources/iframe-setdomain.sub.html
new file mode 100644
index 0000000..4a2f609
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/resources/iframe-setdomain.sub.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>domain: {{domains[]}}</title>
+</head>
+<body>
+  <script>
+    // The purpose of this IFrame is to change the 'document.domain'
+    document.domain = "{{domains[]}}";
+  </script>
+  The resource-timings-level1.sub.html test loads this document into an IFrame to vet that setting
+  'document.domain' does not effect the timing allowed.
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt
index 4eb2240..3aee6be 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt
@@ -202,7 +202,7 @@
 FAIL XRReferenceSpaceEvent interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "XRReferenceSpaceEvent" expected property "XRReferenceSpaceEvent" missing
 FAIL XRReferenceSpaceEvent interface: attribute referenceSpace assert_own_property: self does not have own property "XRReferenceSpaceEvent" expected property "XRReferenceSpaceEvent" missing
 FAIL XRReferenceSpaceEvent interface: attribute transform assert_own_property: self does not have own property "XRReferenceSpaceEvent" expected property "XRReferenceSpaceEvent" missing
-FAIL WebGLRenderingContext interface: operation makeXRCompatible() assert_own_property: interface prototype object missing non-static operation expected property "makeXRCompatible" missing
+PASS WebGLRenderingContext interface: operation makeXRCompatible()
 PASS Navigator interface: attribute xr
 PASS Navigator interface: navigator must inherit property "xr" with the proper type
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/webxr/resources/webxr_util.js b/third_party/blink/web_tests/external/wpt/webxr/resources/webxr_util.js
index ee6e67f4..10bdc12 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/resources/webxr_util.js
+++ b/third_party/blink/web_tests/external/wpt/webxr/resources/webxr_util.js
@@ -48,7 +48,7 @@
               })
               .then((device) => {
                 testDevice = device;
-                return gl.setCompatibleXRDevice(device);
+                return gl.makeXRCompatible();
               })
               .then(() => new Promise((resolve, reject) => {
                       // Perform the session request in a user gesture.
diff --git a/third_party/blink/web_tests/external/wpt/webxr/webGLCanvasContext_create_with_xrdevice.https.html b/third_party/blink/web_tests/external/wpt/webxr/webGLCanvasContext_create_with_xrdevice.https.html
deleted file mode 100644
index 94731cc..0000000
--- a/third_party/blink/web_tests/external/wpt/webxr/webGLCanvasContext_create_with_xrdevice.https.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <script src=/resources/testharness.js></script>
-  <script src=/resources/testharnessreport.js></script>
-  <script src="resources/webxr_util.js"></script>
-  <canvas id="webgl-canvas"></canvas>
-  <script>
-    xr_promise_test("webglCanvasContext can be created with an XRDevice",
-      (t) => {
-        return XRTest.simulateDeviceConnection({ supportsImmersive:true })
-          .then( (controller) => { return navigator.xr.requestDevice() })
-          .then( (device) => {
-            webglCanvas = document.getElementById('webgl-canvas');
-            gl = webglCanvas.getContext('webgl', {compatibleXRDevice: device});
-            assert_equals(gl.getContextAttributes().compatibleXRDevice, device);
-
-            // Check that an offscreen context behaves no different.
-            let offscreenCanvas = document.createElement('canvas');
-            let offscreenGl = webglCanvas.getContext(
-              'webgl', {compatibleXRDevice: device});
-            assert_equals(
-              offscreenGl.getContextAttributes().compatibleXRDevice, device);
-          });
-      });
-
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/webxr/webGLCanvasContext_create_xrcompatible.https.html b/third_party/blink/web_tests/external/wpt/webxr/webGLCanvasContext_create_xrcompatible.https.html
new file mode 100644
index 0000000..8b2f196
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webxr/webGLCanvasContext_create_xrcompatible.https.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <canvas id="webgl-canvas"></canvas>
+  <script>
+    xr_promise_test("An XR-compatible webglCanvasContext can be created",
+      (t) => {
+        return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+          .then( (controller) => {
+            webglCanvas = document.getElementById('webgl-canvas');
+            gl = webglCanvas.getContext('webgl', {xrCompatible: true});
+            assert_true(gl.getContextAttributes().xrCompatible);
+
+            // Check that an offscreen context behaves no different.
+            let offscreenCanvas = document.createElement('canvas');
+            let offscreenGl = webglCanvas.getContext(
+              'webgl', {xrCompatible: true});
+            assert_true(offscreenGl.getContextAttributes().xrCompatible);
+          });
+      });
+
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/webxr/webGLCanvasContext_makecompatible_contextlost.https.html b/third_party/blink/web_tests/external/wpt/webxr/webGLCanvasContext_makecompatible_contextlost.https.html
new file mode 100644
index 0000000..3102008c5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webxr/webGLCanvasContext_makecompatible_contextlost.https.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <canvas id="webgl-canvas"></canvas>
+  <script>
+
+    xr_promise_test(
+      "A lost webglCanvasContext should not be able to set xr compatibility",
+     (t) => {
+      return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+        .then( (controller) => {
+          webglCanvas = document.getElementById('webgl-canvas');
+          gl = webglCanvas.getContext('webgl', {xrCompatible: true});
+          gl.getExtension('WEBGL_lose_context').loseContext();
+          return promise_rejects(t, 'InvalidStateError', gl.makeXRCompatible());
+        });
+    });
+
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/webxr/webGLCanvasContext_setdevice_contextlost.https.html b/third_party/blink/web_tests/external/wpt/webxr/webGLCanvasContext_setdevice_contextlost.https.html
deleted file mode 100644
index 25f9869b..0000000
--- a/third_party/blink/web_tests/external/wpt/webxr/webGLCanvasContext_setdevice_contextlost.https.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <script src=/resources/testharness.js></script>
-  <script src=/resources/testharnessreport.js></script>
-  <script src="resources/webxr_util.js"></script>
-  <canvas id="webgl-canvas"></canvas>
-  <script>
-
-    xr_promise_test(
-      "A lost webglCanvasContext should not be able to set device",
-     (t) => {
-      return XRTest.simulateDeviceConnection({ supportsImmersive:true })
-        .then( (controller) => { return navigator.xr.requestDevice() })
-        .then( (device) => {
-          webglCanvas = document.getElementById('webgl-canvas');
-          gl = webglCanvas.getContext('webgl', {compatibleXRDevice: device});
-          gl.getExtension('WEBGL_lose_context').loseContext();
-          return promise_rejects(t, 'InvalidStateError',
-              gl.setCompatibleXRDevice(device));
-        });
-    });
-
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/fast/frames/location-redirect-user-gesture-expected.txt b/third_party/blink/web_tests/fast/frames/location-redirect-user-gesture-expected.txt
index 30f05320..3c2a4d16 100644
--- a/third_party/blink/web_tests/fast/frames/location-redirect-user-gesture-expected.txt
+++ b/third_party/blink/web_tests/fast/frames/location-redirect-user-gesture-expected.txt
@@ -1,2 +1,3 @@
 Frame with user gesture "false" - in didStartProvisionalLoadForFrame
+Frame with user gesture "false" - in didStartProvisionalLoadForFrame
 SUCCESS
diff --git a/third_party/blink/web_tests/fast/frames/meta-refresh-user-gesture-expected.txt b/third_party/blink/web_tests/fast/frames/meta-refresh-user-gesture-expected.txt
index 142a05f..52cfe9a4 100644
--- a/third_party/blink/web_tests/fast/frames/meta-refresh-user-gesture-expected.txt
+++ b/third_party/blink/web_tests/fast/frames/meta-refresh-user-gesture-expected.txt
@@ -1,2 +1,3 @@
 Frame with user gesture "false" - in didStartProvisionalLoadForFrame
+Frame with user gesture "false" - in didStartProvisionalLoadForFrame
 
diff --git a/third_party/blink/web_tests/fast/loader/subframe-removes-itself-expected.txt b/third_party/blink/web_tests/fast/loader/subframe-removes-itself-expected.txt
index dfd8f14..6baef90 100644
--- a/third_party/blink/web_tests/fast/loader/subframe-removes-itself-expected.txt
+++ b/third_party/blink/web_tests/fast/loader/subframe-removes-itself-expected.txt
@@ -2,6 +2,7 @@
 frame "<!--framePath //<!--frame0-->-->" - didStartProvisionalLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
+frame "<!--framePath //<!--frame0-->-->" - didStartProvisionalLoadForFrame
 frame "<!--framePath //<!--frame0-->-->" - didCommitLoadForFrame
 frame "<!--framePath //<!--frame0-->-->" - didFailLoadWithError
 main frame - didHandleOnloadEventsForFrame
diff --git a/third_party/blink/web_tests/http/tests/fetch/chromium/data-saver-enabled.html b/third_party/blink/web_tests/http/tests/fetch/chromium/data-saver-enabled.html
new file mode 100644
index 0000000..44246ebf
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/fetch/chromium/data-saver-enabled.html
@@ -0,0 +1,63 @@
+<html>
+<title>Tests for 'Save-Data' header.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- Enable save data header and the JavaScript attribute -->
+<script> internals.setSaveDataEnabled(true); </script>
+
+<iframe srcdoc="
+<!DOCTYPE html>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+
+<script>
+internals.setSaveDataEnabled(true);
+
+var CHECK_PATH = './resources/check-save-data-header.php';
+var METHODS = ['GET', 'POST', 'PUT'];
+var REQUESTS =
+    METHODS.map(method => new Request(CHECK_PATH, {method: method}));
+
+window.top.promise_test(t => {
+  internals.setSaveDataEnabled(true);
+  return Promise.all(REQUESTS.map(request => fetch(request.clone())))
+    .then(responses => Promise.all(responses.map(response => response.text())))
+    .then(texts => {
+        for (var i = 0; i < METHODS.length; ++i) {
+          assert_equals(
+              texts[i], 'Save-Data: on',
+              'Save-Data header should be sent when enabled. method: ' +
+              METHODS[i]);
+        }
+      });
+}, 'fetch() from document with Data-Saver enabled.');
+
+window.top.promise_test(t => {
+  internals.setSaveDataEnabled(true);
+  var worker =
+    new Worker('./resources/data-saver-worker.php?dedicated-enabled');
+  return new Promise(resolve =>
+                     worker.addEventListener('message', resolve))
+     .then(msg => {
+         var result = msg.data;
+         assert_equals(
+             result['worker_script_header'], 'Save-Data: on',
+             'Save-Data header should be sent for worker script when enabled.');
+         for (var i = 0; i < METHODS.length; ++i) {
+           assert_equals(
+               result[METHODS[i]], 'Save-Data: on',
+               'Save-Data header should be sent when enabled. method: ' +
+               METHODS[i]);
+         }
+       });
+}, 'fetch() from dedicated worker with Data-Saver enabled.');
+
+// A test for shared workers is implemented as a browser test because shared
+// worker script is requested from the browser process when NetworkService is
+// enabled, and internals.setSaveDataEnable() doesn't work in that case.
+
+</script>
+"></iframe>
+
+</html>
diff --git a/third_party/blink/web_tests/http/tests/fetch/chromium/data-saver.html b/third_party/blink/web_tests/http/tests/fetch/chromium/data-saver.html
index 9777ce6a..560b6f2 100644
--- a/third_party/blink/web_tests/http/tests/fetch/chromium/data-saver.html
+++ b/third_party/blink/web_tests/http/tests/fetch/chromium/data-saver.html
@@ -61,62 +61,4 @@
 </script>
 "></iframe>
 
-
-<!-- Enable save data header and the JavaScript attribute -->
-<script> internals.setSaveDataEnabled(true); </script>
-
-<iframe srcdoc="
-<!DOCTYPE html>
-<script src='/resources/testharness.js'></script>
-<script src='/resources/testharnessreport.js'></script>
-
-<script>
-internals.setSaveDataEnabled(true);
-
-var CHECK_PATH = './resources/check-save-data-header.php';
-var METHODS = ['GET', 'POST', 'PUT'];
-var REQUESTS =
-    METHODS.map(method => new Request(CHECK_PATH, {method: method}));
-
-window.top.promise_test(t => {
-  internals.setSaveDataEnabled(true);
-  return Promise.all(REQUESTS.map(request => fetch(request.clone())))
-    .then(responses => Promise.all(responses.map(response => response.text())))
-    .then(texts => {
-        for (var i = 0; i < METHODS.length; ++i) {
-          assert_equals(
-              texts[i], 'Save-Data: on',
-              'Save-Data header should be sent when disabled. method: ' +
-              METHODS[i]);
-        }
-      });
-}, 'fetch() from document with Data-Saver enabled.');
-
-window.top.promise_test(t => {
-  internals.setSaveDataEnabled(true);
-  var worker =
-    new Worker('./resources/data-saver-worker.php?dedicated-enabled');
-  return new Promise(resolve =>
-                     worker.addEventListener('message', resolve))
-     .then(msg => {
-         var result = msg.data;
-         assert_equals(
-             result['worker_script_header'], 'Save-Data: on',
-             'Save-Data header should be sent for worker script when enabled.');
-         for (var i = 0; i < METHODS.length; ++i) {
-           assert_equals(
-               result[METHODS[i]], 'Save-Data: on',
-               'Save-Data header should be sent when enabled. method: ' +
-               METHODS[i]);
-         }
-       });
-}, 'fetch() from dedicated worker with Data-Saver enabled.');
-
-// A test for shared workers is implemented as a browser test because shared
-// worker script is requested from the browser process when NetworkService is
-// enabled, and internals.setSaveDataEnable() doesn't work in that case.
-
-</script>
-"></iframe>
-
 </html>
diff --git a/third_party/blink/web_tests/http/tests/loading/307-after-303-after-post-expected.txt b/third_party/blink/web_tests/http/tests/loading/307-after-303-after-post-expected.txt
index 7b513fe..8771e8b 100644
--- a/third_party/blink/web_tests/http/tests/loading/307-after-303-after-post-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/307-after-303-after-post-expected.txt
@@ -6,6 +6,7 @@
 main frame - didFinishLoadForFrame
 http://127.0.0.1:8000/loading/resources/post-to-303-target.php - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/loading/resources/post-to-303-target.php, main document URL http://127.0.0.1:8000/loading/resources/post-to-303-target.php, http method POST>
 main frame - didStartProvisionalLoadForFrame
+main frame - didStartProvisionalLoadForFrame
 http://127.0.0.1:8000/loading/resources/303-to-307-target.php - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/loading/resources/303-to-307-target.php, main document URL http://127.0.0.1:8000/loading/resources/303-to-307-target.php, http method GET>
 http://127.0.0.1:8000/loading/resources/307-post-output-target.php - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/loading/resources/307-post-output-target.php, main document URL http://127.0.0.1:8000/loading/resources/307-post-output-target.php, http method GET>
 http://127.0.0.1:8000/loading/resources/307-post-output-target.php - didReceiveResponse <NSURLResponse http://127.0.0.1:8000/loading/resources/307-post-output-target.php, http status code 200>
diff --git a/third_party/blink/web_tests/http/tests/loading/bad-server-subframe-expected.txt b/third_party/blink/web_tests/http/tests/loading/bad-server-subframe-expected.txt
index a004c5f847..ac8966b2 100644
--- a/third_party/blink/web_tests/http/tests/loading/bad-server-subframe-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/bad-server-subframe-expected.txt
@@ -5,6 +5,7 @@
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
 frame "f1" - didFailProvisionalLoadWithError
+frame "f1" - didStartProvisionalLoadForFrame
 frame "f1" - didCommitLoadForFrame
 frame "f1" - didReceiveTitle: Error
 frame "f1" - didFinishDocumentLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/loading/dont-preload-non-img-srcset-expected.txt b/third_party/blink/web_tests/http/tests/loading/dont-preload-non-img-srcset-expected.txt
index 37d19544..0ed7d56 100644
--- a/third_party/blink/web_tests/http/tests/loading/dont-preload-non-img-srcset-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/dont-preload-non-img-srcset-expected.txt
@@ -12,6 +12,7 @@
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
 main frame - didStartProvisionalLoadForFrame
+main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
 frame "<!--framePath //<!--frame0-->-->" - didReceiveTitle: 
 frame "<!--framePath //<!--frame0-->-->" - didStartProvisionalLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/loading/link-preload-image-sizes-2x-expected.txt b/third_party/blink/web_tests/http/tests/loading/link-preload-image-sizes-2x-expected.txt
index 8e164711..f693d37 100644
--- a/third_party/blink/web_tests/http/tests/loading/link-preload-image-sizes-2x-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/link-preload-image-sizes-2x-expected.txt
@@ -5,6 +5,7 @@
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
 main frame - didStartProvisionalLoadForFrame
+main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/loading/link-preload-image-srcset-2x-expected.txt b/third_party/blink/web_tests/http/tests/loading/link-preload-image-srcset-2x-expected.txt
index 1508e941..42c3fc9 100644
--- a/third_party/blink/web_tests/http/tests/loading/link-preload-image-srcset-2x-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/link-preload-image-srcset-2x-expected.txt
@@ -5,6 +5,7 @@
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
 main frame - didStartProvisionalLoadForFrame
+main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/loading/link-preload-image-srcset-expected.txt b/third_party/blink/web_tests/http/tests/loading/link-preload-image-srcset-expected.txt
index 93481da..e40ff034 100644
--- a/third_party/blink/web_tests/http/tests/loading/link-preload-image-srcset-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/link-preload-image-srcset-expected.txt
@@ -5,6 +5,7 @@
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
 main frame - didStartProvisionalLoadForFrame
+main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/loading/onbeforeunload-detach-expected.txt b/third_party/blink/web_tests/http/tests/loading/onbeforeunload-detach-expected.txt
index 7f32d144..9b771ff 100644
--- a/third_party/blink/web_tests/http/tests/loading/onbeforeunload-detach-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/onbeforeunload-detach-expected.txt
@@ -4,6 +4,7 @@
 frame "iframe" - didStartProvisionalLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
+frame "iframe" - didStartProvisionalLoadForFrame
 frame "iframe" - didCommitLoadForFrame
 CONSOLE MESSAGE: line 12: iframe: clicking
 CONSOLE MESSAGE: line 5: iframe: onbeforeunload
diff --git a/third_party/blink/web_tests/http/tests/loading/onload-vs-immediate-refresh-expected.txt b/third_party/blink/web_tests/http/tests/loading/onload-vs-immediate-refresh-expected.txt
index 7cefb1d..1893c44a 100644
--- a/third_party/blink/web_tests/http/tests/loading/onload-vs-immediate-refresh-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/onload-vs-immediate-refresh-expected.txt
@@ -6,6 +6,7 @@
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
 main frame - didStartProvisionalLoadForFrame
+main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/loading/onreadystatechange-detach-expected.txt b/third_party/blink/web_tests/http/tests/loading/onreadystatechange-detach-expected.txt
index db47f72..a9c30a5 100644
--- a/third_party/blink/web_tests/http/tests/loading/onreadystatechange-detach-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/onreadystatechange-detach-expected.txt
@@ -4,6 +4,7 @@
 frame "iframe" - didStartProvisionalLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
+frame "iframe" - didStartProvisionalLoadForFrame
 frame "iframe" - didCommitLoadForFrame
 CONSOLE MESSAGE: line 12: iframe: clicking
 CONSOLE MESSAGE: line 5: iframe: onreadystatechange
diff --git a/third_party/blink/web_tests/http/tests/loading/preload-image-sizes-2x-expected.txt b/third_party/blink/web_tests/http/tests/loading/preload-image-sizes-2x-expected.txt
index 8244f69..c6cdbad7 100644
--- a/third_party/blink/web_tests/http/tests/loading/preload-image-sizes-2x-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/preload-image-sizes-2x-expected.txt
@@ -5,6 +5,7 @@
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
 main frame - didStartProvisionalLoadForFrame
+main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-2x-expected.txt b/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-2x-expected.txt
index 9cd9db77..9c5d6aac 100644
--- a/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-2x-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-2x-expected.txt
@@ -5,6 +5,7 @@
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
 main frame - didStartProvisionalLoadForFrame
+main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-duplicate-expected.txt b/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-duplicate-expected.txt
index c135a4d..c464360 100644
--- a/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-duplicate-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-duplicate-expected.txt
@@ -5,6 +5,7 @@
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
 main frame - didStartProvisionalLoadForFrame
+main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-expected.txt b/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-expected.txt
index 35f1eb6..c4e8cfb 100644
--- a/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-expected.txt
@@ -5,6 +5,7 @@
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
 main frame - didStartProvisionalLoadForFrame
+main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-reverse-order-expected.txt b/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-reverse-order-expected.txt
index 80e4e81..e421b7405 100644
--- a/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-reverse-order-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-reverse-order-expected.txt
@@ -5,6 +5,7 @@
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
 main frame - didStartProvisionalLoadForFrame
+main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-src-preloaded-expected.txt b/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-src-preloaded-expected.txt
index ae9ec54..5e165ad 100644
--- a/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-src-preloaded-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-src-preloaded-expected.txt
@@ -5,6 +5,7 @@
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
 main frame - didStartProvisionalLoadForFrame
+main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-src-preloaded-reverse-order-expected.txt b/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-src-preloaded-reverse-order-expected.txt
index 8968525..22cff36 100644
--- a/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-src-preloaded-reverse-order-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/preload-image-srcset-src-preloaded-reverse-order-expected.txt
@@ -5,6 +5,7 @@
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
 main frame - didStartProvisionalLoadForFrame
+main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/loading/preload-picture-sizes-2x-expected.txt b/third_party/blink/web_tests/http/tests/loading/preload-picture-sizes-2x-expected.txt
index b1d08b9..0377fcf 100644
--- a/third_party/blink/web_tests/http/tests/loading/preload-picture-sizes-2x-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/preload-picture-sizes-2x-expected.txt
@@ -6,6 +6,7 @@
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
 main frame - didStartProvisionalLoadForFrame
+main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
 CONSOLE WARNING: <source src> with a <picture> parent is invalid and therefore ignored. Please use <source srcset> instead.
 main frame - didReceiveTitle: 
diff --git a/third_party/blink/web_tests/http/tests/loading/redirect-methods-expected.txt b/third_party/blink/web_tests/http/tests/loading/redirect-methods-expected.txt
index 9bef8ff..74758a5 100644
--- a/third_party/blink/web_tests/http/tests/loading/redirect-methods-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/redirect-methods-expected.txt
@@ -13,6 +13,7 @@
 frame "0" - didFinishLoadForFrame
 http://127.0.0.1:8000/loading/resources/redirect-methods-form.html - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/loading/resources/redirect-methods-form.html, main document URL http://127.0.0.1:8000/loading/redirect-methods.html, http method GET>
 frame "0" - didStartProvisionalLoadForFrame
+frame "0" - didStartProvisionalLoadForFrame
 http://127.0.0.1:8000/loading/resources/redirect-methods-form.html - didReceiveResponse <NSURLResponse http://127.0.0.1:8000/loading/resources/redirect-methods-form.html, http status code 200>
 frame "0" - didCommitLoadForFrame
 frame "0" - didReceiveTitle: 
@@ -21,6 +22,7 @@
 frame "0" - didFinishLoadForFrame
 http://127.0.0.1:8000/loading/resources/redirect-methods-result.php - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/loading/resources/redirect-methods-result.php, main document URL http://127.0.0.1:8000/loading/redirect-methods.html, http method POST>
 frame "0" - didStartProvisionalLoadForFrame
+frame "0" - didStartProvisionalLoadForFrame
 http://127.0.0.1:8000/loading/resources/redirect-methods-result.php?redirected=true - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/loading/resources/redirect-methods-result.php?redirected=true, main document URL http://127.0.0.1:8000/loading/redirect-methods.html, http method GET>
 http://127.0.0.1:8000/loading/resources/redirect-methods-result.php?redirected=true - didReceiveResponse <NSURLResponse http://127.0.0.1:8000/loading/resources/redirect-methods-result.php?redirected=true, http status code 200>
 frame "0" - didCommitLoadForFrame
@@ -37,6 +39,7 @@
 frame "0" - didFinishLoadForFrame
 http://127.0.0.1:8000/loading/resources/redirect-methods-form.html - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/loading/resources/redirect-methods-form.html, main document URL http://127.0.0.1:8000/loading/redirect-methods.html, http method GET>
 frame "1" - didStartProvisionalLoadForFrame
+frame "1" - didStartProvisionalLoadForFrame
 http://127.0.0.1:8000/loading/resources/redirect-methods-form.html - didReceiveResponse <NSURLResponse http://127.0.0.1:8000/loading/resources/redirect-methods-form.html, http status code 200>
 frame "1" - didCommitLoadForFrame
 frame "1" - didReceiveTitle: 
@@ -45,6 +48,7 @@
 frame "1" - didFinishLoadForFrame
 http://127.0.0.1:8000/loading/resources/redirect-methods-result.php - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/loading/resources/redirect-methods-result.php, main document URL http://127.0.0.1:8000/loading/redirect-methods.html, http method POST>
 frame "1" - didStartProvisionalLoadForFrame
+frame "1" - didStartProvisionalLoadForFrame
 http://127.0.0.1:8000/loading/resources/redirect-methods-result.php?redirected=true - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/loading/resources/redirect-methods-result.php?redirected=true, main document URL http://127.0.0.1:8000/loading/redirect-methods.html, http method GET>
 http://127.0.0.1:8000/loading/resources/redirect-methods-result.php?redirected=true - didReceiveResponse <NSURLResponse http://127.0.0.1:8000/loading/resources/redirect-methods-result.php?redirected=true, http status code 200>
 frame "1" - didCommitLoadForFrame
@@ -61,6 +65,7 @@
 frame "1" - didFinishLoadForFrame
 http://127.0.0.1:8000/loading/resources/redirect-methods-form.html - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/loading/resources/redirect-methods-form.html, main document URL http://127.0.0.1:8000/loading/redirect-methods.html, http method GET>
 frame "2" - didStartProvisionalLoadForFrame
+frame "2" - didStartProvisionalLoadForFrame
 http://127.0.0.1:8000/loading/resources/redirect-methods-form.html - didReceiveResponse <NSURLResponse http://127.0.0.1:8000/loading/resources/redirect-methods-form.html, http status code 200>
 frame "2" - didCommitLoadForFrame
 frame "2" - didReceiveTitle: 
@@ -69,6 +74,7 @@
 frame "2" - didFinishLoadForFrame
 http://127.0.0.1:8000/loading/resources/redirect-methods-result.php - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/loading/resources/redirect-methods-result.php, main document URL http://127.0.0.1:8000/loading/redirect-methods.html, http method POST>
 frame "2" - didStartProvisionalLoadForFrame
+frame "2" - didStartProvisionalLoadForFrame
 http://127.0.0.1:8000/loading/resources/redirect-methods-result.php?redirected=true - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/loading/resources/redirect-methods-result.php?redirected=true, main document URL http://127.0.0.1:8000/loading/redirect-methods.html, http method GET>
 http://127.0.0.1:8000/loading/resources/redirect-methods-result.php?redirected=true - didReceiveResponse <NSURLResponse http://127.0.0.1:8000/loading/resources/redirect-methods-result.php?redirected=true, http status code 200>
 frame "2" - didCommitLoadForFrame
@@ -85,6 +91,7 @@
 frame "2" - didFinishLoadForFrame
 http://127.0.0.1:8000/loading/resources/redirect-methods-form.html - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/loading/resources/redirect-methods-form.html, main document URL http://127.0.0.1:8000/loading/redirect-methods.html, http method GET>
 frame "3" - didStartProvisionalLoadForFrame
+frame "3" - didStartProvisionalLoadForFrame
 http://127.0.0.1:8000/loading/resources/redirect-methods-form.html - didReceiveResponse <NSURLResponse http://127.0.0.1:8000/loading/resources/redirect-methods-form.html, http status code 200>
 frame "3" - didCommitLoadForFrame
 frame "3" - didReceiveTitle: 
@@ -93,6 +100,7 @@
 frame "3" - didFinishLoadForFrame
 http://127.0.0.1:8000/loading/resources/redirect-methods-result.php - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/loading/resources/redirect-methods-result.php, main document URL http://127.0.0.1:8000/loading/redirect-methods.html, http method POST>
 frame "3" - didStartProvisionalLoadForFrame
+frame "3" - didStartProvisionalLoadForFrame
 http://127.0.0.1:8000/loading/resources/redirect-methods-result.php?redirected=true - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/loading/resources/redirect-methods-result.php?redirected=true, main document URL http://127.0.0.1:8000/loading/redirect-methods.html, http method POST>
 http://127.0.0.1:8000/loading/resources/redirect-methods-result.php?redirected=true - didReceiveResponse <NSURLResponse http://127.0.0.1:8000/loading/resources/redirect-methods-result.php?redirected=true, http status code 200>
 frame "3" - didCommitLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/loading/redirect-with-no-location-crash-expected.txt b/third_party/blink/web_tests/http/tests/loading/redirect-with-no-location-crash-expected.txt
index 46480fe..bd0c3c71 100644
--- a/third_party/blink/web_tests/http/tests/loading/redirect-with-no-location-crash-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/redirect-with-no-location-crash-expected.txt
@@ -4,6 +4,7 @@
 frame "<!--framePath //<!--frame0-->-->" - didReceiveTitle: 
 frame "<!--framePath //<!--frame0-->-->" - didStartProvisionalLoadForFrame
 main frame - didFinishDocumentLoadForFrame
+frame "<!--framePath //<!--frame0-->-->" - didStartProvisionalLoadForFrame
 frame "<!--framePath //<!--frame0-->-->" - didCommitLoadForFrame
 frame "<!--framePath //<!--frame0-->-->" - didReceiveTitle: 
 frame "<!--framePath //<!--frame0-->-->" - didFinishDocumentLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/loading/simple-subframe-expected.txt b/third_party/blink/web_tests/http/tests/loading/simple-subframe-expected.txt
index d3a3cd8..7fbc7e8 100644
--- a/third_party/blink/web_tests/http/tests/loading/simple-subframe-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/simple-subframe-expected.txt
@@ -4,6 +4,7 @@
 frame "f1" - didStartProvisionalLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
+frame "f1" - didStartProvisionalLoadForFrame
 frame "f1" - didCommitLoadForFrame
 frame "f1" - didReceiveTitle: 
 frame "f1" - didFinishDocumentLoadForFrame
@@ -11,3 +12,5 @@
 frame "f1" - didFinishLoadForFrame
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
+This is a test of load callbacks. It is only useful inside the regression test tool.
+
diff --git a/third_party/blink/web_tests/http/tests/loading/simple-subframe.html b/third_party/blink/web_tests/http/tests/loading/simple-subframe.html
index 6b22769..7ec895c 100644
--- a/third_party/blink/web_tests/http/tests/loading/simple-subframe.html
+++ b/third_party/blink/web_tests/http/tests/loading/simple-subframe.html
@@ -1,2 +1,6 @@
+<script>
+if (window.testRunner)
+  testRunner.dumpAsText();
+</script>
 This is a test of load callbacks. It is only useful inside the regression test tool.<br>
 <iframe name="f1" src="data:text/html,<b>test</b>"></iframe>
diff --git a/third_party/blink/web_tests/http/tests/loading/slow-parsing-subframe-expected.txt b/third_party/blink/web_tests/http/tests/loading/slow-parsing-subframe-expected.txt
index 486737daa..200bf469 100644
--- a/third_party/blink/web_tests/http/tests/loading/slow-parsing-subframe-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/slow-parsing-subframe-expected.txt
@@ -4,6 +4,7 @@
 frame "f1" - didStartProvisionalLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
+frame "f1" - didStartProvisionalLoadForFrame
 frame "f1" - didCommitLoadForFrame
 frame "f1" - didReceiveTitle: 
 frame "f1" - didFinishDocumentLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/loading/stop-load-at-commit-expected.txt b/third_party/blink/web_tests/http/tests/loading/stop-load-at-commit-expected.txt
index 282f638..da44157 100644
--- a/third_party/blink/web_tests/http/tests/loading/stop-load-at-commit-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/stop-load-at-commit-expected.txt
@@ -5,6 +5,7 @@
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
 main frame - didStartProvisionalLoadForFrame
+main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index f32ab94..91f680e 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -2054,6 +2054,7 @@
     method isVertexArray
     method lineWidth
     method linkProgram
+    method makeXRCompatible
     method memoryBarrier
     method memoryBarrierByRegion
     method pauseTransformFeedback
@@ -2068,7 +2069,6 @@
     method samplerParameterf
     method samplerParameteri
     method scissor
-    method setCompatibleXRDevice
     method shaderSource
     method stencilFunc
     method stencilFuncSeparate
@@ -2847,6 +2847,7 @@
     method isVertexArray
     method lineWidth
     method linkProgram
+    method makeXRCompatible
     method pauseTransformFeedback
     method pixelStorei
     method polygonOffset
@@ -2859,7 +2860,6 @@
     method samplerParameterf
     method samplerParameteri
     method scissor
-    method setCompatibleXRDevice
     method shaderSource
     method stencilFunc
     method stencilFuncSeparate
@@ -3341,13 +3341,13 @@
     method isTexture
     method lineWidth
     method linkProgram
+    method makeXRCompatible
     method pixelStorei
     method polygonOffset
     method readPixels
     method renderbufferStorage
     method sampleCoverage
     method scissor
-    method setCompatibleXRDevice
     method shaderSource
     method stencilFunc
     method stencilFuncSeparate
diff --git a/third_party/blink/web_tests/media/controls/accessibility-caption-button.html b/third_party/blink/web_tests/media/controls/accessibility-caption-button.html
new file mode 100644
index 0000000..ab4b19f
--- /dev/null
+++ b/third_party/blink/web_tests/media/controls/accessibility-caption-button.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Media Controls: caption button accessibility tests</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../media-controls.js"></script>
+<video controls>
+  <track src='../track/captions-webvtt/captions-fast.vtt' kind='captions'>
+  <track src='../track/captions-webvtt/captions-rtl.vtt' kind='captions'>
+</video>
+<script>
+async_test(t => {
+  var video = document.querySelector('video');
+  video.src = '../content/test.ogv';
+
+video.oncanplaythrough = t.step_func_done(_ => {
+    assert_true(isClosedCaptionsButtonEnabled(video));
+
+    var captions_button = toggleClosedCaptionsButton(video);
+    var captions_overflow_item = captionsOverflowItem(video);
+    assert_not_equals(captions_button, null);
+    assert_not_equals(captions_overflow_item, null);
+
+    assert_equals(captions_button.getAttribute('aria-label'),
+                  'show closed captions');
+
+    assert_equals(captions_overflow_item.getAttribute('aria-label'),
+                  'show closed captions');
+  });
+});
+</script>
diff --git a/third_party/blink/web_tests/platform/linux/http/tests/loading/simple-subframe-expected.png b/third_party/blink/web_tests/platform/linux/http/tests/loading/simple-subframe-expected.png
deleted file mode 100644
index 2442b141..0000000
--- a/third_party/blink/web_tests/platform/linux/http/tests/loading/simple-subframe-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/http/tests/loading/simple-subframe-expected.png b/third_party/blink/web_tests/platform/mac/http/tests/loading/simple-subframe-expected.png
deleted file mode 100644
index 1a1f43a..0000000
--- a/third_party/blink/web_tests/platform/mac/http/tests/loading/simple-subframe-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/http/tests/loading/simple-subframe-expected.png b/third_party/blink/web_tests/platform/win/http/tests/loading/simple-subframe-expected.png
deleted file mode 100644
index ea413391..0000000
--- a/third_party/blink/web_tests/platform/win/http/tests/loading/simple-subframe-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 8ce01926..3b2d26c 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -2088,6 +2088,7 @@
 [Worker]     method isVertexArray
 [Worker]     method lineWidth
 [Worker]     method linkProgram
+[Worker]     method makeXRCompatible
 [Worker]     method memoryBarrier
 [Worker]     method memoryBarrierByRegion
 [Worker]     method pauseTransformFeedback
@@ -2102,7 +2103,6 @@
 [Worker]     method samplerParameterf
 [Worker]     method samplerParameteri
 [Worker]     method scissor
-[Worker]     method setCompatibleXRDevice
 [Worker]     method shaderSource
 [Worker]     method stencilFunc
 [Worker]     method stencilFuncSeparate
@@ -2881,6 +2881,7 @@
 [Worker]     method isVertexArray
 [Worker]     method lineWidth
 [Worker]     method linkProgram
+[Worker]     method makeXRCompatible
 [Worker]     method pauseTransformFeedback
 [Worker]     method pixelStorei
 [Worker]     method polygonOffset
@@ -2893,7 +2894,6 @@
 [Worker]     method samplerParameterf
 [Worker]     method samplerParameteri
 [Worker]     method scissor
-[Worker]     method setCompatibleXRDevice
 [Worker]     method shaderSource
 [Worker]     method stencilFunc
 [Worker]     method stencilFuncSeparate
@@ -3375,13 +3375,13 @@
 [Worker]     method isTexture
 [Worker]     method lineWidth
 [Worker]     method linkProgram
+[Worker]     method makeXRCompatible
 [Worker]     method pixelStorei
 [Worker]     method polygonOffset
 [Worker]     method readPixels
 [Worker]     method renderbufferStorage
 [Worker]     method sampleCoverage
 [Worker]     method scissor
-[Worker]     method setCompatibleXRDevice
 [Worker]     method shaderSource
 [Worker]     method stencilFunc
 [Worker]     method stencilFuncSeparate
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index f978ba8..8abd24b 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -8710,6 +8710,7 @@
     method isVertexArray
     method lineWidth
     method linkProgram
+    method makeXRCompatible
     method memoryBarrier
     method memoryBarrierByRegion
     method pauseTransformFeedback
@@ -8724,7 +8725,6 @@
     method samplerParameterf
     method samplerParameteri
     method scissor
-    method setCompatibleXRDevice
     method shaderSource
     method stencilFunc
     method stencilFuncSeparate
@@ -9503,6 +9503,7 @@
     method isVertexArray
     method lineWidth
     method linkProgram
+    method makeXRCompatible
     method pauseTransformFeedback
     method pixelStorei
     method polygonOffset
@@ -9515,7 +9516,6 @@
     method samplerParameterf
     method samplerParameteri
     method scissor
-    method setCompatibleXRDevice
     method shaderSource
     method stencilFunc
     method stencilFuncSeparate
@@ -10001,13 +10001,13 @@
     method isTexture
     method lineWidth
     method linkProgram
+    method makeXRCompatible
     method pixelStorei
     method polygonOffset
     method readPixels
     method renderbufferStorage
     method sampleCoverage
     method scissor
-    method setCompatibleXRDevice
     method shaderSource
     method stencilFunc
     method stencilFuncSeparate
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index 2ab14ea1..9fd2be3 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -1969,6 +1969,7 @@
 [Worker]     method isVertexArray
 [Worker]     method lineWidth
 [Worker]     method linkProgram
+[Worker]     method makeXRCompatible
 [Worker]     method memoryBarrier
 [Worker]     method memoryBarrierByRegion
 [Worker]     method pauseTransformFeedback
@@ -1983,7 +1984,6 @@
 [Worker]     method samplerParameterf
 [Worker]     method samplerParameteri
 [Worker]     method scissor
-[Worker]     method setCompatibleXRDevice
 [Worker]     method shaderSource
 [Worker]     method stencilFunc
 [Worker]     method stencilFuncSeparate
@@ -2762,6 +2762,7 @@
 [Worker]     method isVertexArray
 [Worker]     method lineWidth
 [Worker]     method linkProgram
+[Worker]     method makeXRCompatible
 [Worker]     method pauseTransformFeedback
 [Worker]     method pixelStorei
 [Worker]     method polygonOffset
@@ -2774,7 +2775,6 @@
 [Worker]     method samplerParameterf
 [Worker]     method samplerParameteri
 [Worker]     method scissor
-[Worker]     method setCompatibleXRDevice
 [Worker]     method shaderSource
 [Worker]     method stencilFunc
 [Worker]     method stencilFuncSeparate
@@ -3256,13 +3256,13 @@
 [Worker]     method isTexture
 [Worker]     method lineWidth
 [Worker]     method linkProgram
+[Worker]     method makeXRCompatible
 [Worker]     method pixelStorei
 [Worker]     method polygonOffset
 [Worker]     method readPixels
 [Worker]     method renderbufferStorage
 [Worker]     method sampleCoverage
 [Worker]     method scissor
-[Worker]     method setCompatibleXRDevice
 [Worker]     method shaderSource
 [Worker]     method stencilFunc
 [Worker]     method stencilFuncSeparate
diff --git a/third_party/blink/web_tests/xr/resources/xr-test-utils.js b/third_party/blink/web_tests/xr/resources/xr-test-utils.js
index 23f29f15..ba23310 100644
--- a/third_party/blink/web_tests/xr/resources/xr-test-utils.js
+++ b/third_party/blink/web_tests/xr/resources/xr-test-utils.js
@@ -17,7 +17,7 @@
         })
         .then((device) => {
           if (gl) {
-            return gl.setCompatibleXRDevice(device).then(
+            return gl.makeXRCompatible(device).then(
                 () => Promise.resolve(device));
           } else {
             return Promise.resolve(device);
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index 62489c4..d7d36ea 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: unknown
-Revision: c8a016b99d97e2e5642fd7892b48a5ed0eb6d0d1
+Revision: b3eeffaa18c22288c739325a3a8293065e13593e
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
diff --git a/third_party/crashpad/crashpad/DEPS b/third_party/crashpad/crashpad/DEPS
index 635e141..10c37bc 100644
--- a/third_party/crashpad/crashpad/DEPS
+++ b/third_party/crashpad/crashpad/DEPS
@@ -30,7 +30,7 @@
       '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f',
   'crashpad/third_party/mini_chromium/mini_chromium':
       Var('chromium_git') + '/chromium/mini_chromium@' +
-      '4a6189577978b86fbd6935aec0d16a127803467c',
+      '737433ebade4d446643c6c07daae02a67e8decca',
   'crashpad/third_party/libfuzzer/src':
       Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' +
       'fda403cf93ecb8792cb1d061564d89a6553ca020',
diff --git a/third_party/crashpad/crashpad/client/crash_report_database_mac.mm b/third_party/crashpad/crashpad/client/crash_report_database_mac.mm
index 0980fe3f..414dd5a4 100644
--- a/third_party/crashpad/crashpad/client/crash_report_database_mac.mm
+++ b/third_party/crashpad/crashpad/client/crash_report_database_mac.mm
@@ -29,13 +29,13 @@
 #include "base/mac/scoped_nsautorelease_pool.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/scoped_generic.h"
+#include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/sys_string_conversions.h"
 #include "client/settings.h"
 #include "util/file/file_io.h"
 #include "util/mac/xattr.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/initialization_state_dcheck.h"
 #include "util/misc/metrics.h"
 
@@ -280,7 +280,7 @@
   }
 
   // Create the three processing directories for the database.
-  for (size_t i = 0; i < ArraySize(kReportDirectories); ++i) {
+  for (size_t i = 0; i < base::size(kReportDirectories); ++i) {
     if (!CreateOrEnsureDirectoryExists(base_dir_.Append(kReportDirectories[i])))
       return false;
   }
diff --git a/third_party/crashpad/crashpad/client/simulate_crash_mac.cc b/third_party/crashpad/crashpad/client/simulate_crash_mac.cc
index 1f7eca9..43d05b81 100644
--- a/third_party/crashpad/crashpad/client/simulate_crash_mac.cc
+++ b/third_party/crashpad/crashpad/client/simulate_crash_mac.cc
@@ -20,13 +20,13 @@
 #include "base/logging.h"
 #include "base/mac/mach_logging.h"
 #include "base/mac/scoped_mach_port.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "util/mach/exc_client_variants.h"
 #include "util/mach/exception_behaviors.h"
 #include "util/mach/exception_ports.h"
 #include "util/mach/mach_extensions.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/implicit_cast.h"
 
 namespace crashpad {
@@ -191,7 +191,7 @@
   base::mac::ScopedMachSendRight thread(mach_thread_self());
   exception_type_t exception = kMachExceptionSimulated;
   mach_exception_data_type_t codes[] = {0, 0};
-  mach_msg_type_number_t code_count = ArraySize(codes);
+  mach_msg_type_number_t code_count = base::size(codes);
 
   // Look up the handler for EXC_CRASH exceptions in the same way that the
   // kernel would: try a thread handler, then a task handler, and finally a host
@@ -213,7 +213,7 @@
   bool success = false;
 
   for (size_t target_type_index = 0;
-       !success && target_type_index < ArraySize(kTargetTypes);
+       !success && target_type_index < base::size(kTargetTypes);
        ++target_type_index) {
     ExceptionPorts::ExceptionHandlerVector handlers;
     ExceptionPorts exception_ports(kTargetTypes[target_type_index],
diff --git a/third_party/crashpad/crashpad/client/simulate_crash_mac_test.cc b/third_party/crashpad/crashpad/client/simulate_crash_mac_test.cc
index 910971a..c2f4c20 100644
--- a/third_party/crashpad/crashpad/client/simulate_crash_mac_test.cc
+++ b/third_party/crashpad/crashpad/client/simulate_crash_mac_test.cc
@@ -19,6 +19,7 @@
 #include <sys/types.h>
 
 #include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "gtest/gtest.h"
@@ -31,7 +32,6 @@
 #include "util/mach/mach_message.h"
 #include "util/mach/mach_message_server.h"
 #include "util/mach/symbolic_constants_mach.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/implicit_cast.h"
 
 namespace crashpad {
@@ -343,13 +343,13 @@
 #endif
   };
 
-  for (size_t target_index = 0; target_index < ArraySize(kTargets);
+  for (size_t target_index = 0; target_index < base::size(kTargets);
        ++target_index) {
     TestSimulateCrashMac::ExceptionPortsTarget target = kTargets[target_index];
     SCOPED_TRACE(base::StringPrintf(
         "target_index %zu, target %d", target_index, target));
 
-    for (size_t behavior_index = 0; behavior_index < ArraySize(kBehaviors);
+    for (size_t behavior_index = 0; behavior_index < base::size(kBehaviors);
          ++behavior_index) {
       exception_behavior_t behavior = kBehaviors[behavior_index];
       SCOPED_TRACE(base::StringPrintf(
@@ -363,7 +363,7 @@
             target, behavior, THREAD_STATE_NONE);
         test_simulate_crash_mac.Run();
       } else {
-        for (size_t flavor_index = 0; flavor_index < ArraySize(kFlavors);
+        for (size_t flavor_index = 0; flavor_index < base::size(kFlavors);
              ++flavor_index) {
           thread_state_flavor_t flavor = kFlavors[flavor_index];
           SCOPED_TRACE(base::StringPrintf(
diff --git a/third_party/crashpad/crashpad/handler/mac/file_limit_annotation.cc b/third_party/crashpad/crashpad/handler/mac/file_limit_annotation.cc
index 5646bde..a01e87d9 100644
--- a/third_party/crashpad/crashpad/handler/mac/file_limit_annotation.cc
+++ b/third_party/crashpad/crashpad/handler/mac/file_limit_annotation.cc
@@ -24,10 +24,10 @@
 #include <string>
 
 #include "base/format_macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "client/crashpad_info.h"
 #include "client/simple_string_dictionary.h"
-#include "util/misc/arraysize.h"
 #include "util/posix/scoped_dir.h"
 
 namespace crashpad {
@@ -108,7 +108,7 @@
   int mib[] = {CTL_KERN, KERN_MAXFILES};
   size = sizeof(value);
   std::string max_files = FormatFromSysctl(
-      sysctl(mib, ArraySize(mib), &value, &size, nullptr, 0), &value, &size);
+      sysctl(mib, base::size(mib), &value, &size, nullptr, 0), &value, &size);
 
   std::string open_files = CountOpenFileDescriptors();
 
diff --git a/third_party/crashpad/crashpad/handler/win/crashy_test_program.cc b/third_party/crashpad/crashpad/handler/win/crashy_test_program.cc
index d6b4dead..990929b 100644
--- a/third_party/crashpad/crashpad/handler/win/crashy_test_program.cc
+++ b/third_party/crashpad/crashpad/handler/win/crashy_test_program.cc
@@ -26,10 +26,10 @@
 
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "build/build_config.h"
 #include "client/crashpad_client.h"
 #include "client/crashpad_info.h"
-#include "util/misc/arraysize.h"
 #include "util/win/critical_section_with_debug_info.h"
 #include "util/win/get_function.h"
 
@@ -83,11 +83,11 @@
   // All of these allocations are leaked, we want to view them in windbg via
   // !vprot.
   void* reserve = VirtualAlloc(
-      nullptr, ArraySize(kPageTypes) * kPageSize, MEM_RESERVE, PAGE_READWRITE);
+      nullptr, base::size(kPageTypes) * kPageSize, MEM_RESERVE, PAGE_READWRITE);
   PCHECK(reserve) << "VirtualAlloc MEM_RESERVE";
   uintptr_t reserve_as_int = reinterpret_cast<uintptr_t>(reserve);
 
-  for (size_t i = 0; i < ArraySize(kPageTypes); ++i) {
+  for (size_t i = 0; i < base::size(kPageTypes); ++i) {
     void* result =
         VirtualAlloc(reinterpret_cast<void*>(reserve_as_int + (kPageSize * i)),
                      kPageSize,
diff --git a/third_party/crashpad/crashpad/handler/win/hanging_program.cc b/third_party/crashpad/crashpad/handler/win/hanging_program.cc
index 49d5cea..f4e5b983 100644
--- a/third_party/crashpad/crashpad/handler/win/hanging_program.cc
+++ b/third_party/crashpad/crashpad/handler/win/hanging_program.cc
@@ -17,11 +17,11 @@
 
 #include "base/debug/alias.h"
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "client/crashpad_client.h"
 #include "client/crashpad_info.h"
-#include "util/misc/arraysize.h"
 
 namespace {
 
@@ -124,7 +124,7 @@
 
   // This is not expected to return.
   DWORD count = WaitForMultipleObjects(
-      static_cast<DWORD>(ArraySize(threads)), threads, true, INFINITE);
+      static_cast<DWORD>(base::size(threads)), threads, true, INFINITE);
   if (count == WAIT_FAILED) {
     PLOG(ERROR) << "WaitForMultipleObjects";
   } else {
diff --git a/third_party/crashpad/crashpad/minidump/minidump_annotation_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_annotation_writer_test.cc
index f86ff467..dc05cc64 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_annotation_writer_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_annotation_writer_test.cc
@@ -16,13 +16,13 @@
 
 #include <memory>
 
+#include "base/stl_util.h"
 #include "gtest/gtest.h"
 #include "minidump/minidump_extensions.h"
 #include "minidump/test/minidump_byte_array_writer_test_util.h"
 #include "minidump/test/minidump_string_writer_test_util.h"
 #include "minidump/test/minidump_writable_test_util.h"
 #include "util/file/string_file.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -107,7 +107,7 @@
 
   MinidumpAnnotationListWriter list_writer;
 
-  for (size_t i = 0; i < ArraySize(kNames); ++i) {
+  for (size_t i = 0; i < base::size(kNames); ++i) {
     auto annotation = std::make_unique<MinidumpAnnotationWriter>();
     annotation->InitializeWithData(kNames[i], kTypes[i], kValues[i]);
     list_writer.AddObject(std::move(annotation));
diff --git a/third_party/crashpad/crashpad/minidump/minidump_byte_array_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_byte_array_writer_test.cc
index 94fad5c..e4cd526e 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_byte_array_writer_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_byte_array_writer_test.cc
@@ -17,11 +17,11 @@
 #include <memory>
 
 #include "base/format_macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
 #include "minidump/test/minidump_writable_test_util.h"
 #include "util/file/string_file.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -35,7 +35,7 @@
       {},
   };
 
-  for (size_t i = 0; i < ArraySize(kTests); ++i) {
+  for (size_t i = 0; i < base::size(kTests); ++i) {
     SCOPED_TRACE(base::StringPrintf("index %" PRIuS, i));
 
     StringFile string_file;
@@ -67,7 +67,7 @@
     {},
   };
 
-  for (size_t i = 0; i < ArraySize(kTests); ++i) {
+  for (size_t i = 0; i < base::size(kTests); ++i) {
     SCOPED_TRACE(base::StringPrintf("index %" PRIuS, i));
 
     crashpad::MinidumpByteArrayWriter writer;
diff --git a/third_party/crashpad/crashpad/minidump/minidump_exception_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_exception_writer_test.cc
index b3a4e809..75adbee2 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_exception_writer_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_exception_writer_test.cc
@@ -17,6 +17,7 @@
 #include <string>
 #include <utility>
 
+#include "base/stl_util.h"
 #include "gtest/gtest.h"
 #include "minidump/minidump_context.h"
 #include "minidump/minidump_context_writer.h"
@@ -29,7 +30,6 @@
 #include "snapshot/test/test_exception_snapshot.h"
 #include "test/gtest_death.h"
 #include "util/file/string_file.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -81,7 +81,7 @@
             expected->ExceptionRecord.NumberParameters);
   EXPECT_EQ(observed->ExceptionRecord.__unusedAlignment, 0u);
   for (size_t index = 0;
-       index < ArraySize(observed->ExceptionRecord.ExceptionInformation);
+       index < base::size(observed->ExceptionRecord.ExceptionInformation);
        ++index) {
     EXPECT_EQ(observed->ExceptionRecord.ExceptionInformation[index],
               expected->ExceptionRecord.ExceptionInformation[index]);
diff --git a/third_party/crashpad/crashpad/minidump/minidump_file_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_file_writer_test.cc
index ef4813e..8483229 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_file_writer_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_file_writer_test.cc
@@ -20,6 +20,7 @@
 #include <string>
 #include <utility>
 
+#include "base/stl_util.h"
 #include "build/build_config.h"
 #include "gtest/gtest.h"
 #include "minidump/minidump_stream_writer.h"
@@ -36,7 +37,6 @@
 #include "snapshot/test/test_thread_snapshot.h"
 #include "test/gtest_death.h"
 #include "util/file/string_file.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -135,7 +135,7 @@
   minidump_file.SetTimestamp(kTimestamp);
 
   static constexpr uint8_t kStreamData[] = "Hello World!";
-  constexpr size_t kStreamSize = ArraySize(kStreamData);
+  constexpr size_t kStreamSize = base::size(kStreamData);
   constexpr MinidumpStreamType kStreamType =
       static_cast<MinidumpStreamType>(0x4d);
 
diff --git a/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc
index fa71caf..90287cb 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc
@@ -17,6 +17,7 @@
 #include <utility>
 
 #include "base/format_macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
 #include "minidump/minidump_extensions.h"
@@ -26,7 +27,6 @@
 #include "minidump/test/minidump_writable_test_util.h"
 #include "snapshot/test/test_memory_snapshot.h"
 #include "util/file/string_file.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -341,7 +341,7 @@
 
 TEST(MinidumpMemoryWriter, AddFromSnapshot) {
   MINIDUMP_MEMORY_DESCRIPTOR expect_memory_descriptors[3] = {};
-  uint8_t values[ArraySize(expect_memory_descriptors)] = {};
+  uint8_t values[base::size(expect_memory_descriptors)] = {};
 
   expect_memory_descriptors[0].StartOfMemoryRange = 0;
   expect_memory_descriptors[0].Memory.DataSize = 0x1000;
@@ -357,7 +357,7 @@
 
   std::vector<std::unique_ptr<TestMemorySnapshot>> memory_snapshots_owner;
   std::vector<const MemorySnapshot*> memory_snapshots;
-  for (size_t index = 0; index < ArraySize(expect_memory_descriptors);
+  for (size_t index = 0; index < base::size(expect_memory_descriptors);
        ++index) {
     memory_snapshots_owner.push_back(std::make_unique<TestMemorySnapshot>());
     TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get();
@@ -396,7 +396,7 @@
 
 TEST(MinidumpMemoryWriter, CoalesceExplicitMultiple) {
   MINIDUMP_MEMORY_DESCRIPTOR expect_memory_descriptors[4] = {};
-  uint8_t values[ArraySize(expect_memory_descriptors)] = {};
+  uint8_t values[base::size(expect_memory_descriptors)] = {};
 
   expect_memory_descriptors[0].StartOfMemoryRange = 0;
   expect_memory_descriptors[0].Memory.DataSize = 1000;
diff --git a/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.cc
index 2d70d00..a134076 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer.cc
@@ -18,6 +18,7 @@
 
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
@@ -26,7 +27,6 @@
 #include "snapshot/process_snapshot.h"
 #include "snapshot/system_snapshot.h"
 #include "util/file/file_writer.h"
-#include "util/misc/arraysize.h"
 #include "util/numeric/in_range_cast.h"
 #include "util/numeric/safe_assignment.h"
 
@@ -302,7 +302,7 @@
 
   internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
       misc_info_.TimeZone.StandardName,
-      ArraySize(misc_info_.TimeZone.StandardName),
+      base::size(misc_info_.TimeZone.StandardName),
       standard_name);
 
   misc_info_.TimeZone.StandardDate = standard_date;
@@ -310,7 +310,7 @@
 
   internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
       misc_info_.TimeZone.DaylightName,
-      ArraySize(misc_info_.TimeZone.DaylightName),
+      base::size(misc_info_.TimeZone.DaylightName),
       daylight_name);
 
   misc_info_.TimeZone.DaylightDate = daylight_date;
@@ -327,10 +327,10 @@
   misc_info_.Flags1 |= MINIDUMP_MISC4_BUILDSTRING;
 
   internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
-      misc_info_.BuildString, ArraySize(misc_info_.BuildString), build_string);
+      misc_info_.BuildString, base::size(misc_info_.BuildString), build_string);
   internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
       misc_info_.DbgBldStr,
-      ArraySize(misc_info_.DbgBldStr),
+      base::size(misc_info_.DbgBldStr),
       debug_build_string);
 }
 
diff --git a/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer_test.cc
index e134ccd..bd927338 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer_test.cc
@@ -21,6 +21,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/format_macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -31,7 +32,6 @@
 #include "snapshot/test/test_process_snapshot.h"
 #include "snapshot/test/test_system_snapshot.h"
 #include "util/file/string_file.h"
-#include "util/misc/arraysize.h"
 #include "util/stdlib/strlcpy.h"
 
 namespace crashpad {
@@ -127,7 +127,7 @@
     SCOPED_TRACE("Standard");
     ExpectNULPaddedString16Equal(expected->TimeZone.StandardName,
                                  observed->TimeZone.StandardName,
-                                 ArraySize(expected->TimeZone.StandardName));
+                                 base::size(expected->TimeZone.StandardName));
     ExpectSystemTimeEqual(&expected->TimeZone.StandardDate,
                           &observed->TimeZone.StandardDate);
     EXPECT_EQ(observed->TimeZone.StandardBias, expected->TimeZone.StandardBias);
@@ -136,7 +136,7 @@
     SCOPED_TRACE("Daylight");
     ExpectNULPaddedString16Equal(expected->TimeZone.DaylightName,
                                  observed->TimeZone.DaylightName,
-                                 ArraySize(expected->TimeZone.DaylightName));
+                                 base::size(expected->TimeZone.DaylightName));
     ExpectSystemTimeEqual(&expected->TimeZone.DaylightDate,
                           &observed->TimeZone.DaylightDate);
     EXPECT_EQ(observed->TimeZone.DaylightBias, expected->TimeZone.DaylightBias);
@@ -154,13 +154,13 @@
     SCOPED_TRACE("BuildString");
     ExpectNULPaddedString16Equal(expected->BuildString,
                                  observed->BuildString,
-                                 ArraySize(expected->BuildString));
+                                 base::size(expected->BuildString));
   }
   {
     SCOPED_TRACE("DbgBldStr");
     ExpectNULPaddedString16Equal(expected->DbgBldStr,
                                  observed->DbgBldStr,
-                                 ArraySize(expected->DbgBldStr));
+                                 base::size(expected->DbgBldStr));
   }
 }
 
@@ -176,7 +176,7 @@
   EXPECT_EQ(observed->XStateData.EnabledFeatures,
             expected->XStateData.EnabledFeatures);
   for (size_t feature_index = 0;
-       feature_index < ArraySize(observed->XStateData.Features);
+       feature_index < base::size(observed->XStateData.Features);
        ++feature_index) {
     SCOPED_TRACE(base::StringPrintf("feature_index %" PRIuS, feature_index));
     EXPECT_EQ(observed->XStateData.Features[feature_index].Offset,
@@ -396,7 +396,7 @@
   base::string16 standard_name_utf16 = base::UTF8ToUTF16(kStandardName);
   c16lcpy(expected.TimeZone.StandardName,
           standard_name_utf16.c_str(),
-          ArraySize(expected.TimeZone.StandardName));
+          base::size(expected.TimeZone.StandardName));
   memcpy(&expected.TimeZone.StandardDate,
          &kStandardDate,
          sizeof(expected.TimeZone.StandardDate));
@@ -404,7 +404,7 @@
   base::string16 daylight_name_utf16 = base::UTF8ToUTF16(kDaylightName);
   c16lcpy(expected.TimeZone.DaylightName,
           daylight_name_utf16.c_str(),
-          ArraySize(expected.TimeZone.DaylightName));
+          base::size(expected.TimeZone.DaylightName));
   memcpy(&expected.TimeZone.DaylightDate,
          &kDaylightDate,
          sizeof(expected.TimeZone.DaylightDate));
@@ -424,9 +424,9 @@
   constexpr int32_t kBias = 300;
   MINIDUMP_MISC_INFO_N tmp;
   ALLOW_UNUSED_LOCAL(tmp);
-  std::string standard_name(ArraySize(tmp.TimeZone.StandardName) + 1, 's');
+  std::string standard_name(base::size(tmp.TimeZone.StandardName) + 1, 's');
   constexpr int32_t kStandardBias = 0;
-  std::string daylight_name(ArraySize(tmp.TimeZone.DaylightName), 'd');
+  std::string daylight_name(base::size(tmp.TimeZone.DaylightName), 'd');
   constexpr int32_t kDaylightBias = -60;
 
   // Test using kSystemTimeZero, because not all platforms will be able to
@@ -457,7 +457,7 @@
   base::string16 standard_name_utf16 = base::UTF8ToUTF16(standard_name);
   c16lcpy(expected.TimeZone.StandardName,
           standard_name_utf16.c_str(),
-          ArraySize(expected.TimeZone.StandardName));
+          base::size(expected.TimeZone.StandardName));
   memcpy(&expected.TimeZone.StandardDate,
          &kSystemTimeZero,
          sizeof(expected.TimeZone.StandardDate));
@@ -465,7 +465,7 @@
   base::string16 daylight_name_utf16 = base::UTF8ToUTF16(daylight_name);
   c16lcpy(expected.TimeZone.DaylightName,
           daylight_name_utf16.c_str(),
-          ArraySize(expected.TimeZone.DaylightName));
+          base::size(expected.TimeZone.DaylightName));
   memcpy(&expected.TimeZone.DaylightDate,
          &kSystemTimeZero,
          sizeof(expected.TimeZone.DaylightDate));
@@ -496,12 +496,12 @@
   base::string16 build_string_utf16 = base::UTF8ToUTF16(kBuildString);
   c16lcpy(expected.BuildString,
           build_string_utf16.c_str(),
-          ArraySize(expected.BuildString));
+          base::size(expected.BuildString));
   base::string16 debug_build_string_utf16 =
       base::UTF8ToUTF16(kDebugBuildString);
   c16lcpy(expected.DbgBldStr,
           debug_build_string_utf16.c_str(),
-          ArraySize(expected.DbgBldStr));
+          base::size(expected.DbgBldStr));
 
   ExpectMiscInfoEqual(&expected, observed);
 }
@@ -515,8 +515,8 @@
 
   MINIDUMP_MISC_INFO_N tmp;
   ALLOW_UNUSED_LOCAL(tmp);
-  std::string build_string(ArraySize(tmp.BuildString) + 1, 'B');
-  std::string debug_build_string(ArraySize(tmp.DbgBldStr), 'D');
+  std::string build_string(base::size(tmp.BuildString) + 1, 'B');
+  std::string debug_build_string(base::size(tmp.DbgBldStr), 'D');
 
   misc_info_writer->SetBuildString(build_string, debug_build_string);
 
@@ -533,12 +533,12 @@
   base::string16 build_string_utf16 = base::UTF8ToUTF16(build_string);
   c16lcpy(expected.BuildString,
           build_string_utf16.c_str(),
-          ArraySize(expected.BuildString));
+          base::size(expected.BuildString));
   base::string16 debug_build_string_utf16 =
       base::UTF8ToUTF16(debug_build_string);
   c16lcpy(expected.DbgBldStr,
           debug_build_string_utf16.c_str(),
-          ArraySize(expected.DbgBldStr));
+          base::size(expected.DbgBldStr));
 
   ExpectMiscInfoEqual(&expected, observed);
 }
@@ -678,7 +678,7 @@
   base::string16 standard_name_utf16 = base::UTF8ToUTF16(kStandardName);
   c16lcpy(expected.TimeZone.StandardName,
           standard_name_utf16.c_str(),
-          ArraySize(expected.TimeZone.StandardName));
+          base::size(expected.TimeZone.StandardName));
   memcpy(&expected.TimeZone.StandardDate,
          &kSystemTimeZero,
          sizeof(expected.TimeZone.StandardDate));
@@ -686,7 +686,7 @@
   base::string16 daylight_name_utf16 = base::UTF8ToUTF16(kDaylightName);
   c16lcpy(expected.TimeZone.DaylightName,
           daylight_name_utf16.c_str(),
-          ArraySize(expected.TimeZone.DaylightName));
+          base::size(expected.TimeZone.DaylightName));
   memcpy(&expected.TimeZone.DaylightDate,
          &kSystemTimeZero,
          sizeof(expected.TimeZone.DaylightDate));
@@ -694,12 +694,12 @@
   base::string16 build_string_utf16 = base::UTF8ToUTF16(kBuildString);
   c16lcpy(expected.BuildString,
           build_string_utf16.c_str(),
-          ArraySize(expected.BuildString));
+          base::size(expected.BuildString));
   base::string16 debug_build_string_utf16 =
       base::UTF8ToUTF16(kDebugBuildString);
   c16lcpy(expected.DbgBldStr,
           debug_build_string_utf16.c_str(),
-          ArraySize(expected.DbgBldStr));
+          base::size(expected.DbgBldStr));
 
   ExpectMiscInfoEqual(&expected, observed);
 }
@@ -743,18 +743,18 @@
   expect_misc_info.TimeZone.Bias = 300;
   c16lcpy(expect_misc_info.TimeZone.StandardName,
           standard_time_name_utf16.c_str(),
-          ArraySize(expect_misc_info.TimeZone.StandardName));
+          base::size(expect_misc_info.TimeZone.StandardName));
   expect_misc_info.TimeZone.StandardBias = 0;
   c16lcpy(expect_misc_info.TimeZone.DaylightName,
           daylight_time_name_utf16.c_str(),
-          ArraySize(expect_misc_info.TimeZone.DaylightName));
+          base::size(expect_misc_info.TimeZone.DaylightName));
   expect_misc_info.TimeZone.DaylightBias = -60;
   c16lcpy(expect_misc_info.BuildString,
           build_string_utf16.c_str(),
-          ArraySize(expect_misc_info.BuildString));
+          base::size(expect_misc_info.BuildString));
   c16lcpy(expect_misc_info.DbgBldStr,
           debug_build_string_utf16.c_str(),
-          ArraySize(expect_misc_info.DbgBldStr));
+          base::size(expect_misc_info.DbgBldStr));
 
   const timeval kStartTime =
       { static_cast<time_t>(expect_misc_info.ProcessCreateTime), 0 };
diff --git a/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer_test.cc
index 83401eca..ba4ab05 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_module_crashpad_info_writer_test.cc
@@ -19,6 +19,7 @@
 
 #include <utility>
 
+#include "base/stl_util.h"
 #include "gtest/gtest.h"
 #include "minidump/minidump_annotation_writer.h"
 #include "minidump/minidump_simple_string_dictionary_writer.h"
@@ -28,7 +29,6 @@
 #include "minidump/test/minidump_writable_test_util.h"
 #include "snapshot/test/test_module_snapshot.h"
 #include "util/file/string_file.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -155,9 +155,9 @@
                 sizeof(MinidumpSimpleStringDictionaryEntry) +
                 sizeof(MinidumpAnnotationList) + 2 +  // padding
                 sizeof(MinidumpAnnotation) + sizeof(MinidumpUTF8String) +
-                ArraySize(kEntry) + 2 +  // padding
-                sizeof(MinidumpUTF8String) + ArraySize(kKey) +
-                sizeof(MinidumpUTF8String) + ArraySize(kValue) +
+                base::size(kEntry) + 2 +  // padding
+                sizeof(MinidumpUTF8String) + base::size(kKey) +
+                sizeof(MinidumpUTF8String) + base::size(kValue) +
                 sizeof(MinidumpUTF8String) + annotation.name.size() + 1 +
                 sizeof(MinidumpByteArray) + annotation.value.size());
 
diff --git a/third_party/crashpad/crashpad/minidump/minidump_module_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_module_writer_test.cc
index bef6a8c..8b5fc062 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_module_writer_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_module_writer_test.cc
@@ -20,6 +20,7 @@
 #include <utility>
 
 #include "base/format_macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "gtest/gtest.h"
@@ -30,7 +31,6 @@
 #include "snapshot/test/test_module_snapshot.h"
 #include "test/gtest_death.h"
 #include "util/file/string_file.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/implicit_cast.h"
 #include "util/misc/uuid.h"
 
@@ -651,10 +651,10 @@
 
 TEST(MinidumpModuleWriter, InitializeFromSnapshot) {
   MINIDUMP_MODULE expect_modules[3] = {};
-  const char* module_paths[ArraySize(expect_modules)] = {};
-  const char* module_pdbs[ArraySize(expect_modules)] = {};
-  UUID uuids[ArraySize(expect_modules)] = {};
-  uint32_t ages[ArraySize(expect_modules)] = {};
+  const char* module_paths[base::size(expect_modules)] = {};
+  const char* module_pdbs[base::size(expect_modules)] = {};
+  UUID uuids[base::size(expect_modules)] = {};
+  uint32_t ages[base::size(expect_modules)] = {};
 
   expect_modules[0].BaseOfImage = 0x100101000;
   expect_modules[0].SizeOfImage = 0xf000;
@@ -706,7 +706,7 @@
 
   std::vector<std::unique_ptr<TestModuleSnapshot>> module_snapshots_owner;
   std::vector<const ModuleSnapshot*> module_snapshots;
-  for (size_t index = 0; index < ArraySize(expect_modules); ++index) {
+  for (size_t index = 0; index < base::size(expect_modules); ++index) {
     module_snapshots_owner.push_back(std::make_unique<TestModuleSnapshot>());
     TestModuleSnapshot* module_snapshot = module_snapshots_owner.back().get();
     InitializeTestModuleSnapshotFromMinidumpModule(module_snapshot,
diff --git a/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer_test.cc
index bd376a9f..3807432 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_rva_list_writer_test.cc
@@ -17,12 +17,12 @@
 #include <utility>
 
 #include "base/format_macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
 #include "minidump/test/minidump_rva_list_test_util.h"
 #include "minidump/test/minidump_writable_test_util.h"
 #include "util/file/string_file.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -87,10 +87,10 @@
   ASSERT_TRUE(list_writer.WriteEverything(&string_file));
 
   const MinidumpRVAList* list =
-      MinidumpRVAListAtStart(string_file.string(), ArraySize(kValues));
+      MinidumpRVAListAtStart(string_file.string(), base::size(kValues));
   ASSERT_TRUE(list);
 
-  for (size_t index = 0; index < ArraySize(kValues); ++index) {
+  for (size_t index = 0; index < base::size(kValues); ++index) {
     SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index));
 
     const uint32_t* child = MinidumpWritableAtRVA<uint32_t>(
diff --git a/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc
index aa73503..ddb8c48 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc
@@ -18,6 +18,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/format_macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "gtest/gtest.h"
@@ -25,7 +26,6 @@
 #include "minidump/test/minidump_string_writer_test_util.h"
 #include "minidump/test/minidump_writable_test_util.h"
 #include "util/file/string_file.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -67,15 +67,16 @@
       {4, "\360\220\204\202", 2, {0xd800, 0xdd02}},  // 𐄂 (non-BMP)
   };
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     SCOPED_TRACE(base::StringPrintf(
         "index %" PRIuS ", input %s", index, kTestData[index].input_string));
 
     // Make sure that the expected output string with its NUL terminator fits in
     // the space provided.
-    ASSERT_EQ(kTestData[index]
-                  .output_string[ArraySize(kTestData[index].output_string) - 1],
-              0);
+    ASSERT_EQ(
+        kTestData[index]
+            .output_string[base::size(kTestData[index].output_string) - 1],
+        0);
 
     string_file.Reset();
     crashpad::internal::MinidumpUTF16StringWriter string_writer;
@@ -119,7 +120,7 @@
       "\303\0\251",  // NUL in middle of valid sequence
   };
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     SCOPED_TRACE(base::StringPrintf(
         "index %" PRIuS ", input %s", index, kTestData[index]));
     string_file.Reset();
@@ -182,7 +183,7 @@
       {4, "\360\220\204\202"},  // 𐄂 (non-BMP)
   };
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     SCOPED_TRACE(base::StringPrintf(
         "index %" PRIuS ", input %s", index, kTestData[index].string));
 
diff --git a/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.cc
index df0e348b..9bc013d 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_system_info_writer.cc
@@ -17,6 +17,7 @@
 #include <string.h>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "minidump/minidump_string_writer.h"
 #include "snapshot/system_snapshot.h"
 #include "util/file/file_writer.h"
@@ -230,7 +231,7 @@
       sizeof(registers) == sizeof(system_info_.Cpu.X86CpuInfo.VendorId),
       "VendorId sizes must be equal");
 
-  for (size_t index = 0; index < ArraySize(registers); ++index) {
+  for (size_t index = 0; index < base::size(registers); ++index) {
     memcpy(&registers[index],
            &vendor[index * sizeof(*registers)],
            sizeof(*registers));
diff --git a/third_party/crashpad/crashpad/minidump/minidump_thread_id_map_test.cc b/third_party/crashpad/crashpad/minidump/minidump_thread_id_map_test.cc
index d64d8106..548729e 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_thread_id_map_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_thread_id_map_test.cc
@@ -19,9 +19,9 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/stl_util.h"
 #include "gtest/gtest.h"
 #include "snapshot/test/test_thread_snapshot.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -39,7 +39,8 @@
 
   // testing::Test:
   void SetUp() override {
-    for (size_t index = 0; index < ArraySize(test_thread_snapshots_); ++index) {
+    for (size_t index = 0; index < base::size(test_thread_snapshots_);
+         ++index) {
       thread_snapshots_.push_back(&test_thread_snapshots_[index]);
     }
   }
@@ -60,7 +61,7 @@
   }
 
   void SetThreadID(size_t index, uint64_t thread_id) {
-    ASSERT_LT(index, ArraySize(test_thread_snapshots_));
+    ASSERT_LT(index, base::size(test_thread_snapshots_));
     test_thread_snapshots_[index].SetThreadID(thread_id);
   }
 
diff --git a/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc
index 63269ce..956d1005 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc
@@ -19,6 +19,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/format_macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
 #include "minidump/minidump_context_writer.h"
@@ -33,7 +34,6 @@
 #include "snapshot/test/test_thread_snapshot.h"
 #include "test/gtest_death.h"
 #include "util/file/string_file.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -523,10 +523,10 @@
 void RunInitializeFromSnapshotTest(bool thread_id_collision) {
   using MinidumpContextType = typename Traits::MinidumpContextType;
   MINIDUMP_THREAD expect_threads[3] = {};
-  uint64_t thread_ids[ArraySize(expect_threads)] = {};
-  uint8_t memory_values[ArraySize(expect_threads)] = {};
-  uint32_t context_seeds[ArraySize(expect_threads)] = {};
-  MINIDUMP_MEMORY_DESCRIPTOR tebs[ArraySize(expect_threads)] = {};
+  uint64_t thread_ids[base::size(expect_threads)] = {};
+  uint8_t memory_values[base::size(expect_threads)] = {};
+  uint32_t context_seeds[base::size(expect_threads)] = {};
+  MINIDUMP_MEMORY_DESCRIPTOR tebs[base::size(expect_threads)] = {};
 
   constexpr size_t kTebSize = 1024;
 
@@ -582,7 +582,7 @@
 
   std::vector<std::unique_ptr<TestThreadSnapshot>> thread_snapshots_owner;
   std::vector<const ThreadSnapshot*> thread_snapshots;
-  for (size_t index = 0; index < ArraySize(expect_threads); ++index) {
+  for (size_t index = 0; index < base::size(expect_threads); ++index) {
     thread_snapshots_owner.push_back(std::make_unique<TestThreadSnapshot>());
     TestThreadSnapshot* thread_snapshot = thread_snapshots_owner.back().get();
 
diff --git a/third_party/crashpad/crashpad/minidump/minidump_writable.cc b/third_party/crashpad/crashpad/minidump/minidump_writable.cc
index c8075c61..fa3e2f1 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_writable.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_writable.cc
@@ -17,8 +17,8 @@
 #include <stdint.h>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "util/file/file_writer.h"
-#include "util/misc/arraysize.h"
 #include "util/numeric/safe_assignment.h"
 
 namespace {
@@ -245,7 +245,7 @@
   // The number of elements in kZeroes must be at least one less than the
   // maximum Alignment() ever encountered.
   static constexpr uint8_t kZeroes[kMaximumAlignment - 1] = {};
-  DCHECK_LE(leading_pad_bytes_, ArraySize(kZeroes));
+  DCHECK_LE(leading_pad_bytes_, base::size(kZeroes));
 
   if (leading_pad_bytes_) {
     if (!file_writer->Write(&kZeroes, leading_pad_bytes_)) {
diff --git a/third_party/crashpad/crashpad/minidump/test/minidump_context_test_util.cc b/third_party/crashpad/crashpad/minidump/test/minidump_context_test_util.cc
index ae80d003..c94fa20d 100644
--- a/third_party/crashpad/crashpad/minidump/test/minidump_context_test_util.cc
+++ b/third_party/crashpad/crashpad/minidump/test/minidump_context_test_util.cc
@@ -18,12 +18,12 @@
 #include <sys/types.h>
 
 #include "base/format_macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
 #include "snapshot/cpu_context.h"
 #include "snapshot/test/test_cpu_context.h"
 #include "test/hex_string.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -128,7 +128,8 @@
   context->ds = static_cast<uint16_t>(value++);
   context->es = static_cast<uint16_t>(value++);
   context->ss = static_cast<uint16_t>(value++);
-  for (size_t index = 0; index < ArraySize(context->vector_register); ++index) {
+  for (size_t index = 0; index < base::size(context->vector_register);
+       ++index) {
     context->vector_register[index].lo = value++;
     context->vector_register[index].hi = value++;
   }
@@ -151,7 +152,7 @@
 
   uint32_t value = seed;
 
-  for (size_t index = 0; index < ArraySize(context->regs); ++index) {
+  for (size_t index = 0; index < base::size(context->regs); ++index) {
     context->regs[index] = value++;
   }
   context->fp = value++;
@@ -162,7 +163,7 @@
   context->pc = value++;
   context->cpsr = value++;
 
-  for (size_t index = 0; index < ArraySize(context->vfp); ++index) {
+  for (size_t index = 0; index < base::size(context->vfp); ++index) {
     context->vfp[index] = value++;
   }
   context->fpscr = value++;
@@ -180,7 +181,7 @@
 
   uint32_t value = seed;
 
-  for (size_t index = 0; index < ArraySize(context->regs); ++index) {
+  for (size_t index = 0; index < base::size(context->regs); ++index) {
     context->regs[index] = value++;
   }
   context->fp = value++;
@@ -189,7 +190,7 @@
   context->pc = value++;
   context->cpsr = value++;
 
-  for (size_t index = 0; index < ArraySize(context->fpsimd); ++index) {
+  for (size_t index = 0; index < base::size(context->fpsimd); ++index) {
     context->fpsimd[index].lo = value++;
     context->fpsimd[index].hi = value++;
   }
@@ -209,7 +210,7 @@
 
   uint32_t value = seed;
 
-  for (size_t index = 0; index < ArraySize(context->regs); ++index) {
+  for (size_t index = 0; index < base::size(context->regs); ++index) {
     context->regs[index] = value++;
   }
 
@@ -220,7 +221,7 @@
   context->status = value++;
   context->cause = value++;
 
-  for (size_t index = 0; index < ArraySize(context->fpregs.fregs); ++index) {
+  for (size_t index = 0; index < base::size(context->fpregs.fregs); ++index) {
     context->fpregs.fregs[index]._fp_fregs = static_cast<float>(value++);
   }
 
@@ -247,7 +248,7 @@
 
   uint64_t value = seed;
 
-  for (size_t index = 0; index < ArraySize(context->regs); ++index) {
+  for (size_t index = 0; index < base::size(context->regs); ++index) {
     context->regs[index] = value++;
   }
 
@@ -258,7 +259,7 @@
   context->status = value++;
   context->cause = value++;
 
-  for (size_t index = 0; index < ArraySize(context->fpregs.dregs); ++index) {
+  for (size_t index = 0; index < base::size(context->fpregs.dregs); ++index) {
     context->fpregs.dregs[index] = static_cast<double>(value++);
   }
   context->fpcsr = value++;
@@ -293,33 +294,33 @@
   EXPECT_EQ(observed->reserved_3, expected->reserved_3);
   EXPECT_EQ(observed->mxcsr, expected->mxcsr);
   EXPECT_EQ(observed->mxcsr_mask, expected->mxcsr_mask);
-  for (size_t st_mm_index = 0; st_mm_index < ArraySize(expected->st_mm);
+  for (size_t st_mm_index = 0; st_mm_index < base::size(expected->st_mm);
        ++st_mm_index) {
     SCOPED_TRACE(base::StringPrintf("st_mm_index %" PRIuS, st_mm_index));
     EXPECT_EQ(BytesToHexString(observed->st_mm[st_mm_index].st,
-                               ArraySize(observed->st_mm[st_mm_index].st)),
+                               base::size(observed->st_mm[st_mm_index].st)),
               BytesToHexString(expected->st_mm[st_mm_index].st,
-                               ArraySize(expected->st_mm[st_mm_index].st)));
+                               base::size(expected->st_mm[st_mm_index].st)));
     EXPECT_EQ(
         BytesToHexString(observed->st_mm[st_mm_index].st_reserved,
-                         ArraySize(observed->st_mm[st_mm_index].st_reserved)),
+                         base::size(observed->st_mm[st_mm_index].st_reserved)),
         BytesToHexString(expected->st_mm[st_mm_index].st_reserved,
-                         ArraySize(expected->st_mm[st_mm_index].st_reserved)));
+                         base::size(expected->st_mm[st_mm_index].st_reserved)));
   }
-  for (size_t xmm_index = 0; xmm_index < ArraySize(expected->xmm);
+  for (size_t xmm_index = 0; xmm_index < base::size(expected->xmm);
        ++xmm_index) {
     EXPECT_EQ(BytesToHexString(observed->xmm[xmm_index],
-                               ArraySize(observed->xmm[xmm_index])),
+                               base::size(observed->xmm[xmm_index])),
               BytesToHexString(expected->xmm[xmm_index],
-                               ArraySize(expected->xmm[xmm_index])))
+                               base::size(expected->xmm[xmm_index])))
         << "xmm_index " << xmm_index;
   }
   EXPECT_EQ(
-      BytesToHexString(observed->reserved_4, ArraySize(observed->reserved_4)),
-      BytesToHexString(expected->reserved_4, ArraySize(expected->reserved_4)));
+      BytesToHexString(observed->reserved_4, base::size(observed->reserved_4)),
+      BytesToHexString(expected->reserved_4, base::size(expected->reserved_4)));
   EXPECT_EQ(
-      BytesToHexString(observed->available, ArraySize(observed->available)),
-      BytesToHexString(expected->available, ArraySize(expected->available)));
+      BytesToHexString(observed->available, base::size(observed->available)),
+      BytesToHexString(expected->available, base::size(expected->available)));
 }
 
 }  // namespace
@@ -344,11 +345,11 @@
   EXPECT_EQ(observed->fsave.fpu_cs, expected.fsave.fpu_cs);
   EXPECT_EQ(observed->fsave.fpu_dp, expected.fsave.fpu_dp);
   EXPECT_EQ(observed->fsave.fpu_ds, expected.fsave.fpu_ds);
-  for (size_t index = 0; index < ArraySize(expected.fsave.st); ++index) {
+  for (size_t index = 0; index < base::size(expected.fsave.st); ++index) {
     EXPECT_EQ(BytesToHexString(observed->fsave.st[index],
-                               ArraySize(observed->fsave.st[index])),
+                               base::size(observed->fsave.st[index])),
               BytesToHexString(expected.fsave.st[index],
-                               ArraySize(expected.fsave.st[index])))
+                               base::size(expected.fsave.st[index])))
         << "index " << index;
   }
   if (snapshot) {
@@ -447,7 +448,8 @@
 
   ExpectMinidumpContextFxsave(&expected.fxsave, &observed->fxsave);
 
-  for (size_t index = 0; index < ArraySize(expected.vector_register); ++index) {
+  for (size_t index = 0; index < base::size(expected.vector_register);
+       ++index) {
     if (snapshot) {
       EXPECT_EQ(observed->vector_register[index].lo, 0u) << "index " << index;
       EXPECT_EQ(observed->vector_register[index].hi, 0u) << "index " << index;
@@ -487,7 +489,7 @@
 
   EXPECT_EQ(observed->context_flags, expected.context_flags);
 
-  for (size_t index = 0; index < ArraySize(expected.regs); ++index) {
+  for (size_t index = 0; index < base::size(expected.regs); ++index) {
     EXPECT_EQ(observed->regs[index], expected.regs[index]);
   }
   EXPECT_EQ(observed->fp, expected.fp);
@@ -498,10 +500,10 @@
   EXPECT_EQ(observed->cpsr, expected.cpsr);
 
   EXPECT_EQ(observed->fpscr, expected.fpscr);
-  for (size_t index = 0; index < ArraySize(expected.vfp); ++index) {
+  for (size_t index = 0; index < base::size(expected.vfp); ++index) {
     EXPECT_EQ(observed->vfp[index], expected.vfp[index]);
   }
-  for (size_t index = 0; index < ArraySize(expected.extra); ++index) {
+  for (size_t index = 0; index < base::size(expected.extra); ++index) {
     EXPECT_EQ(observed->extra[index], snapshot ? 0 : expected.extra[index]);
   }
 }
@@ -514,14 +516,14 @@
 
   EXPECT_EQ(observed->context_flags, expected.context_flags);
 
-  for (size_t index = 0; index < ArraySize(expected.regs); ++index) {
+  for (size_t index = 0; index < base::size(expected.regs); ++index) {
     EXPECT_EQ(observed->regs[index], expected.regs[index]);
   }
   EXPECT_EQ(observed->cpsr, expected.cpsr);
 
   EXPECT_EQ(observed->fpsr, expected.fpsr);
   EXPECT_EQ(observed->fpcr, expected.fpcr);
-  for (size_t index = 0; index < ArraySize(expected.fpsimd); ++index) {
+  for (size_t index = 0; index < base::size(expected.fpsimd); ++index) {
     EXPECT_EQ(observed->fpsimd[index].lo, expected.fpsimd[index].lo);
     EXPECT_EQ(observed->fpsimd[index].hi, expected.fpsimd[index].hi);
   }
@@ -535,7 +537,7 @@
 
   EXPECT_EQ(observed->context_flags, expected.context_flags);
 
-  for (size_t index = 0; index < ArraySize(expected.regs); ++index) {
+  for (size_t index = 0; index < base::size(expected.regs); ++index) {
     EXPECT_EQ(observed->regs[index], expected.regs[index]);
   }
 
@@ -546,7 +548,7 @@
   EXPECT_EQ(observed->status, expected.status);
   EXPECT_EQ(observed->cause, expected.cause);
 
-  for (size_t index = 0; index < ArraySize(expected.fpregs.fregs); ++index) {
+  for (size_t index = 0; index < base::size(expected.fpregs.fregs); ++index) {
     EXPECT_EQ(observed->fpregs.fregs[index]._fp_fregs,
               expected.fpregs.fregs[index]._fp_fregs);
   }
@@ -568,7 +570,7 @@
 
   EXPECT_EQ(observed->context_flags, expected.context_flags);
 
-  for (size_t index = 0; index < ArraySize(expected.regs); ++index) {
+  for (size_t index = 0; index < base::size(expected.regs); ++index) {
     EXPECT_EQ(observed->regs[index], expected.regs[index]);
   }
 
@@ -579,7 +581,7 @@
   EXPECT_EQ(observed->status, expected.status);
   EXPECT_EQ(observed->cause, expected.cause);
 
-  for (size_t index = 0; index < ArraySize(expected.fpregs.dregs); ++index) {
+  for (size_t index = 0; index < base::size(expected.fpregs.dregs); ++index) {
     EXPECT_EQ(observed->fpregs.dregs[index], expected.fpregs.dregs[index]);
   }
   EXPECT_EQ(observed->fpcsr, expected.fpcsr);
diff --git a/third_party/crashpad/crashpad/snapshot/BUILD.gn b/third_party/crashpad/crashpad/snapshot/BUILD.gn
index 8d331fc..8925714 100644
--- a/third_party/crashpad/crashpad/snapshot/BUILD.gn
+++ b/third_party/crashpad/crashpad/snapshot/BUILD.gn
@@ -142,10 +142,16 @@
     ]
   }
 
-  if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
+  if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia ||
+      crashpad_is_win) {
     sources += [
       "crashpad_types/crashpad_info_reader.cc",
       "crashpad_types/crashpad_info_reader.h",
+    ]
+  }
+
+  if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
+    sources += [
       "crashpad_types/image_annotation_reader.cc",
       "crashpad_types/image_annotation_reader.h",
       "elf/elf_dynamic_array_reader.cc",
@@ -332,9 +338,13 @@
     sources += [ "crashpad_info_client_options_test.cc" ]
   }
 
+  if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia ||
+      crashpad_is_win) {
+    sources += [ "crashpad_types/crashpad_info_reader_test.cc" ]
+  }
+
   if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
     sources += [
-      "crashpad_types/crashpad_info_reader_test.cc",
       "crashpad_types/image_annotation_reader_test.cc",
       "elf/elf_image_reader_test.cc",
       "elf/elf_image_reader_test_note.S",
diff --git a/third_party/crashpad/crashpad/snapshot/capture_memory.cc b/third_party/crashpad/crashpad/snapshot/capture_memory.cc
index 3ffbcd2..a51626c 100644
--- a/third_party/crashpad/crashpad/snapshot/capture_memory.cc
+++ b/third_party/crashpad/crashpad/snapshot/capture_memory.cc
@@ -19,8 +19,8 @@
 #include <limits>
 #include <memory>
 
+#include "base/stl_util.h"
 #include "snapshot/memory_snapshot.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace internal {
@@ -98,17 +98,17 @@
 #elif defined(ARCH_CPU_ARM_FAMILY)
   if (context.architecture == kCPUArchitectureARM64) {
     MaybeCaptureMemoryAround(delegate, context.arm64->pc);
-    for (size_t i = 0; i < ArraySize(context.arm64->regs); ++i) {
+    for (size_t i = 0; i < base::size(context.arm64->regs); ++i) {
       MaybeCaptureMemoryAround(delegate, context.arm64->regs[i]);
     }
   } else {
     MaybeCaptureMemoryAround(delegate, context.arm->pc);
-    for (size_t i = 0; i < ArraySize(context.arm->regs); ++i) {
+    for (size_t i = 0; i < base::size(context.arm->regs); ++i) {
       MaybeCaptureMemoryAround(delegate, context.arm->regs[i]);
     }
   }
 #elif defined(ARCH_CPU_MIPS_FAMILY)
-  for (size_t i = 0; i < ArraySize(context.mipsel->regs); ++i) {
+  for (size_t i = 0; i < base::size(context.mipsel->regs); ++i) {
     MaybeCaptureMemoryAround(delegate, context.mipsel->regs[i]);
   }
 #else
diff --git a/third_party/crashpad/crashpad/snapshot/cpu_context.cc b/third_party/crashpad/crashpad/snapshot/cpu_context.cc
index 0ed1691..6fb8d7e 100644
--- a/third_party/crashpad/crashpad/snapshot/cpu_context.cc
+++ b/third_party/crashpad/crashpad/snapshot/cpu_context.cc
@@ -18,6 +18,7 @@
 #include <string.h>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "util/misc/arraysize.h"
 #include "util/misc/implicit_cast.h"
 
@@ -57,7 +58,7 @@
   fsave->reserved_4 = 0;
   static_assert(ArraySize(fsave->st) == ArraySize(fxsave.st_mm),
                 "FPU stack registers must be equivalent");
-  for (size_t index = 0; index < ArraySize(fsave->st); ++index) {
+  for (size_t index = 0; index < base::size(fsave->st); ++index) {
     memcpy(fsave->st[index], fxsave.st_mm[index].st, sizeof(fsave->st[index]));
   }
 }
@@ -79,7 +80,7 @@
   fxsave->mxcsr_mask = 0;
   static_assert(ArraySize(fxsave->st_mm) == ArraySize(fsave.st),
                 "FPU stack registers must be equivalent");
-  for (size_t index = 0; index < ArraySize(fsave.st); ++index) {
+  for (size_t index = 0; index < base::size(fsave.st); ++index) {
     memcpy(fxsave->st_mm[index].st, fsave.st[index], sizeof(fsave.st[index]));
     memset(fxsave->st_mm[index].st_reserved,
            0,
diff --git a/third_party/crashpad/crashpad/snapshot/cpu_context_test.cc b/third_party/crashpad/crashpad/snapshot/cpu_context_test.cc
index 794d9b9f..109510d 100644
--- a/third_party/crashpad/crashpad/snapshot/cpu_context_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/cpu_context_test.cc
@@ -18,9 +18,9 @@
 #include <string.h>
 #include <sys/types.h>
 
+#include "base/stl_util.h"
 #include "gtest/gtest.h"
 #include "test/hex_string.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -124,7 +124,7 @@
       &fxsave.st_mm[6].st, kExponentAllZero, false, kFractionAllZero);
   SetX87Register(
       &fxsave.st_mm[7].st, kExponentNormal, true, kFractionNormal);  // valid
-  for (size_t index = 0; index < ArraySize(fxsave.st_mm); ++index) {
+  for (size_t index = 0; index < base::size(fxsave.st_mm); ++index) {
     memset(&fxsave.st_mm[index].st_reserved,
            0x5a,
            sizeof(fxsave.st_mm[index].st_reserved));
@@ -148,10 +148,10 @@
   EXPECT_EQ(fsave.fpu_dp, fxsave.fpu_dp);
   EXPECT_EQ(fsave.fpu_ds, fxsave.fpu_ds);
   EXPECT_EQ(fsave.reserved_4, 0);
-  for (size_t index = 0; index < ArraySize(fsave.st); ++index) {
-    EXPECT_EQ(BytesToHexString(fsave.st[index], ArraySize(fsave.st[index])),
+  for (size_t index = 0; index < base::size(fsave.st); ++index) {
+    EXPECT_EQ(BytesToHexString(fsave.st[index], base::size(fsave.st[index])),
               BytesToHexString(fxsave.st_mm[index].st,
-                               ArraySize(fxsave.st_mm[index].st)))
+                               base::size(fxsave.st_mm[index].st)))
         << "index " << index;
   }
 }
@@ -204,14 +204,14 @@
   EXPECT_EQ(fxsave.reserved_3, 0);
   EXPECT_EQ(fxsave.mxcsr, 0u);
   EXPECT_EQ(fxsave.mxcsr_mask, 0u);
-  for (size_t index = 0; index < ArraySize(fxsave.st_mm); ++index) {
+  for (size_t index = 0; index < base::size(fxsave.st_mm); ++index) {
     EXPECT_EQ(BytesToHexString(fxsave.st_mm[index].st,
-                               ArraySize(fxsave.st_mm[index].st)),
-              BytesToHexString(fsave.st[index], ArraySize(fsave.st[index])))
+                               base::size(fxsave.st_mm[index].st)),
+              BytesToHexString(fsave.st[index], base::size(fsave.st[index])))
         << "index " << index;
     EXPECT_EQ(BytesToHexString(fxsave.st_mm[index].st_reserved,
-                               ArraySize(fxsave.st_mm[index].st_reserved)),
-              std::string(ArraySize(fxsave.st_mm[index].st_reserved) * 2, '0'))
+                               base::size(fxsave.st_mm[index].st_reserved)),
+              std::string(base::size(fxsave.st_mm[index].st_reserved) * 2, '0'))
         << "index " << index;
   }
   size_t unused_len = sizeof(fxsave) - offsetof(decltype(fxsave), xmm);
@@ -318,7 +318,7 @@
   // In this set, everything is valid.
   fsw = 0 << 11;  // top = 0: logical 0-7 maps to physical 0-7
   fxsave_tag = 0xff;  // nothing empty
-  for (size_t index = 0; index < ArraySize(st_mm); ++index) {
+  for (size_t index = 0; index < base::size(st_mm); ++index) {
     SetX87OrMMXRegister(&st_mm[index], kExponentNormal, true, kFractionAllZero);
   }
   EXPECT_EQ(CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm), 0);
diff --git a/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.cc b/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.cc
index dfc438f..527d372 100644
--- a/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.cc
+++ b/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.cc
@@ -66,9 +66,8 @@
       return false;
     }
 
-    if (!memory->Read(address,
-                      std::min(VMSize{info.size}, VMSize{sizeof(info)}),
-                      &info)) {
+    if (!memory->Read(
+            address, std::min<VMSize>(info.size, sizeof(info)), &info)) {
       return false;
     }
 
@@ -116,7 +115,7 @@
 #define NATIVE_TRAITS Traits32
 #endif
   static_assert(!std::is_same<Traits, NATIVE_TRAITS>::value ||
-                    sizeof(info) == sizeof(CrashpadInfo),
+                    sizeof(decltype(info)) == sizeof(CrashpadInfo),
                 "CrashpadInfo size mismtach");
 #undef NATIVE_TRAITS
 };
diff --git a/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader_test.cc b/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader_test.cc
index 87bafc6..ebc6a4d 100644
--- a/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader_test.cc
@@ -15,7 +15,6 @@
 #include "snapshot/crashpad_types/crashpad_info_reader.h"
 
 #include <sys/types.h>
-#include <unistd.h>
 
 #include <memory>
 
@@ -31,10 +30,6 @@
 #include "util/misc/from_pointer_cast.h"
 #include "util/process/process_memory_native.h"
 
-#if defined(OS_FUCHSIA)
-#include <zircon/process.h>
-#endif
-
 namespace crashpad {
 namespace test {
 namespace {
diff --git a/third_party/crashpad/crashpad/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.cc b/third_party/crashpad/crashpad/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.cc
index 45d39d2..1133c4d 100644
--- a/third_party/crashpad/crashpad/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.cc
+++ b/third_party/crashpad/crashpad/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.cc
@@ -15,7 +15,7 @@
 #include "snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h"
 
 #include "base/logging.h"
-#include "util/misc/arraysize.h"
+#include "base/stl_util.h"
 
 namespace crashpad {
 namespace internal {
@@ -50,7 +50,7 @@
 
   const uint32_t index =
       flags & (ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE);
-  DCHECK_LT(index, ArraySize(mapping));
+  DCHECK_LT(index, base::size(mapping));
 
   const uint32_t protect_flags = mapping[index];
   DCHECK_NE(protect_flags, 0u);
diff --git a/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia_test.cc b/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia_test.cc
index 5832a221..83385a8 100644
--- a/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia_test.cc
@@ -20,11 +20,11 @@
 #include <zircon/syscalls/port.h>
 #include <zircon/types.h>
 
+#include "base/stl_util.h"
 #include "gtest/gtest.h"
 #include "test/multiprocess_exec.h"
 #include "test/test_paths.h"
 #include "util/fuchsia/scoped_task_suspend.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -35,7 +35,7 @@
   ASSERT_TRUE(process_reader.Initialize(*zx::process::self()));
 
   static constexpr char kTestMemory[] = "Some test memory";
-  char buffer[ArraySize(kTestMemory)];
+  char buffer[base::size(kTestMemory)];
   ASSERT_TRUE(process_reader.Memory()->Read(
       reinterpret_cast<zx_vaddr_t>(kTestMemory), sizeof(kTestMemory), &buffer));
   EXPECT_STREQ(kTestMemory, buffer);
diff --git a/third_party/crashpad/crashpad/snapshot/fuchsia/process_snapshot_fuchsia_test.cc b/third_party/crashpad/crashpad/snapshot/fuchsia/process_snapshot_fuchsia_test.cc
index ebb612be..a96d37c 100644
--- a/third_party/crashpad/crashpad/snapshot/fuchsia/process_snapshot_fuchsia_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/fuchsia/process_snapshot_fuchsia_test.cc
@@ -19,12 +19,12 @@
 
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
 #include "snapshot/fuchsia/process_snapshot_fuchsia.h"
 #include "test/multiprocess_exec.h"
 #include "util/fuchsia/scoped_task_suspend.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -104,8 +104,8 @@
 
  private:
   void MultiprocessParent() override {
-    uintptr_t test_addresses[ArraySize(kTestMappingPermAndSizes)];
-    for (size_t i = 0; i < ArraySize(test_addresses); ++i) {
+    uintptr_t test_addresses[base::size(kTestMappingPermAndSizes)];
+    for (size_t i = 0; i < base::size(test_addresses); ++i) {
       ASSERT_TRUE(ReadFileExactly(
           ReadPipeHandle(), &test_addresses[i], sizeof(test_addresses[i])));
     }
@@ -115,7 +115,7 @@
     ProcessSnapshotFuchsia process_snapshot;
     ASSERT_TRUE(process_snapshot.Initialize(*ChildProcess()));
 
-    for (size_t i = 0; i < ArraySize(test_addresses); ++i) {
+    for (size_t i = 0; i < base::size(test_addresses); ++i) {
       const auto& t = kTestMappingPermAndSizes[i];
       EXPECT_TRUE(HasSingleMatchingMapping(process_snapshot.MemoryMap(),
                                            test_addresses[i],
diff --git a/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc b/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc
index e632bc1..e4ff1ab7 100644
--- a/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc
@@ -23,6 +23,7 @@
 
 #include "base/bit_cast.h"
 #include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
 #include "snapshot/cpu_architecture.h"
@@ -32,7 +33,6 @@
 #include "test/errors.h"
 #include "test/linux/fake_ptrace_connection.h"
 #include "util/linux/address_types.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/clock.h"
 #include "util/misc/from_pointer_cast.h"
 #include "util/posix/signals.h"
@@ -171,7 +171,7 @@
   test_context->vfp.head.magic = VFP_MAGIC;
   test_context->vfp.head.size = sizeof(test_context->vfp);
   memset(&test_context->vfp.context, 'v', sizeof(test_context->vfp.context));
-  for (size_t reg = 0; reg < ArraySize(test_context->vfp.context.vfp.fpregs);
+  for (size_t reg = 0; reg < base::size(test_context->vfp.context.vfp.fpregs);
        ++reg) {
     test_context->vfp.context.vfp.fpregs[reg] = reg;
   }
@@ -219,7 +219,7 @@
 void InitializeContext(NativeCPUContext* context) {
   memset(context, 'x', sizeof(*context));
 
-  for (size_t index = 0; index < ArraySize(context->uc_mcontext.regs);
+  for (size_t index = 0; index < base::size(context->uc_mcontext.regs);
        ++index) {
     context->uc_mcontext.regs[index] = index;
   }
@@ -238,7 +238,7 @@
   test_context->fpsimd.head.size = sizeof(test_context->fpsimd);
   test_context->fpsimd.fpsr = 1;
   test_context->fpsimd.fpcr = 2;
-  for (size_t reg = 0; reg < ArraySize(test_context->fpsimd.vregs); ++reg) {
+  for (size_t reg = 0; reg < base::size(test_context->fpsimd.vregs); ++reg) {
     test_context->fpsimd.vregs[reg] = reg;
   }
 
@@ -271,7 +271,7 @@
 using NativeCPUContext = ucontext_t;
 
 void InitializeContext(NativeCPUContext* context) {
-  for (size_t reg = 0; reg < ArraySize(context->uc_mcontext.gregs); ++reg) {
+  for (size_t reg = 0; reg < base::size(context->uc_mcontext.gregs); ++reg) {
     context->uc_mcontext.gregs[reg] = reg;
   }
   memset(&context->uc_mcontext.fpregs, 44, sizeof(context->uc_mcontext.fpregs));
@@ -286,7 +286,7 @@
 #define CPU_ARCH_NAME mips64
 #endif
 
-  for (size_t reg = 0; reg < ArraySize(expected.uc_mcontext.gregs); ++reg) {
+  for (size_t reg = 0; reg < base::size(expected.uc_mcontext.gregs); ++reg) {
     EXPECT_EQ(actual.CPU_ARCH_NAME->regs[reg], expected.uc_mcontext.gregs[reg]);
   }
 
diff --git a/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux_test.cc b/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux_test.cc
index 61b84f3..e2e5566 100644
--- a/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux_test.cc
@@ -33,6 +33,7 @@
 
 #include "base/format_macros.h"
 #include "base/memory/free_deleter.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "gtest/gtest.h"
@@ -47,7 +48,6 @@
 #include "util/file/filesystem.h"
 #include "util/linux/direct_ptrace_connection.h"
 #include "util/misc/address_sanitizer.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/from_pointer_cast.h"
 #include "util/synchronization/semaphore.h"
 
@@ -80,7 +80,7 @@
   EXPECT_EQ(process_reader.ParentProcessID(), getppid());
 
   static constexpr char kTestMemory[] = "Some test memory";
-  char buffer[ArraySize(kTestMemory)];
+  char buffer[base::size(kTestMemory)];
   ASSERT_TRUE(process_reader.Memory()->Read(
       reinterpret_cast<LinuxVMAddress>(kTestMemory),
       sizeof(kTestMemory),
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.cc
index e730b511..eefd7f4 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_annotations_reader.cc
@@ -178,9 +178,9 @@
   }
 }
 
-// TODO(rsesek): When there is a platform-agnostic remote memory reader
-// interface available, use it so that the implementation is not duplicated
-// in the PEImageAnnotationsReader.
+// TODO(https://crbug.com/crashpad/270): Replace implementations of
+// ReadCrashpadAnnotationsList and ReadCrashpadSimpleAnnotations with the
+// platform-agnostic implementations in ImageAnnotationReader.
 void MachOImageAnnotationsReader::ReadCrashpadAnnotationsList(
     std::vector<AnnotationSnapshot>* annotations) const {
   process_types::CrashpadInfo crashpad_info;
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc
index 650b0c7..bd258caf 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc
@@ -22,13 +22,13 @@
 #include <utility>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "client/crashpad_info.h"
 #include "snapshot/mac/mach_o_image_segment_reader.h"
 #include "snapshot/mac/mach_o_image_symbol_table_reader.h"
 #include "snapshot/mac/process_reader_mac.h"
 #include "util/mac/checked_mach_address_range.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/implicit_cast.h"
 
 namespace {
@@ -183,7 +183,7 @@
   // This vector is parallel to the kLoadCommandReaders array, and tracks
   // whether a singleton load command matching the |command| field has been
   // found yet.
-  std::vector<uint32_t> singleton_indices(ArraySize(kLoadCommandReaders),
+  std::vector<uint32_t> singleton_indices(base::size(kLoadCommandReaders),
                                           kInvalidSegmentIndex);
 
   size_t offset = mach_header.Size();
@@ -236,7 +236,8 @@
       return false;
     }
 
-    for (size_t reader_index = 0; reader_index < ArraySize(kLoadCommandReaders);
+    for (size_t reader_index = 0;
+         reader_index < base::size(kLoadCommandReaders);
          ++reader_index) {
       if (load_command.cmd != kLoadCommandReaders[reader_index].command) {
         continue;
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader_test.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader_test.cc
index cfbddc1..4731a5f 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader_test.cc
@@ -16,9 +16,9 @@
 
 #include <mach-o/loader.h>
 
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -63,7 +63,7 @@
       SEG_IMPORT,
   };
 
-  for (size_t index = 0; index < ArraySize(kSegmentTestData); ++index) {
+  for (size_t index = 0; index < base::size(kSegmentTestData); ++index) {
     EXPECT_EQ(
         MachOImageSegmentReader::SegmentNameString(kSegmentTestData[index]),
         kSegmentTestData[index])
@@ -106,7 +106,7 @@
       SECT_ICON_TIFF,
   };
 
-  for (size_t index = 0; index < ArraySize(kSectionTestData); ++index) {
+  for (size_t index = 0; index < base::size(kSectionTestData); ++index) {
     EXPECT_EQ(
         MachOImageSegmentReader::SectionNameString(kSectionTestData[index]),
         kSectionTestData[index])
@@ -169,7 +169,7 @@
       {SEG_IMPORT, "", "__IMPORT,"},
   };
 
-  for (size_t index = 0; index < ArraySize(kSegmentAndSectionTestData);
+  for (size_t index = 0; index < base::size(kSegmentAndSectionTestData);
        ++index) {
     const auto& test = kSegmentAndSectionTestData[index];
     EXPECT_EQ(MachOImageSegmentReader::SegmentAndSectionNameString(
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc
index e3f4df1..9325b2c 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc
@@ -29,6 +29,7 @@
 #include "base/logging.h"
 #include "base/mac/mach_logging.h"
 #include "base/posix/eintr_wrapper.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "gtest/gtest.h"
@@ -41,7 +42,6 @@
 #include "util/file/file_io.h"
 #include "util/mac/mac_util.h"
 #include "util/mach/mach_extensions.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/from_pointer_cast.h"
 #include "util/synchronization/semaphore.h"
 
@@ -65,7 +65,7 @@
   EXPECT_EQ(process_reader.ParentProcessID(), getppid());
 
   static constexpr char kTestMemory[] = "Some test memory";
-  char buffer[ArraySize(kTestMemory)];
+  char buffer[base::size(kTestMemory)];
   ASSERT_TRUE(process_reader.Memory()->Read(
       FromPointerCast<mach_vm_address_t>(kTestMemory),
       sizeof(kTestMemory),
@@ -613,11 +613,11 @@
     const size_t source_lengths[] = {
         strlen(sources[0]),
     };
-    static_assert(ArraySize(sources) == ArraySize(source_lengths),
+    static_assert(base::size(sources) == base::size(source_lengths),
                   "arrays must be parallel");
 
     program_ = clCreateProgramWithSource(
-        context_, ArraySize(sources), sources, source_lengths, &rv);
+        context_, base::size(sources), sources, source_lengths, &rv);
     ASSERT_EQ(rv, CL_SUCCESS) << "clCreateProgramWithSource";
 
     rv = clBuildProgram(
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types.cc b/third_party/crashpad/crashpad/snapshot/mac/process_types.cc
index 9ae8f7c..0a1b7f9 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_types.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_types.cc
@@ -20,8 +20,8 @@
 
 #include <memory>
 
+#include "base/stl_util.h"
 #include "snapshot/mac/process_types/internal.h"
-#include "util/misc/arraysize.h"
 #include "util/process/process_memory_mac.h"
 
 namespace crashpad {
@@ -74,7 +74,7 @@
 template <>
 inline void Assign<UInt64Array4, UInt32Array4>(UInt64Array4* destination,
                                                const UInt32Array4& source) {
-  for (size_t index = 0; index < ArraySize(source); ++index) {
+  for (size_t index = 0; index < base::size(source); ++index) {
     (*destination)[index] = source[index];
   }
 }
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types/custom.cc b/third_party/crashpad/crashpad/snapshot/mac/process_types/custom.cc
index 3d5976c..3ca5646c 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_types/custom.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_types/custom.cc
@@ -23,9 +23,9 @@
 
 #include "base/logging.h"
 #include "base/numerics/safe_math.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "snapshot/mac/process_types/internal.h"
-#include "util/misc/arraysize.h"
 #include "util/process/process_memory_mac.h"
 
 #if !DOXYGEN
@@ -145,8 +145,8 @@
       sizeof(dyld_all_image_infos<Traits>),  // 16
   };
 
-  if (version >= ArraySize(kSizeForVersion)) {
-    return kSizeForVersion[ArraySize(kSizeForVersion) - 1];
+  if (version >= base::size(kSizeForVersion)) {
+    return kSizeForVersion[base::size(kSizeForVersion) - 1];
   }
 
   static_assert(std::is_unsigned<decltype(version)>::value,
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types_test.cc b/third_party/crashpad/crashpad/snapshot/mac/process_types_test.cc
index 9a9dc9d4..2ab3034c 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_types_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_types_test.cc
@@ -20,13 +20,13 @@
 
 #include <vector>
 
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "gtest/gtest.h"
 #include "snapshot/mac/process_types/internal.h"
 #include "test/mac/dyld.h"
 #include "util/mac/mac_util.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/from_pointer_cast.h"
 #include "util/misc/implicit_cast.h"
 
@@ -147,7 +147,7 @@
       {15, 164, 304},
       {16, 176, 320},
   };
-  for (size_t index = 0; index < ArraySize(kVersionsAndSizes); ++index) {
+  for (size_t index = 0; index < base::size(kVersionsAndSizes); ++index) {
     uint32_t version = kVersionsAndSizes[index].version;
     SCOPED_TRACE(base::StringPrintf("index %zu, version %u", index, version));
 
@@ -268,7 +268,7 @@
               self_image_infos->sharedCacheBaseAddress);
     EXPECT_EQ(proctype_image_infos.dyldPath,
               reinterpret_cast<uint64_t>(self_image_infos->dyldPath));
-    for (size_t index = 0; index < ArraySize(self_image_infos->notifyPorts);
+    for (size_t index = 0; index < base::size(self_image_infos->notifyPorts);
          ++index) {
       EXPECT_EQ(proctype_image_infos.notifyPorts[index],
                 self_image_infos->notifyPorts[index])
@@ -288,7 +288,7 @@
   // process_types version. It’s difficult to compare the reserved fields in
   // these older SDKs, so only do it where the declarations match.
   if (proctype_image_infos.version >= 14) {
-    for (size_t index = 0; index < ArraySize(proctype_image_infos.reserved);
+    for (size_t index = 0; index < base::size(proctype_image_infos.reserved);
          ++index) {
       EXPECT_EQ(proctype_image_infos.reserved[index],
                 implicit_cast<uint64_t>(self_image_infos->reserved[index]))
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc
index 47876036..f4b611d 100644
--- a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc
@@ -21,6 +21,7 @@
 #include <memory>
 
 #include "base/numerics/safe_math.h"
+#include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "gtest/gtest.h"
 #include "minidump/minidump_context.h"
@@ -28,7 +29,6 @@
 #include "snapshot/minidump/minidump_annotation_reader.h"
 #include "snapshot/module_snapshot.h"
 #include "util/file/string_file.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/pdb_structures.h"
 
 namespace crashpad {
@@ -859,27 +859,27 @@
   minidump_context.fxsave.fpu_ip_64 = 42;
   minidump_context.fxsave.fpu_dp_64 = 43;
 
-  for (size_t i = 0; i < ArraySize(minidump_context.vector_register); i++) {
+  for (size_t i = 0; i < base::size(minidump_context.vector_register); i++) {
     minidump_context.vector_register[i].lo = i * 2 + 44;
     minidump_context.vector_register[i].hi = i * 2 + 45;
   }
 
-  for (uint8_t i = 0; i < ArraySize(minidump_context.fxsave.reserved_4); i++) {
+  for (uint8_t i = 0; i < base::size(minidump_context.fxsave.reserved_4); i++) {
     minidump_context.fxsave.reserved_4[i] = i * 2 + 115;
     minidump_context.fxsave.available[i] = i * 2 + 116;
   }
 
-  for (size_t i = 0; i < ArraySize(minidump_context.fxsave.st_mm); i++) {
+  for (size_t i = 0; i < base::size(minidump_context.fxsave.st_mm); i++) {
     for (uint8_t j = 0;
-         j < ArraySize(minidump_context.fxsave.st_mm[0].mm_value);
+         j < base::size(minidump_context.fxsave.st_mm[0].mm_value);
          j++) {
       minidump_context.fxsave.st_mm[i].mm_value[j] = j + 1;
       minidump_context.fxsave.st_mm[i].mm_reserved[j] = j + 1;
     }
   }
 
-  for (size_t i = 0; i < ArraySize(minidump_context.fxsave.xmm); i++) {
-    for (uint8_t j = 0; j < ArraySize(minidump_context.fxsave.xmm[0]); j++) {
+  for (size_t i = 0; i < base::size(minidump_context.fxsave.xmm); i++) {
+    for (uint8_t j = 0; j < base::size(minidump_context.fxsave.xmm[0]); j++) {
       minidump_context.fxsave.xmm[i][j] = j + 1;
     }
   }
@@ -962,20 +962,20 @@
   EXPECT_EQ(ctx->fxsave.fpu_ip_64, 42U);
   EXPECT_EQ(ctx->fxsave.fpu_dp_64, 43U);
 
-  for (uint8_t i = 0; i < ArraySize(ctx->fxsave.reserved_4); i++) {
+  for (uint8_t i = 0; i < base::size(ctx->fxsave.reserved_4); i++) {
     EXPECT_EQ(ctx->fxsave.reserved_4[i], i * 2 + 115);
     EXPECT_EQ(ctx->fxsave.available[i], i * 2 + 116);
   }
 
-  for (size_t i = 0; i < ArraySize(ctx->fxsave.st_mm); i++) {
-    for (uint8_t j = 0; j < ArraySize(ctx->fxsave.st_mm[0].mm_value); j++) {
+  for (size_t i = 0; i < base::size(ctx->fxsave.st_mm); i++) {
+    for (uint8_t j = 0; j < base::size(ctx->fxsave.st_mm[0].mm_value); j++) {
       EXPECT_EQ(ctx->fxsave.st_mm[i].mm_value[j], j + 1);
       EXPECT_EQ(ctx->fxsave.st_mm[i].mm_reserved[j], j + 1);
     }
   }
 
-  for (size_t i = 0; i < ArraySize(ctx->fxsave.xmm); i++) {
-    for (uint8_t j = 0; j < ArraySize(ctx->fxsave.xmm[0]); j++) {
+  for (size_t i = 0; i < base::size(ctx->fxsave.xmm); i++) {
+    for (uint8_t j = 0; j < base::size(ctx->fxsave.xmm[0]); j++) {
       EXPECT_EQ(ctx->fxsave.xmm[i][j], j + 1);
     }
   }
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.cc b/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.cc
index 46ae97a..e77bab8 100644
--- a/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.cc
+++ b/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.cc
@@ -17,8 +17,8 @@
 #include <stddef.h>
 #include <string.h>
 
+#include "base/stl_util.h"
 #include "minidump/minidump_context.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace internal {
@@ -193,7 +193,7 @@
       return false;
     }
 
-    for (size_t i = 0; i < ArraySize(src->regs); i++) {
+    for (size_t i = 0; i < base::size(src->regs); i++) {
       context_.arm->regs[i] = src->regs[i];
     }
 
@@ -205,7 +205,7 @@
     context_.arm->cpsr = src->cpsr;
     context_.arm->vfp_regs.fpscr = src->fpscr;
 
-    for (size_t i = 0; i < ArraySize(src->vfp); i++) {
+    for (size_t i = 0; i < base::size(src->vfp); i++) {
       context_.arm->vfp_regs.vfp[i] = src->vfp[i];
     }
 
@@ -225,14 +225,14 @@
       return false;
     }
 
-    for (size_t i = 0; i < ArraySize(src->regs); i++) {
+    for (size_t i = 0; i < base::size(src->regs); i++) {
       context_.arm64->regs[i] = src->regs[i];
     }
 
     context_.arm64->regs[29] = src->fp;
     context_.arm64->regs[30] = src->lr;
 
-    for (size_t i = 0; i < ArraySize(src->fpsimd); i++) {
+    for (size_t i = 0; i < base::size(src->fpsimd); i++) {
       context_.arm64->fpsimd[i] = src->fpsimd[i];
     }
 
@@ -255,7 +255,7 @@
       return false;
     }
 
-    for (size_t i = 0; i < ArraySize(src->regs); i++) {
+    for (size_t i = 0; i < base::size(src->regs); i++) {
       context_.mipsel->regs[i] = src->regs[i];
     }
 
@@ -263,7 +263,7 @@
     context_.mipsel->mdlo = static_cast<uint32_t>(src->mdlo);
     context_.mipsel->dsp_control = src->dsp_control;
 
-    for (size_t i = 0; i < ArraySize(src->hi); i++) {
+    for (size_t i = 0; i < base::size(src->hi); i++) {
       context_.mipsel->hi[i] = src->hi[i];
       context_.mipsel->lo[i] = src->lo[i];
     }
@@ -292,7 +292,7 @@
       return false;
     }
 
-    for (size_t i = 0; i < ArraySize(src->regs); i++) {
+    for (size_t i = 0; i < base::size(src->regs); i++) {
       context_.mips64->regs[i] = src->regs[i];
     }
 
@@ -300,7 +300,7 @@
     context_.mips64->mdlo = src->mdlo;
     context_.mips64->dsp_control = src->dsp_control;
 
-    for (size_t i = 0; i < ArraySize(src->hi); i++) {
+    for (size_t i = 0; i < base::size(src->hi); i++) {
       context_.mips64->hi[i] = src->hi[i];
       context_.mips64->lo[i] = src->lo[i];
     }
diff --git a/third_party/crashpad/crashpad/snapshot/posix/timezone.cc b/third_party/crashpad/crashpad/snapshot/posix/timezone.cc
index 8b2b8221..9451e11 100644
--- a/third_party/crashpad/crashpad/snapshot/posix/timezone.cc
+++ b/third_party/crashpad/crashpad/snapshot/posix/timezone.cc
@@ -18,8 +18,8 @@
 #include <time.h>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "build/build_config.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace internal {
@@ -60,7 +60,8 @@
     static constexpr int kMonthDeltas[] =
         {0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6,
          7, -7, 8, -8, 9, -9, 10, -10, 11, -11, 12, -12};
-    for (size_t index = 0; index < ArraySize(kMonthDeltas) && !found_transition;
+    for (size_t index = 0;
+         index < base::size(kMonthDeltas) && !found_transition;
          ++index) {
       // Look at a day of each month at local noon. Set tm_isdst to -1 to avoid
       // giving mktime() any hints about whether to consider daylight saving
diff --git a/third_party/crashpad/crashpad/snapshot/posix/timezone_test.cc b/third_party/crashpad/crashpad/snapshot/posix/timezone_test.cc
index 1bb19c4e..b4405bad 100644
--- a/third_party/crashpad/crashpad/snapshot/posix/timezone_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/posix/timezone_test.cc
@@ -22,10 +22,10 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
 #include "test/errors.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -155,7 +155,7 @@
       {"UTC", false, 0, 0, "UTC", "UTC"},
   };
 
-  for (size_t index = 0; index < ArraySize(kTestTimeZones); ++index) {
+  for (size_t index = 0; index < base::size(kTestTimeZones); ++index) {
     const auto& test_time_zone = kTestTimeZones[index];
     const char* tz = test_time_zone.tz;
     SCOPED_TRACE(base::StringPrintf("index %zu, tz %s", index, tz));
diff --git a/third_party/crashpad/crashpad/snapshot/sanitized/sanitization_information_test.cc b/third_party/crashpad/crashpad/snapshot/sanitized/sanitization_information_test.cc
index c7d836a5..e426f28 100644
--- a/third_party/crashpad/crashpad/snapshot/sanitized/sanitization_information_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/sanitized/sanitization_information_test.cc
@@ -14,9 +14,9 @@
 
 #include "snapshot/sanitized/sanitization_information.h"
 
+#include "base/stl_util.h"
 #include "build/build_config.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/from_pointer_cast.h"
 #include "util/process/process_memory_linux.h"
 
@@ -60,8 +60,8 @@
 
 TEST_F(WhitelistTest, NonEmptyWhitelist) {
   ASSERT_TRUE(ReadWhitelist(kNonEmptyWhitelist));
-  ASSERT_EQ(whitelist_.size(), ArraySize(kNonEmptyWhitelist) - 1);
-  for (size_t index = 0; index < ArraySize(kNonEmptyWhitelist) - 1; ++index) {
+  ASSERT_EQ(whitelist_.size(), base::size(kNonEmptyWhitelist) - 1);
+  for (size_t index = 0; index < base::size(kNonEmptyWhitelist) - 1; ++index) {
     EXPECT_EQ(whitelist_[index], kNonEmptyWhitelist[index]);
   }
 }
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_cpu_context.cc b/third_party/crashpad/crashpad/snapshot/test/test_cpu_context.cc
index 7694d7e..746e340 100644
--- a/third_party/crashpad/crashpad/snapshot/test/test_cpu_context.cc
+++ b/third_party/crashpad/crashpad/snapshot/test/test_cpu_context.cc
@@ -17,7 +17,7 @@
 #include <string.h>
 #include <sys/types.h>
 
-#include "util/misc/arraysize.h"
+#include "base/stl_util.h"
 
 namespace crashpad {
 namespace test {
@@ -44,28 +44,28 @@
   fxsave->reserved_3 = static_cast<uint16_t>(value++);
   fxsave->mxcsr = value++;
   fxsave->mxcsr_mask = value++;
-  for (size_t st_mm_index = 0; st_mm_index < ArraySize(fxsave->st_mm);
+  for (size_t st_mm_index = 0; st_mm_index < base::size(fxsave->st_mm);
        ++st_mm_index) {
-    for (size_t byte = 0; byte < ArraySize(fxsave->st_mm[st_mm_index].st);
+    for (size_t byte = 0; byte < base::size(fxsave->st_mm[st_mm_index].st);
          ++byte) {
       fxsave->st_mm[st_mm_index].st[byte] = static_cast<uint8_t>(value++);
     }
     for (size_t byte = 0;
-         byte < ArraySize(fxsave->st_mm[st_mm_index].st_reserved);
+         byte < base::size(fxsave->st_mm[st_mm_index].st_reserved);
          ++byte) {
       fxsave->st_mm[st_mm_index].st_reserved[byte] =
           static_cast<uint8_t>(value);
     }
   }
-  for (size_t xmm_index = 0; xmm_index < ArraySize(fxsave->xmm); ++xmm_index) {
-    for (size_t byte = 0; byte < ArraySize(fxsave->xmm[xmm_index]); ++byte) {
+  for (size_t xmm_index = 0; xmm_index < base::size(fxsave->xmm); ++xmm_index) {
+    for (size_t byte = 0; byte < base::size(fxsave->xmm[xmm_index]); ++byte) {
       fxsave->xmm[xmm_index][byte] = static_cast<uint8_t>(value++);
     }
   }
-  for (size_t byte = 0; byte < ArraySize(fxsave->reserved_4); ++byte) {
+  for (size_t byte = 0; byte < base::size(fxsave->reserved_4); ++byte) {
     fxsave->reserved_4[byte] = static_cast<uint8_t>(value++);
   }
-  for (size_t byte = 0; byte < ArraySize(fxsave->available); ++byte) {
+  for (size_t byte = 0; byte < base::size(fxsave->available); ++byte) {
     fxsave->available[byte] = static_cast<uint8_t>(value++);
   }
 
@@ -174,7 +174,7 @@
 
   uint32_t value = seed;
 
-  for (size_t index = 0; index < ArraySize(arm->regs); ++index) {
+  for (size_t index = 0; index < base::size(arm->regs); ++index) {
     arm->regs[index] = value++;
   }
   arm->fp = value++;
@@ -185,7 +185,7 @@
   arm->pc = value++;
   arm->cpsr = value++;
 
-  for (size_t index = 0; index < ArraySize(arm->vfp_regs.vfp); ++index) {
+  for (size_t index = 0; index < base::size(arm->vfp_regs.vfp); ++index) {
     arm->vfp_regs.vfp[index] = value++;
   }
   arm->vfp_regs.fpscr = value++;
@@ -205,14 +205,14 @@
 
   uint32_t value = seed;
 
-  for (size_t index = 0; index < ArraySize(arm64->regs); ++index) {
+  for (size_t index = 0; index < base::size(arm64->regs); ++index) {
     arm64->regs[index] = value++;
   }
   arm64->sp = value++;
   arm64->pc = value++;
   arm64->spsr = value++;
 
-  for (size_t index = 0; index < ArraySize(arm64->fpsimd); ++index) {
+  for (size_t index = 0; index < base::size(arm64->fpsimd); ++index) {
     arm64->fpsimd[index].lo = value++;
     arm64->fpsimd[index].hi = value++;
   }
@@ -231,7 +231,7 @@
 
   uint32_t value = seed;
 
-  for (size_t index = 0; index < ArraySize(mipsel->regs); ++index) {
+  for (size_t index = 0; index < base::size(mipsel->regs); ++index) {
     mipsel->regs[index] = value++;
   }
 
@@ -242,7 +242,7 @@
   mipsel->cp0_status = value++;
   mipsel->cp0_cause = value++;
 
-  for (size_t index = 0; index < ArraySize(mipsel->fpregs.fregs); ++index) {
+  for (size_t index = 0; index < base::size(mipsel->fpregs.fregs); ++index) {
     mipsel->fpregs.fregs[index]._fp_fregs = static_cast<float>(value++);
   }
 
@@ -267,7 +267,7 @@
 
   uint64_t value = seed;
 
-  for (size_t index = 0; index < ArraySize(mips64->regs); ++index) {
+  for (size_t index = 0; index < base::size(mips64->regs); ++index) {
     mips64->regs[index] = value++;
   }
 
@@ -278,7 +278,7 @@
   mips64->cp0_status = value++;
   mips64->cp0_cause = value++;
 
-  for (size_t index = 0; index < ArraySize(mips64->fpregs.dregs); ++index) {
+  for (size_t index = 0; index < base::size(mips64->fpregs.dregs); ++index) {
     mips64->fpregs.dregs[index] = static_cast<double>(value++);
   }
 
diff --git a/third_party/crashpad/crashpad/snapshot/win/cpu_context_win_test.cc b/third_party/crashpad/crashpad/snapshot/win/cpu_context_win_test.cc
index 7f66f9d3..e63f04e 100644
--- a/third_party/crashpad/crashpad/snapshot/win/cpu_context_win_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/win/cpu_context_win_test.cc
@@ -16,11 +16,11 @@
 
 #include <windows.h>
 
+#include "base/stl_util.h"
 #include "build/build_config.h"
 #include "gtest/gtest.h"
 #include "snapshot/cpu_context.h"
 #include "test/hex_string.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -87,13 +87,13 @@
     for (size_t st_mm = 0; st_mm < 7; ++st_mm) {
       EXPECT_EQ(
           BytesToHexString(cpu_context_x86.fxsave.st_mm[st_mm].st,
-                           ArraySize(cpu_context_x86.fxsave.st_mm[st_mm].st)),
-          std::string(ArraySize(cpu_context_x86.fxsave.st_mm[st_mm].st) * 2,
+                           base::size(cpu_context_x86.fxsave.st_mm[st_mm].st)),
+          std::string(base::size(cpu_context_x86.fxsave.st_mm[st_mm].st) * 2,
                       '0'))
           << "st_mm " << st_mm;
     }
     EXPECT_EQ(BytesToHexString(cpu_context_x86.fxsave.st_mm[7].st,
-                               ArraySize(cpu_context_x86.fxsave.st_mm[7].st)),
+                               base::size(cpu_context_x86.fxsave.st_mm[7].st)),
               "0000000000000080ff7f");
 
     EXPECT_EQ(cpu_context_x86.dr0, 3u);
diff --git a/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_image_reader.cc b/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_image_reader.cc
index e6a9e5d..8bccbd6 100644
--- a/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_image_reader.cc
+++ b/third_party/crashpad/crashpad/snapshot/win/crashpad_snapshot_test_image_reader.cc
@@ -15,9 +15,9 @@
 #include <windows.h>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "client/crashpad_info.h"
 #include "util/file/file_io.h"
-#include "util/misc/arraysize.h"
 #include "util/synchronization/semaphore.h"
 #include "util/win/scoped_handle.h"
 
@@ -29,7 +29,7 @@
 
   // Allocate a bunch of pointers to things on the stack.
   int* pointers[1000];
-  for (size_t i = 0; i < ArraySize(pointers); ++i) {
+  for (size_t i = 0; i < base::size(pointers); ++i) {
     pointers[i] = new int[2048];
   }
 
@@ -53,7 +53,7 @@
   // verify the cap on pointed-to memory.
   crashpad::Semaphore semaphore(0);
   crashpad::ScopedKernelHANDLE threads[100];
-  for (size_t i = 0; i < ArraySize(threads); ++i) {
+  for (size_t i = 0; i < base::size(threads); ++i) {
     threads[i].reset(CreateThread(nullptr,
                                   0,
                                   &LotsOfReferencesThreadProc,
@@ -66,7 +66,7 @@
     }
   }
 
-  for (size_t i = 0; i < ArraySize(threads); ++i) {
+  for (size_t i = 0; i < base::size(threads); ++i) {
     semaphore.Wait();
   }
 
diff --git a/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.cc b/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.cc
index d76a187..d9d74c1 100644
--- a/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.cc
+++ b/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.cc
@@ -33,14 +33,16 @@
       name_(),
       pdb_name_(),
       uuid_(),
-      pe_image_reader_(),
+      memory_range_(),
+      streams_(),
+      vs_fixed_file_info_(),
+      initialized_vs_fixed_file_info_(),
       process_reader_(nullptr),
+      pe_image_reader_(),
+      crashpad_info_(),
       timestamp_(0),
       age_(0),
-      initialized_(),
-      vs_fixed_file_info_(),
-      initialized_vs_fixed_file_info_() {
-}
+      initialized_() {}
 
 ModuleSnapshotWin::~ModuleSnapshotWin() {
 }
@@ -75,6 +77,26 @@
     pdb_name_ = base::UTF16ToUTF8(name_);
   }
 
+  if (!memory_range_.Initialize(process_reader_->Memory(),
+                                process_reader_->Is64Bit())) {
+    return false;
+  }
+
+  WinVMAddress crashpad_info_address;
+  WinVMSize crashpad_info_size;
+  if (pe_image_reader_->GetCrashpadInfoSection(&crashpad_info_address,
+                                               &crashpad_info_size)) {
+    ProcessMemoryRange info_range;
+    info_range.Initialize(memory_range_);
+    info_range.RestrictRange(crashpad_info_address,
+                             crashpad_info_address + crashpad_info_size);
+
+    auto info = std::make_unique<CrashpadInfoReader>();
+    if (info->Initialize(&info_range, crashpad_info_address)) {
+      crashpad_info_ = std::move(info);
+    }
+  }
+
   INITIALIZATION_STATE_SET_VALID(initialized_);
   return true;
 }
@@ -228,8 +250,7 @@
 template <class Traits>
 void ModuleSnapshotWin::GetCrashpadOptionsInternal(
     CrashpadInfoClientOptions* options) {
-  process_types::CrashpadInfo<Traits> crashpad_info;
-  if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info)) {
+  if (!crashpad_info_) {
     options->crashpad_handler_behavior = TriState::kUnset;
     options->system_crash_reporter_forwarding = TriState::kUnset;
     options->gather_indirectly_referenced_memory = TriState::kUnset;
@@ -238,19 +259,13 @@
   }
 
   options->crashpad_handler_behavior =
-      CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
-          crashpad_info.crashpad_handler_behavior);
-
+      crashpad_info_->CrashpadHandlerBehavior();
   options->system_crash_reporter_forwarding =
-      CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
-          crashpad_info.system_crash_reporter_forwarding);
-
+      crashpad_info_->SystemCrashReporterForwarding();
   options->gather_indirectly_referenced_memory =
-      CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
-          crashpad_info.gather_indirectly_referenced_memory);
-
+      crashpad_info_->GatherIndirectlyReferencedMemory();
   options->indirectly_referenced_memory_cap =
-      crashpad_info.indirectly_referenced_memory_cap;
+      crashpad_info_->IndirectlyReferencedMemoryCap();
 }
 
 const VS_FIXEDFILEINFO* ModuleSnapshotWin::VSFixedFileInfo() const {
@@ -270,16 +285,13 @@
 template <class Traits>
 void ModuleSnapshotWin::GetCrashpadExtraMemoryRanges(
     std::set<CheckedRange<uint64_t>>* ranges) const {
-  process_types::CrashpadInfo<Traits> crashpad_info;
-  if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info) ||
-      !crashpad_info.extra_address_ranges) {
+  if (!crashpad_info_ || !crashpad_info_->ExtraMemoryRanges())
     return;
-  }
 
   std::vector<SimpleAddressRangeBag::Entry> simple_ranges(
       SimpleAddressRangeBag::num_entries);
   if (!process_reader_->Memory()->Read(
-          crashpad_info.extra_address_ranges,
+          crashpad_info_->ExtraMemoryRanges(),
           simple_ranges.size() * sizeof(simple_ranges[0]),
           &simple_ranges[0])) {
     LOG(WARNING) << "could not read simple address_ranges from "
@@ -298,11 +310,10 @@
 template <class Traits>
 void ModuleSnapshotWin::GetCrashpadUserMinidumpStreams(
     std::vector<std::unique_ptr<const UserMinidumpStream>>* streams) const {
-  process_types::CrashpadInfo<Traits> crashpad_info;
-  if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info))
+  if (!crashpad_info_)
     return;
 
-  for (uint64_t cur = crashpad_info.user_data_minidump_stream_head; cur;) {
+  for (uint64_t cur = crashpad_info_->UserDataMinidumpStreamHead(); cur;) {
     internal::UserDataMinidumpStreamListEntry list_entry;
     if (!process_reader_->Memory()->Read(
             cur, sizeof(list_entry), &list_entry)) {
diff --git a/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.h b/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.h
index 693588d..f18a880 100644
--- a/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.h
+++ b/third_party/crashpad/crashpad/snapshot/win/module_snapshot_win.h
@@ -25,6 +25,7 @@
 
 #include "base/macros.h"
 #include "snapshot/crashpad_info_client_options.h"
+#include "snapshot/crashpad_types/crashpad_info_reader.h"
 #include "snapshot/module_snapshot.h"
 #include "snapshot/win/process_reader_win.h"
 #include "util/misc/initialization_state.h"
@@ -109,18 +110,19 @@
   std::wstring name_;
   std::string pdb_name_;
   UUID uuid_;
-  std::unique_ptr<PEImageReader> pe_image_reader_;
-  ProcessReaderWin* process_reader_;  // weak
-  time_t timestamp_;
-  uint32_t age_;
+  ProcessMemoryRange memory_range_;
   // Too const-y: https://crashpad.chromium.org/bug/9.
   mutable std::vector<std::unique_ptr<const UserMinidumpStream>> streams_;
-  InitializationStateDcheck initialized_;
-
   // VSFixedFileInfo() is logically const, but updates these members on the
   // call. See https://crashpad.chromium.org/bug/9.
   mutable VS_FIXEDFILEINFO vs_fixed_file_info_;
   mutable InitializationState initialized_vs_fixed_file_info_;
+  ProcessReaderWin* process_reader_;  // weak
+  std::unique_ptr<PEImageReader> pe_image_reader_;
+  std::unique_ptr<CrashpadInfoReader> crashpad_info_;
+  time_t timestamp_;
+  uint32_t age_;
+  InitializationStateDcheck initialized_;
 
   DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotWin);
 };
diff --git a/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader.cc b/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader.cc
index c5e5373..058342dba 100644
--- a/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader.cc
+++ b/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader.cc
@@ -17,13 +17,13 @@
 #include <string.h>
 #include <sys/types.h>
 
+#include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "client/annotation.h"
 #include "client/simple_string_dictionary.h"
 #include "snapshot/snapshot_constants.h"
 #include "snapshot/win/pe_image_reader.h"
 #include "snapshot/win/process_reader_win.h"
-#include "util/misc/arraysize.h"
 #include "util/win/process_structs.h"
 
 namespace crashpad {
@@ -156,7 +156,8 @@
     snapshot.type = current.type;
 
     char name[Annotation::kNameMaxLength];
-    if (!process_reader_->Memory()->Read(current.name, ArraySize(name), name)) {
+    if (!process_reader_->Memory()->Read(
+            current.name, base::size(name), name)) {
       LOG(WARNING) << "could not read annotation name at index " << index
                    << " in " << base::UTF16ToUTF8(name_);
       continue;
diff --git a/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.cc b/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.cc
index 094cf0fd..e69faef 100644
--- a/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.cc
+++ b/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.cc
@@ -21,10 +21,10 @@
 #include <memory>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "client/crashpad_info.h"
 #include "snapshot/win/pe_image_resource_reader.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/from_pointer_cast.h"
 #include "util/misc/pdb_structures.h"
 #include "util/win/process_structs.h"
@@ -72,6 +72,18 @@
   return true;
 }
 
+bool PEImageReader::GetCrashpadInfoSection(WinVMAddress* address,
+                                           WinVMSize* size) const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  if (module_subrange_reader_.Is64Bit()) {
+    return GetCrashpadInfoSectionInternal<process_types::internal::Traits64>(
+        address, size);
+  } else {
+    return GetCrashpadInfoSectionInternal<process_types::internal::Traits32>(
+        address, size);
+  }
+}
+
 template <class Traits>
 bool PEImageReader::GetCrashpadInfo(
     process_types::CrashpadInfo<Traits>* crashpad_info) const {
@@ -276,7 +288,7 @@
       version_info.wType != 0 ||
       wcsncmp(version_info.szKey,
               L"VS_VERSION_INFO",
-              ArraySize(version_info.szKey)) != 0) {
+              base::size(version_info.szKey)) != 0) {
     LOG(WARNING) << "unexpected VS_VERSIONINFO in "
                  << module_subrange_reader_.name();
     return false;
@@ -294,6 +306,31 @@
   return true;
 }
 
+template <class Traits>
+bool PEImageReader::GetCrashpadInfoSectionInternal(WinVMAddress* address,
+                                                   WinVMSize* size) const {
+  IMAGE_SECTION_HEADER section;
+  if (!GetSectionByName<typename NtHeadersForTraits<Traits>::type>("CPADinfo",
+                                                                   &section)) {
+    return false;
+  }
+
+  process_types::CrashpadInfo<Traits> crashpad_info;
+  if (section.Misc.VirtualSize <
+      offsetof(process_types::CrashpadInfo<Traits>, size) +
+          sizeof(crashpad_info.size)) {
+    LOG(WARNING) << "small crashpad info section size "
+                 << section.Misc.VirtualSize << ", "
+                 << module_subrange_reader_.name();
+    return false;
+  }
+
+  *address = Address() + section.VirtualAddress;
+  *size = std::min<WinVMSize>(sizeof(crashpad_info), section.Misc.VirtualSize);
+
+  return true;
+}
+
 template <class NtHeadersType>
 bool PEImageReader::ReadNtHeaders(NtHeadersType* nt_headers,
                                   WinVMAddress* nt_headers_address) const {
diff --git a/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.h b/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.h
index 56a991b..ebdbe578 100644
--- a/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.h
+++ b/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.h
@@ -94,6 +94,16 @@
   //! This is the value passed as \a size to Initialize().
   WinVMSize Size() const { return module_subrange_reader_.Size(); }
 
+  //! \brief Obtains the module's CrashpadInfo structure address and size.
+  //!
+  //! \param[out] address The CrashpadInfo structure address.
+  //! \param[out] size The CrashpadInfo structure size.
+  //!
+  //! \return `true` on success, `false` on failure. If the module does not have
+  //!     a `CPADinfo` section, this will return `false` without logging any
+  //!     messages. Other failures will result in messages being logged.
+  bool GetCrashpadInfoSection(WinVMAddress* address, WinVMSize* size) const;
+
   //! \brief Obtains the module's CrashpadInfo structure.
   //!
   //! \return `true` on success, `false` on failure. If the module does not have
@@ -137,6 +147,13 @@
   bool VSFixedFileInfo(VS_FIXEDFILEINFO* vs_fixed_file_info) const;
 
  private:
+  //! \brief Performs the internal logic for GetCrashpadInfoSection().
+  //!
+  //! \sa GetCrashpadInfoSection
+  template <class Traits>
+  bool GetCrashpadInfoSectionInternal(WinVMAddress* address,
+                                      WinVMSize* size) const;
+
   //! \brief Reads the `IMAGE_NT_HEADERS` from the beginning of the image.
   //!
   //! \param[out] nt_headers The contents of the templated NtHeadersType
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc b/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc
index eca6f48..1880c871 100644
--- a/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc
@@ -17,9 +17,9 @@
 #include <windows.h>
 #include <string.h>
 
+#include "base/stl_util.h"
 #include "gtest/gtest.h"
 #include "test/win/win_multiprocess.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/from_pointer_cast.h"
 #include "util/synchronization/semaphore.h"
 #include "util/thread/thread.h"
@@ -44,7 +44,7 @@
   EXPECT_EQ(process_reader.GetProcessInfo().ProcessID(), GetCurrentProcessId());
 
   static constexpr char kTestMemory[] = "Some test memory";
-  char buffer[ArraySize(kTestMemory)];
+  char buffer[base::size(kTestMemory)];
   ASSERT_TRUE(process_reader.Memory()->Read(
       reinterpret_cast<uintptr_t>(kTestMemory), sizeof(kTestMemory), &buffer));
   EXPECT_STREQ(kTestMemory, buffer);
@@ -185,7 +185,7 @@
     // the pipe.
     CheckedReadFileAtEOF(ReadPipeHandle());
 
-    for (size_t i = 0; i < ArraySize(threads); ++i)
+    for (size_t i = 0; i < base::size(threads); ++i)
       done.Signal();
     for (auto& thread : threads)
       thread.Join();
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc b/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc
index 5d98a5f..f91ef29 100644
--- a/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc
+++ b/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc
@@ -22,9 +22,9 @@
 
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/from_pointer_cast.h"
 #include "util/misc/time.h"
 #include "util/win/nt_internals.h"
@@ -335,7 +335,7 @@
           uet.TimeDateStamp,
           base::UTF16ToUTF8(base::StringPiece16(
               uet.ImageName,
-              wcsnlen(uet.ImageName, ArraySize(uet.ImageName))))));
+              wcsnlen(uet.ImageName, base::size(uet.ImageName))))));
     }
   }
 }
@@ -535,9 +535,9 @@
   env_block.resize(
       static_cast<unsigned int>(bytes_read / sizeof(env_block[0])));
   static constexpr wchar_t terminator[] = {0, 0};
-  size_t at = env_block.find(std::wstring(terminator, ArraySize(terminator)));
+  size_t at = env_block.find(std::wstring(terminator, base::size(terminator)));
   if (at != std::wstring::npos)
-    env_block.resize(at + ArraySize(terminator));
+    env_block.resize(at + base::size(terminator));
 
   return env_block.size() * sizeof(env_block[0]);
 }
diff --git a/third_party/crashpad/crashpad/test/hex_string.h b/third_party/crashpad/crashpad/test/hex_string.h
index 2d7801b..b0d4453 100644
--- a/third_party/crashpad/crashpad/test/hex_string.h
+++ b/third_party/crashpad/crashpad/test/hex_string.h
@@ -29,8 +29,8 @@
 //!   uint8_t expected[10];
 //!   uint8_t observed[10];
 //!   // …
-//!   EXPECT_EQ(BytesToHexString(observed, ArraySize(observed)),
-//!             BytesToHexString(expected, ArraySize(expected)));
+//!   EXPECT_EQ(BytesToHexString(observed, base::size(observed)),
+//!             BytesToHexString(expected, base::size(expected)));
 //! \endcode
 std::string BytesToHexString(const void* bytes, size_t length);
 
diff --git a/third_party/crashpad/crashpad/test/hex_string_test.cc b/third_party/crashpad/crashpad/test/hex_string_test.cc
index 3d44cc9..3a09eb76 100644
--- a/third_party/crashpad/crashpad/test/hex_string_test.cc
+++ b/third_party/crashpad/crashpad/test/hex_string_test.cc
@@ -14,8 +14,8 @@
 
 #include "test/hex_string.h"
 
+#include "base/stl_util.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -25,7 +25,7 @@
   EXPECT_EQ(BytesToHexString(nullptr, 0), "");
 
   static constexpr char kBytes[] = "Abc123xyz \x0a\x7f\xf0\x9f\x92\xa9_";
-  EXPECT_EQ(BytesToHexString(kBytes, ArraySize(kBytes)),
+  EXPECT_EQ(BytesToHexString(kBytes, base::size(kBytes)),
             "41626331323378797a200a7ff09f92a95f00");
 }
 
diff --git a/third_party/crashpad/crashpad/tools/crashpad_database_util.cc b/third_party/crashpad/crashpad/tools/crashpad_database_util.cc
index d8274c5..17a4771 100644
--- a/third_party/crashpad/crashpad/tools/crashpad_database_util.cc
+++ b/third_party/crashpad/crashpad/tools/crashpad_database_util.cc
@@ -29,6 +29,7 @@
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "client/crash_report_database.h"
@@ -36,7 +37,6 @@
 #include "tools/tool_support.h"
 #include "util/file/file_io.h"
 #include "util/file/file_reader.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/uuid.h"
 #include "util/stdlib/string_number_conversion.h"
 
@@ -109,14 +109,14 @@
       "set",
   };
 
-  for (size_t index = 0; index < ArraySize(kFalseWords); ++index) {
+  for (size_t index = 0; index < base::size(kFalseWords); ++index) {
     if (strcasecmp(string, kFalseWords[index]) == 0) {
       *boolean = false;
       return true;
     }
   }
 
-  for (size_t index = 0; index < ArraySize(kTrueWords); ++index) {
+  for (size_t index = 0; index < base::size(kTrueWords); ++index) {
     if (strcasecmp(string, kTrueWords[index]) == 0) {
       *boolean = true;
       return true;
@@ -159,7 +159,7 @@
       "%+",
   };
 
-  for (size_t index = 0; index < ArraySize(kFormats); ++index) {
+  for (size_t index = 0; index < base::size(kFormats); ++index) {
     tm time_tm;
     const char* strptime_result = strptime(string, kFormats[index], &time_tm);
     if (strptime_result == end) {
@@ -214,7 +214,7 @@
 
   char string[64];
   CHECK_NE(
-      strftime(string, ArraySize(string), "%Y-%m-%d %H:%M:%S %Z", &time_tm),
+      strftime(string, base::size(string), "%Y-%m-%d %H:%M:%S %Z", &time_tm),
       0u);
 
   return std::string(string);
diff --git a/third_party/crashpad/crashpad/util/file/delimited_file_reader.cc b/third_party/crashpad/crashpad/util/file/delimited_file_reader.cc
index 2a8678f0..2275ade 100644
--- a/third_party/crashpad/crashpad/util/file/delimited_file_reader.cc
+++ b/third_party/crashpad/crashpad/util/file/delimited_file_reader.cc
@@ -21,7 +21,7 @@
 
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
-#include "util/misc/arraysize.h"
+#include "base/stl_util.h"
 
 namespace crashpad {
 
@@ -76,7 +76,7 @@
         return Result::kEndOfFile;
       }
 
-      DCHECK_LE(static_cast<size_t>(read_result), ArraySize(buf_));
+      DCHECK_LE(static_cast<size_t>(read_result), base::size(buf_));
       DCHECK(
           base::IsValueInRangeForNumericType<decltype(buf_len_)>(read_result));
       buf_len_ = static_cast<decltype(buf_len_)>(read_result);
diff --git a/third_party/crashpad/crashpad/util/file/delimited_file_reader_test.cc b/third_party/crashpad/crashpad/util/file/delimited_file_reader_test.cc
index b226f3d..a2fd7d5 100644
--- a/third_party/crashpad/crashpad/util/file/delimited_file_reader_test.cc
+++ b/third_party/crashpad/crashpad/util/file/delimited_file_reader_test.cc
@@ -17,10 +17,10 @@
 #include <vector>
 
 #include "base/format_macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
 #include "util/file/string_file.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -260,7 +260,7 @@
 TEST(DelimitedFileReader, EmbeddedNUL) {
   static constexpr char kString[] = "embedded\0NUL\n";
   StringFile string_file;
-  string_file.SetString(std::string(kString, ArraySize(kString) - 1));
+  string_file.SetString(std::string(kString, base::size(kString) - 1));
   DelimitedFileReader delimited_file_reader(&string_file);
 
   std::string line;
@@ -278,7 +278,7 @@
 TEST(DelimitedFileReader, NULDelimiter) {
   static constexpr char kString[] = "aa\0b\0ccc\0";
   StringFile string_file;
-  string_file.SetString(std::string(kString, ArraySize(kString) - 1));
+  string_file.SetString(std::string(kString, base::size(kString) - 1));
   DelimitedFileReader delimited_file_reader(&string_file);
 
   std::string field;
@@ -302,7 +302,7 @@
 TEST(DelimitedFileReader, EdgeCases) {
   static constexpr size_t kSizes[] =
       {4094, 4095, 4096, 4097, 8190, 8191, 8192, 8193};
-  for (size_t index = 0; index < ArraySize(kSizes); ++index) {
+  for (size_t index = 0; index < base::size(kSizes); ++index) {
     size_t size = kSizes[index];
     SCOPED_TRACE(
         base::StringPrintf("index %" PRIuS ", size %" PRIuS, index, size));
diff --git a/third_party/crashpad/crashpad/util/file/file_io_test.cc b/third_party/crashpad/crashpad/util/file/file_io_test.cc
index d341a5d..0fdd25c 100644
--- a/third_party/crashpad/crashpad/util/file/file_io_test.cc
+++ b/third_party/crashpad/crashpad/util/file/file_io_test.cc
@@ -22,12 +22,12 @@
 #include "base/atomicops.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
+#include "base/stl_util.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "test/errors.h"
 #include "test/file.h"
 #include "test/scoped_temp_dir.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/implicit_cast.h"
 #include "util/thread/thread.h"
 
@@ -613,7 +613,7 @@
 
   LockingTestThread threads[20];
   int expected_iterations = 0;
-  for (size_t index = 0; index < ArraySize(threads); ++index) {
+  for (size_t index = 0; index < base::size(threads); ++index) {
     int iterations_for_this_thread = static_cast<int>(index * 10);
     threads[index].Init(
         (other_locks == FileLocking::kShared)
diff --git a/third_party/crashpad/crashpad/util/linux/direct_ptrace_connection.cc b/third_party/crashpad/crashpad/util/linux/direct_ptrace_connection.cc
index 371bed2..e757d8f 100644
--- a/third_party/crashpad/crashpad/util/linux/direct_ptrace_connection.cc
+++ b/third_party/crashpad/crashpad/util/linux/direct_ptrace_connection.cc
@@ -19,10 +19,10 @@
 #include <utility>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "util/file/directory_reader.h"
 #include "util/file/file_io.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/as_underlying_type.h"
 
 namespace crashpad {
@@ -93,7 +93,7 @@
   DCHECK(threads->empty());
 
   char path[32];
-  snprintf(path, ArraySize(path), "/proc/%d/task", pid_);
+  snprintf(path, base::size(path), "/proc/%d/task", pid_);
   DirectoryReader reader;
   if (!reader.Open(base::FilePath(path))) {
     return false;
diff --git a/third_party/crashpad/crashpad/util/linux/proc_stat_reader.cc b/third_party/crashpad/crashpad/util/linux/proc_stat_reader.cc
index 60f1054..88658f5 100644
--- a/third_party/crashpad/crashpad/util/linux/proc_stat_reader.cc
+++ b/third_party/crashpad/crashpad/util/linux/proc_stat_reader.cc
@@ -20,8 +20,8 @@
 
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "util/file/file_io.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/lexing.h"
 #include "util/misc/time.h"
 
@@ -48,7 +48,7 @@
   INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
 
   char path[32];
-  snprintf(path, ArraySize(path), "/proc/%d/stat", tid);
+  snprintf(path, base::size(path), "/proc/%d/stat", tid);
   if (!connection->ReadFileContents(base::FilePath(path), &contents_)) {
     return false;
   }
diff --git a/third_party/crashpad/crashpad/util/linux/ptrace_client.cc b/third_party/crashpad/crashpad/util/linux/ptrace_client.cc
index e8afaa4..d822d10e 100644
--- a/third_party/crashpad/crashpad/util/linux/ptrace_client.cc
+++ b/third_party/crashpad/crashpad/util/linux/ptrace_client.cc
@@ -20,10 +20,10 @@
 #include <string>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "util/file/file_io.h"
 #include "util/linux/ptrace_broker.h"
-#include "util/misc/arraysize.h"
 #include "util/process/process_memory_linux.h"
 
 namespace crashpad {
@@ -271,7 +271,7 @@
   threads->push_back(pid_);
 
   char path[32];
-  snprintf(path, ArraySize(path), "/proc/%d/task", pid_);
+  snprintf(path, base::size(path), "/proc/%d/task", pid_);
 
   PtraceBroker::Request request;
   request.type = PtraceBroker::Request::kTypeListDirectory;
diff --git a/third_party/crashpad/crashpad/util/mac/checked_mach_address_range_test.cc b/third_party/crashpad/crashpad/util/mac/checked_mach_address_range_test.cc
index f2f8f54..8531379 100644
--- a/third_party/crashpad/crashpad/util/mac/checked_mach_address_range_test.cc
+++ b/third_party/crashpad/crashpad/util/mac/checked_mach_address_range_test.cc
@@ -19,10 +19,10 @@
 
 #include <limits>
 
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -116,7 +116,7 @@
       {0xffffffffffffffff, 1, kInvalid},
   };
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     const auto& testcase = kTestData[index];
     SCOPED_TRACE(base::StringPrintf("index %zu, base 0x%llx, size 0x%llx",
                                     index,
@@ -166,7 +166,7 @@
   CheckedMachAddressRange parent_range_32(false, 0x2000, 0x1000);
   ASSERT_TRUE(parent_range_32.IsValid());
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     const auto& testcase = kTestData[index];
     SCOPED_TRACE(
         base::StringPrintf("index %zu, value 0x%llx", index, testcase.value));
@@ -223,7 +223,7 @@
   CheckedMachAddressRange parent_range_32(false, 0x2000, 0x1000);
   ASSERT_TRUE(parent_range_32.IsValid());
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     const auto& testcase = kTestData[index];
     SCOPED_TRACE(base::StringPrintf("index %zu, base 0x%llx, size 0x%llx",
                                     index,
diff --git a/third_party/crashpad/crashpad/util/mac/launchd_test.mm b/third_party/crashpad/crashpad/util/mac/launchd_test.mm
index 4a6402d..b793a9b 100644
--- a/third_party/crashpad/crashpad/util/mac/launchd_test.mm
+++ b/third_party/crashpad/crashpad/util/mac/launchd_test.mm
@@ -23,8 +23,8 @@
 #include <limits>
 
 #include "base/mac/scoped_launch_data.h"
+#include "base/stl_util.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 #include "util/stdlib/objc.h"
 
 namespace crashpad {
@@ -58,7 +58,7 @@
       @0xfedcba9876543210,
     };
 
-    for (size_t index = 0; index < ArraySize(integer_nses); ++index) {
+    for (size_t index = 0; index < base::size(integer_nses); ++index) {
       NSNumber* integer_ns = integer_nses[index];
       launch_data.reset(CFPropertyToLaunchData(integer_ns));
       ASSERT_TRUE(launch_data.get());
@@ -88,7 +88,7 @@
       [NSNumber numberWithDouble:std::numeric_limits<double>::signaling_NaN()],
     };
 
-    for (size_t index = 0; index < ArraySize(double_nses); ++index) {
+    for (size_t index = 0; index < base::size(double_nses); ++index) {
       NSNumber* double_ns = double_nses[index];
       launch_data.reset(CFPropertyToLaunchData(double_ns));
       ASSERT_TRUE(launch_data.get());
@@ -114,7 +114,7 @@
       @YES,
     };
 
-    for (size_t index = 0; index < ArraySize(bool_nses); ++index) {
+    for (size_t index = 0; index < base::size(bool_nses); ++index) {
       NSNumber* bool_ns = bool_nses[index];
       launch_data.reset(CFPropertyToLaunchData(bool_ns));
       ASSERT_TRUE(launch_data.get());
@@ -138,7 +138,7 @@
       @"Üñîçø∂é",
     };
 
-    for (size_t index = 0; index < ArraySize(string_nses); ++index) {
+    for (size_t index = 0; index < base::size(string_nses); ++index) {
       NSString* string_ns = string_nses[index];
       launch_data.reset(CFPropertyToLaunchData(string_ns));
       ASSERT_TRUE(launch_data.get());
diff --git a/third_party/crashpad/crashpad/util/mach/child_port_handshake.cc b/third_party/crashpad/crashpad/util/mach/child_port_handshake.cc
index 721e560e..a1918c0 100644
--- a/third_party/crashpad/crashpad/util/mach/child_port_handshake.cc
+++ b/third_party/crashpad/crashpad/util/mach/child_port_handshake.cc
@@ -31,6 +31,7 @@
 #include "base/mac/scoped_mach_port.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/rand_util.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "util/file/file_io.h"
 #include "util/mach/child_port.h"
@@ -38,7 +39,6 @@
 #include "util/mach/mach_extensions.h"
 #include "util/mach/mach_message.h"
 #include "util/mach/mach_message_server.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/implicit_cast.h"
 #include "util/misc/random_string.h"
 
@@ -158,8 +158,8 @@
          0,
          0,
          nullptr);
-  int rv = HANDLE_EINTR(
-      kevent(kq.get(), changelist, ArraySize(changelist), nullptr, 0, nullptr));
+  int rv = HANDLE_EINTR(kevent(
+      kq.get(), changelist, base::size(changelist), nullptr, 0, nullptr));
   PCHECK(rv != -1) << "kevent";
 
   ChildPortServer child_port_server(this);
diff --git a/third_party/crashpad/crashpad/util/mach/child_port_server.cc b/third_party/crashpad/crashpad/util/mach/child_port_server.cc
index 678d3b2..aa7a1b8 100644
--- a/third_party/crashpad/crashpad/util/mach/child_port_server.cc
+++ b/third_party/crashpad/crashpad/util/mach/child_port_server.cc
@@ -15,9 +15,9 @@
 #include "util/mach/child_port_server.h"
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "util/mach/child_portServer.h"
 #include "util/mach/mach_message.h"
-#include "util/misc/arraysize.h"
 
 namespace {
 
@@ -91,7 +91,7 @@
   static constexpr mach_msg_id_t request_ids[] =
       {kMachMessageIDChildPortCheckIn};
   return std::set<mach_msg_id_t>(&request_ids[0],
-                                 &request_ids[ArraySize(request_ids)]);
+                                 &request_ids[base::size(request_ids)]);
 }
 
 mach_msg_size_t ChildPortServer::MachMessageServerRequestSize() {
diff --git a/third_party/crashpad/crashpad/util/mach/composite_mach_message_server_test.cc b/third_party/crashpad/crashpad/util/mach/composite_mach_message_server_test.cc
index c2ed5d7..87242be 100644
--- a/third_party/crashpad/crashpad/util/mach/composite_mach_message_server_test.cc
+++ b/third_party/crashpad/crashpad/util/mach/composite_mach_message_server_test.cc
@@ -16,11 +16,11 @@
 
 #include <sys/types.h>
 
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
 #include "test/gtest_death.h"
 #include "util/mach/mach_message.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -197,7 +197,7 @@
   TestMachMessageHandler handlers[3];
   std::set<mach_msg_id_t> expect_request_ids;
 
-  for (size_t index = 0; index < ArraySize(kRequestIDs0); ++index) {
+  for (size_t index = 0; index < base::size(kRequestIDs0); ++index) {
     const mach_msg_id_t request_id = kRequestIDs0[index];
     handlers[0].AddRequestID(request_id);
     expect_request_ids.insert(request_id);
@@ -206,7 +206,7 @@
   handlers[0].SetReplySize(sizeof(mig_reply_error_t));
   handlers[0].SetReturnCodes(true, kReturnCode0, false);
 
-  for (size_t index = 0; index < ArraySize(kRequestIDs1); ++index) {
+  for (size_t index = 0; index < base::size(kRequestIDs1); ++index) {
     const mach_msg_id_t request_id = kRequestIDs1[index];
     handlers[1].AddRequestID(request_id);
     expect_request_ids.insert(request_id);
@@ -215,7 +215,7 @@
   handlers[1].SetReplySize(200);
   handlers[1].SetReturnCodes(false, kReturnCode1, true);
 
-  for (size_t index = 0; index < ArraySize(kRequestIDs2); ++index) {
+  for (size_t index = 0; index < base::size(kRequestIDs2); ++index) {
     const mach_msg_id_t request_id = kRequestIDs2[index];
     handlers[2].AddRequestID(request_id);
     expect_request_ids.insert(request_id);
@@ -253,7 +253,7 @@
 
   // Send messages with known request IDs.
 
-  for (size_t index = 0; index < ArraySize(kRequestIDs0); ++index) {
+  for (size_t index = 0; index < base::size(kRequestIDs0); ++index) {
     request.header.msgh_id = kRequestIDs0[index];
     SCOPED_TRACE(base::StringPrintf(
         "handler 0, index %zu, id %d", index, request.header.msgh_id));
@@ -264,7 +264,7 @@
     EXPECT_FALSE(destroy_complex_request);
   }
 
-  for (size_t index = 0; index < ArraySize(kRequestIDs1); ++index) {
+  for (size_t index = 0; index < base::size(kRequestIDs1); ++index) {
     request.header.msgh_id = kRequestIDs1[index];
     SCOPED_TRACE(base::StringPrintf(
         "handler 1, index %zu, id %d", index, request.header.msgh_id));
@@ -275,7 +275,7 @@
     EXPECT_TRUE(destroy_complex_request);
   }
 
-  for (size_t index = 0; index < ArraySize(kRequestIDs2); ++index) {
+  for (size_t index = 0; index < base::size(kRequestIDs2); ++index) {
     request.header.msgh_id = kRequestIDs2[index];
     SCOPED_TRACE(base::StringPrintf(
         "handler 2, index %zu, id %d", index, request.header.msgh_id));
diff --git a/third_party/crashpad/crashpad/util/mach/exc_client_variants_test.cc b/third_party/crashpad/crashpad/util/mach/exc_client_variants_test.cc
index 7d1d00d..007442ab 100644
--- a/third_party/crashpad/crashpad/util/mach/exc_client_variants_test.cc
+++ b/third_party/crashpad/crashpad/util/mach/exc_client_variants_test.cc
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 
 #include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
 #include "test/mac/mach_errors.h"
@@ -29,7 +30,6 @@
 #include "util/mach/mach_extensions.h"
 #include "util/mach/mach_message.h"
 #include "util/mach/mach_message_server.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/implicit_cast.h"
 
 namespace crashpad {
@@ -182,11 +182,11 @@
       // These aren’t real flavors, it’s just for testing.
       flavor = exception_ + 10;
       flavor_p = &flavor;
-      for (size_t index = 0; index < ArraySize(old_state); ++index) {
+      for (size_t index = 0; index < base::size(old_state); ++index) {
         old_state[index] = index;
       }
       old_state_p = reinterpret_cast<thread_state_t>(&old_state);
-      old_state_count = ArraySize(old_state);
+      old_state_count = base::size(old_state);
 
       // new_state and new_state_count are out parameters that the server should
       // never see or use, so set them to bogus values. The call to the server
@@ -203,7 +203,7 @@
                                       task,
                                       exception,
                                       code,
-                                      ArraySize(code),
+                                      base::size(code),
                                       flavor_p,
                                       old_state_p,
                                       old_state_count,
@@ -274,7 +274,7 @@
       kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,
   };
 
-  for (size_t index = 0; index < ArraySize(kBehaviors); ++index) {
+  for (size_t index = 0; index < base::size(kBehaviors); ++index) {
     exception_behavior_t behavior = kBehaviors[index];
     SCOPED_TRACE(base::StringPrintf("index %zu, behavior %d", index, behavior));
 
diff --git a/third_party/crashpad/crashpad/util/mach/exc_server_variants.cc b/third_party/crashpad/crashpad/util/mach/exc_server_variants.cc
index f7b0b3d6..6264cb3 100644
--- a/third_party/crashpad/crashpad/util/mach/exc_server_variants.cc
+++ b/third_party/crashpad/crashpad/util/mach/exc_server_variants.cc
@@ -21,6 +21,7 @@
 #include <vector>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "util/mac/mac_util.h"
 #include "util/mach/composite_mach_message_server.h"
 #include "util/mach/exc.h"
@@ -29,7 +30,6 @@
 #include "util/mach/mach_exc.h"
 #include "util/mach/mach_excServer.h"
 #include "util/mach/mach_message.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 
@@ -243,7 +243,7 @@
         Traits::kMachMessageIDExceptionRaiseStateIdentity,
     };
     return std::set<mach_msg_id_t>(&request_ids[0],
-                                   &request_ids[ArraySize(request_ids)]);
+                                   &request_ids[base::size(request_ids)]);
   }
 
   mach_msg_size_t MachMessageServerRequestSize() override {
@@ -320,7 +320,7 @@
       using Reply = typename Traits::ExceptionRaiseStateReply;
       Reply* out_reply = reinterpret_cast<Reply*>(out_header);
       out_reply->flavor = in_request_1->flavor;
-      out_reply->new_stateCnt = ArraySize(out_reply->new_state);
+      out_reply->new_stateCnt = base::size(out_reply->new_state);
       out_reply->RetCode =
           interface_->CatchExceptionRaiseState(in_header->msgh_local_port,
                                                in_request->exception,
@@ -363,7 +363,7 @@
       using Reply = typename Traits::ExceptionRaiseStateIdentityReply;
       Reply* out_reply = reinterpret_cast<Reply*>(out_header);
       out_reply->flavor = in_request_1->flavor;
-      out_reply->new_stateCnt = ArraySize(out_reply->new_state);
+      out_reply->new_stateCnt = base::size(out_reply->new_state);
       out_reply->RetCode = interface_->CatchExceptionRaiseStateIdentity(
           in_header->msgh_local_port,
           in_request->thread.name,
diff --git a/third_party/crashpad/crashpad/util/mach/exc_server_variants_test.cc b/third_party/crashpad/crashpad/util/mach/exc_server_variants_test.cc
index b50ded1..078d4aed 100644
--- a/third_party/crashpad/crashpad/util/mach/exc_server_variants_test.cc
+++ b/third_party/crashpad/crashpad/util/mach/exc_server_variants_test.cc
@@ -19,6 +19,7 @@
 #include <string.h>
 #include <sys/types.h>
 
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "gmock/gmock.h"
@@ -29,7 +30,6 @@
 #include "util/mach/exception_behaviors.h"
 #include "util/mach/exception_types.h"
 #include "util/mach/mach_message.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/implicit_cast.h"
 
 namespace crashpad {
@@ -229,7 +229,7 @@
     EXPECT_EQ(memcmp(&NDR, &NDR_record, sizeof(NDR)), 0);
     EXPECT_EQ(RetCode, KERN_SUCCESS);
     EXPECT_EQ(flavor, kThreadStateFlavor);
-    EXPECT_EQ(new_stateCnt, ArraySize(new_state));
+    EXPECT_EQ(new_stateCnt, base::size(new_state));
   }
 
   mach_msg_header_t Head;
@@ -660,7 +660,7 @@
           AreExceptionCodes(kTestExceptonCodes[0], kTestExceptonCodes[1]),
           Pointee(Eq(kThreadStateFlavor)),
           IsThreadStateAndCount(kThreadStateFlavorCount),
-          IsThreadStateAndCount(ArraySize(reply.new_state)),
+          IsThreadStateAndCount(base::size(reply.new_state)),
           Eq(request.Trailer())))
       .WillOnce(Return(KERN_SUCCESS))
       .RetiresOnSaturation();
@@ -709,7 +709,7 @@
           AreExceptionCodes(kTestExceptonCodes[0], kTestExceptonCodes[1]),
           Pointee(Eq(kThreadStateFlavor)),
           IsThreadStateAndCount(kThreadStateFlavorCount),
-          IsThreadStateAndCount(ArraySize(reply.new_state)),
+          IsThreadStateAndCount(base::size(reply.new_state)),
           Eq(request.Trailer())))
       .WillOnce(Return(KERN_SUCCESS))
       .RetiresOnSaturation();
@@ -803,7 +803,7 @@
                                                kTestMachExceptionCodes[1]),
                              Pointee(Eq(kThreadStateFlavor)),
                              IsThreadStateAndCount(kThreadStateFlavorCount),
-                             IsThreadStateAndCount(ArraySize(reply.new_state)),
+                             IsThreadStateAndCount(base::size(reply.new_state)),
                              Eq(request.Trailer())))
       .WillOnce(Return(KERN_SUCCESS))
       .RetiresOnSaturation();
@@ -853,7 +853,7 @@
                                                kTestMachExceptionCodes[1]),
                              Pointee(Eq(kThreadStateFlavor)),
                              IsThreadStateAndCount(kThreadStateFlavorCount),
-                             IsThreadStateAndCount(ArraySize(reply.new_state)),
+                             IsThreadStateAndCount(base::size(reply.new_state)),
                              Eq(request.Trailer())))
       .WillOnce(Return(KERN_SUCCESS))
       .RetiresOnSaturation();
@@ -907,7 +907,7 @@
       2508,
   };
 
-  for (size_t index = 0; index < ArraySize(unknown_ids); ++index) {
+  for (size_t index = 0; index < base::size(unknown_ids); ++index) {
     mach_msg_id_t id = unknown_ids[index];
 
     SCOPED_TRACE(base::StringPrintf("unknown id %d", id));
@@ -1180,7 +1180,7 @@
 #endif
   };
 
-  for (size_t index = 0; index < ArraySize(test_data); ++index) {
+  for (size_t index = 0; index < base::size(test_data); ++index) {
     const auto& test = test_data[index];
     SCOPED_TRACE(
         base::StringPrintf("index %zu, flavor %d", index, test.flavor));
@@ -1253,7 +1253,7 @@
        KERN_SUCCESS},
   };
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     const auto& test_data = kTestData[index];
     SCOPED_TRACE(
         base::StringPrintf("index %zu, behavior %d, set_thread_state %s",
@@ -1272,8 +1272,8 @@
   static constexpr natural_t old_state[] = {1, 2, 3, 4, 5};
   natural_t new_state[10] = {};
 
-  constexpr mach_msg_type_number_t old_state_count = ArraySize(old_state);
-  mach_msg_type_number_t new_state_count = ArraySize(new_state);
+  constexpr mach_msg_type_number_t old_state_count = base::size(old_state);
+  mach_msg_type_number_t new_state_count = base::size(new_state);
 
   // EXCEPTION_DEFAULT (with or without MACH_EXCEPTION_CODES) is not
   // state-carrying. new_state and new_state_count should be untouched.
@@ -1282,8 +1282,8 @@
                      old_state_count,
                      new_state,
                      &new_state_count);
-  EXPECT_EQ(new_state_count, ArraySize(new_state));
-  for (size_t i = 0; i < ArraySize(new_state); ++i) {
+  EXPECT_EQ(new_state_count, base::size(new_state));
+  for (size_t i = 0; i < base::size(new_state); ++i) {
     EXPECT_EQ(new_state[i], 0u) << "i " << i;
   }
 
@@ -1292,8 +1292,8 @@
                      old_state_count,
                      new_state,
                      &new_state_count);
-  EXPECT_EQ(new_state_count, ArraySize(new_state));
-  for (size_t i = 0; i < ArraySize(new_state); ++i) {
+  EXPECT_EQ(new_state_count, base::size(new_state));
+  for (size_t i = 0; i < base::size(new_state); ++i) {
     EXPECT_EQ(new_state[i], 0u) << "i " << i;
   }
 
@@ -1305,7 +1305,7 @@
   for (size_t i = 0; i < copy_limit; ++i) {
     EXPECT_EQ(new_state[i], old_state[i]) << "i " << i;
   }
-  for (size_t i = copy_limit; i < ArraySize(new_state); ++i) {
+  for (size_t i = copy_limit; i < base::size(new_state); ++i) {
     EXPECT_EQ(new_state[i], 0u) << "i " << i;
   }
 
@@ -1321,23 +1321,23 @@
   for (size_t i = 0; i < copy_limit; ++i) {
     EXPECT_EQ(new_state[i], old_state[i]) << "i " << i;
   }
-  for (size_t i = copy_limit; i < ArraySize(new_state); ++i) {
+  for (size_t i = copy_limit; i < base::size(new_state); ++i) {
     EXPECT_EQ(new_state[i], 0u) << "i " << i;
   }
 
   // This is a state-carrying exception where all of old_state is copied to
   // new_state, which is large enough to receive it and then some.
-  new_state_count = ArraySize(new_state);
+  new_state_count = base::size(new_state);
   ExcServerCopyState(MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY,
                      old_state,
                      old_state_count,
                      new_state,
                      &new_state_count);
   EXPECT_EQ(new_state_count, old_state_count);
-  for (size_t i = 0; i < ArraySize(old_state); ++i) {
+  for (size_t i = 0; i < base::size(old_state); ++i) {
     EXPECT_EQ(new_state[i], old_state[i]) << "i " << i;
   }
-  for (size_t i = ArraySize(old_state); i < ArraySize(new_state); ++i) {
+  for (size_t i = base::size(old_state); i < base::size(new_state); ++i) {
     EXPECT_EQ(new_state[i], 0u) << "i " << i;
   }
 }
diff --git a/third_party/crashpad/crashpad/util/mach/exception_behaviors_test.cc b/third_party/crashpad/crashpad/util/mach/exception_behaviors_test.cc
index fd74d22..bdbf673 100644
--- a/third_party/crashpad/crashpad/util/mach/exception_behaviors_test.cc
+++ b/third_party/crashpad/crashpad/util/mach/exception_behaviors_test.cc
@@ -16,10 +16,10 @@
 
 #include <sys/types.h>
 
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
 #include "util/mach/mach_extensions.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -53,7 +53,7 @@
        EXCEPTION_STATE_IDENTITY},
   };
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     const auto& test_data = kTestData[index];
     SCOPED_TRACE(base::StringPrintf(
         "index %zu, behavior %d", index, test_data.behavior));
diff --git a/third_party/crashpad/crashpad/util/mach/exception_types_test.cc b/third_party/crashpad/crashpad/util/mach/exception_types_test.cc
index 030c7f3..da2c822 100644
--- a/third_party/crashpad/crashpad/util/mach/exception_types_test.cc
+++ b/third_party/crashpad/crashpad/util/mach/exception_types_test.cc
@@ -20,11 +20,11 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
 #include "util/mac/mac_util.h"
 #include "util/mach/mach_extensions.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -67,7 +67,7 @@
       {0, 0, 0, 0},
   };
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     const auto& test_data = kTestData[index];
     SCOPED_TRACE(base::StringPrintf(
         "index %zu, code_0 0x%llx", index, test_data.code_0));
@@ -84,7 +84,7 @@
 
   // Now make sure that ExcCrashRecoverOriginalException() properly ignores
   // optional arguments.
-  static_assert(ArraySize(kTestData) >= 1, "must have something to test");
+  static_assert(base::size(kTestData) >= 1, "must have something to test");
   const auto& test_data = kTestData[0];
   EXPECT_EQ(
       ExcCrashRecoverOriginalException(test_data.code_0, nullptr, nullptr),
@@ -238,7 +238,7 @@
       {0x00010000, 0x00010000, static_cast<int32_t>(0xffffffff)},
   };
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     const auto& test_data = kTestData[index];
     SCOPED_TRACE(base::StringPrintf("index %zu, exception 0x%x, code_0 0x%llx",
                                     index,
diff --git a/third_party/crashpad/crashpad/util/mach/mach_message_server_test.cc b/third_party/crashpad/crashpad/util/mach/mach_message_server_test.cc
index 724d84e..e3a3f94 100644
--- a/third_party/crashpad/crashpad/util/mach/mach_message_server_test.cc
+++ b/third_party/crashpad/crashpad/util/mach/mach_message_server_test.cc
@@ -23,13 +23,13 @@
 
 #include "base/mac/scoped_mach_port.h"
 #include "base/macros.h"
+#include "base/stl_util.h"
 #include "gtest/gtest.h"
 #include "test/mac/mach_errors.h"
 #include "test/mac/mach_multiprocess.h"
 #include "util/file/file_io.h"
 #include "util/mach/mach_extensions.h"
 #include "util/mach/mach_message.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/implicit_cast.h"
 
 namespace crashpad {
@@ -282,7 +282,7 @@
   std::set<mach_msg_id_t> MachMessageServerRequestIDs() override {
     static constexpr mach_msg_id_t request_ids[] = {kRequestMessageID};
     return std::set<mach_msg_id_t>(&request_ids[0],
-                                   &request_ids[ArraySize(request_ids)]);
+                                   &request_ids[base::size(request_ids)]);
   }
 
   mach_msg_size_t MachMessageServerRequestSize() override {
diff --git a/third_party/crashpad/crashpad/util/mach/mig.py b/third_party/crashpad/crashpad/util/mach/mig.py
index 9833c8c5..c2357338 100755
--- a/third_party/crashpad/crashpad/util/mach/mig.py
+++ b/third_party/crashpad/crashpad/util/mach/mig.py
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # coding: utf-8
 
-# Copyright 2014 The Crashpad Authors. All rights reserved.
+# Copyright 2019 The Crashpad Authors. All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -15,143 +15,19 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import argparse
-import os
-import re
-import subprocess
 import sys
 
-def FixUserImplementation(implementation):
-  """Rewrites a MIG-generated user implementation (.c) file.
-
-  Rewrites the file at |implementation| by adding “__attribute__((unused))” to
-  the definition of any structure typedefed as “__Reply” by searching for the
-  pattern unique to those structure definitions. These structures are in fact
-  unused in the user implementation file, and this will trigger a
-  -Wunused-local-typedefs warning in gcc unless removed or marked with the
-  “unused” attribute.
-  """
-
-  file = open(implementation, 'r+')
-  contents = file.read()
-
-  pattern = re.compile('^(\t} __Reply);$', re.MULTILINE)
-  contents = pattern.sub(r'\1 __attribute__((unused));', contents)
-
-  file.seek(0)
-  file.truncate()
-  file.write(contents)
-  file.close()
-
-def FixServerImplementation(implementation):
-  """Rewrites a MIG-generated server implementation (.c) file.
-
-  Rewrites the file at |implementation| by replacing “mig_internal” with
-  “mig_external” on functions that begin with “__MIG_check__”. This makes these
-  functions available to other callers outside this file from a linkage
-  perspective. It then returns, as a list of lines, declarations that can be
-  added to a header file, so that other files that include that header file will
-  have access to these declarations from a compilation perspective.
-  """
-
-  file = open(implementation, 'r+')
-  contents = file.read()
-
-  # Find interesting declarations.
-  declaration_pattern = \
-      re.compile('^mig_internal (kern_return_t __MIG_check__.*)$',
-                 re.MULTILINE)
-  declarations = declaration_pattern.findall(contents)
-
-  # Remove “__attribute__((__unused__))” from the declarations, and call them
-  # “mig_external” or “extern” depending on whether “mig_external” is defined.
-  attribute_pattern = re.compile(r'__attribute__\(\(__unused__\)\) ')
-  declarations = ['#ifdef mig_external\nmig_external\n#else\nextern\n#endif\n' +
-                  attribute_pattern.sub('', x) +
-                  ';\n' for x in declarations]
-
-  # Rewrite the declarations in this file as “mig_external”.
-  contents = declaration_pattern.sub(r'mig_external \1', contents);
-
-  # Crashpad never implements the mach_msg_server() MIG callouts. To avoid
-  # needing to provide stub implementations, set KERN_FAILURE as the RetCode
-  # and abort().
-  routine_callout_pattern = re.compile(
-      r'OutP->RetCode = (([a-zA-Z0-9_]+)\(.+\));')
-  routine_callouts = routine_callout_pattern.findall(contents)
-  for routine in routine_callouts:
-    contents = contents.replace(routine[0], 'KERN_FAILURE; abort()')
-
-  # Include the header for abort().
-  contents = '#include <stdlib.h>\n' + contents
-
-  file.seek(0)
-  file.truncate()
-  file.write(contents)
-  file.close()
-  return declarations
-
-def FixHeader(header, declarations=[]):
-  """Rewrites a MIG-generated header (.h) file.
-
-  Rewrites the file at |header| by placing it inside an “extern "C"” block, so
-  that it declares things properly when included by a C++ compilation unit.
-  |declarations| can be a list of additional declarations to place inside the
-  “extern "C"” block after the original contents of |header|.
-  """
-
-  file = open(header, 'r+')
-  contents = file.read()
-  declarations_text = ''.join(declarations)
-  contents = '''\
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-%s
-%s
-#ifdef __cplusplus
-}
-#endif
-''' % (contents, declarations_text)
-  file.seek(0)
-  file.truncate()
-  file.write(contents)
-  file.close()
+import mig_fix
+import mig_gen
 
 def main(args):
-  parser = argparse.ArgumentParser()
-  parser.add_argument('--developer-dir', help='Path to Xcode')
-  parser.add_argument('--sdk', help='Path to SDK')
-  parser.add_argument('--include',
-                      default=[],
-                      action='append',
-                      help='Additional include directory')
-  parser.add_argument('defs')
-  parser.add_argument('user_c')
-  parser.add_argument('server_c')
-  parser.add_argument('user_h')
-  parser.add_argument('server_h')
-  parsed = parser.parse_args(args)
+    parsed = mig_gen.parse_args(args)
 
-  command = ['mig',
-             '-user', parsed.user_c,
-             '-server', parsed.server_c,
-             '-header', parsed.user_h,
-             '-sheader', parsed.server_h,
-            ]
-  if parsed.developer_dir is not None:
-    os.environ['DEVELOPER_DIR'] = parsed.developer_dir
-  if parsed.sdk is not None:
-    command.extend(['-isysroot', parsed.sdk])
-  for include in parsed.include:
-    command.extend(['-I' + include])
-  command.append(parsed.defs)
-  subprocess.check_call(command)
-  FixUserImplementation(parsed.user_c)
-  server_declarations = FixServerImplementation(parsed.server_c)
-  FixHeader(parsed.user_h)
-  FixHeader(parsed.server_h, server_declarations)
+    interface = mig_gen.MigInterface(parsed.user_c, parsed.server_c,
+                                     parsed.user_h, parsed.server_h)
+    mig_gen.generate_interface(parsed.defs, interface, parsed.include,
+                               parsed.developer_dir, parsed.sdk)
+    mig_fix.fix_interface(interface)
 
 if __name__ == '__main__':
-  sys.exit(main(sys.argv[1:]))
+    sys.exit(main(sys.argv[1:]))
diff --git a/third_party/crashpad/crashpad/util/mach/mig_fix.py b/third_party/crashpad/crashpad/util/mach/mig_fix.py
new file mode 100755
index 0000000..8cd5e4f6d
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/mach/mig_fix.py
@@ -0,0 +1,197 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+# Copyright 2019 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import os
+import re
+import sys
+
+from mig_gen import MigInterface
+
+def _fix_user_implementation(implementation, fixed_implementation, header,
+                             fixed_header):
+    """Rewrites a MIG-generated user implementation (.c) file.
+
+    Rewrites the file at |implementation| by adding
+    “__attribute__((unused))” to the definition of any structure typedefed
+    as “__Reply” by searching for the pattern unique to those structure
+    definitions. These structures are in fact unused in the user
+    implementation file, and this will trigger a -Wunused-local-typedefs
+    warning in gcc unless removed or marked with the “unused” attribute.
+    Also changes header references to point to the new header filename, if
+    changed.
+
+    If |fixed_implementation| is None, overwrites the original; otherwise, puts
+    the result in the file at |fixed_implementation|.
+    """
+
+    file = open(implementation, 'r+' if fixed_implementation is None else 'r')
+    contents = file.read()
+
+    pattern = re.compile('^(\t} __Reply);$', re.MULTILINE)
+    contents = pattern.sub(r'\1 __attribute__((unused));', contents)
+
+    if fixed_header is not None:
+        contents = contents.replace(
+            '#include "%s"' % os.path.basename(header),
+            '#include "%s"' % os.path.basename(fixed_header))
+
+    if fixed_implementation is None:
+        file.seek(0)
+        file.truncate()
+    else:
+        file.close()
+        file = open(fixed_implementation, 'w')
+    file.write(contents)
+    file.close()
+
+def _fix_server_implementation(implementation, fixed_implementation, header,
+                               fixed_header):
+    """Rewrites a MIG-generated server implementation (.c) file.
+
+    Rewrites the file at |implementation| by replacing “mig_internal” with
+    “mig_external” on functions that begin with “__MIG_check__”. This makes
+    these functions available to other callers outside this file from a linkage
+    perspective. It then returns, as a list of lines, declarations that can be
+    added to a header file, so that other files that include that header file
+    will have access to these declarations from a compilation perspective. Also
+    changes header references to point to the new header filename, if changed.
+
+    If |fixed_implementation| is None or not provided, overwrites the original;
+    otherwise, puts the result in the file at |fixed_implementation|.
+    """
+
+    file = open(implementation, 'r+' if fixed_implementation is None else 'r')
+    contents = file.read()
+
+    # Find interesting declarations.
+    declaration_pattern = \
+        re.compile('^mig_internal (kern_return_t __MIG_check__.*)$',
+                   re.MULTILINE)
+    declarations = declaration_pattern.findall(contents)
+
+    # Remove “__attribute__((__unused__))” from the declarations, and call them
+    # “mig_external” or “extern” depending on whether “mig_external” is defined.
+    attribute_pattern = re.compile(r'__attribute__\(\(__unused__\)\) ')
+    declarations = ['''\
+#ifdef mig_external
+mig_external
+#else
+extern
+#endif
+''' + attribute_pattern.sub('', x) + ';\n' for x in declarations]
+
+    # Rewrite the declarations in this file as “mig_external”.
+    contents = declaration_pattern.sub(r'mig_external \1', contents);
+
+    # Crashpad never implements the mach_msg_server() MIG callouts. To avoid
+    # needing to provide stub implementations, set KERN_FAILURE as the RetCode
+    # and abort().
+    routine_callout_pattern = re.compile(
+        r'OutP->RetCode = (([a-zA-Z0-9_]+)\(.+\));')
+    routine_callouts = routine_callout_pattern.findall(contents)
+    for routine in routine_callouts:
+        contents = contents.replace(routine[0], 'KERN_FAILURE; abort()')
+
+    # Include the header for abort().
+    contents = '#include <stdlib.h>\n' + contents
+
+    if fixed_header is not None:
+        contents = contents.replace(
+            '#include "%s"' % os.path.basename(header),
+            '#include "%s"' % os.path.basename(fixed_header))
+
+    if fixed_implementation is None:
+        file.seek(0)
+        file.truncate()
+    else:
+        file.close()
+        file = open(fixed_implementation, 'w')
+    file.write(contents)
+    file.close()
+    return declarations
+
+def _fix_header(header, fixed_header, declarations=[]):
+    """Rewrites a MIG-generated header (.h) file.
+
+    Rewrites the file at |header| by placing it inside an “extern "C"” block, so
+    that it declares things properly when included by a C++ compilation unit.
+    |declarations| can be a list of additional declarations to place inside the
+    “extern "C"” block after the original contents of |header|.
+
+    If |fixed_header| is None or not provided, overwrites the original;
+    otherwise, puts the result in the file at |fixed_header|.
+    """
+
+    file = open(header, 'r+' if fixed_header is None else 'r')
+    contents = file.read()
+    declarations_text = ''.join(declarations)
+    contents = '''\
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+%s
+%s
+#ifdef __cplusplus
+}
+#endif
+''' % (contents, declarations_text)
+
+    if fixed_header is None:
+        file.seek(0)
+        file.truncate()
+    else:
+        file.close()
+        file = open(fixed_header, 'w')
+    file.write(contents)
+    file.close()
+
+def fix_interface(interface, fixed_interface=None):
+    if fixed_interface is None:
+        fixed_interface = MigInterface(None, None, None, None)
+
+    _fix_user_implementation(interface.user_c, fixed_interface.user_c,
+                             interface.user_h, fixed_interface.user_h)
+    server_declarations = _fix_server_implementation(interface.server_c,
+                                                     fixed_interface.server_c,
+                                                     interface.server_h,
+                                                     fixed_interface.server_h)
+    _fix_header(interface.user_h, fixed_interface.user_h)
+    _fix_header(interface.server_h, fixed_interface.server_h,
+                server_declarations)
+
+def main(args):
+    parser = argparse.ArgumentParser()
+    parser.add_argument('user_c')
+    parser.add_argument('--fixed_user_c', default=None)
+    parser.add_argument('server_c')
+    parser.add_argument('--fixed_server_c', default=None)
+    parser.add_argument('user_h')
+    parser.add_argument('--fixed_user_h', default=None)
+    parser.add_argument('server_h')
+    parser.add_argument('--fixed_server_h', default=None)
+    parsed = parser.parse_args(args)
+
+    interface = MigInterface(parsed.user_c, parsed.server_c,
+                             parsed.user_h, parsed.server_h)
+    fixed_interface = MigInterface(parsed.fixed_user_c, parsed.fixed_server_c,
+                                   parsed.fixed_user_h, parsed.fixed_server_h)
+    fix_interface(interface, fixed_interface)
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv[1:]))
diff --git a/third_party/crashpad/crashpad/util/mach/mig_gen.py b/third_party/crashpad/crashpad/util/mach/mig_gen.py
new file mode 100755
index 0000000..d71029a
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/mach/mig_gen.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+# Copyright 2019 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import collections
+import os
+import subprocess
+import sys
+
+MigInterface = collections.namedtuple('MigInterface', ['user_c', 'server_c',
+                                                       'user_h', 'server_h'])
+
+def generate_interface(defs, interface, includes=[],
+                       developer_dir=None, sdk=None):
+    command = ['mig',
+               '-user', interface.user_c,
+               '-server', interface.server_c,
+               '-header', interface.user_h,
+               '-sheader', interface.server_h,
+              ]
+    if developer_dir is not None:
+        os.environ['DEVELOPER_DIR'] = developer_dir
+    if sdk is not None:
+        command.extend(['-isysroot', sdk])
+    for include in includes:
+        command.extend(['-I' + include])
+    command.append(defs)
+    subprocess.check_call(command)
+
+def parse_args(args):
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--developer-dir', help='Path to Xcode')
+    parser.add_argument('--sdk', help='Path to SDK')
+    parser.add_argument('--include',
+                        default=[],
+                        action='append',
+                        help='Additional include directory')
+    parser.add_argument('defs')
+    parser.add_argument('user_c')
+    parser.add_argument('server_c')
+    parser.add_argument('user_h')
+    parser.add_argument('server_h')
+    return parser.parse_args(args)
+
+def main(args):
+    parsed = parse_args(args)
+    interface = MigInterface(parsed.user_c, parsed.server_c,
+                             parsed.user_h, parsed.server_h)
+    generate_interface(parsed.defs, interface, parsed.include,
+                       parsed.developer_dir, parsed.sdk)
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv[1:]))
diff --git a/third_party/crashpad/crashpad/util/mach/notify_server.cc b/third_party/crashpad/crashpad/util/mach/notify_server.cc
index 5d0b3c0c..b9da7958 100644
--- a/third_party/crashpad/crashpad/util/mach/notify_server.cc
+++ b/third_party/crashpad/crashpad/util/mach/notify_server.cc
@@ -15,9 +15,9 @@
 #include "util/mach/notify_server.h"
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "util/mach/mach_message.h"
 #include "util/mach/notifyServer.h"
-#include "util/misc/arraysize.h"
 
 namespace {
 
@@ -228,7 +228,7 @@
       MACH_NOTIFY_DEAD_NAME,
   };
   return std::set<mach_msg_id_t>(&request_ids[0],
-                                 &request_ids[ArraySize(request_ids)]);
+                                 &request_ids[base::size(request_ids)]);
 }
 
 mach_msg_size_t NotifyServer::MachMessageServerRequestSize() {
diff --git a/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.cc b/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.cc
index fa6eefb..71fe115 100644
--- a/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.cc
+++ b/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach.cc
@@ -17,10 +17,10 @@
 #include <string.h>
 #include <sys/types.h>
 
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "util/mach/exception_behaviors.h"
 #include "util/mach/mach_extensions.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/implicit_cast.h"
 #include "util/stdlib/string_number_conversion.h"
 
@@ -45,7 +45,7 @@
     "GUARD",
     "CORPSE_NOTIFY",
 };
-static_assert(ArraySize(kExceptionNames) == EXC_TYPES_COUNT,
+static_assert(base::size(kExceptionNames) == EXC_TYPES_COUNT,
               "kExceptionNames length");
 
 constexpr char kExcPrefix[] = "EXC_";
@@ -170,7 +170,7 @@
         {"_STATE32", "32"},
         {"_STATE64", "64"},
     };
-    for (size_t suffix_index = 0; suffix_index < ArraySize(kStateSuffixes);
+    for (size_t suffix_index = 0; suffix_index < base::size(kStateSuffixes);
          ++suffix_index) {
       const char* suffix = kStateSuffixes[suffix_index].orig;
       size_t suffix_len = strlen(suffix);
@@ -194,7 +194,7 @@
 std::string ExceptionToString(exception_type_t exception,
                               SymbolicConstantToStringOptions options) {
   const char* exception_name =
-      implicit_cast<size_t>(exception) < ArraySize(kExceptionNames)
+      implicit_cast<size_t>(exception) < base::size(kExceptionNames)
           ? kExceptionNames[exception]
           : nullptr;
   if (!exception_name) {
@@ -220,7 +220,7 @@
     base::StringPiece short_string =
         can_match_full ? string.substr(strlen(kExcPrefix)) : string;
     for (exception_type_t index = 0;
-         index < implicit_cast<exception_type_t>(ArraySize(kExceptionNames));
+         index < implicit_cast<exception_type_t>(base::size(kExceptionNames));
          ++index) {
       const char* exception_name = kExceptionNames[index];
       if (!exception_name) {
@@ -250,7 +250,7 @@
   exception_mask_t local_exception_mask = exception_mask;
   std::string mask_string;
   bool has_forbidden_or = false;
-  for (size_t exception = 0; exception < ArraySize(kExceptionNames);
+  for (size_t exception = 0; exception < base::size(kExceptionNames);
        ++exception) {
     const char* exception_name = kExceptionNames[exception];
     exception_mask_t exception_mask_value = 1 << exception;
@@ -324,7 +324,7 @@
     base::StringPiece short_string =
         can_match_full ? string.substr(strlen(kExcMaskPrefix)) : string;
     for (exception_type_t index = 0;
-         index < implicit_cast<exception_type_t>(ArraySize(kExceptionNames));
+         index < implicit_cast<exception_type_t>(base::size(kExceptionNames));
          ++index) {
       const char* exception_name = kExceptionNames[index];
       if (!exception_name) {
@@ -363,7 +363,7 @@
   const exception_behavior_t basic_behavior = ExceptionBehaviorBasic(behavior);
 
   const char* behavior_name =
-      implicit_cast<size_t>(basic_behavior) < ArraySize(kBehaviorNames)
+      implicit_cast<size_t>(basic_behavior) < base::size(kBehaviorNames)
           ? kBehaviorNames[basic_behavior]
           : nullptr;
   if (!behavior_name) {
@@ -430,7 +430,8 @@
     base::StringPiece short_string =
         can_match_full ? sp.substr(strlen(kBehaviorPrefix)) : sp;
     for (exception_behavior_t index = 0;
-         index < implicit_cast<exception_behavior_t>(ArraySize(kBehaviorNames));
+         index <
+         implicit_cast<exception_behavior_t>(base::size(kBehaviorNames));
          ++index) {
       const char* behavior_name = kBehaviorNames[index];
       if (!behavior_name) {
@@ -466,13 +467,13 @@
 std::string ThreadStateFlavorToString(thread_state_flavor_t flavor,
                                       SymbolicConstantToStringOptions options) {
   const char* flavor_name =
-      implicit_cast<size_t>(flavor) < ArraySize(kFlavorNames)
+      implicit_cast<size_t>(flavor) < base::size(kFlavorNames)
           ? kFlavorNames[flavor]
           : nullptr;
 
   if (!flavor_name) {
     for (size_t generic_flavor_index = 0;
-         generic_flavor_index < ArraySize(kGenericFlavorNames);
+         generic_flavor_index < base::size(kGenericFlavorNames);
          ++generic_flavor_index) {
       if (flavor == kGenericFlavorNames[generic_flavor_index].flavor) {
         flavor_name = kGenericFlavorNames[generic_flavor_index].name;
@@ -499,7 +500,7 @@
                                thread_state_flavor_t* flavor) {
   if ((options & kAllowFullName) || (options & kAllowShortName)) {
     for (thread_state_flavor_t index = 0;
-         index < implicit_cast<thread_state_flavor_t>(ArraySize(kFlavorNames));
+         index < implicit_cast<thread_state_flavor_t>(base::size(kFlavorNames));
          ++index) {
       const char* flavor_name = kFlavorNames[index];
       if (!flavor_name) {
@@ -519,7 +520,7 @@
     }
 
     for (size_t generic_flavor_index = 0;
-         generic_flavor_index < ArraySize(kGenericFlavorNames);
+         generic_flavor_index < base::size(kGenericFlavorNames);
          ++generic_flavor_index) {
       const char* flavor_name = kGenericFlavorNames[generic_flavor_index].name;
       thread_state_flavor_t flavor_number =
diff --git a/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach_test.cc b/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach_test.cc
index 4856f0ad..e58a9c5 100644
--- a/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach_test.cc
+++ b/third_party/crashpad/crashpad/util/mach/symbolic_constants_mach_test.cc
@@ -18,15 +18,15 @@
 #include <string.h>
 #include <sys/types.h>
 
+#include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
 #include "util/mach/mach_extensions.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/implicit_cast.h"
 
 #define NUL_TEST_DATA(string) \
-  { string, ArraySize(string) - 1 }
+  { string, base::size(string) - 1 }
 
 namespace crashpad {
 namespace test {
@@ -160,7 +160,7 @@
 }
 
 TEST(SymbolicConstantsMach, ExceptionToString) {
-  for (size_t index = 0; index < ArraySize(kExceptionTestData); ++index) {
+  for (size_t index = 0; index < base::size(kExceptionTestData); ++index) {
     SCOPED_TRACE(base::StringPrintf("index %zu", index));
     TestExceptionToString(kExceptionTestData[index].exception,
                           kExceptionTestData[index].full_name,
@@ -188,11 +188,11 @@
 }
 
 TEST(SymbolicConstantsMach, StringToException) {
-  for (size_t option_index = 0; option_index < ArraySize(kNormalOptions);
+  for (size_t option_index = 0; option_index < base::size(kNormalOptions);
        ++option_index) {
     SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index));
     StringToSymbolicConstantOptions options = kNormalOptions[option_index];
-    for (size_t index = 0; index < ArraySize(kExceptionTestData); ++index) {
+    for (size_t index = 0; index < base::size(kExceptionTestData); ++index) {
       SCOPED_TRACE(base::StringPrintf("index %zu", index));
       exception_type_t exception = kExceptionTestData[index].exception;
       {
@@ -230,7 +230,7 @@
         "",
     };
 
-    for (size_t index = 0; index < ArraySize(kNegativeTestData); ++index) {
+    for (size_t index = 0; index < base::size(kNegativeTestData); ++index) {
       SCOPED_TRACE(base::StringPrintf("index %zu", index));
       TestStringToException(kNegativeTestData[index], options, false, 0);
     }
@@ -251,7 +251,7 @@
         NUL_TEST_DATA("1\0002"),
     };
 
-    for (size_t index = 0; index < ArraySize(kNULTestData); ++index) {
+    for (size_t index = 0; index < base::size(kNULTestData); ++index) {
       SCOPED_TRACE(base::StringPrintf("index %zu", index));
       base::StringPiece string(kNULTestData[index].string,
                                kNULTestData[index].length);
@@ -334,7 +334,7 @@
 }
 
 TEST(SymbolicConstantsMach, ExceptionMaskToString) {
-  for (size_t index = 0; index < ArraySize(kExceptionMaskTestData); ++index) {
+  for (size_t index = 0; index < base::size(kExceptionMaskTestData); ++index) {
     SCOPED_TRACE(base::StringPrintf("index %zu", index));
     TestExceptionMaskToString(kExceptionMaskTestData[index].exception_mask,
                               kExceptionMaskTestData[index].full_name,
@@ -389,11 +389,12 @@
       kAllowFullName | kAllowShortName | kAllowNumber | kAllowOr,
   };
 
-  for (size_t option_index = 0; option_index < ArraySize(kOptions);
+  for (size_t option_index = 0; option_index < base::size(kOptions);
        ++option_index) {
     SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index));
     StringToSymbolicConstantOptions options = kOptions[option_index];
-    for (size_t index = 0; index < ArraySize(kExceptionMaskTestData); ++index) {
+    for (size_t index = 0; index < base::size(kExceptionMaskTestData);
+         ++index) {
       SCOPED_TRACE(base::StringPrintf("index %zu", index));
       exception_mask_t exception_mask =
           kExceptionMaskTestData[index].exception_mask;
@@ -444,7 +445,7 @@
         "",
     };
 
-    for (size_t index = 0; index < ArraySize(kNegativeTestData); ++index) {
+    for (size_t index = 0; index < base::size(kNegativeTestData); ++index) {
       SCOPED_TRACE(base::StringPrintf("index %zu", index));
       TestStringToExceptionMask(kNegativeTestData[index], options, false, 0);
     }
@@ -470,7 +471,7 @@
         NUL_TEST_DATA("ARITHMETIC|\0EMULATION"),
     };
 
-    for (size_t index = 0; index < ArraySize(kNULTestData); ++index) {
+    for (size_t index = 0; index < base::size(kNULTestData); ++index) {
       SCOPED_TRACE(base::StringPrintf("index %zu", index));
       base::StringPiece string(kNULTestData[index].string,
                                kNULTestData[index].length);
@@ -505,7 +506,7 @@
        EXC_MASK_SYSCALL | 0x100},
     };
 
-  for (size_t index = 0; index < ArraySize(kNonCanonicalTestData); ++index) {
+  for (size_t index = 0; index < base::size(kNonCanonicalTestData); ++index) {
     SCOPED_TRACE(base::StringPrintf("index %zu", index));
     TestStringToExceptionMask(kNonCanonicalTestData[index].string,
                               kNonCanonicalTestData[index].options,
@@ -576,7 +577,7 @@
 }
 
 TEST(SymbolicConstantsMach, ExceptionBehaviorToString) {
-  for (size_t index = 0; index < ArraySize(kExceptionBehaviorTestData);
+  for (size_t index = 0; index < base::size(kExceptionBehaviorTestData);
        ++index) {
     SCOPED_TRACE(base::StringPrintf("index %zu", index));
     TestExceptionBehaviorToString(kExceptionBehaviorTestData[index].behavior,
@@ -606,11 +607,11 @@
 }
 
 TEST(SymbolicConstantsMach, StringToExceptionBehavior) {
-  for (size_t option_index = 0; option_index < ArraySize(kNormalOptions);
+  for (size_t option_index = 0; option_index < base::size(kNormalOptions);
        ++option_index) {
     SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index));
     StringToSymbolicConstantOptions options = kNormalOptions[option_index];
-    for (size_t index = 0; index < ArraySize(kExceptionBehaviorTestData);
+    for (size_t index = 0; index < base::size(kExceptionBehaviorTestData);
          ++index) {
       SCOPED_TRACE(base::StringPrintf("index %zu", index));
       exception_behavior_t behavior =
@@ -656,7 +657,7 @@
         "",
     };
 
-    for (size_t index = 0; index < ArraySize(kNegativeTestData); ++index) {
+    for (size_t index = 0; index < base::size(kNegativeTestData); ++index) {
       SCOPED_TRACE(base::StringPrintf("index %zu", index));
       TestStringToExceptionBehavior(
           kNegativeTestData[index], options, false, 0);
@@ -682,7 +683,7 @@
         NUL_TEST_DATA("STATE_IDENTITY|\0MACH"),
     };
 
-    for (size_t index = 0; index < ArraySize(kNULTestData); ++index) {
+    for (size_t index = 0; index < base::size(kNULTestData); ++index) {
       SCOPED_TRACE(base::StringPrintf("index %zu", index));
       base::StringPiece string(kNULTestData[index].string,
                                kNULTestData[index].length);
@@ -719,7 +720,7 @@
        implicit_cast<exception_behavior_t>(MACH_EXCEPTION_CODES | 0x2)},
   };
 
-  for (size_t index = 0; index < ArraySize(kNonCanonicalTestData); ++index) {
+  for (size_t index = 0; index < base::size(kNonCanonicalTestData); ++index) {
     SCOPED_TRACE(base::StringPrintf("index %zu", index));
     TestStringToExceptionBehavior(kNonCanonicalTestData[index].string,
                                   kNonCanonicalTestData[index].options,
@@ -836,7 +837,7 @@
 }
 
 TEST(SymbolicConstantsMach, ThreadStateFlavorToString) {
-  for (size_t index = 0; index < ArraySize(kThreadStateFlavorTestData);
+  for (size_t index = 0; index < base::size(kThreadStateFlavorTestData);
        ++index) {
     SCOPED_TRACE(base::StringPrintf("index %zu", index));
     TestThreadStateFlavorToString(kThreadStateFlavorTestData[index].flavor,
@@ -878,11 +879,11 @@
 }
 
 TEST(SymbolicConstantsMach, StringToThreadStateFlavor) {
-  for (size_t option_index = 0; option_index < ArraySize(kNormalOptions);
+  for (size_t option_index = 0; option_index < base::size(kNormalOptions);
        ++option_index) {
     SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index));
     StringToSymbolicConstantOptions options = kNormalOptions[option_index];
-    for (size_t index = 0; index < ArraySize(kThreadStateFlavorTestData);
+    for (size_t index = 0; index < base::size(kThreadStateFlavorTestData);
          ++index) {
       SCOPED_TRACE(base::StringPrintf("index %zu", index));
       thread_state_flavor_t flavor = kThreadStateFlavorTestData[index].flavor;
@@ -952,7 +953,7 @@
 #endif
     };
 
-    for (size_t index = 0; index < ArraySize(kNegativeTestData); ++index) {
+    for (size_t index = 0; index < base::size(kNegativeTestData); ++index) {
       SCOPED_TRACE(base::StringPrintf("index %zu", index));
       TestStringToThreadStateFlavor(
           kNegativeTestData[index], options, false, 0);
@@ -1018,7 +1019,7 @@
 #endif
     };
 
-    for (size_t index = 0; index < ArraySize(kNULTestData); ++index) {
+    for (size_t index = 0; index < base::size(kNULTestData); ++index) {
       SCOPED_TRACE(base::StringPrintf("index %zu", index));
       base::StringPiece string(kNULTestData[index].string,
                                kNULTestData[index].length);
diff --git a/third_party/crashpad/crashpad/util/misc/arraysize.h b/third_party/crashpad/crashpad/util/misc/arraysize.h
index d476edb..93a6388 100644
--- a/third_party/crashpad/crashpad/util/misc/arraysize.h
+++ b/third_party/crashpad/crashpad/util/misc/arraysize.h
@@ -34,6 +34,10 @@
 }  // namespace crashpad
 
 //! \brief A way of computing an array’s size.
+//!
+//! Use this only where `base::size()` or `std::size()` won’t work, such as in
+//! constant expressions (including `static_assert` expressions) that consider
+//! the sizes of non-static data members.
 #define ArraySize(array) crashpad::internal::ArraySizeHelper<decltype(array)>()
 
 #endif  // CRASHPAD_UTIL_MISC_ARRAYSIZE_H_
diff --git a/third_party/crashpad/crashpad/util/misc/capture_context_test_util_win.cc b/third_party/crashpad/crashpad/util/misc/capture_context_test_util_win.cc
index d8abd37..16d81b72 100644
--- a/third_party/crashpad/crashpad/util/misc/capture_context_test_util_win.cc
+++ b/third_party/crashpad/crashpad/util/misc/capture_context_test_util_win.cc
@@ -15,8 +15,8 @@
 #include "util/misc/capture_context_test_util.h"
 #include "util/win/context_wrappers.h"
 
+#include "base/stl_util.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -59,7 +59,7 @@
 
 #if defined(ARCH_CPU_X86)
   // fxsave doesn’t write these bytes.
-  for (size_t i = 464; i < ArraySize(context.ExtendedRegisters); ++i) {
+  for (size_t i = 464; i < base::size(context.ExtendedRegisters); ++i) {
     SCOPED_TRACE(i);
     EXPECT_EQ(context.ExtendedRegisters[i], 0);
   }
@@ -69,7 +69,7 @@
   EXPECT_EQ(context.FltSave.MxCsr, context.MxCsr);
 
   // fxsave doesn’t write these bytes.
-  for (size_t i = 0; i < ArraySize(context.FltSave.Reserved4); ++i) {
+  for (size_t i = 0; i < base::size(context.FltSave.Reserved4); ++i) {
     SCOPED_TRACE(i);
     EXPECT_EQ(context.FltSave.Reserved4[i], 0);
   }
@@ -81,7 +81,7 @@
   EXPECT_EQ(context.P4Home, 0u);
   EXPECT_EQ(context.P5Home, 0u);
   EXPECT_EQ(context.P6Home, 0u);
-  for (size_t i = 0; i < ArraySize(context.VectorRegister); ++i) {
+  for (size_t i = 0; i < base::size(context.VectorRegister); ++i) {
     SCOPED_TRACE(i);
     EXPECT_EQ(context.VectorRegister[i].Low, 0u);
     EXPECT_EQ(context.VectorRegister[i].High, 0u);
diff --git a/third_party/crashpad/crashpad/util/misc/clock_test.cc b/third_party/crashpad/crashpad/util/misc/clock_test.cc
index ca4bf00..6bfb87b 100644
--- a/third_party/crashpad/crashpad/util/misc/clock_test.cc
+++ b/third_party/crashpad/crashpad/util/misc/clock_test.cc
@@ -20,9 +20,9 @@
 
 #include "base/format_macros.h"
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -83,7 +83,7 @@
       static_cast<uint64_t>(5E7),  // 50 milliseconds
   };
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     const uint64_t nanoseconds = kTestData[index];
     SCOPED_TRACE(base::StringPrintf(
         "index %zu, nanoseconds %" PRIu64, index, nanoseconds));
diff --git a/third_party/crashpad/crashpad/util/misc/paths_win.cc b/third_party/crashpad/crashpad/util/misc/paths_win.cc
index aa5c786e..f05bdcf40 100644
--- a/third_party/crashpad/crashpad/util/misc/paths_win.cc
+++ b/third_party/crashpad/crashpad/util/misc/paths_win.cc
@@ -17,19 +17,21 @@
 #include <windows.h>
 
 #include "base/logging.h"
-#include "util/misc/arraysize.h"
+#include "base/stl_util.h"
 
 namespace crashpad {
 
 // static
 bool Paths::Executable(base::FilePath* path) {
   wchar_t executable_path[_MAX_PATH];
-  unsigned int len = GetModuleFileName(
-      nullptr, executable_path, static_cast<DWORD>(ArraySize(executable_path)));
+  unsigned int len =
+      GetModuleFileName(nullptr,
+                        executable_path,
+                        static_cast<DWORD>(base::size(executable_path)));
   if (len == 0) {
     PLOG(ERROR) << "GetModuleFileName";
     return false;
-  } else if (len >= ArraySize(executable_path)) {
+  } else if (len >= base::size(executable_path)) {
     LOG(ERROR) << "GetModuleFileName";
     return false;
   }
diff --git a/third_party/crashpad/crashpad/util/misc/random_string_test.cc b/third_party/crashpad/crashpad/util/misc/random_string_test.cc
index 5d9fad6..f5f0f325 100644
--- a/third_party/crashpad/crashpad/util/misc/random_string_test.cc
+++ b/third_party/crashpad/crashpad/util/misc/random_string_test.cc
@@ -18,8 +18,8 @@
 
 #include <set>
 
+#include "base/stl_util.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -33,7 +33,7 @@
   const std::string allowed_characters("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
 
   size_t character_counts[26] = {};
-  ASSERT_EQ(allowed_characters.size(), ArraySize(character_counts));
+  ASSERT_EQ(allowed_characters.size(), base::size(character_counts));
 
   std::set<std::string> strings;
 
@@ -61,7 +61,7 @@
   // Make sure every character appears at least once. It is possible, but
   // extremely unlikely, for a character to not appear at all.
   for (size_t character_index = 0;
-       character_index < ArraySize(character_counts);
+       character_index < base::size(character_counts);
        ++character_index) {
     EXPECT_GT(character_counts[character_index], 0u)
         << allowed_characters[character_index];
diff --git a/third_party/crashpad/crashpad/util/misc/uuid_test.cc b/third_party/crashpad/crashpad/util/misc/uuid_test.cc
index b851919..35f9ecd 100644
--- a/third_party/crashpad/crashpad/util/misc/uuid_test.cc
+++ b/third_party/crashpad/crashpad/util/misc/uuid_test.cc
@@ -21,9 +21,9 @@
 
 #include "base/format_macros.h"
 #include "base/scoped_generic.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -95,12 +95,12 @@
   ++uuid.data_3;
   EXPECT_NE(uuid, uuid_2);
   --uuid.data_3;
-  for (size_t index = 0; index < ArraySize(uuid.data_4); ++index) {
+  for (size_t index = 0; index < base::size(uuid.data_4); ++index) {
     ++uuid.data_4[index];
     EXPECT_NE(uuid, uuid_2);
     --uuid.data_4[index];
   }
-  for (size_t index = 0; index < ArraySize(uuid.data_5); ++index) {
+  for (size_t index = 0; index < base::size(uuid.data_5); ++index) {
     ++uuid.data_5[index];
     EXPECT_NE(uuid, uuid_2);
     --uuid.data_5[index];
@@ -190,7 +190,7 @@
   uuid_zero.InitializeToZero();
   const std::string empty_uuid = uuid_zero.ToString();
 
-  for (size_t index = 0; index < ArraySize(kCases); ++index) {
+  for (size_t index = 0; index < base::size(kCases); ++index) {
     const TestCase& test_case = kCases[index];
     SCOPED_TRACE(base::StringPrintf(
         "index %" PRIuS ": %s", index, test_case.uuid_string));
@@ -226,7 +226,7 @@
   };
   // clang-format on
   EXPECT_TRUE(uuid.InitializeFromString(
-      base::StringPiece16(kChar16UUID, ArraySize(kChar16UUID))));
+      base::StringPiece16(kChar16UUID, base::size(kChar16UUID))));
   EXPECT_EQ(uuid.ToString(), "f32e5bdc-2681-4c73-a4e6-333ffd33b333");
 
 #if defined(OS_WIN)
diff --git a/third_party/crashpad/crashpad/util/net/http_transport_socket.cc b/third_party/crashpad/crashpad/util/net/http_transport_socket.cc
index b390238..f2dd681 100644
--- a/third_party/crashpad/crashpad/util/net/http_transport_socket.cc
+++ b/third_party/crashpad/crashpad/util/net/http_transport_socket.cc
@@ -24,10 +24,10 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/scoped_generic.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "util/file/file_io.h"
-#include "util/misc/arraysize.h"
 #include "util/net/http_body.h"
 #include "util/net/url.h"
 #include "util/stdlib/string_number_conversion.h"
@@ -366,7 +366,7 @@
 
   FileOperationResult data_bytes;
   do {
-    constexpr size_t kCRLFSize = ArraySize(kCRLFTerminator) - 1;
+    constexpr size_t kCRLFSize = base::size(kCRLFTerminator) - 1;
     struct __attribute__((packed)) {
       char size[8];
       char crlf[2];
diff --git a/third_party/crashpad/crashpad/util/net/http_transport_win.cc b/third_party/crashpad/crashpad/util/net/http_transport_win.cc
index 06d0b7b..09876882 100644
--- a/third_party/crashpad/crashpad/util/net/http_transport_win.cc
+++ b/third_party/crashpad/crashpad/util/net/http_transport_win.cc
@@ -25,13 +25,13 @@
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/scoped_generic.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "package.h"
 #include "util/file/file_io.h"
-#include "util/misc/arraysize.h"
 #include "util/net/http_body.h"
 #include "util/numeric/safe_assignment.h"
 #include "util/win/module_version.h"
@@ -96,7 +96,7 @@
                              error_code,
                              0,
                              msgbuf,
-                             static_cast<DWORD>(ArraySize(msgbuf)),
+                             static_cast<DWORD>(base::size(msgbuf)),
                              NULL);
   if (!len) {
     return base::StringPrintf("%s: error 0x%lx while retrieving error 0x%lx",
diff --git a/third_party/crashpad/crashpad/util/numeric/checked_address_range_test.cc b/third_party/crashpad/crashpad/util/numeric/checked_address_range_test.cc
index f5dc8adc..08bc551 100644
--- a/third_party/crashpad/crashpad/util/numeric/checked_address_range_test.cc
+++ b/third_party/crashpad/crashpad/util/numeric/checked_address_range_test.cc
@@ -19,10 +19,10 @@
 #include <limits>
 
 #include "base/format_macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -119,7 +119,7 @@
       {0xffffffffffffffff, 1, kInvalid},
   };
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     const auto& testcase = kTestData[index];
     SCOPED_TRACE(base::StringPrintf("index %" PRIuS
                                     ", base 0x%" PRIx64 ", size 0x%" PRIx64,
@@ -170,7 +170,7 @@
   CheckedAddressRange parent_range_32(false, 0x2000, 0x1000);
   ASSERT_TRUE(parent_range_32.IsValid());
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     const auto& testcase = kTestData[index];
     SCOPED_TRACE(base::StringPrintf(
         "index %" PRIuS ", value 0x%" PRIx64, index, testcase.value));
@@ -227,7 +227,7 @@
   CheckedAddressRange parent_range_32(false, 0x2000, 0x1000);
   ASSERT_TRUE(parent_range_32.IsValid());
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     const auto& testcase = kTestData[index];
     SCOPED_TRACE(base::StringPrintf("index %" PRIuS
                                     ", base 0x%" PRIx64 ", size 0x%" PRIx64,
diff --git a/third_party/crashpad/crashpad/util/numeric/checked_range_test.cc b/third_party/crashpad/crashpad/util/numeric/checked_range_test.cc
index 2977b42e..9d611e8 100644
--- a/third_party/crashpad/crashpad/util/numeric/checked_range_test.cc
+++ b/third_party/crashpad/crashpad/util/numeric/checked_range_test.cc
@@ -20,9 +20,9 @@
 #include <limits>
 
 #include "base/format_macros.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -78,7 +78,7 @@
       {0xffffffff, 0xffffffff, false},
   };
 
-  for (size_t index = 0; index < ArraySize(kUnsignedTestData); ++index) {
+  for (size_t index = 0; index < base::size(kUnsignedTestData); ++index) {
     const auto& testcase = kUnsignedTestData[index];
     SCOPED_TRACE(base::StringPrintf("unsigned index %" PRIuS
                                     ", base 0x%x, size 0x%x",
@@ -140,7 +140,7 @@
       {-1, 0xffffffff, false},
   };
 
-  for (size_t index = 0; index < ArraySize(kSignedTestData); ++index) {
+  for (size_t index = 0; index < base::size(kSignedTestData); ++index) {
     const auto& testcase = kSignedTestData[index];
     SCOPED_TRACE(base::StringPrintf("signed index %" PRIuS
                                     ", base 0x%x, size 0x%x",
@@ -186,7 +186,7 @@
   CheckedRange<uint32_t> parent_range(0x2000, 0x1000);
   ASSERT_TRUE(parent_range.IsValid());
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     const auto& testcase = kTestData[index];
     SCOPED_TRACE(base::StringPrintf(
         "index %" PRIuS ", value 0x%x", index, testcase.value));
@@ -234,7 +234,7 @@
   CheckedRange<uint32_t> parent_range(0x2000, 0x1000);
   ASSERT_TRUE(parent_range.IsValid());
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     const auto& testcase = kTestData[index];
     SCOPED_TRACE(base::StringPrintf("index %" PRIuS ", base 0x%x, size 0x%x",
                                     index,
@@ -287,7 +287,7 @@
   CheckedRange<uint32_t> first_range(0x2000, 0x1000);
   ASSERT_TRUE(first_range.IsValid());
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     const auto& testcase = kTestData[index];
     SCOPED_TRACE(base::StringPrintf("index %" PRIuS ", base 0x%x, size 0x%x",
                                     index,
diff --git a/third_party/crashpad/crashpad/util/posix/close_multiple.cc b/third_party/crashpad/crashpad/util/posix/close_multiple.cc
index 22f89f52..238f158c 100644
--- a/third_party/crashpad/crashpad/util/posix/close_multiple.cc
+++ b/third_party/crashpad/crashpad/util/posix/close_multiple.cc
@@ -25,10 +25,10 @@
 #include "base/files/scoped_file.h"
 #include "base/logging.h"
 #include "base/posix/eintr_wrapper.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "build/build_config.h"
 #include "util/file/directory_reader.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/implicit_cast.h"
 
 #if defined(OS_MACOSX)
@@ -153,7 +153,7 @@
   int maxfilesperproc;
   size_t maxfilesperproc_size = sizeof(maxfilesperproc);
   if (sysctl(oid,
-             ArraySize(oid),
+             base::size(oid),
              &maxfilesperproc,
              &maxfilesperproc_size,
              nullptr,
diff --git a/third_party/crashpad/crashpad/util/posix/process_info_mac.cc b/third_party/crashpad/crashpad/util/posix/process_info_mac.cc
index 8b9a6ec0..9e86e08 100644
--- a/third_party/crashpad/crashpad/util/posix/process_info_mac.cc
+++ b/third_party/crashpad/crashpad/util/posix/process_info_mac.cc
@@ -18,7 +18,7 @@
 
 #include "base/logging.h"
 #include "base/mac/mach_logging.h"
-#include "util/misc/arraysize.h"
+#include "base/stl_util.h"
 
 namespace crashpad {
 
@@ -33,7 +33,7 @@
 
   int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
   size_t len = sizeof(kern_proc_info_);
-  if (sysctl(mib, ArraySize(mib), &kern_proc_info_, &len, nullptr, 0) != 0) {
+  if (sysctl(mib, base::size(mib), &kern_proc_info_, &len, nullptr, 0) != 0) {
     PLOG(ERROR) << "sysctl for pid " << pid;
     return false;
   }
@@ -112,7 +112,7 @@
   const short ngroups = kern_proc_info_.kp_eproc.e_ucred.cr_ngroups;
   DCHECK_GE(ngroups, 0);
   DCHECK_LE(static_cast<size_t>(ngroups),
-            ArraySize(kern_proc_info_.kp_eproc.e_ucred.cr_groups));
+            base::size(kern_proc_info_.kp_eproc.e_ucred.cr_groups));
 
   const gid_t* groups = kern_proc_info_.kp_eproc.e_ucred.cr_groups;
   return std::set<gid_t>(&groups[0], &groups[ngroups]);
@@ -169,7 +169,7 @@
   do {
     int mib[] = {CTL_KERN, KERN_PROCARGS2, pid};
     int rv =
-        sysctl(mib, ArraySize(mib), nullptr, &args_size_estimate, nullptr, 0);
+        sysctl(mib, base::size(mib), nullptr, &args_size_estimate, nullptr, 0);
     if (rv != 0) {
       PLOG(ERROR) << "sysctl (size) for pid " << pid;
       return false;
@@ -177,7 +177,7 @@
 
     args_size = args_size_estimate + 1;
     args.resize(args_size);
-    rv = sysctl(mib, ArraySize(mib), &args[0], &args_size, nullptr, 0);
+    rv = sysctl(mib, base::size(mib), &args[0], &args_size, nullptr, 0);
     if (rv != 0) {
       PLOG(ERROR) << "sysctl (data) for pid " << pid;
       return false;
diff --git a/third_party/crashpad/crashpad/util/posix/scoped_mmap_test.cc b/third_party/crashpad/crashpad/util/posix/scoped_mmap_test.cc
index 0a6a1fa1..5c69e7e4 100644
--- a/third_party/crashpad/crashpad/util/posix/scoped_mmap_test.cc
+++ b/third_party/crashpad/crashpad/util/posix/scoped_mmap_test.cc
@@ -20,10 +20,10 @@
 
 #include "base/numerics/safe_conversions.h"
 #include "base/rand_util.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
 #include "test/gtest_death.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -151,7 +151,7 @@
   EXPECT_EQ(mapping.len(), 3 * kPageSize);
 
   TestCookie cookies[3];
-  for (size_t index = 0; index < ArraySize(cookies); ++index) {
+  for (size_t index = 0; index < base::size(cookies); ++index) {
     cookies[index].SetUp(reinterpret_cast<uint64_t*>(
         mapping.addr_as<uintptr_t>() + index * kPageSize));
   }
@@ -186,7 +186,7 @@
   EXPECT_EQ(mapping.len(), kPageSize);
 
   TestCookie cookies[3];
-  for (size_t index = 0; index < ArraySize(cookies); ++index) {
+  for (size_t index = 0; index < base::size(cookies); ++index) {
     cookies[index].SetUp(reinterpret_cast<uint64_t*>(
         reinterpret_cast<uintptr_t>(pages) + index * kPageSize));
   }
@@ -197,7 +197,7 @@
   EXPECT_EQ(mapping.addr(), pages);
   EXPECT_EQ(mapping.len(), 3 * kPageSize);
 
-  for (size_t index = 0; index < ArraySize(cookies); ++index) {
+  for (size_t index = 0; index < base::size(cookies); ++index) {
     SCOPED_TRACE(base::StringPrintf("index %zu", index));
     EXPECT_EQ(cookies[index].Observed(), cookies[index].Expected());
   }
@@ -218,7 +218,7 @@
   EXPECT_EQ(mapping.len(), kPageSize);
 
   TestCookie cookies[3];
-  for (size_t index = 0; index < ArraySize(cookies); ++index) {
+  for (size_t index = 0; index < base::size(cookies); ++index) {
     cookies[index].SetUp(reinterpret_cast<uint64_t*>(
         reinterpret_cast<uintptr_t>(pages) + index * kPageSize));
   }
@@ -249,7 +249,7 @@
   EXPECT_EQ(mapping.len(), 2 * kPageSize);
 
   TestCookie cookies[3];
-  for (size_t index = 0; index < ArraySize(cookies); ++index) {
+  for (size_t index = 0; index < base::size(cookies); ++index) {
     cookies[index].SetUp(reinterpret_cast<uint64_t*>(
         reinterpret_cast<uintptr_t>(pages) + index * kPageSize));
   }
diff --git a/third_party/crashpad/crashpad/util/posix/signals.cc b/third_party/crashpad/crashpad/util/posix/signals.cc
index 69cdfca3..1384de1 100644
--- a/third_party/crashpad/crashpad/util/posix/signals.cc
+++ b/third_party/crashpad/crashpad/util/posix/signals.cc
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "base/logging.h"
-#include "util/misc/arraysize.h"
+#include "base/stl_util.h"
 
 namespace crashpad {
 
@@ -119,7 +119,7 @@
 struct sigaction* Signals::OldActions::ActionForSignal(int sig) {
   DCHECK_GT(sig, 0);
   const size_t slot = sig - 1;
-  DCHECK_LT(slot, ArraySize(actions_));
+  DCHECK_LT(slot, base::size(actions_));
   return &actions_[slot];
 }
 
@@ -153,7 +153,8 @@
                                    int flags,
                                    OldActions* old_actions) {
   return InstallHandlers(
-      std::vector<int>(kCrashSignals, kCrashSignals + ArraySize(kCrashSignals)),
+      std::vector<int>(kCrashSignals,
+                       kCrashSignals + base::size(kCrashSignals)),
       handler,
       flags,
       old_actions);
@@ -165,7 +166,7 @@
                                        OldActions* old_actions) {
   return InstallHandlers(
       std::vector<int>(kTerminateSignals,
-                       kTerminateSignals + ArraySize(kTerminateSignals)),
+                       kTerminateSignals + base::size(kTerminateSignals)),
       handler,
       flags,
       old_actions);
@@ -280,12 +281,12 @@
 
 // static
 bool Signals::IsCrashSignal(int sig) {
-  return IsSignalInSet(sig, kCrashSignals, ArraySize(kCrashSignals));
+  return IsSignalInSet(sig, kCrashSignals, base::size(kCrashSignals));
 }
 
 // static
 bool Signals::IsTerminateSignal(int sig) {
-  return IsSignalInSet(sig, kTerminateSignals, ArraySize(kTerminateSignals));
+  return IsSignalInSet(sig, kTerminateSignals, base::size(kTerminateSignals));
 }
 
 }  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/posix/signals_test.cc b/third_party/crashpad/crashpad/util/posix/signals_test.cc
index b0c8407..d91e3cc 100644
--- a/third_party/crashpad/crashpad/util/posix/signals_test.cc
+++ b/third_party/crashpad/crashpad/util/posix/signals_test.cc
@@ -26,13 +26,13 @@
 #include "base/compiler_specific.h"
 #include "base/files/scoped_file.h"
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "gtest/gtest.h"
 #include "test/errors.h"
 #include "test/multiprocess.h"
 #include "test/scoped_temp_dir.h"
-#include "util/misc/arraysize.h"
 #include "util/posix/scoped_mmap.h"
 
 namespace crashpad {
@@ -341,7 +341,7 @@
       {SIGHUP, SEGV_MAPERR, false},
       {SIGINT, SI_USER, false},
   };
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     const auto test_data = kTestData[index];
     SCOPED_TRACE(base::StringPrintf(
         "index %zu, sig %d, code %d", index, test_data.sig, test_data.code));
diff --git a/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc b/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc
index f1fa87f0..5937c576 100644
--- a/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc
+++ b/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix.cc
@@ -18,9 +18,9 @@
 #include <string.h>
 #include <sys/types.h>
 
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/implicit_cast.h"
 #include "util/stdlib/string_number_conversion.h"
 
@@ -137,9 +137,9 @@
 };
 #if defined(OS_LINUX) || defined(OS_ANDROID)
 // NSIG is 64 to account for real-time signals.
-static_assert(ArraySize(kSignalNames) == 32, "kSignalNames length");
+static_assert(base::size(kSignalNames) == 32, "kSignalNames length");
 #else
-static_assert(ArraySize(kSignalNames) == NSIG, "kSignalNames length");
+static_assert(base::size(kSignalNames) == NSIG, "kSignalNames length");
 #endif
 
 constexpr char kSigPrefix[] = "SIG";
@@ -151,7 +151,7 @@
 std::string SignalToString(int signal,
                            SymbolicConstantToStringOptions options) {
   const char* signal_name =
-      implicit_cast<size_t>(signal) < ArraySize(kSignalNames)
+      implicit_cast<size_t>(signal) < base::size(kSignalNames)
           ? kSignalNames[signal]
           : nullptr;
   if (!signal_name) {
@@ -176,7 +176,7 @@
         string.substr(0, strlen(kSigPrefix)).compare(kSigPrefix) == 0;
     base::StringPiece short_string =
         can_match_full ? string.substr(strlen(kSigPrefix)) : string;
-    for (int index = 0; index < implicit_cast<int>(ArraySize(kSignalNames));
+    for (int index = 0; index < implicit_cast<int>(base::size(kSignalNames));
          ++index) {
       const char* signal_name = kSignalNames[index];
       if (!signal_name) {
diff --git a/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix_test.cc b/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix_test.cc
index 1a6e0b9..c7d62ee 100644
--- a/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix_test.cc
+++ b/third_party/crashpad/crashpad/util/posix/symbolic_constants_posix_test.cc
@@ -17,14 +17,14 @@
 #include <signal.h>
 #include <sys/types.h>
 
+#include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 
 #define NUL_TEST_DATA(string) \
-  { string, ArraySize(string) - 1 }
+  { string, base::size(string) - 1 }
 
 namespace crashpad {
 namespace test {
@@ -116,7 +116,7 @@
 }
 
 TEST(SymbolicConstantsPOSIX, SignalToString) {
-  for (size_t index = 0; index < ArraySize(kSignalTestData); ++index) {
+  for (size_t index = 0; index < base::size(kSignalTestData); ++index) {
     SCOPED_TRACE(base::StringPrintf("index %zu", index));
     TestSignalToString(kSignalTestData[index].signal,
                        kSignalTestData[index].full_name,
@@ -171,11 +171,11 @@
       kAllowFullName | kAllowShortName | kAllowNumber,
   };
 
-  for (size_t option_index = 0; option_index < ArraySize(kOptions);
+  for (size_t option_index = 0; option_index < base::size(kOptions);
        ++option_index) {
     SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index));
     StringToSymbolicConstantOptions options = kOptions[option_index];
-    for (size_t index = 0; index < ArraySize(kSignalTestData); ++index) {
+    for (size_t index = 0; index < base::size(kSignalTestData); ++index) {
       SCOPED_TRACE(base::StringPrintf("index %zu", index));
       int signal = kSignalTestData[index].signal;
       {
@@ -213,7 +213,7 @@
         "",
     };
 
-    for (size_t index = 0; index < ArraySize(kNegativeTestData); ++index) {
+    for (size_t index = 0; index < base::size(kNegativeTestData); ++index) {
       SCOPED_TRACE(base::StringPrintf("index %zu", index));
       TestStringToSignal(kNegativeTestData[index], options, false, 0);
     }
@@ -234,7 +234,7 @@
         NUL_TEST_DATA("1\0002"),
     };
 
-    for (size_t index = 0; index < ArraySize(kNULTestData); ++index) {
+    for (size_t index = 0; index < base::size(kNULTestData); ++index) {
       SCOPED_TRACE(base::StringPrintf("index %zu", index));
       base::StringPiece string(kNULTestData[index].string,
                                kNULTestData[index].length);
diff --git a/third_party/crashpad/crashpad/util/process/process_memory_range_test.cc b/third_party/crashpad/crashpad/util/process/process_memory_range_test.cc
index 9431d442..2df2b5cb 100644
--- a/third_party/crashpad/crashpad/util/process/process_memory_range_test.cc
+++ b/third_party/crashpad/crashpad/util/process/process_memory_range_test.cc
@@ -17,10 +17,10 @@
 #include <limits>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "build/build_config.h"
 #include "gtest/gtest.h"
 #include "test/process_type.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/from_pointer_cast.h"
 #include "util/process/process_memory_native.h"
 
@@ -59,28 +59,28 @@
   auto string1_addr = FromPointerCast<VMAddress>(kTestObject.string1);
   auto string2_addr = FromPointerCast<VMAddress>(kTestObject.string2);
   ASSERT_TRUE(range.ReadCStringSizeLimited(
-      string1_addr, ArraySize(kTestObject.string1), &string));
+      string1_addr, base::size(kTestObject.string1), &string));
   EXPECT_STREQ(string.c_str(), kTestObject.string1);
 
   ASSERT_TRUE(range.ReadCStringSizeLimited(
-      string2_addr, ArraySize(kTestObject.string2), &string));
+      string2_addr, base::size(kTestObject.string2), &string));
   EXPECT_STREQ(string.c_str(), kTestObject.string2);
 
   // Limit the range to remove access to string2.
   ProcessMemoryRange range2;
   ASSERT_TRUE(range2.Initialize(range));
   ASSERT_TRUE(
-      range2.RestrictRange(string1_addr, ArraySize(kTestObject.string1)));
+      range2.RestrictRange(string1_addr, base::size(kTestObject.string1)));
   EXPECT_TRUE(range2.ReadCStringSizeLimited(
-      string1_addr, ArraySize(kTestObject.string1), &string));
+      string1_addr, base::size(kTestObject.string1), &string));
   EXPECT_FALSE(range2.ReadCStringSizeLimited(
-      string2_addr, ArraySize(kTestObject.string2), &string));
+      string2_addr, base::size(kTestObject.string2), &string));
   EXPECT_FALSE(range2.Read(object_addr, sizeof(object), &object));
 
   // String reads fail if the NUL terminator is outside the range.
   ASSERT_TRUE(range2.RestrictRange(string1_addr, strlen(kTestObject.string1)));
   EXPECT_FALSE(range2.ReadCStringSizeLimited(
-      string1_addr, ArraySize(kTestObject.string1), &string));
+      string1_addr, base::size(kTestObject.string1), &string));
 
   // New range outside the old range.
   EXPECT_FALSE(range2.RestrictRange(string1_addr - 1, 1));
diff --git a/third_party/crashpad/crashpad/util/stdlib/string_number_conversion_test.cc b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion_test.cc
index ee19429..689b4ad 100644
--- a/third_party/crashpad/crashpad/util/stdlib/string_number_conversion_test.cc
+++ b/third_party/crashpad/crashpad/util/stdlib/string_number_conversion_test.cc
@@ -18,8 +18,8 @@
 
 #include <limits>
 
+#include "base/stl_util.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -94,7 +94,7 @@
       {"18446744073709551616", false, 0},
   };
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     int value;
     bool valid = StringToNumber(kTestData[index].string, &value);
     if (kTestData[index].valid) {
@@ -114,7 +114,7 @@
   // is split to avoid MSVC warning:
   //   "decimal digit terminates octal escape sequence".
   static constexpr char input[] = "6\000" "6";
-  std::string input_string(input, ArraySize(input) - 1);
+  std::string input_string(input, base::size(input) - 1);
   int output;
   EXPECT_FALSE(StringToNumber(input_string, &output));
 }
@@ -188,7 +188,7 @@
       {"18446744073709551616", false, 0},
   };
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     unsigned int value;
     bool valid = StringToNumber(kTestData[index].string, &value);
     if (kTestData[index].valid) {
@@ -208,7 +208,7 @@
   // is split to avoid MSVC warning:
   //   "decimal digit terminates octal escape sequence".
   static constexpr char input[] = "6\000" "6";
-  std::string input_string(input, ArraySize(input) - 1);
+  std::string input_string(input, base::size(input) - 1);
   unsigned int output;
   EXPECT_FALSE(StringToNumber(input_string, &output));
 }
@@ -245,7 +245,7 @@
       {"0x7Fffffffffffffff", true, std::numeric_limits<int64_t>::max()},
   };
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     int64_t value;
     bool valid = StringToNumber(kTestData[index].string, &value);
     if (kTestData[index].valid) {
@@ -295,7 +295,7 @@
       {"0xFfffffffffffffff", true, std::numeric_limits<uint64_t>::max()},
   };
 
-  for (size_t index = 0; index < ArraySize(kTestData); ++index) {
+  for (size_t index = 0; index < base::size(kTestData); ++index) {
     uint64_t value;
     bool valid = StringToNumber(kTestData[index].string, &value);
     if (kTestData[index].valid) {
diff --git a/third_party/crashpad/crashpad/util/stdlib/strlcpy_test.cc b/third_party/crashpad/crashpad/util/stdlib/strlcpy_test.cc
index 172a09ab..bfb00fe 100644
--- a/third_party/crashpad/crashpad/util/stdlib/strlcpy_test.cc
+++ b/third_party/crashpad/crashpad/util/stdlib/strlcpy_test.cc
@@ -20,10 +20,10 @@
 #include <algorithm>
 
 #include "base/format_macros.h"
+#include "base/stl_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 
 namespace crashpad {
 namespace test {
@@ -64,7 +64,7 @@
   static constexpr base::char16 test_characters[] =
       {0x4d, 0xe9, 0x100, 0x151, 0x1e18};
 
-  for (size_t index = 0; index < ArraySize(test_characters); ++index) {
+  for (size_t index = 0; index < base::size(test_characters); ++index) {
     base::char16 test_character = test_characters[index];
     SCOPED_TRACE(base::StringPrintf(
         "character index %" PRIuS ", character 0x%x", index, test_character));
@@ -78,13 +78,13 @@
 
       EXPECT_EQ(c16lcpy(destination.data,
                         test_string.c_str(),
-                        ArraySize(destination.data)),
+                        base::size(destination.data)),
                 length);
 
       // Make sure that the destination buffer is NUL-terminated, and that as
       // much of the test string was copied as could fit.
       size_t expected_destination_length =
-          std::min(length, ArraySize(destination.data) - 1);
+          std::min(length, base::size(destination.data) - 1);
 
       EXPECT_EQ(destination.data[expected_destination_length], '\0');
       EXPECT_EQ(C16Len(destination.data), expected_destination_length);
@@ -97,15 +97,15 @@
       // of the buffer passed to c16lcpy.
       EXPECT_TRUE(C16Memcmp(expected_untouched.lead_guard,
                             destination.lead_guard,
-                            ArraySize(destination.lead_guard)) == 0);
+                            base::size(destination.lead_guard)) == 0);
       size_t expected_untouched_length =
-          ArraySize(destination.data) - expected_destination_length - 1;
+          base::size(destination.data) - expected_destination_length - 1;
       EXPECT_TRUE(C16Memcmp(expected_untouched.data,
                             &destination.data[expected_destination_length + 1],
                             expected_untouched_length) == 0);
       EXPECT_TRUE(C16Memcmp(expected_untouched.trail_guard,
                             destination.trail_guard,
-                            ArraySize(destination.trail_guard)) == 0);
+                            base::size(destination.trail_guard)) == 0);
     }
   }
 }
diff --git a/third_party/crashpad/crashpad/util/stdlib/thread_safe_vector_test.cc b/third_party/crashpad/crashpad/util/stdlib/thread_safe_vector_test.cc
index 183c6366..1d26b09d0 100644
--- a/third_party/crashpad/crashpad/util/stdlib/thread_safe_vector_test.cc
+++ b/third_party/crashpad/crashpad/util/stdlib/thread_safe_vector_test.cc
@@ -14,8 +14,8 @@
 
 #include "util/stdlib/thread_safe_vector.h"
 
+#include "base/stl_util.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 #include "util/thread/thread.h"
 
 namespace crashpad {
@@ -54,12 +54,12 @@
   EXPECT_TRUE(vector.empty());
 
   ThreadSafeVectorTestThread threads[100];
-  for (size_t index = 0; index < ArraySize(threads); ++index) {
+  for (size_t index = 0; index < base::size(threads); ++index) {
     threads[index].SetTestParameters(
         &thread_safe_vector, static_cast<int>(index * kElementsPerThread));
   }
 
-  for (size_t index = 0; index < ArraySize(threads); ++index) {
+  for (size_t index = 0; index < base::size(threads); ++index) {
     threads[index].Start();
 
     if (index % 10 == 0) {
@@ -76,8 +76,8 @@
 
   std::vector<int> drained = thread_safe_vector.Drain();
   vector.insert(vector.end(), drained.begin(), drained.end());
-  bool found[ArraySize(threads) * kElementsPerThread] = {};
-  EXPECT_EQ(vector.size(), ArraySize(found));
+  bool found[base::size(threads) * kElementsPerThread] = {};
+  EXPECT_EQ(vector.size(), base::size(found));
   for (int element : vector) {
     EXPECT_FALSE(found[element]) << element;
     found[element] = true;
diff --git a/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc b/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc
index fedead1..4f1c1cd 100644
--- a/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc
+++ b/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc
@@ -16,8 +16,8 @@
 
 #include <sys/types.h>
 
+#include "base/stl_util.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 
 #if defined(OS_POSIX)
 #include <pthread.h>
@@ -126,7 +126,7 @@
   Semaphore semaphore(5);
   ThreadMainInfo info[10];
   size_t iterations = 0;
-  for (size_t index = 0; index < ArraySize(info); ++index) {
+  for (size_t index = 0; index < base::size(info); ++index) {
     info[index].semaphore = &semaphore;
     info[index].iterations = index;
     iterations += info[index].iterations;
@@ -138,7 +138,7 @@
     semaphore.Signal();
   }
 
-  for (size_t index = 0; index < ArraySize(info); ++index) {
+  for (size_t index = 0; index < base::size(info); ++index) {
     JoinThread(&info[index]);
   }
 }
diff --git a/third_party/crashpad/crashpad/util/thread/thread_log_messages_test.cc b/third_party/crashpad/crashpad/util/thread/thread_log_messages_test.cc
index f186ed2..94920e7 100644
--- a/third_party/crashpad/crashpad/util/thread/thread_log_messages_test.cc
+++ b/third_party/crashpad/crashpad/util/thread/thread_log_messages_test.cc
@@ -18,9 +18,9 @@
 #include <sys/types.h>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
-#include "util/misc/arraysize.h"
 #include "util/thread/thread.h"
 
 namespace crashpad {
@@ -94,8 +94,8 @@
     const std::vector<std::string>& log_messages =
         thread_log_messages.log_messages();
 
-    EXPECT_EQ(log_messages.size(), ArraySize(kMessages));
-    for (size_t index = 0; index < ArraySize(kMessages); ++index) {
+    EXPECT_EQ(log_messages.size(), base::size(kMessages));
+    for (size_t index = 0; index < base::size(kMessages); ++index) {
       EXPECT_EQ(MessageString(log_messages[index]), kMessages[index])
           << "index " << index;
     }
@@ -174,7 +174,7 @@
 
   LoggingTestThread threads[20];
   int start = 0;
-  for (size_t index = 0; index < ArraySize(threads); ++index) {
+  for (size_t index = 0; index < base::size(threads); ++index) {
     threads[index].Initialize(
         index, static_cast<int>(start), static_cast<int>(index));
     start += static_cast<int>(index);
diff --git a/third_party/crashpad/crashpad/util/win/command_line_test.cc b/third_party/crashpad/crashpad/util/win/command_line_test.cc
index e5ceef8..032d5555 100644
--- a/third_party/crashpad/crashpad/util/win/command_line_test.cc
+++ b/third_party/crashpad/crashpad/util/win/command_line_test.cc
@@ -20,9 +20,9 @@
 
 #include "base/logging.h"
 #include "base/scoped_generic.h"
+#include "base/stl_util.h"
 #include "gtest/gtest.h"
 #include "test/errors.h"
-#include "util/misc/arraysize.h"
 #include "util/win/scoped_local_alloc.h"
 
 namespace crashpad {
@@ -65,7 +65,7 @@
         L"argument 1",
         L"argument 2",
     };
-    AppendCommandLineArgumentTest(ArraySize(kArguments), kArguments);
+    AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
   }
 
   {
@@ -77,7 +77,7 @@
         L"argument 2",
         L"\\some\\path with\\spaces",
     };
-    AppendCommandLineArgumentTest(ArraySize(kArguments), kArguments);
+    AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
   }
 
   {
@@ -89,7 +89,7 @@
         L"she said, \"you had me at hello\"",
         L"\\some\\path with\\spaces",
     };
-    AppendCommandLineArgumentTest(ArraySize(kArguments), kArguments);
+    AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
   }
 
   {
@@ -102,7 +102,7 @@
         L"argument3",
         L"argument4",
     };
-    AppendCommandLineArgumentTest(ArraySize(kArguments), kArguments);
+    AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
   }
 
   {
@@ -113,7 +113,7 @@
         L"\\some\\directory with\\spaces\\",
         L"argument2",
     };
-    AppendCommandLineArgumentTest(ArraySize(kArguments), kArguments);
+    AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
   }
 
   {
@@ -124,7 +124,7 @@
         L"",
         L"argument2",
     };
-    AppendCommandLineArgumentTest(ArraySize(kArguments), kArguments);
+    AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
   }
 
   {
@@ -159,7 +159,7 @@
         L"\"\"",
         L" \t\n\v\"",
     };
-    AppendCommandLineArgumentTest(ArraySize(kArguments), kArguments);
+    AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
   }
 }
 
diff --git a/third_party/crashpad/crashpad/util/win/exception_handler_server.cc b/third_party/crashpad/crashpad/util/win/exception_handler_server.cc
index 8ca3d25..2593ff2d 100644
--- a/third_party/crashpad/crashpad/util/win/exception_handler_server.cc
+++ b/third_party/crashpad/crashpad/util/win/exception_handler_server.cc
@@ -23,13 +23,13 @@
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/rand_util.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "minidump/minidump_file_writer.h"
 #include "snapshot/crashpad_info_client_options.h"
 #include "snapshot/win/process_snapshot_win.h"
 #include "util/file/file_writer.h"
-#include "util/misc/arraysize.h"
 #include "util/misc/tri_state.h"
 #include "util/misc/uuid.h"
 #include "util/win/get_function.h"
@@ -308,7 +308,7 @@
 void ExceptionHandlerServer::Run(Delegate* delegate) {
   uint64_t shutdown_token = base::RandUint64();
   ScopedKernelHANDLE thread_handles[kPipeInstances];
-  for (size_t i = 0; i < ArraySize(thread_handles); ++i) {
+  for (size_t i = 0; i < base::size(thread_handles); ++i) {
     HANDLE pipe;
     if (first_pipe_instance_.is_valid()) {
       pipe = first_pipe_instance_.release();
@@ -360,7 +360,7 @@
   }
 
   // Signal to the named pipe instances that they should terminate.
-  for (size_t i = 0; i < ArraySize(thread_handles); ++i) {
+  for (size_t i = 0; i < base::size(thread_handles); ++i) {
     ClientToServerMessage message;
     memset(&message, 0, sizeof(message));
     message.type = ClientToServerMessage::kShutdown;
diff --git a/third_party/crashpad/crashpad/util/win/ntstatus_logging.cc b/third_party/crashpad/crashpad/util/win/ntstatus_logging.cc
index 118bfef..e9a9b61 100644
--- a/third_party/crashpad/crashpad/util/win/ntstatus_logging.cc
+++ b/third_party/crashpad/crashpad/util/win/ntstatus_logging.cc
@@ -16,8 +16,8 @@
 
 #include <string>
 
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
-#include "util/misc/arraysize.h"
 
 namespace {
 
@@ -30,7 +30,7 @@
       ntstatus,
       0,
       msgbuf,
-      static_cast<DWORD>(ArraySize(msgbuf)),
+      static_cast<DWORD>(base::size(msgbuf)),
       nullptr);
   if (len) {
     // Most system messages end in a period and a space. Remove the space if
diff --git a/third_party/crashpad/crashpad/util/win/process_info.cc b/third_party/crashpad/crashpad/util/win/process_info.cc
index cd6bcd3e..ff8f34d 100644
--- a/third_party/crashpad/crashpad/util/win/process_info.cc
+++ b/third_party/crashpad/crashpad/util/win/process_info.cc
@@ -224,7 +224,7 @@
                                                  sizeof(wow64_peb_address),
                                                  &bytes_returned);
     if (!NT_SUCCESS(status)) {
-      NTSTATUS_LOG(ERROR, status), "NtQueryInformationProcess";
+      NTSTATUS_LOG(ERROR, status) << "NtQueryInformationProcess";
       return false;
     }
     if (bytes_returned != sizeof(wow64_peb_address)) {
diff --git a/third_party/crashpad/crashpad/util/win/registration_protocol_win.cc b/third_party/crashpad/crashpad/util/win/registration_protocol_win.cc
index 412f8da..359e330 100644
--- a/third_party/crashpad/crashpad/util/win/registration_protocol_win.cc
+++ b/third_party/crashpad/crashpad/util/win/registration_protocol_win.cc
@@ -18,7 +18,7 @@
 #include <windows.h>
 
 #include "base/logging.h"
-#include "util/misc/arraysize.h"
+#include "base/stl_util.h"
 #include "util/win/exception_handler_server.h"
 #include "util/win/scoped_handle.h"
 
@@ -168,7 +168,8 @@
               ACL_REVISION,  // AclRevision.
               0,  // Sbz1.
               sizeof(kSecDescBlob.sacl),  // AclSize.
-              static_cast<WORD>(ArraySize(kSecDescBlob.sacl.ace)),  // AceCount.
+              static_cast<WORD>(
+                  base::size(kSecDescBlob.sacl.ace)),  // AceCount.
               0,  // Sbz2.
           },
 
@@ -189,8 +190,8 @@
                   {
                       SID_REVISION,  // Revision.
                                      // SubAuthorityCount.
-                      static_cast<BYTE>(
-                          ArraySize(kSecDescBlob.sacl.ace[0].sid.SubAuthority)),
+                      static_cast<BYTE>(base::size(
+                          kSecDescBlob.sacl.ace[0].sid.SubAuthority)),
                       // IdentifierAuthority.
                       {SECURITY_MANDATORY_LABEL_AUTHORITY},
                       {SECURITY_MANDATORY_UNTRUSTED_RID},  // SubAuthority.
diff --git a/third_party/crashpad/crashpad/util/win/safe_terminate_process_test.cc b/third_party/crashpad/crashpad/util/win/safe_terminate_process_test.cc
index a62fb14..d2e4b6d 100644
--- a/third_party/crashpad/crashpad/util/win/safe_terminate_process_test.cc
+++ b/third_party/crashpad/crashpad/util/win/safe_terminate_process_test.cc
@@ -22,12 +22,12 @@
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/stl_util.h"
 #include "build/build_config.h"
 #include "gtest/gtest.h"
 #include "test/errors.h"
 #include "test/test_paths.h"
 #include "test/win/child_launcher.h"
-#include "util/misc/arraysize.h"
 #include "util/win/scoped_handle.h"
 
 namespace crashpad {
@@ -149,7 +149,7 @@
     };
 
     void* target = reinterpret_cast<void*>(TerminateProcess);
-    ScopedExecutablePatch executable_patch(target, patch, ArraySize(patch));
+    ScopedExecutablePatch executable_patch(target, patch, base::size(patch));
 
     // Make sure that SafeTerminateProcess() can be called. Since it’s been
     // patched with a no-op stub, GetLastError() shouldn’t be modified.
diff --git a/tools/android/forwarder2/device_controller.h b/tools/android/forwarder2/device_controller.h
index 9b3e3a1..953c68a 100644
--- a/tools/android/forwarder2/device_controller.h
+++ b/tools/android/forwarder2/device_controller.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
 
 #include "base/containers/hash_tables.h"
 #include "base/macros.h"
@@ -52,7 +53,8 @@
   // Lets ensure DeviceListener instances are deleted on the thread they were
   // created on.
   const scoped_refptr<base::SingleThreadTaskRunner> construction_task_runner_;
-  base::hash_map<int /* port */, std::unique_ptr<DeviceListener>> listeners_;
+  std::unordered_map<int /* port */, std::unique_ptr<DeviceListener>>
+      listeners_;
 
   //WeakPtrFactory's documentation says:
   // Member variables should appear before the WeakPtrFactory, to ensure
diff --git a/tools/android/forwarder2/host_controllers_manager.cc b/tools/android/forwarder2/host_controllers_manager.cc
index 6362234..2b3428a2 100644
--- a/tools/android/forwarder2/host_controllers_manager.cc
+++ b/tools/android/forwarder2/host_controllers_manager.cc
@@ -232,7 +232,7 @@
 void HostControllersManager::RemoveAdbPortForDeviceIfNeeded(
     const std::string& adb_path,
     const std::string& device_serial) {
-  base::hash_map<std::string, int>::const_iterator it =
+  std::unordered_map<std::string, int>::const_iterator it =
       device_serial_to_adb_port_map_.find(device_serial);
   if (it == device_serial_to_adb_port_map_.end())
     return;
@@ -278,7 +278,7 @@
 int HostControllersManager::GetAdbPortForDevice(
     const std::string adb_path,
     const std::string& device_serial) {
-  base::hash_map<std::string, int>::const_iterator it =
+  std::unordered_map<std::string, int>::const_iterator it =
       device_serial_to_adb_port_map_.find(device_serial);
   if (it != device_serial_to_adb_port_map_.end())
     return it->second;
diff --git a/tools/android/forwarder2/host_controllers_manager.h b/tools/android/forwarder2/host_controllers_manager.h
index fbdb4532..4a86cb86 100644
--- a/tools/android/forwarder2/host_controllers_manager.h
+++ b/tools/android/forwarder2/host_controllers_manager.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
 
 #include "base/at_exit.h"
 #include "base/containers/hash_tables.h"
@@ -44,7 +45,7 @@
   FRIEND_TEST_ALL_PREFIXES(HostControllersManagerTest, AdbArgumentSequence);
 
   using HostControllerMap =
-      base::hash_map<std::string, std::unique_ptr<HostController>>;
+      std::unordered_map<std::string, std::unique_ptr<HostController>>;
 
   static std::string MakeHostControllerMapKey(int adb_port, int device_port);
 
@@ -102,7 +103,7 @@
   virtual bool GetAppOutputAndError(const std::vector<std::string>& argv,
                                     std::string* output);
 
-  base::hash_map<std::string, int> device_serial_to_adb_port_map_;
+  std::unordered_map<std::string, int> device_serial_to_adb_port_map_;
   std::unique_ptr<HostControllerMap> controllers_;
   std::unique_ptr<base::AtExitManager>
       at_exit_manager_;  // Needed by base::Thread.
diff --git a/tools/android/memdump/memdump.cc b/tools/android/memdump/memdump.cc
index 572a838d..9d27a55c 100644
--- a/tools/android/memdump/memdump.cc
+++ b/tools/android/memdump/memdump.cc
@@ -15,6 +15,7 @@
 #include <iostream>
 #include <limits>
 #include <string>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -123,7 +124,7 @@
 }
 
 // Number of times a physical page is mapped in a process.
-typedef base::hash_map<uint64_t, int> PFNMap;
+typedef std::unordered_map<uint64_t, int> PFNMap;
 
 // Parses lines from /proc/<PID>/maps, e.g.:
 // 401e7000-401f5000 r-xp 00000000 103:02 158       /system/bin/linker
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index cc36eed..9438bac 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -924,9 +924,6 @@
   parser = argparse.ArgumentParser(description='Build Clang.')
   parser.add_argument('--bootstrap', action='store_true',
                       help='first build clang with CC, then with itself.')
-  # TODO(phajdan.jr): remove --if-needed after fixing callers. It's no-op.
-  parser.add_argument('--if-needed', action='store_true',
-                      help="run only if the script thinks clang is needed")
   parser.add_argument('--force-local-build', action='store_true',
                       help="don't try to download prebuild binaries")
   parser.add_argument('--gcc-toolchain', help='set the version for which gcc '
diff --git a/tools/imagediff/image_diff.cc b/tools/imagediff/image_diff.cc
index c3b4415..2b051363 100644
--- a/tools/imagediff/image_diff.cc
+++ b/tools/imagediff/image_diff.cc
@@ -15,6 +15,7 @@
 #include <iostream>
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/command_line.h"
@@ -175,7 +176,7 @@
   return 100.0f * pixels_different / total_pixels;
 }
 
-typedef base::hash_map<uint32_t, int32_t> RgbaToCountMap;
+typedef std::unordered_map<uint32_t, int32_t> RgbaToCountMap;
 
 float HistogramPercentageDifferent(const Image& baseline, const Image& actual) {
   // TODO(johnme): Consider using a joint histogram instead, as described in
diff --git a/tools/ipc_fuzzer/fuzzer/fuzzer.h b/tools/ipc_fuzzer/fuzzer/fuzzer.h
index 38bf58f2..531bbe774 100644
--- a/tools/ipc_fuzzer/fuzzer/fuzzer.h
+++ b/tools/ipc_fuzzer/fuzzer/fuzzer.h
@@ -10,6 +10,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/strings/string_util.h"
@@ -73,7 +74,7 @@
 
 // Used for mutating messages. Once populated, the map associates a message ID
 // with a FuzzerFunction used for mutation of that message type.
-using FuzzerFunctionMap = base::hash_map<uint32_t, FuzzerFunction>;
+using FuzzerFunctionMap = std::unordered_map<uint32_t, FuzzerFunction>;
 void PopulateFuzzerFunctionMap(FuzzerFunctionMap* map);
 
 // Used for generating new messages. Once populated, the vector contains
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 4f4c81a..59081cae 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -3049,12 +3049,17 @@
 <enum name="AutofillLocalCardMigrationDialogOffer">
   <int value="0" label="Shown"/>
   <int value="1" label="Not shown due to legal message being invalid"/>
+  <int value="2" label="Feedback dialog shown"/>
+  <int value="3" label="Feedback server error dialog shown"/>
 </enum>
 
 <enum name="AutofillLocalCardMigrationDialogUserInteraction">
   <int value="0" label="User explicitly accepted dialog"/>
   <int value="1" label="User explicitly denied dialog"/>
   <int value="2" label="User clicked legal message link"/>
+  <int value="3" label="User clicked view card button"/>
+  <int value="4" label="User clicked done button"/>
+  <int value="5" label="User clicked delete card icon"/>
 </enum>
 
 <enum name="AutofillMacAddressBook">
@@ -3506,7 +3511,6 @@
   <int value="15" label="Component update task"/>
   <int value="16" label="Deprecated Explore Sites refresh task"/>
   <int value="17" label="Explore Sites refresh task"/>
-  <int value="18" label="Download auto-resumption task"/>
 </enum>
 
 <enum name="BackgroundTracingState">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 2442d75..9039466 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -8473,7 +8473,7 @@
   </summary>
 </histogram>
 
-<histogram name="Autofill.Upload.BackoffDelay" units="ms" expires_after="M73">
+<histogram name="Autofill.Upload.BackoffDelay" units="ms">
   <owner>rogerm@chromium.org</owner>
   <summary>
     The delay of a network request for an upload due to exponential backoff.
@@ -8499,7 +8499,7 @@
 </histogram>
 
 <histogram name="Autofill.Upload.HttpResponseOrErrorCode"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="M73">
+    enum="CombinedHttpResponseAndNetErrorCode">
   <owner>rogerm@chromium.org</owner>
   <summary>
     The http response code or net error code returned on an upload.
@@ -8524,8 +8524,7 @@
   </summary>
 </histogram>
 
-<histogram name="Autofill.Upload.RequestDuration" units="ms"
-    expires_after="M73">
+<histogram name="Autofill.Upload.RequestDuration" units="ms">
   <owner>rogerm@chromium.org</owner>
   <summary>The duration of a network request for an upload.</summary>
 </histogram>
@@ -54837,6 +54836,20 @@
 </histogram>
 
 <histogram name="Navigation.ReadyToCommitUntilCommit" units="ms">
+  <obsolete>
+    Deprecated 2019-01 (M73), replaced by Navigation.ReadyToCommitUntilCommit2
+    (same histogram, different bucketing).
+  </obsolete>
+  <owner>clamy@chromium.org</owner>
+  <owner>nasko@chromium.org</owner>
+  <summary>
+    The time needed to commit a navigation once it is ready to commit. This is
+    the time between ReadyToCommit and DidFinishNavigation (for a navigation
+    that commits).
+  </summary>
+</histogram>
+
+<histogram name="Navigation.ReadyToCommitUntilCommit2" units="ms">
   <owner>clamy@chromium.org</owner>
   <owner>nasko@chromium.org</owner>
   <summary>
@@ -55028,6 +55041,19 @@
 </histogram>
 
 <histogram name="Navigation.TimeToReadyToCommit" units="ms">
+  <obsolete>
+    Deprecated 2019-01 (M73), replaced by Navigation.TimeToReadyToCommit2 (same
+    histogram, different bucketing).
+  </obsolete>
+  <owner>clamy@chromium.org</owner>
+  <owner>nasko@chromium.org</owner>
+  <summary>
+    The time delta between the start of a navigation and the time it is ready to
+    commit.
+  </summary>
+</histogram>
+
+<histogram name="Navigation.TimeToReadyToCommit2" units="ms">
   <owner>clamy@chromium.org</owner>
   <owner>nasko@chromium.org</owner>
   <summary>
@@ -79748,8 +79774,10 @@
 </histogram>
 
 <histogram name="PasswordProtection.InterstitialActionByUserNavigation"
-    enum="PasswordProtectionWarningAction" expires_after="M73">
-  <owner>jialiul@chromium.org</owner>
+    enum="PasswordProtectionWarningAction">
+  <obsolete>
+    Removed 2019-01 due to lack of usage. https://crbug.com/915894
+  </obsolete>
   <owner>nparker@chromium.org</owner>
   <summary>
     When user manually navigates to chrome://reset-password page, records how
@@ -129231,6 +129259,9 @@
     name="AutofillLocalCardMigrationDialogDurationWithCloseEvent" separator=".">
   <suffix name="Accepted"
       label="The dialog was closed due to the user clicking the save button."/>
+  <suffix name="Closed"
+      label="The dialog was closed due to the user clicking the view cards or
+             done button."/>
   <suffix name="Denied"
       label="The dialog was closed due to the user clicking the cancel
              button."/>
@@ -134124,6 +134155,7 @@
   <affected-histogram name="Navigation.StartToCommit.CrossProcess"/>
   <affected-histogram name="Navigation.StartToCommit.SameProcess"/>
   <affected-histogram name="Navigation.TimeToReadyToCommit"/>
+  <affected-histogram name="Navigation.TimeToReadyToCommit2"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="NavigationPredictor.DSEAffix" separator="."
@@ -134173,6 +134205,7 @@
   <suffix name="SameProcess" label="Same process navigation."/>
   <affected-histogram name="Navigation.StartToCommit"/>
   <affected-histogram name="Navigation.TimeToReadyToCommit"/>
+  <affected-histogram name="Navigation.TimeToReadyToCommit2"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="NavigationType" separator=".">
@@ -134215,6 +134248,7 @@
   <suffix name="Reload" label="Reload"/>
   <affected-histogram name="Navigation.IsSameProcess"/>
   <affected-histogram name="Navigation.ReadyToCommitUntilCommit"/>
+  <affected-histogram name="Navigation.ReadyToCommitUntilCommit2"/>
   <affected-histogram name="Navigation.Renderer.ReadyToCommitUntilCommit"/>
   <affected-histogram name="Navigation.StartToCommit"/>
   <affected-histogram name="Navigation.StartToCommit.CrossProcess"/>
@@ -134230,6 +134264,11 @@
   <affected-histogram name="Navigation.TimeToReadyToCommit.MainFrame"/>
   <affected-histogram name="Navigation.TimeToReadyToCommit.SameProcess"/>
   <affected-histogram name="Navigation.TimeToReadyToCommit.Subframe"/>
+  <affected-histogram name="Navigation.TimeToReadyToCommit2"/>
+  <affected-histogram name="Navigation.TimeToReadyToCommit2.CrossProcess"/>
+  <affected-histogram name="Navigation.TimeToReadyToCommit2.MainFrame"/>
+  <affected-histogram name="Navigation.TimeToReadyToCommit2.SameProcess"/>
+  <affected-histogram name="Navigation.TimeToReadyToCommit2.Subframe"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="Net.BidirectionalStreamExperiment" separator=".">
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 2111922..a4c2b3c1 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -154,6 +154,7 @@
 crbug.com/865400 [ Pixel_2 Android_Webview ] loading.mobile/Hongkiat_3g [ Skip ]
 crbug.com/865400 [ Pixel_2 Android_Webview ] loading.mobile/OLX_3g [ Skip ]
 crbug.com/865400 [ Pixel_2 Android_Webview ] loading.mobile/VoiceMemos_cold_3g [ Skip ]
+crbug.com/919191 [ Nexus5X_Webview ] loading.mobile/OLX_3g [ Skip ]
 
 # Benchmark: memory.long_running_idle_gmail_tbmv2
 crbug.com/611167 [ Android_Svelte ] memory.long_running_idle_gmail_tbmv2/* [ Skip ]
diff --git a/ui/accessibility/ax_table_info.h b/ui/accessibility/ax_table_info.h
index 167c5818..3929109 100644
--- a/ui/accessibility/ax_table_info.h
+++ b/ui/accessibility/ax_table_info.h
@@ -6,6 +6,7 @@
 #define UI_ACCESSIBILITY_AX_TABLE_INFO_H_
 
 #include <set>
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -80,7 +81,7 @@
   std::vector<AXNode*> extra_mac_nodes;
 
   // Map from each cell's node ID to its index in unique_cell_ids.
-  base::hash_map<int32_t, int32_t> cell_id_to_index;
+  std::unordered_map<int32_t, int32_t> cell_id_to_index;
 
   // Map from each row's node ID to its row index.
   base::hash_map<int32_t, int32_t> row_id_to_index;
diff --git a/ui/accessibility/ax_tree.h b/ui/accessibility/ax_tree.h
index 95cc105..dba99ae 100644
--- a/ui/accessibility/ax_tree.h
+++ b/ui/accessibility/ax_tree.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <set>
+#include <unordered_map>
 
 #include "base/containers/hash_tables.h"
 #include "base/observer_list.h"
@@ -203,7 +204,7 @@
 
   base::ObserverList<AXTreeObserver> observers_;
   AXNode* root_ = nullptr;
-  base::hash_map<int32_t, AXNode*> id_map_;
+  std::unordered_map<int32_t, AXNode*> id_map_;
   std::string error_;
   AXTreeData data_;
 
@@ -218,7 +219,7 @@
 
   // Map from node ID to cached table info, if the given node is a table.
   // Invalidated every time the tree is updated.
-  mutable base::hash_map<int32_t, AXTableInfo*> table_info_map_;
+  mutable std::unordered_map<int32_t, AXTableInfo*> table_info_map_;
 
   // The next negative node ID to use for internal nodes.
   int32_t next_negative_internal_node_id_ = -1;
diff --git a/ui/accessibility/ax_tree_serializer.h b/ui/accessibility/ax_tree_serializer.h
index 21f8e6f..4e7a6e1f 100644
--- a/ui/accessibility/ax_tree_serializer.h
+++ b/ui/accessibility/ax_tree_serializer.h
@@ -8,6 +8,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -183,7 +184,7 @@
   ClientTreeNode* client_root_ = nullptr;
 
   // A map from IDs to nodes in the client tree.
-  base::hash_map<int32_t, ClientTreeNode*> client_id_map_;
+  std::unordered_map<int32_t, ClientTreeNode*> client_id_map_;
 
   // The maximum number of nodes to serialize in a given call to
   // SerializeChanges, or 0 if there's no maximum.
@@ -347,7 +348,7 @@
 ClientTreeNode*
 AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::ClientTreeNodeById(
     int32_t id) {
-  base::hash_map<int32_t, ClientTreeNode*>::iterator iter =
+  std::unordered_map<int32_t, ClientTreeNode*>::iterator iter =
       client_id_map_.find(id);
   if (iter != client_id_map_.end())
     return iter->second;
@@ -529,7 +530,7 @@
   // first in a separate pass so that nodes that are reparented
   // don't end up children of two different parents in the middle
   // of an update, which can lead to a double-free.
-  base::hash_map<int32_t, ClientTreeNode*> client_child_id_map;
+  std::unordered_map<int32_t, ClientTreeNode*> client_child_id_map;
   std::vector<ClientTreeNode*> old_children;
   old_children.swap(client_node->children);
   for (size_t i = 0; i < old_children.size(); ++i) {
diff --git a/ui/accessibility/ax_tree_update.h b/ui/accessibility/ax_tree_update.h
index a2d8e28..0f70cd2 100644
--- a/ui/accessibility/ax_tree_update.h
+++ b/ui/accessibility/ax_tree_update.h
@@ -9,6 +9,7 @@
 #include <stdint.h>
 
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/containers/hash_tables.h"
@@ -103,7 +104,7 @@
   // to the rest of the tree for context, so we have to try to show the
   // relative indentation of child nodes in this update relative to their
   // parents.
-  base::hash_map<int32_t, int> id_to_indentation;
+  std::unordered_map<int32_t, int> id_to_indentation;
   for (size_t i = 0; i < nodes.size(); ++i) {
     int indent = id_to_indentation[nodes[i].id];
     result += std::string(2 * indent, ' ');
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index ecba303..65de261 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -9,6 +9,7 @@
 #include <algorithm>
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -1454,7 +1455,7 @@
   return AtkObjectToAXPlatformNodeAuraLinux(accessible);
 }
 
-using UniqueIdMap = base::hash_map<int32_t, AXPlatformNodeAuraLinux*>;
+using UniqueIdMap = std::unordered_map<int32_t, AXPlatformNodeAuraLinux*>;
 // Map from each AXPlatformNode's unique id to its instance.
 base::LazyInstance<UniqueIdMap>::Leaky g_unique_id_map =
     LAZY_INSTANCE_INITIALIZER;
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index 212abd0..aee5d86 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -10,6 +10,7 @@
 #include <map>
 #include <set>
 #include <string>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -244,7 +245,7 @@
   return ax_platform_node.Get();
 }
 
-using UniqueIdMap = base::hash_map<int32_t, AXPlatformNode*>;
+using UniqueIdMap = std::unordered_map<int32_t, AXPlatformNode*>;
 // Map from each AXPlatformNode's unique id to its instance.
 base::LazyInstance<UniqueIdMap>::Leaky g_unique_id_map =
     LAZY_INSTANCE_INITIALIZER;
diff --git a/ui/accessibility/platform/test_ax_node_wrapper.cc b/ui/accessibility/platform/test_ax_node_wrapper.cc
index a044980..98006e1 100644
--- a/ui/accessibility/platform/test_ax_node_wrapper.cc
+++ b/ui/accessibility/platform/test_ax_node_wrapper.cc
@@ -16,7 +16,7 @@
 namespace {
 
 // A global map from AXNodes to TestAXNodeWrappers.
-base::hash_map<AXNode*, TestAXNodeWrapper*> g_node_to_wrapper_map;
+std::unordered_map<AXNode*, TestAXNodeWrapper*> g_node_to_wrapper_map;
 
 // A global coordinate offset.
 gfx::Vector2d g_offset;
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index 500df822..9a132426 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -128,13 +128,8 @@
 const base::Feature kMashOopViz = {"MashOopViz",
                                    base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Runs the window service in-process. Launch bug https://crbug.com/909816
 const base::Feature kSingleProcessMash = {"SingleProcessMash",
-#if defined(OS_CHROMEOS)
-                                          base::FEATURE_ENABLED_BY_DEFAULT};
-#else
                                           base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
 
 bool IsUsingWindowService() {
   return IsSingleProcessMash() || IsMultiProcessMash();
diff --git a/ui/compositor/test/in_process_context_factory.h b/ui/compositor/test/in_process_context_factory.h
index 1dd70c5..1ecf65d 100644
--- a/ui/compositor/test/in_process_context_factory.h
+++ b/ui/compositor/test/in_process_context_factory.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 #include <memory>
+#include <unordered_map>
 
 #include "base/macros.h"
 #include "cc/test/test_image_factory.h"
@@ -114,7 +115,7 @@
 
   viz::RendererSettings renderer_settings_;
   using PerCompositorDataMap =
-      base::hash_map<ui::Compositor*, std::unique_ptr<PerCompositorData>>;
+      std::unordered_map<ui::Compositor*, std::unique_ptr<PerCompositorData>>;
   PerCompositorDataMap per_compositor_data_;
 
   DISALLOW_COPY_AND_ASSIGN(InProcessContextFactory);
diff --git a/ui/events/keycodes/keysym_to_unicode.cc b/ui/events/keycodes/keysym_to_unicode.cc
index d58059cf..6865b0d 100644
--- a/ui/events/keycodes/keysym_to_unicode.cc
+++ b/ui/events/keycodes/keysym_to_unicode.cc
@@ -839,7 +839,7 @@
   }
 
  private:
-  typedef base::hash_map<KeySym, uint16_t> KeySymToUnicodeMap;
+  typedef std::unordered_map<KeySym, uint16_t> KeySymToUnicodeMap;
   KeySymToUnicodeMap keysym_to_unicode_map_;
 
   DISALLOW_COPY_AND_ASSIGN(KeySymToUnicode);
diff --git a/ui/events/ozone/evdev/event_device_info.cc b/ui/events/ozone/evdev/event_device_info.cc
index 7b89ebe..4a001a7 100644
--- a/ui/events/ozone/evdev/event_device_info.cc
+++ b/ui/events/ozone/evdev/event_device_info.cc
@@ -467,7 +467,7 @@
   };
 
   if (id.bustype == BUS_USB) {
-    for (size_t i = 0; i < arraysize(kUSBInternalDevices); ++i) {
+    for (size_t i = 0; i < base::size(kUSBInternalDevices); ++i) {
       if (id.vendor == kUSBInternalDevices[i].vid &&
           id.product == kUSBInternalDevices[i].pid)
         return InputDeviceType::INPUT_DEVICE_INTERNAL;
diff --git a/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc b/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc
index bc8fd80..3756b1f5 100644
--- a/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc
+++ b/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.cc
@@ -614,7 +614,7 @@
   // Unowned default properties (owned by the configuration file). Their values
   // will be applied when a property of the same name is created. These are
   // usually only a small portion of all properties in use.
-  base::hash_map<std::string, GesturesProp*> default_properties;
+  std::unordered_map<std::string, GesturesProp*> default_properties;
 };
 
 // Base class for device match criterias in conf files.
diff --git a/ui/gfx/generic_shared_memory_id.h b/ui/gfx/generic_shared_memory_id.h
index ed77e529..9374b06 100644
--- a/ui/gfx/generic_shared_memory_id.h
+++ b/ui/gfx/generic_shared_memory_id.h
@@ -49,12 +49,12 @@
 
 }  // namespace gfx
 
-namespace BASE_HASH_NAMESPACE {
+namespace std {
 
 template <>
 struct hash<gfx::GenericSharedMemoryId> {
   size_t operator()(gfx::GenericSharedMemoryId key) const {
-    return BASE_HASH_NAMESPACE::hash<int>()(key.id);
+    return std::hash<int>()(key.id);
   }
 };
 
@@ -66,6 +66,6 @@
   }
 };
 
-}  // namespace BASE_HASH_NAMESPACE
+}  // namespace std
 
 #endif  // UI_GFX_GENERIC_SHARED_MEMORY_ID_H_
diff --git a/ui/gfx/nine_image_painter.cc b/ui/gfx/nine_image_painter.cc
index fa1afe44..1d2ae8e 100644
--- a/ui/gfx/nine_image_painter.cc
+++ b/ui/gfx/nine_image_painter.cc
@@ -8,7 +8,7 @@
 
 #include <limits>
 
-#include "base/macros.h"
+#include "base/stl_util.h"
 #include "cc/paint/paint_flags.h"
 #include "third_party/skia/include/core/SkRect.h"
 #include "third_party/skia/include/core/SkScalar.h"
@@ -52,8 +52,8 @@
 }  // namespace
 
 NineImagePainter::NineImagePainter(const std::vector<ImageSkia>& images) {
-  DCHECK_EQ(arraysize(images_), images.size());
-  for (size_t i = 0; i < arraysize(images_); ++i)
+  DCHECK_EQ(base::size(images_), images.size());
+  for (size_t i = 0; i < base::size(images_); ++i)
     images_[i] = images[i];
 }
 
@@ -112,8 +112,8 @@
   canvas->Translate(gfx::Vector2d(left_in_pixels, top_in_pixels));
 
   ImageSkiaRep image_reps[9];
-  static_assert(arraysize(image_reps) == arraysize(images_), "");
-  for (size_t i = 0; i < arraysize(image_reps); ++i) {
+  static_assert(base::size(image_reps) == std::extent<decltype(images_)>(), "");
+  for (size_t i = 0; i < base::size(image_reps); ++i) {
     image_reps[i] = images_[i].GetRepresentation(scale);
     DCHECK(image_reps[i].is_null() || image_reps[i].scale() == scale);
   }
diff --git a/ui/gfx/sequential_id_generator.h b/ui/gfx/sequential_id_generator.h
index 40bda2e..aca503f1 100644
--- a/ui/gfx/sequential_id_generator.h
+++ b/ui/gfx/sequential_id_generator.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <map>
+#include <unordered_map>
 
 #include "base/containers/hash_tables.h"
 #include "base/macros.h"
@@ -39,7 +40,7 @@
   void ResetForTest();
 
  private:
-  typedef base::hash_map<uint32_t, uint32_t> IDMap;
+  typedef std::unordered_map<uint32_t, uint32_t> IDMap;
 
   uint32_t GetNextAvailableID();
 
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js b/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js
index f050178..4fc3e105 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js
@@ -318,7 +318,7 @@
         this.focusDialogInput_();
       } else {
         this.error_ = ErrorType.NONE;
-        this.$.closeDialogs_();
+        this.closeDialogs_();
         this.delayUpdateLockEnabled_();
       }
     });
diff --git a/ui/webui/resources/cr_elements/paper_button_style_css.html b/ui/webui/resources/cr_elements/paper_button_style_css.html
index 4cd4472..7fdc6169 100644
--- a/ui/webui/resources/cr_elements/paper_button_style_css.html
+++ b/ui/webui/resources/cr_elements/paper_button_style_css.html
@@ -36,13 +36,13 @@
 
       paper-button:not([raised]).keyboard-focus,
       paper-button:not([raised]).action-button.keyboard-focus {
-        box-shadow: 0 0 0 2px rgba(var(--google-blue-900-rgb), .4);
+        box-shadow: 0 0 0 2px rgba(var(--google-blue-600-rgb), .4);
         /* Override default paper-button's internal font-weight style. */
         font-weight: 500;
       }
 
       paper-button:not(.action-button):hover {
-        background-color: rgba(66, 133, 244, .04);
+        background-color: rgba(var(--google-blue-500-rgb), .04);
         border-color: rgb(210, 227, 252);
       }
 
diff --git a/ui/webui/resources/js/cr.js b/ui/webui/resources/js/cr.js
index 9385eba..9fca69b6 100644
--- a/ui/webui/resources/js/cr.js
+++ b/ui/webui/resources/js/cr.js
@@ -2,18 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/**
- * The global object.
- * @type {!Object}
- * @const
- */
-var global = this;
-
 /** @typedef {{eventName: string, uid: number}} */
 var WebUIListener;
 
 /** Platform, package, object property, and Event support. **/
-var cr = cr || function() {
+var cr = cr || function(global) {
   'use strict';
 
   /**
@@ -497,4 +490,4 @@
       return /iPad|iPhone|iPod/.test(navigator.platform);
     }
   };
-}();
+}(this);
diff --git a/ui/webui/resources/js/find_shortcut_behavior.js b/ui/webui/resources/js/find_shortcut_behavior.js
index 369474f..683cec3b 100644
--- a/ui/webui/resources/js/find_shortcut_behavior.js
+++ b/ui/webui/resources/js/find_shortcut_behavior.js
@@ -8,10 +8,11 @@
  * top of the stack will be notified that a find shortcut has been invoked.
  */
 
+
 const FindShortcutManager = (() => {
   /**
    * Stack of listeners. Only the top listener will handle the shortcut.
-   * @type {!Array<!HTMLElement>}
+   * @type {!Array}
    */
   const listeners = [];
 
@@ -27,16 +28,21 @@
       new cr.ui.KeyboardShortcutList(cr.isMac ? 'meta|f' : 'ctrl|f');
 
   window.addEventListener('keydown', e => {
-    if (e.defaultPrevented || listeners.length == 0) {
+    if (e.defaultPrevented || listeners.length == 0 ||
+        !shortcut.matchesEvent(e)) {
       return;
     }
 
-    if (shortcut.matchesEvent(e)) {
-      const listener = /** @type {!{handleFindShortcut: function(boolean)}} */ (
-          listeners[listeners.length - 1]);
-      if (listener.handleFindShortcut(modalContextOpen)) {
-        e.preventDefault();
-      }
+    const focusIndex =
+        listeners.findIndex(listener => listener.searchInputHasFocus());
+    // If no listener has focus or the first (outer-most) listener has focus,
+    // try the last (inner-most) listener.
+    // If a listener has a search input with focus, the next listener that
+    // should be called is the right before it in |listeners| such that the
+    // goes from inner-most to outer-most.
+    const index = focusIndex <= 0 ? listeners.length - 1 : focusIndex - 1;
+    if (listeners[index].handleFindShortcut(modalContextOpen)) {
+      e.preventDefault();
     }
   });
 
@@ -63,13 +69,21 @@
  */
 const FindShortcutBehavior = {
   /**
-   * If handled, return true.
-   * @param {boolean} modalContextOpen
-   * @return {boolean}
+   * @type {boolean}
    * @protected
    */
-  handleFindShortcut(modalContextOpen) {
-    assertNotReached();
+  findShortcutListenOnAttach: true,
+
+  attached() {
+    if (this.findShortcutListenOnAttach) {
+      this.becomeActiveFindShortcutListener();
+    }
+  },
+
+  detached() {
+    if (this.findShortcutListenOnAttach) {
+      this.removeSelfAsFindShortcutListener();
+    }
   },
 
   becomeActiveFindShortcutListener() {
@@ -84,4 +98,22 @@
     assert(listeners.includes(this), 'Find shortcut listener not found.');
     listeners.splice(index, 1);
   },
+
+  /**
+   * If handled, return true.
+   * @param {boolean} modalContextOpen
+   * @return {boolean}
+   * @protected
+   */
+  handleFindShortcut(modalContextOpen) {
+    assertNotReached();
+  },
+
+  /**
+   * @return {boolean}
+   * @protected
+   */
+  searchInputHasFocus() {
+    assertNotReached();
+  },
 };