diff --git a/DEPS b/DEPS
index 2c15647..8bcd4d5cb 100644
--- a/DEPS
+++ b/DEPS
@@ -111,11 +111,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '4f598e8c829aeb3300f221cd990e595114dda59a',
+  'skia_revision': '7e4c630e73995659fbbd8ab496f66aee64b5b474',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '7bd88028021f32ba5457ecc0e6927165dee884af',
+  'v8_revision': '1ef470c48d917990ec028db06331d44e25f14664',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -123,7 +123,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'c3ee7ec01dda6f04aa91f37a5524955b362207bf',
+  'angle_revision': '672267fcc74feff4693eee1a72d1d20edda84d2d',
   # 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.
@@ -135,7 +135,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '02d42b65b779e29737275a258cb9f30fa8449e5e',
+  'pdfium_revision': '7bcba336e4b83c44d5331a0f988a9d062851c47e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -171,7 +171,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'bdcfce54a8d5dada227a0554771d86d704324aad',
+  'catapult_revision': 'ed6fe0f638403e1afd377e38975e4fd430f53432',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -1156,7 +1156,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '0d55c887e92b645f6effe753528323ab2ffd94c2',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '44a262a6aae2556d8eb7fe5c33074a8919da4adb',
+    Var('webrtc_git') + '/src.git' + '@' + '01c68b8001774cea0e17f9b23ddebb9a2bb1f43c',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1187,7 +1187,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@062e25f6a6b08ab876c6741fa0009dfaa234ca06',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@0c93d9ffc58387938cce7eaf7507ca99c3bcbd81',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index a808cd7..3802484 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -248,6 +248,8 @@
     "assistant/util/views_util.h",
     "autoclick/autoclick_controller.cc",
     "autoclick/autoclick_controller.h",
+    "autoclick/autoclick_drag_event_rewriter.cc",
+    "autoclick/autoclick_drag_event_rewriter.h",
     "autoclick/autoclick_ring_handler.cc",
     "autoclick/autoclick_ring_handler.h",
     "bluetooth_devices_observer.cc",
@@ -748,8 +750,6 @@
     "system/message_center/message_center_view.h",
     "system/message_center/message_list_view.cc",
     "system/message_center/message_list_view.h",
-    "system/message_center/new_unified_message_center_view.cc",
-    "system/message_center/new_unified_message_center_view.h",
     "system/message_center/notification_swipe_control_view.cc",
     "system/message_center/notification_swipe_control_view.h",
     "system/message_center/notification_tray.cc",
@@ -760,6 +760,8 @@
     "system/message_center/session_state_notification_blocker.h",
     "system/message_center/slidable_message_view.cc",
     "system/message_center/slidable_message_view.h",
+    "system/message_center/unified_message_center_view.cc",
+    "system/message_center/unified_message_center_view.h",
     "system/message_center/unified_message_list_view.cc",
     "system/message_center/unified_message_list_view.h",
     "system/model/clock_model.cc",
@@ -1729,6 +1731,7 @@
     "assistant/assistant_screen_context_controller_unittest.cc",
     "assistant/ui/assistant_container_view_unittest.cc",
     "assistant/util/deep_link_util_unittest.cc",
+    "autoclick/autoclick_drag_event_rewriter_unittest.cc",
     "autoclick/autoclick_unittest.cc",
     "cursor_unittest.cc",
     "detachable_base/detachable_base_handler_unittest.cc",
@@ -1871,10 +1874,10 @@
     "system/message_center/message_center_ui_controller_unittest.cc",
     "system/message_center/message_center_view_unittest.cc",
     "system/message_center/message_list_view_unittest.cc",
-    "system/message_center/new_unified_message_center_view_unittest.cc",
     "system/message_center/notification_tray_unittest.cc",
     "system/message_center/notifier_settings_view_unittest.cc",
     "system/message_center/session_state_notification_blocker_unittest.cc",
+    "system/message_center/unified_message_center_view_unittest.cc",
     "system/message_center/unified_message_list_view_unittest.cc",
     "system/network/auto_connect_notifier_unittest.cc",
     "system/network/network_icon_unittest.cc",
diff --git a/ash/autoclick/autoclick_controller.cc b/ash/autoclick/autoclick_controller.cc
index f6e6403d..fbd31a4b 100644
--- a/ash/autoclick/autoclick_controller.cc
+++ b/ash/autoclick/autoclick_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/autoclick/autoclick_controller.h"
 
+#include "ash/autoclick/autoclick_drag_event_rewriter.h"
 #include "ash/autoclick/autoclick_ring_handler.h"
 #include "ash/public/cpp/ash_constants.h"
 #include "ash/public/cpp/shell_window_ids.h"
@@ -72,12 +73,19 @@
       delay_(GetDefaultAutoclickDelay()),
       mouse_event_flags_(ui::EF_NONE),
       anchor_location_(-kMovementThreshold, -kMovementThreshold),
-      autoclick_ring_handler_(std::make_unique<AutoclickRingHandler>()) {
+      autoclick_ring_handler_(std::make_unique<AutoclickRingHandler>()),
+      drag_event_rewriter_(std::make_unique<AutoclickDragEventRewriter>()) {
+  Shell::GetPrimaryRootWindow()->GetHost()->GetEventSource()->AddEventRewriter(
+      drag_event_rewriter_.get());
   InitClickTimer();
 }
 
 AutoclickController::~AutoclickController() {
   SetTapDownTarget(nullptr);
+  Shell::GetPrimaryRootWindow()
+      ->GetHost()
+      ->GetEventSource()
+      ->RemoveEventRewriter(drag_event_rewriter_.get());
 }
 
 void AutoclickController::SetTapDownTarget(aura::Window* target) {
@@ -171,24 +179,39 @@
   aura::WindowTreeHost* host = root_window->GetHost();
   host->ConvertDIPToPixels(&location_in_pixels);
 
-  // TODO(katie): Implement drag-and-drop.
+  bool drag_start = event_type_ == mojom::AutoclickEventType::kDragAndDrop &&
+                    !drag_event_rewriter_->IsEnabled();
+  bool drag_stop = event_type_ == mojom::AutoclickEventType::kDragAndDrop &&
+                   drag_event_rewriter_->IsEnabled();
+
   if (event_type_ == mojom::AutoclickEventType::kLeftClick ||
       event_type_ == mojom::AutoclickEventType::kRightClick ||
-      event_type_ == mojom::AutoclickEventType::kDoubleClick) {
+      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;
-    ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, location_in_pixels,
-                               location_in_pixels, ui::EventTimeForNow(),
-                               mouse_event_flags_ | button, button);
+
+    ui::EventDispatchDetails details;
+    if (!drag_stop) {
+      // Left click, right click, double click, and beginning of a drag have
+      // a pressed event next.
+      ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, location_in_pixels,
+                                 location_in_pixels, ui::EventTimeForNow(),
+                                 mouse_event_flags_ | button, button);
+      details = host->event_sink()->OnEventFromSource(&press_event);
+      if (drag_start) {
+        drag_event_rewriter_->SetEnabled(true);
+        return;
+      }
+      if (details.dispatcher_destroyed)
+        return;
+    }
+    if (drag_stop)
+      drag_event_rewriter_->SetEnabled(false);
     ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, location_in_pixels,
                                  location_in_pixels, ui::EventTimeForNow(),
                                  mouse_event_flags_ | button, button);
-
-    ui::EventDispatchDetails details =
-        host->event_sink()->OnEventFromSource(&press_event);
-    if (details.dispatcher_destroyed)
-      return;
     details = host->event_sink()->OnEventFromSource(&release_event);
 
     // Now a single click has been completed.
@@ -216,6 +239,7 @@
     autoclick_timer_->Stop();
   }
   autoclick_ring_handler_->StopGesture();
+  drag_event_rewriter_->SetEnabled(false);
   SetTapDownTarget(nullptr);
 }
 
@@ -240,8 +264,10 @@
   if (event_type_ == mojom::AutoclickEventType::kNoAction)
     return;
   gfx::Point point_in_screen = event->target()->GetScreenLocation(*event);
-  if (event->type() == ui::ET_MOUSE_MOVED &&
-      !(event->flags() & ui::EF_IS_SYNTHESIZED)) {
+  if (!(event->flags() & ui::EF_IS_SYNTHESIZED) &&
+      (event->type() == ui::ET_MOUSE_MOVED ||
+       (event->type() == ui::ET_MOUSE_DRAGGED &&
+        drag_event_rewriter_->IsEnabled()))) {
     mouse_event_flags_ = event->flags();
     UpdateRingWidget(point_in_screen);
 
@@ -260,7 +286,8 @@
     } else if (autoclick_timer_->IsRunning()) {
       autoclick_ring_handler_->SetGestureCenter(point_in_screen, widget_.get());
     }
-  } else if (event->type() == ui::ET_MOUSE_PRESSED) {
+  } else if (event->type() == ui::ET_MOUSE_PRESSED ||
+             event->type() == ui::ET_MOUSE_RELEASED) {
     CancelAutoclickAction();
   } else if (event->type() == ui::ET_MOUSEWHEEL &&
              autoclick_timer_->IsRunning()) {
diff --git a/ash/autoclick/autoclick_controller.h b/ash/autoclick/autoclick_controller.h
index 809a3250..ef22251 100644
--- a/ash/autoclick/autoclick_controller.h
+++ b/ash/autoclick/autoclick_controller.h
@@ -23,6 +23,7 @@
 
 namespace ash {
 
+class AutoclickDragEventRewriter;
 class AutoclickRingHandler;
 
 // Autoclick is one of the accessibility features. If enabled, two circles will
@@ -84,6 +85,7 @@
   // the distance the mouse has moved.
   gfx::Point anchor_location_;
   std::unique_ptr<AutoclickRingHandler> autoclick_ring_handler_;
+  std::unique_ptr<AutoclickDragEventRewriter> drag_event_rewriter_;
 
   DISALLOW_COPY_AND_ASSIGN(AutoclickController);
 };
diff --git a/ash/autoclick/autoclick_drag_event_rewriter.cc b/ash/autoclick/autoclick_drag_event_rewriter.cc
new file mode 100644
index 0000000..90368eb
--- /dev/null
+++ b/ash/autoclick/autoclick_drag_event_rewriter.cc
@@ -0,0 +1,41 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/autoclick/autoclick_drag_event_rewriter.h"
+
+namespace ash {
+
+void AutoclickDragEventRewriter::SetEnabled(bool enabled) {
+  enabled_ = enabled;
+}
+
+bool AutoclickDragEventRewriter::IsEnabled() const {
+  return enabled_;
+}
+
+ui::EventRewriteStatus AutoclickDragEventRewriter::RewriteEvent(
+    const ui::Event& event,
+    std::unique_ptr<ui::Event>* new_event) {
+  // Only rewrite mouse moved events to drag events when enabled.
+  if (!enabled_ || event.type() != ui::ET_MOUSE_MOVED)
+    return ui::EVENT_REWRITE_CONTINUE;
+  // TODO(katie): Should this have an ui::EF_LEFT_MOUSE_BUTTON flag for drag?
+  const ui::MouseEvent* mouse_event = event.AsMouseEvent();
+  ui::MouseEvent* rewritten_event = new ui::MouseEvent(
+      ui::ET_MOUSE_DRAGGED, mouse_event->location(),
+      mouse_event->root_location(), mouse_event->time_stamp(),
+      mouse_event->flags(), mouse_event->changed_button_flags(),
+      mouse_event->pointer_details());
+  new_event->reset(rewritten_event);
+  return ui::EVENT_REWRITE_REWRITTEN;
+}
+
+ui::EventRewriteStatus AutoclickDragEventRewriter::NextDispatchEvent(
+    const ui::Event& last_event,
+    std::unique_ptr<ui::Event>* new_event) {
+  // Unused.
+  return ui::EVENT_REWRITE_CONTINUE;
+}
+
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/autoclick/autoclick_drag_event_rewriter.h b/ash/autoclick/autoclick_drag_event_rewriter.h
new file mode 100644
index 0000000..cb42803
--- /dev/null
+++ b/ash/autoclick/autoclick_drag_event_rewriter.h
@@ -0,0 +1,41 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_AUTOCLICK_AUTOCLICK_DRAG_EVENT_REWRITER_H_
+#define ASH_AUTOCLICK_AUTOCLICK_DRAG_EVENT_REWRITER_H_
+
+#include "ash/ash_export.h"
+#include "ui/events/event_rewriter.h"
+
+namespace ash {
+
+// EventRewriter to change move events into drag events during drag-and-drop
+// actions from Autoclick.
+class ASH_EXPORT AutoclickDragEventRewriter : public ui::EventRewriter {
+ public:
+  AutoclickDragEventRewriter() = default;
+  ~AutoclickDragEventRewriter() override = default;
+
+  void SetEnabled(bool enabled);
+  bool IsEnabled() const;
+
+  // ui::EventRewriter (visible for testing):
+  ui::EventRewriteStatus RewriteEvent(
+      const ui::Event& event,
+      std::unique_ptr<ui::Event>* new_event) override;
+
+ private:
+  // ui::EventRewriter:
+  ui::EventRewriteStatus NextDispatchEvent(
+      const ui::Event& last_event,
+      std::unique_ptr<ui::Event>* new_event) override;
+
+  bool enabled_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(AutoclickDragEventRewriter);
+};
+
+}  // namespace ash
+
+#endif  // ASH_AUTOCLICK_AUTOCLICK_DRAG_EVENT_REWRITER_H_
diff --git a/ash/autoclick/autoclick_drag_event_rewriter_unittest.cc b/ash/autoclick/autoclick_drag_event_rewriter_unittest.cc
new file mode 100644
index 0000000..1524599c
--- /dev/null
+++ b/ash/autoclick/autoclick_drag_event_rewriter_unittest.cc
@@ -0,0 +1,161 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/autoclick/autoclick_drag_event_rewriter.h"
+#include "ash/test/ash_test_base.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/event_rewriter.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/events/test/event_generator.h"
+
+namespace ash {
+
+class EventRecorder : public ui::EventRewriter {
+ public:
+  EventRecorder() = default;
+  ~EventRecorder() override = default;
+
+  // ui::EventRewriter:
+  ui::EventRewriteStatus RewriteEvent(
+      const ui::Event& event,
+      std::unique_ptr<ui::Event>* new_event) override {
+    recorded_event_count_++;
+    last_recorded_event_type_ = event.type();
+    return ui::EVENT_REWRITE_CONTINUE;
+  }
+  ui::EventRewriteStatus NextDispatchEvent(
+      const ui::Event& last_event,
+      std::unique_ptr<ui::Event>* new_event) override {
+    NOTREACHED();
+    return ui::EVENT_REWRITE_CONTINUE;
+  }
+
+  // Count of events sent to the rewriter.
+  size_t recorded_event_count_ = 0;
+  ui::EventType last_recorded_event_type_ = ui::EventType::ET_UNKNOWN;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(EventRecorder);
+};
+
+class AutoclickDragEventRewriterTest : public AshTestBase {
+ public:
+  AutoclickDragEventRewriterTest() = default;
+
+  ~AutoclickDragEventRewriterTest() override = default;
+
+  void SetUp() override {
+    AshTestBase::SetUp();
+    generator_ = AshTestBase::GetEventGenerator();
+    CurrentContext()->GetHost()->GetEventSource()->AddEventRewriter(
+        &drag_event_rewriter_);
+    CurrentContext()->GetHost()->GetEventSource()->AddEventRewriter(
+        &event_recorder_);
+  }
+
+  void TearDown() override {
+    CurrentContext()->GetHost()->GetEventSource()->RemoveEventRewriter(
+        &event_recorder_);
+    CurrentContext()->GetHost()->GetEventSource()->RemoveEventRewriter(
+        &drag_event_rewriter_);
+    generator_ = nullptr;
+    AshTestBase::TearDown();
+  }
+
+ protected:
+  // Generates ui::Events from simulated user input.
+  ui::test::EventGenerator* generator_ = nullptr;
+  // Records events delivered to the next event rewriter after
+  // AutoclickDragEventRewriter.
+  EventRecorder event_recorder_;
+
+  AutoclickDragEventRewriter drag_event_rewriter_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AutoclickDragEventRewriterTest);
+};
+
+TEST_F(AutoclickDragEventRewriterTest, EventsNotConsumedWhenDisabled) {
+  drag_event_rewriter_.SetEnabled(false);
+  // Events are not consume.
+  generator_->PressKey(ui::VKEY_A, ui::EF_NONE);
+  EXPECT_EQ(1U, event_recorder_.recorded_event_count_);
+  generator_->ReleaseKey(ui::VKEY_A, ui::EF_NONE);
+  EXPECT_EQ(2U, event_recorder_.recorded_event_count_);
+  generator_->PressLeftButton();
+  EXPECT_EQ(3U, event_recorder_.recorded_event_count_);
+  EXPECT_EQ(ui::EventType::ET_MOUSE_PRESSED,
+            event_recorder_.last_recorded_event_type_);
+  generator_->MoveMouseTo(gfx::Point(200, 200), 1);
+  EXPECT_EQ(4U, event_recorder_.recorded_event_count_);
+  EXPECT_EQ(ui::EventType::ET_MOUSE_DRAGGED,
+            event_recorder_.last_recorded_event_type_);
+  generator_->ReleaseLeftButton();
+  EXPECT_EQ(5U, event_recorder_.recorded_event_count_);
+  EXPECT_EQ(ui::EventType::ET_MOUSE_RELEASED,
+            event_recorder_.last_recorded_event_type_);
+
+  // Move events are not consumed either.
+  generator_->MoveMouseTo(gfx::Point(100, 100), 1);
+  EXPECT_EQ(6U, event_recorder_.recorded_event_count_);
+  EXPECT_EQ(ui::EventType::ET_MOUSE_MOVED,
+            event_recorder_.last_recorded_event_type_);
+}
+
+TEST_F(AutoclickDragEventRewriterTest, OnlyMouseMoveEventsConsumedWhenEnabled) {
+  drag_event_rewriter_.SetEnabled(true);
+  // Most events are still not consumed.
+  generator_->PressKey(ui::VKEY_A, ui::EF_NONE);
+  EXPECT_EQ(1U, event_recorder_.recorded_event_count_);
+  generator_->ReleaseKey(ui::VKEY_A, ui::EF_NONE);
+  EXPECT_EQ(2U, event_recorder_.recorded_event_count_);
+  generator_->PressLeftButton();
+  EXPECT_EQ(3U, event_recorder_.recorded_event_count_);
+  EXPECT_EQ(ui::EventType::ET_MOUSE_PRESSED,
+            event_recorder_.last_recorded_event_type_);
+  generator_->MoveMouseTo(gfx::Point(200, 200), 1);
+  EXPECT_EQ(4U, event_recorder_.recorded_event_count_);
+  EXPECT_EQ(ui::EventType::ET_MOUSE_DRAGGED,
+            event_recorder_.last_recorded_event_type_);
+  generator_->ReleaseLeftButton();
+  EXPECT_EQ(5U, event_recorder_.recorded_event_count_);
+  EXPECT_EQ(ui::EventType::ET_MOUSE_RELEASED,
+            event_recorder_.last_recorded_event_type_);
+
+  // Mouse move events are consumed and changed into drag events.
+  generator_->MoveMouseTo(gfx::Point(100, 100), 1);
+  EXPECT_EQ(5U, event_recorder_.recorded_event_count_);
+  generator_->MoveMouseTo(gfx::Point(150, 150), 1);
+  EXPECT_EQ(5U, event_recorder_.recorded_event_count_);
+}
+
+TEST_F(AutoclickDragEventRewriterTest, RewritesMouseMovesToDrags) {
+  drag_event_rewriter_.SetEnabled(true);
+  base::TimeTicks time_stamp = ui::EventTimeForNow();
+  gfx::Point location(100, 100);
+  gfx::Point root_location(150, 150);
+  int flags = ui::EF_SHIFT_DOWN;                        // Set a random flag.
+  int changed_button_flags = ui::EF_LEFT_MOUSE_BUTTON;  // Set a random flag.
+  ui::MouseEvent event(ui::EventType::ET_MOUSE_MOVED, location, root_location,
+                       time_stamp, flags, changed_button_flags);
+  std::unique_ptr<ui::Event> rewritten_event;
+  ui::EventRewriteStatus status =
+      drag_event_rewriter_.RewriteEvent(event, &rewritten_event);
+  EXPECT_EQ(ui::EVENT_REWRITE_REWRITTEN, status);
+  // The type should be a drag.
+  ASSERT_EQ(ui::ET_MOUSE_DRAGGED, rewritten_event->type());
+
+  // Everything else should be the same as the original.
+  ui::MouseEvent* rewritten_mouse_event = rewritten_event->AsMouseEvent();
+  EXPECT_EQ(location, rewritten_mouse_event->location());
+  EXPECT_EQ(root_location, rewritten_mouse_event->root_location());
+  EXPECT_EQ(time_stamp, rewritten_mouse_event->time_stamp());
+  EXPECT_EQ(flags, rewritten_mouse_event->flags());
+  EXPECT_EQ(changed_button_flags,
+            rewritten_mouse_event->changed_button_flags());
+}
+
+}  // namespace ash
diff --git a/ash/autoclick/autoclick_unittest.cc b/ash/autoclick/autoclick_unittest.cc
index 08f5d25b..bd0d942 100644
--- a/ash/autoclick/autoclick_unittest.cc
+++ b/ash/autoclick/autoclick_unittest.cc
@@ -26,24 +26,33 @@
   void Reset() { events_.clear(); }
 
   void OnMouseEvent(ui::MouseEvent* event) override {
-    // Only track left and right mouse button events, ensuring that we get
-    // left-click, right-click and double-click.
-    if (!(event->flags() & ui::EF_LEFT_MOUSE_BUTTON) &&
-        (!(event->flags() & ui::EF_RIGHT_MOUSE_BUTTON)))
-      return;
+    bool save_event = false;
+    bool stop_event = false;
     // Filter out extraneous mouse events like mouse entered, exited,
     // capture changed, etc.
     ui::EventType type = event->type();
-    if (type == ui::ET_MOUSE_MOVED || type == ui::ET_MOUSE_PRESSED ||
-        type == ui::ET_MOUSE_RELEASED) {
+    if (type == ui::ET_MOUSE_PRESSED || type == ui::ET_MOUSE_RELEASED) {
+      // Only track left and right mouse button events, ensuring that we get
+      // left-click, right-click and double-click.
+      if (!(event->flags() & ui::EF_LEFT_MOUSE_BUTTON) &&
+          (!(event->flags() & ui::EF_RIGHT_MOUSE_BUTTON)))
+        return;
+      save_event = true;
+      // Stop event propagation so we don't click on random stuff that
+      // might break test assumptions.
+      stop_event = true;
+    } else if (type == ui::ET_MOUSE_DRAGGED) {
+      save_event = true;
+      stop_event = false;
+    }
+    if (save_event) {
       events_.push_back(ui::MouseEvent(event->type(), event->location(),
                                        event->root_location(),
                                        ui::EventTimeForNow(), event->flags(),
                                        event->changed_button_flags()));
-      // Stop event propagation so we don't click on random stuff that
-      // might break test assumptions.
-      event->StopPropagation();
     }
+    if (stop_event)
+      event->StopPropagation();
 
     // If there is a possibility that we're in an infinite loop, we should
     // exit early with a sensible error rather than letting the test time out.
@@ -89,15 +98,21 @@
   }
 
   const std::vector<ui::MouseEvent>& WaitForMouseEvents() {
-    mouse_event_capturer_.Reset();
+    ClearMouseEvents();
     RunAllPendingInMessageLoop();
-    return mouse_event_capturer_.captured_events();
+    return GetMouseEvents();
   }
 
   AutoclickController* GetAutoclickController() {
     return Shell::Get()->autoclick_controller();
   }
 
+  void ClearMouseEvents() { mouse_event_capturer_.Reset(); }
+
+  const std::vector<ui::MouseEvent>& GetMouseEvents() {
+    return mouse_event_capturer_.captured_events();
+  }
+
  private:
   MouseEventCapturer mouse_event_capturer_;
 
@@ -359,4 +374,35 @@
   EXPECT_EQ(0u, events.size());
 }
 
+TEST_F(AutoclickTest, AutoclickDragAndDropEvents) {
+  GetAutoclickController()->SetEnabled(true);
+  GetAutoclickController()->SetAutoclickEventType(
+      mojom::AutoclickEventType::kDragAndDrop);
+  std::vector<ui::MouseEvent> events;
+
+  GetEventGenerator()->MoveMouseTo(30, 30);
+  events = WaitForMouseEvents();
+  ASSERT_EQ(1u, events.size());
+  EXPECT_EQ(ui::ET_MOUSE_PRESSED, events[0].type());
+  EXPECT_TRUE(ui::EF_LEFT_MOUSE_BUTTON & events[0].flags());
+
+  ClearMouseEvents();
+  GetEventGenerator()->MoveMouseTo(60, 60);
+  events = GetMouseEvents();
+  ASSERT_EQ(1u, events.size());
+  EXPECT_EQ(ui::ET_MOUSE_DRAGGED, events[0].type());
+
+  // Another move creates a drag
+  ClearMouseEvents();
+  GetEventGenerator()->MoveMouseTo(90, 90);
+  events = GetMouseEvents();
+  ASSERT_EQ(1u, events.size());
+  EXPECT_EQ(ui::ET_MOUSE_DRAGGED, events[0].type());
+
+  // Waiting in place creates the released event.
+  events = WaitForMouseEvents();
+  ASSERT_EQ(1u, events.size());
+  EXPECT_EQ(ui::ET_MOUSE_RELEASED, events[0].type());
+}
+
 }  // namespace ash
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index e7e85a5..6d4a7b7e 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -35,9 +35,6 @@
     "LockScreenHideSensitiveNotificationsSupport",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kNewMessageListView{"NewMessageListView",
-                                        base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kMediaSessionAccelerators{
     "MediaSessionAccelerators", base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -49,9 +46,6 @@
 const base::Feature kOverviewSwipeToClose{"OverviewSwipeToClose",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kSystemTrayUnified{"SystemTrayUnified",
-                                       base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kTrilinearFiltering{"TrilinearFiltering",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -88,10 +82,6 @@
       kLockScreenHideSensitiveNotificationsSupport);
 }
 
-bool IsNewMessageListViewEnabled() {
-  return base::FeatureList::IsEnabled(kNewMessageListView);
-}
-
 bool IsNightLightEnabled() {
   return base::FeatureList::IsEnabled(kNightLight);
 }
@@ -101,7 +91,7 @@
 }
 
 bool IsSystemTrayUnifiedEnabled() {
-  return base::FeatureList::IsEnabled(kSystemTrayUnified);
+  return true;
 }
 
 bool IsTrilinearFilteringEnabled() {
diff --git a/ash/public/cpp/ash_features.h b/ash/public/cpp/ash_features.h
index 43df5bd..4534c54 100644
--- a/ash/public/cpp/ash_features.h
+++ b/ash/public/cpp/ash_features.h
@@ -54,9 +54,6 @@
 // TODO(beccahughes): Remove after launch. (https://crbug.com/894255)
 ASH_PUBLIC_EXPORT extern const base::Feature kMediaSessionAccelerators;
 
-// Enables new message list view. https://crbug.com/769219
-ASH_PUBLIC_EXPORT extern const base::Feature kNewMessageListView;
-
 // Enables the Night Light feature.
 ASH_PUBLIC_EXPORT extern const base::Feature kNightLight;
 
@@ -68,9 +65,6 @@
 // https://crbug.com/828646.
 ASH_PUBLIC_EXPORT extern const base::Feature kOverviewSwipeToClose;
 
-// Enables new system menu.
-ASH_PUBLIC_EXPORT extern const base::Feature kSystemTrayUnified;
-
 // Enables trilinear filtering.
 ASH_PUBLIC_EXPORT extern const base::Feature kTrilinearFiltering;
 
@@ -95,8 +89,6 @@
 
 ASH_PUBLIC_EXPORT bool IsLockScreenHideSensitiveNotificationsSupported();
 
-ASH_PUBLIC_EXPORT bool IsNewMessageListViewEnabled();
-
 ASH_PUBLIC_EXPORT bool IsNightLightEnabled();
 
 ASH_PUBLIC_EXPORT bool IsNotificationScrollBarEnabled();
diff --git a/ash/public/cpp/window_animation_types.h b/ash/public/cpp/window_animation_types.h
index f5a4f5a..244fa65 100644
--- a/ash/public/cpp/window_animation_types.h
+++ b/ash/public/cpp/window_animation_types.h
@@ -20,7 +20,10 @@
   WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE,
   // Window slides down from above screen to show and, meanwhile, home launcher
   // slides down off screen.
-  WINDOW_VISIBILITY_ANIMATION_TYPE_SLIDE_DOWN
+  WINDOW_VISIBILITY_ANIMATION_TYPE_SLIDE_DOWN,
+  // Animate a window out of the closest side of the screen.
+  // This is for hiding only, and does not do anything for showing.
+  WINDOW_VISIBILITY_ANIMATION_TYPE_SLIDE_OUT,
 };
 
 }  // namespace wm
diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/app_list_button.cc
index 7d4cf5b..6995f6a 100644
--- a/ash/shelf/app_list_button.cc
+++ b/ash/shelf/app_list_button.cc
@@ -54,6 +54,12 @@
          chromeos::switches::IsAssistantEnabled();
 }
 
+bool IsHomeLauncherShown() {
+  return Shell::Get()
+      ->app_list_controller()
+      ->IsHomeLauncherEnabledInTabletMode();
+}
+
 }  // namespace
 
 AppListButton::AppListButton(InkDropButtonListener* listener,
@@ -99,7 +105,7 @@
 void AppListButton::OnAppListShown() {
   // Do not show a highlight in tablet mode since the "homecher" view is always
   // open in the background.
-  if (shelf_view_->ShouldShowAppListButtonHighlight())
+  if (!IsHomeLauncherShown())
     AnimateInkDrop(views::InkDropState::ACTIVATED, nullptr);
   is_showing_app_list_ = true;
   shelf_->UpdateAutoHideState();
@@ -124,6 +130,8 @@
         assistant_overlay_->EndAnimation();
         assistant_animation_delay_timer_->Stop();
       }
+      if (IsHomeLauncherShown())
+        AnimateInkDrop(views::InkDropState::ACTION_TRIGGERED, event);
       ImageButton::OnGestureEvent(event);
       return;
     case ui::ET_GESTURE_TAP_DOWN:
@@ -135,8 +143,10 @@
             base::Bind(&AppListButton::StartVoiceInteractionAnimation,
                        base::Unretained(this)));
       }
-      if (!Shell::Get()->app_list_controller()->IsVisible())
+      if (!Shell::Get()->app_list_controller()->IsVisible() ||
+          IsHomeLauncherShown()) {
         AnimateInkDrop(views::InkDropState::ACTION_PENDING, event);
+      }
       ImageButton::OnGestureEvent(event);
       return;
     case ui::ET_GESTURE_LONG_PRESS:
diff --git a/ash/shelf/shelf.cc b/ash/shelf/shelf.cc
index ab65fc36..23910e7 100644
--- a/ash/shelf/shelf.cc
+++ b/ash/shelf/shelf.cc
@@ -209,7 +209,7 @@
              : 0;
 }
 
-gfx::Rect Shelf::GetIdealBounds() {
+gfx::Rect Shelf::GetIdealBounds() const {
   return shelf_layout_manager_->GetIdealBounds();
 }
 
@@ -294,10 +294,29 @@
   return shelf_widget_->status_area_widget();
 }
 
-TrayBackgroundView* Shelf::GetSystemTrayAnchor() const {
+TrayBackgroundView* Shelf::GetSystemTrayAnchorView() const {
   return GetStatusAreaWidget()->GetSystemTrayAnchor();
 }
 
+gfx::Rect Shelf::GetSystemTrayAnchorRect() const {
+  gfx::Rect workspace_bounds = GetUserWorkAreaBounds();
+  switch (alignment_) {
+    case SHELF_ALIGNMENT_BOTTOM:
+    case SHELF_ALIGNMENT_BOTTOM_LOCKED:
+      return gfx::Rect(base::i18n::IsRTL() ? workspace_bounds.x()
+                                           : (workspace_bounds.right() - 1),
+                       workspace_bounds.bottom() - 1, 0, 0);
+    case SHELF_ALIGNMENT_LEFT:
+      return gfx::Rect(workspace_bounds.x(), workspace_bounds.bottom() - 1, 0,
+                       0);
+    case SHELF_ALIGNMENT_RIGHT:
+      return gfx::Rect(workspace_bounds.right() - 1,
+                       workspace_bounds.bottom() - 1, 0, 0);
+  }
+  NOTREACHED();
+  return gfx::Rect();
+}
+
 bool Shelf::ShouldHideOnSecondaryDisplay(session_manager::SessionState state) {
   if (Shell::GetPrimaryRootWindowController()->shelf() == this)
     return false;
diff --git a/ash/shelf/shelf.h b/ash/shelf/shelf.h
index 9a6418d..7a21553 100644
--- a/ash/shelf/shelf.h
+++ b/ash/shelf/shelf.h
@@ -102,7 +102,7 @@
   int GetDockedMagnifierHeight() const;
 
   // Returns the ideal bounds of the shelf assuming it is visible.
-  gfx::Rect GetIdealBounds();
+  gfx::Rect GetIdealBounds() const;
 
   gfx::Rect GetUserWorkAreaBounds() const;
 
@@ -138,7 +138,13 @@
 
   // Get the tray button that the system tray bubble and the notification center
   // bubble will be anchored. See also: StatusAreaWidget::GetSystemTrayAnchor()
-  TrayBackgroundView* GetSystemTrayAnchor() const;
+  TrayBackgroundView* GetSystemTrayAnchorView() const;
+
+  // Get the anchor rect that the system tray bubble and the notification center
+  // bubble will be anchored.
+  // x() and y() designates anchor point, but width() and height() are dummy.
+  // See also: BubbleDialogDelegateView::GetBubbleBounds()
+  gfx::Rect GetSystemTrayAnchorRect() const;
 
   void set_is_tablet_mode_animation_running(bool value) {
     is_tablet_mode_animation_running_ = value;
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index b523e620..ef7dc28 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -179,6 +179,7 @@
   state_.session_state = Shell::Get()->session_controller()->GetSessionState();
   keyboard::KeyboardController::Get()->AddObserver(this);
   wallpaper_controller_observer_.Add(Shell::Get()->wallpaper_controller());
+  display::Screen::GetScreen()->AddObserver(this);
 }
 
 ShelfLayoutManager::~ShelfLayoutManager() {
@@ -187,6 +188,7 @@
 
   for (auto& observer : observers_)
     observer.WillDeleteShelfLayoutManager();
+  display::Screen::GetScreen()->RemoveObserver(this);
   keyboard::KeyboardController::Get()->RemoveObserver(this);
   Shell::Get()->RemoveShellObserver(this);
   Shell::Get()->lock_state_controller()->RemoveObserver(this);
@@ -207,7 +209,7 @@
            state_.auto_hide_state == SHELF_AUTO_HIDE_SHOWN));
 }
 
-gfx::Rect ShelfLayoutManager::GetIdealBounds() {
+gfx::Rect ShelfLayoutManager::GetIdealBounds() const {
   const int shelf_size = ShelfConstants::shelf_size();
   aura::Window* shelf_window = shelf_widget_->GetNativeWindow();
   gfx::Rect rect(screen_util::GetDisplayBoundsInParent(shelf_window));
@@ -1449,6 +1451,14 @@
   return false;
 }
 
+void ShelfLayoutManager::OnDisplayMetricsChanged(
+    const display::Display& display,
+    uint32_t changed_metrics) {
+  // Update |user_work_area_bounds_| for the new display arrangement.
+  TargetBounds target_bounds;
+  CalculateTargetBounds(state_, &target_bounds);
+}
+
 void ShelfLayoutManager::UpdateWorkspaceMask(
     wm::WorkspaceWindowState window_state) {
   // Disable the mask on NonLockScreenContainer if maximized/fullscreen window
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h
index 5f8eb7ec..29b4d46 100644
--- a/ash/shelf/shelf_layout_manager.h
+++ b/ash/shelf/shelf_layout_manager.h
@@ -20,6 +20,7 @@
 #include "base/observer_list.h"
 #include "base/scoped_observer.h"
 #include "base/timer/timer.h"
+#include "ui/display/display_observer.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/keyboard/keyboard_controller_observer.h"
@@ -52,6 +53,7 @@
       public keyboard::KeyboardControllerObserver,
       public LockStateObserver,
       public wm::WmSnapToPixelLayoutManager,
+      public display::DisplayObserver,
       public SessionObserver,
       public WallpaperControllerObserver {
  public:
@@ -80,7 +82,7 @@
   bool IsVisible() const;
 
   // Returns the ideal bounds of the shelf assuming it is visible.
-  gfx::Rect GetIdealBounds();
+  gfx::Rect GetIdealBounds() const;
 
   // Returns the preferred size of the shelf for the target visibility state.
   gfx::Size GetPreferredSize();
@@ -188,6 +190,10 @@
   void OnWallpaperBlurChanged() override;
   void OnFirstWallpaperShown() override;
 
+  // DisplayObserver:
+  void OnDisplayMetricsChanged(const display::Display& display,
+                               uint32_t changed_metrics) override;
+
   // TODO(harrym|oshima): These templates will be moved to a new Shelf class.
   // A helper function for choosing values specific to a shelf alignment.
   template <typename T>
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 52e111a..5d1fb01 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -456,12 +456,6 @@
   return overflow_bubble_.get() && overflow_bubble_->IsShowing();
 }
 
-bool ShelfView::ShouldShowAppListButtonHighlight() const {
-  // In tablet mode, the app list is always shown as the home-cher, so we do
-  // not want to show a permanent highlight.
-  return !IsTabletModeEnabled();
-}
-
 AppListButton* ShelfView::GetAppListButton() const {
   for (int i = 0; i < model_->item_count(); ++i) {
     if (model_->items()[i].type == TYPE_APP_LIST) {
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index 22ca42d..b9c0993 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -141,10 +141,6 @@
   // Returns true if overflow bubble is shown.
   bool IsShowingOverflowBubble() const;
 
-  // Whether we should show a visual highlight on the app list button when
-  // the app list is shown.
-  bool ShouldShowAppListButtonHighlight() const;
-
   // Sets owner overflow bubble instance from which this shelf view pops
   // out as overflow.
   void set_owner_overflow_bubble(OverflowBubble* owner) {
diff --git a/ash/shell.cc b/ash/shell.cc
index a7c733be..d7a360a 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -832,6 +832,7 @@
   // These need a valid Shell instance to clean up properly, so explicitly
   // delete them before invalidating the instance.
   // Alphabetical. TODO(oshima): sort.
+  autoclick_controller_.reset();
   magnification_controller_.reset();
   tooltip_controller_.reset();
   event_client_.reset();
diff --git a/ash/system/bluetooth/tray_bluetooth_helper_experimental.cc b/ash/system/bluetooth/tray_bluetooth_helper_experimental.cc
index 1783f62..5efb7d2 100644
--- a/ash/system/bluetooth/tray_bluetooth_helper_experimental.cc
+++ b/ash/system/bluetooth/tray_bluetooth_helper_experimental.cc
@@ -82,4 +82,9 @@
   Shell::Get()->system_tray_notifier()->NotifyRefreshBluetooth();
 }
 
+void TrayBluetoothHelperExperimental::OnScanStateChanged(
+    device::mojom::BluetoothSystem::ScanState state) {
+  Shell::Get()->system_tray_notifier()->NotifyBluetoothDiscoveringChanged();
+}
+
 }  // namespace ash
diff --git a/ash/system/bluetooth/tray_bluetooth_helper_experimental.h b/ash/system/bluetooth/tray_bluetooth_helper_experimental.h
index d52478d..2db5251 100644
--- a/ash/system/bluetooth/tray_bluetooth_helper_experimental.h
+++ b/ash/system/bluetooth/tray_bluetooth_helper_experimental.h
@@ -41,6 +41,8 @@
 
   // device::mojom::BluetoothSystemClient
   void OnStateChanged(device::mojom::BluetoothSystem::State state) override;
+  void OnScanStateChanged(
+      device::mojom::BluetoothSystem::ScanState state) override;
 
  private:
   service_manager::Connector* connector_;
diff --git a/ash/system/message_center/arc/arc_notification_view.cc b/ash/system/message_center/arc/arc_notification_view.cc
index 3a0181e..cdb050c 100644
--- a/ash/system/message_center/arc/arc_notification_view.cc
+++ b/ash/system/message_center/arc/arc_notification_view.cc
@@ -162,6 +162,7 @@
 }
 
 void ArcNotificationView::OnSlideChanged() {
+  MessageView::OnSlideChanged();
   content_view_->OnSlideChanged();
 }
 
diff --git a/ash/system/message_center/notification_swipe_control_view.cc b/ash/system/message_center/notification_swipe_control_view.cc
index 0e800237..19abc73 100644
--- a/ash/system/message_center/notification_swipe_control_view.cc
+++ b/ash/system/message_center/notification_swipe_control_view.cc
@@ -77,6 +77,14 @@
   bool has_settings_button = buttons->settings_button();
   bool has_snooze_button = buttons->snooze_button();
   ShowButtons(button_position, has_settings_button, has_snooze_button);
+
+  int control_button_count =
+      (has_settings_button ? 1 : 0) + (has_snooze_button ? 1 : 0);
+  int control_button_width =
+      message_center::kSwipeControlButtonSize * control_button_count +
+      message_center::kSwipeControlButtonHorizontalMargin *
+          (control_button_count + 1);
+  message_view_->SetSlideButtonWidth(control_button_width);
 }
 
 void NotificationSwipeControlView::UpdateCornerRadius(int top_radius,
diff --git a/ash/system/message_center/notification_tray.cc b/ash/system/message_center/notification_tray.cc
index 84ff8a7..8edae620 100644
--- a/ash/system/message_center/notification_tray.cc
+++ b/ash/system/message_center/notification_tray.cc
@@ -368,7 +368,7 @@
   // horizontal (i.e. bottom) shelves, anchor to the system tray.
   TrayBackgroundView* anchor_tray = this;
   if (shelf()->IsHorizontalAlignment())
-    anchor_tray = shelf()->GetSystemTrayAnchor();
+    anchor_tray = shelf()->GetSystemTrayAnchorView();
 
   message_center_bubble_ = std::make_unique<NotificationBubbleWrapper>(
       this, anchor_tray, message_center_bubble, show_by_click);
@@ -433,7 +433,7 @@
     const gfx::Rect& new_bounds) {
   TrayBackgroundView::UpdateAfterRootWindowBoundsChange(old_bounds, new_bounds);
   // Hide the message center bubble, since the bounds may not have enough to
-  // show the current size of the message center. This handler is invoked when
+  // show the current size of the message center. This  handler is invoked when
   // the screen is rotated or the screen size is changed.
   message_center_ui_controller_->HideMessageCenterBubble();
 }
@@ -441,7 +441,7 @@
 void NotificationTray::AnchorUpdated() {
   if (message_center_bubble()) {
     UpdateClippingWindowBounds();
-    shelf()->GetSystemTrayAnchor()->UpdateClippingWindowBounds();
+    shelf()->GetSystemTrayAnchorView()->UpdateClippingWindowBounds();
     message_center_bubble()->bubble_view()->UpdateBubble();
     // Should check |message_center_bubble_| again here. Since UpdateBubble
     // above set the bounds of the bubble which will stop the current
diff --git a/ash/system/message_center/new_unified_message_center_view.cc b/ash/system/message_center/unified_message_center_view.cc
similarity index 85%
rename from ash/system/message_center/new_unified_message_center_view.cc
rename to ash/system/message_center/unified_message_center_view.cc
index 58d66dc..dd48a96 100644
--- a/ash/system/message_center/new_unified_message_center_view.cc
+++ b/ash/system/message_center/unified_message_center_view.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 "ash/system/message_center/new_unified_message_center_view.h"
+#include "ash/system/message_center/unified_message_center_view.h"
 
+#include "ash/public/cpp/ash_features.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -108,7 +109,7 @@
   views::View::OnPaint(canvas);
 }
 
-NewUnifiedMessageCenterView::NewUnifiedMessageCenterView(
+UnifiedMessageCenterView::UnifiedMessageCenterView(
     UnifiedSystemTrayView* parent,
     UnifiedSystemTrayModel* model)
     : parent_(parent),
@@ -132,15 +133,15 @@
   UpdateVisibility();
 }
 
-NewUnifiedMessageCenterView::~NewUnifiedMessageCenterView() {
+UnifiedMessageCenterView::~UnifiedMessageCenterView() {
   RemovedFromWidget();
 }
 
-void NewUnifiedMessageCenterView::SetMaxHeight(int max_height) {
+void UnifiedMessageCenterView::SetMaxHeight(int max_height) {
   scroller_->ClipHeightTo(0, max_height);
 }
 
-void NewUnifiedMessageCenterView::ListPreferredSizeChanged() {
+void UnifiedMessageCenterView::ListPreferredSizeChanged() {
   UpdateVisibility();
   PreferredSizeChanged();
   ScrollToPositionFromBottom();
@@ -150,25 +151,25 @@
     GetWidget()->SynthesizeMouseMoveEvent();
 }
 
-void NewUnifiedMessageCenterView::ConfigureMessageView(
+void UnifiedMessageCenterView::ConfigureMessageView(
     message_center::MessageView* message_view) {
   message_view->set_scroller(scroller_);
 }
 
-void NewUnifiedMessageCenterView::AddedToWidget() {
+void UnifiedMessageCenterView::AddedToWidget() {
   focus_manager_ = GetFocusManager();
   if (focus_manager_)
     focus_manager_->AddFocusChangeListener(this);
 }
 
-void NewUnifiedMessageCenterView::RemovedFromWidget() {
+void UnifiedMessageCenterView::RemovedFromWidget() {
   if (!focus_manager_)
     return;
   focus_manager_->RemoveFocusChangeListener(this);
   focus_manager_ = nullptr;
 }
 
-void NewUnifiedMessageCenterView::Layout() {
+void UnifiedMessageCenterView::Layout() {
   stacking_counter_->SetCount(GetStackedNotificationCount());
   if (stacking_counter_->visible()) {
     gfx::Rect counter_bounds(GetContentsBounds());
@@ -187,14 +188,14 @@
   NotifyHeightBelowScroll();
 }
 
-gfx::Size NewUnifiedMessageCenterView::CalculatePreferredSize() const {
+gfx::Size UnifiedMessageCenterView::CalculatePreferredSize() const {
   gfx::Size preferred_size = scroller_->GetPreferredSize();
   // Hide Clear All button at the buttom from initial viewport.
   preferred_size.set_height(preferred_size.height() - kClearAllButtonRowHeight);
   return preferred_size;
 }
 
-void NewUnifiedMessageCenterView::OnMessageCenterScrolled() {
+void UnifiedMessageCenterView::OnMessageCenterScrolled() {
   position_from_bottom_ =
       scroll_bar_->GetMaxPosition() - scroller_->GetVisibleRect().y();
 
@@ -211,8 +212,8 @@
   NotifyHeightBelowScroll();
 }
 
-void NewUnifiedMessageCenterView::ButtonPressed(views::Button* sender,
-                                                const ui::Event& event) {
+void UnifiedMessageCenterView::ButtonPressed(views::Button* sender,
+                                             const ui::Event& event) {
   base::RecordAction(
       base::UserMetricsAction("StatusArea_Notifications_ClearAll"));
 
@@ -224,31 +225,32 @@
   message_list_view_->set_enable_animation(true);
 }
 
-void NewUnifiedMessageCenterView::OnWillChangeFocus(views::View* before,
-                                                    views::View* now) {}
+void UnifiedMessageCenterView::OnWillChangeFocus(views::View* before,
+                                                 views::View* now) {}
 
-void NewUnifiedMessageCenterView::OnDidChangeFocus(views::View* before,
-                                                   views::View* now) {
+void UnifiedMessageCenterView::OnDidChangeFocus(views::View* before,
+                                                views::View* now) {
   OnMessageCenterScrolled();
 }
 
-void NewUnifiedMessageCenterView::SetNotificationHeightBelowScroll(
+void UnifiedMessageCenterView::SetNotificationHeightBelowScroll(
     int height_below_scroll) {
   parent_->SetNotificationHeightBelowScroll(height_below_scroll);
 }
 
-void NewUnifiedMessageCenterView::UpdateVisibility() {
+void UnifiedMessageCenterView::UpdateVisibility() {
   SessionController* session_controller = Shell::Get()->session_controller();
   SetVisible(message_list_view_->GetPreferredSize().height() > 0 &&
              session_controller->ShouldShowNotificationTray() &&
-             !session_controller->IsScreenLocked());
+             (!session_controller->IsScreenLocked() ||
+              features::IsLockScreenNotificationsEnabled()));
   // When notification list went invisible, |position_from_bottom_| should be
   // reset.
   if (!visible())
     position_from_bottom_ = kClearAllButtonRowHeight;
 }
 
-void NewUnifiedMessageCenterView::ScrollToPositionFromBottom() {
+void UnifiedMessageCenterView::ScrollToPositionFromBottom() {
   // Following logic doesn't work when the view is invisible, because it uses
   // the height of |scroller_|.
   if (!visible())
@@ -271,21 +273,21 @@
       scroll_bar_, scroll_bar_->GetMaxPosition() - position_from_bottom_);
 }
 
-int NewUnifiedMessageCenterView::GetStackedNotificationCount() const {
+int UnifiedMessageCenterView::GetStackedNotificationCount() const {
   // CountNotificationsAboveY() only works after SetBoundsRect() is called at
   // least once.
   if (scroller_->bounds().IsEmpty())
     scroller_->SetBoundsRect(GetContentsBounds());
 
   // Consistently use the y offset absolutely kStackingNotificationCounterHeight
-  // below the top of NewUnifiedMessageCenterView to count number of hidden
+  // below the top of UnifiedMessageCenterView to count number of hidden
   // notifications.
   const int y_offset = scroller_->GetVisibleRect().y() - scroller_->y() +
                        kStackingNotificationCounterHeight;
   return message_list_view_->CountNotificationsAboveY(y_offset);
 }
 
-void NewUnifiedMessageCenterView::NotifyHeightBelowScroll() {
+void UnifiedMessageCenterView::NotifyHeightBelowScroll() {
   SetNotificationHeightBelowScroll(std::max(
       0, message_list_view_->height() - scroller_->GetVisibleRect().bottom()));
 }
diff --git a/ash/system/message_center/new_unified_message_center_view.h b/ash/system/message_center/unified_message_center_view.h
similarity index 85%
rename from ash/system/message_center/new_unified_message_center_view.h
rename to ash/system/message_center/unified_message_center_view.h
index 7f19f10..ea70b1c 100644
--- a/ash/system/message_center/new_unified_message_center_view.h
+++ b/ash/system/message_center/unified_message_center_view.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_SYSTEM_MESSAGE_CENTER_NEW_UNIFIED_MESSAGE_CENTER_VIEW_H_
-#define ASH_SYSTEM_MESSAGE_CENTER_NEW_UNIFIED_MESSAGE_CENTER_VIEW_H_
+#ifndef ASH_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_CENTER_VIEW_H_
+#define ASH_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_CENTER_VIEW_H_
 
 #include "ash/ash_export.h"
 #include "ash/system/message_center/message_center_scroll_bar.h"
@@ -41,15 +41,15 @@
 
 // Manages scrolling of notification list.
 // TODO(tetsui): Rename to UnifiedMessageCenterView after old code is removed.
-class ASH_EXPORT NewUnifiedMessageCenterView
+class ASH_EXPORT UnifiedMessageCenterView
     : public views::View,
       public MessageCenterScrollBar::Observer,
       public views::ButtonListener,
       public views::FocusChangeListener {
  public:
-  NewUnifiedMessageCenterView(UnifiedSystemTrayView* parent,
-                              UnifiedSystemTrayModel* model);
-  ~NewUnifiedMessageCenterView() override;
+  UnifiedMessageCenterView(UnifiedSystemTrayView* parent,
+                           UnifiedSystemTrayModel* model);
+  ~UnifiedMessageCenterView() override;
 
   // Sets the maximum height that the view can take.
   void SetMaxHeight(int max_height);
@@ -82,7 +82,7 @@
   virtual void SetNotificationHeightBelowScroll(int height_below_scroll);
 
  private:
-  friend class NewUnifiedMessageCenterViewTest;
+  friend class UnifiedMessageCenterViewTest;
 
   void UpdateVisibility();
 
@@ -107,9 +107,9 @@
 
   views::FocusManager* focus_manager_ = nullptr;
 
-  DISALLOW_COPY_AND_ASSIGN(NewUnifiedMessageCenterView);
+  DISALLOW_COPY_AND_ASSIGN(UnifiedMessageCenterView);
 };
 
 }  // namespace ash
 
-#endif  // ASH_SYSTEM_MESSAGE_CENTER_NEW_UNIFIED_MESSAGE_CENTER_VIEW_H_
+#endif  // ASH_SYSTEM_MESSAGE_CENTER_UNIFIED_MESSAGE_CENTER_VIEW_H_
diff --git a/ash/system/message_center/new_unified_message_center_view_unittest.cc b/ash/system/message_center/unified_message_center_view_unittest.cc
similarity index 88%
rename from ash/system/message_center/new_unified_message_center_view_unittest.cc
rename to ash/system/message_center/unified_message_center_view_unittest.cc
index bd7dd9cc..3f328eab 100644
--- a/ash/system/message_center/new_unified_message_center_view_unittest.cc
+++ b/ash/system/message_center/unified_message_center_view_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 "ash/system/message_center/new_unified_message_center_view.h"
+#include "ash/system/message_center/unified_message_center_view.h"
 
 #include "ash/system/message_center/message_center_scroll_bar.h"
 #include "ash/system/tray/tray_constants.h"
@@ -32,12 +32,12 @@
   ~DummyEvent() override = default;
 };
 
-class TestNewUnifiedMessageCenterView : public NewUnifiedMessageCenterView {
+class TestUnifiedMessageCenterView : public UnifiedMessageCenterView {
  public:
-  explicit TestNewUnifiedMessageCenterView(UnifiedSystemTrayModel* model)
-      : NewUnifiedMessageCenterView(nullptr, model) {}
+  explicit TestUnifiedMessageCenterView(UnifiedSystemTrayModel* model)
+      : UnifiedMessageCenterView(nullptr, model) {}
 
-  ~TestNewUnifiedMessageCenterView() override = default;
+  ~TestUnifiedMessageCenterView() override = default;
 
   void SetNotificationHeightBelowScroll(int height_below_scroll) override {
     height_below_scroll_ = height_below_scroll;
@@ -48,16 +48,16 @@
  private:
   int height_below_scroll_ = -1;
 
-  DISALLOW_COPY_AND_ASSIGN(TestNewUnifiedMessageCenterView);
+  DISALLOW_COPY_AND_ASSIGN(TestUnifiedMessageCenterView);
 };
 
 }  // namespace
 
-class NewUnifiedMessageCenterViewTest : public AshTestBase,
-                                        public views::ViewObserver {
+class UnifiedMessageCenterViewTest : public AshTestBase,
+                                     public views::ViewObserver {
  public:
-  NewUnifiedMessageCenterViewTest() = default;
-  ~NewUnifiedMessageCenterViewTest() override = default;
+  UnifiedMessageCenterViewTest() = default;
+  ~UnifiedMessageCenterViewTest() override = default;
 
   // AshTestBase:
   void SetUp() override {
@@ -95,7 +95,7 @@
 
   void CreateMessageCenterView(int max_height = kDefaultMaxHeight) {
     message_center_view_ =
-        std::make_unique<TestNewUnifiedMessageCenterView>(model_.get());
+        std::make_unique<TestUnifiedMessageCenterView>(model_.get());
     message_center_view_->AddObserver(this);
     message_center_view_->SetMaxHeight(max_height);
     OnViewPreferredSizeChanged(message_center_view_.get());
@@ -140,7 +140,7 @@
     return message_center_view()->stacking_counter_;
   }
 
-  TestNewUnifiedMessageCenterView* message_center_view() {
+  TestUnifiedMessageCenterView* message_center_view() {
     return message_center_view_.get();
   }
 
@@ -151,12 +151,12 @@
   int size_changed_count_ = 0;
 
   std::unique_ptr<UnifiedSystemTrayModel> model_;
-  std::unique_ptr<TestNewUnifiedMessageCenterView> message_center_view_;
+  std::unique_ptr<TestUnifiedMessageCenterView> message_center_view_;
 
-  DISALLOW_COPY_AND_ASSIGN(NewUnifiedMessageCenterViewTest);
+  DISALLOW_COPY_AND_ASSIGN(UnifiedMessageCenterViewTest);
 };
 
-TEST_F(NewUnifiedMessageCenterViewTest, AddAndRemoveNotification) {
+TEST_F(UnifiedMessageCenterViewTest, AddAndRemoveNotification) {
   CreateMessageCenterView();
   EXPECT_FALSE(message_center_view()->visible());
 
@@ -174,7 +174,7 @@
   EXPECT_FALSE(message_center_view()->visible());
 }
 
-TEST_F(NewUnifiedMessageCenterViewTest, ContentsRelayout) {
+TEST_F(UnifiedMessageCenterViewTest, ContentsRelayout) {
   std::vector<std::string> ids;
   for (size_t i = 0; i < 10; ++i)
     ids.push_back(AddNotification());
@@ -193,7 +193,7 @@
   EXPECT_GT(previous_list_height, GetMessageListView()->height());
 }
 
-TEST_F(NewUnifiedMessageCenterViewTest, NotVisibleWhenLocked) {
+TEST_F(UnifiedMessageCenterViewTest, NotVisibleWhenLocked) {
   AddNotification();
   AddNotification();
 
@@ -203,7 +203,7 @@
   EXPECT_FALSE(message_center_view()->visible());
 }
 
-TEST_F(NewUnifiedMessageCenterViewTest, ClearAllPressed) {
+TEST_F(UnifiedMessageCenterViewTest, ClearAllPressed) {
   AddNotification();
   AddNotification();
   CreateMessageCenterView();
@@ -232,7 +232,7 @@
   EXPECT_FALSE(message_center_view()->visible());
 }
 
-TEST_F(NewUnifiedMessageCenterViewTest, InitialPosition) {
+TEST_F(UnifiedMessageCenterViewTest, InitialPosition) {
   AddNotification();
   AddNotification();
   CreateMessageCenterView();
@@ -247,7 +247,7 @@
                 GetMessageViewVisibleBounds(1).bottom());
 }
 
-TEST_F(NewUnifiedMessageCenterViewTest, InitialPositionMaxOut) {
+TEST_F(UnifiedMessageCenterViewTest, InitialPositionMaxOut) {
   for (size_t i = 0; i < 6; ++i)
     AddNotification();
   CreateMessageCenterView();
@@ -262,7 +262,7 @@
                 GetMessageViewVisibleBounds(5).bottom());
 }
 
-TEST_F(NewUnifiedMessageCenterViewTest, InitialPositionWithLargeNotification) {
+TEST_F(UnifiedMessageCenterViewTest, InitialPositionWithLargeNotification) {
   AddNotification();
   AddNotification();
   CreateMessageCenterView(100 /* max_height */);
@@ -277,7 +277,7 @@
   EXPECT_EQ(0, message_view_bounds.y());
 }
 
-TEST_F(NewUnifiedMessageCenterViewTest, ScrollPositionWhenResized) {
+TEST_F(UnifiedMessageCenterViewTest, ScrollPositionWhenResized) {
   for (size_t i = 0; i < 6; ++i)
     AddNotification();
   CreateMessageCenterView();
@@ -308,7 +308,7 @@
             GetScroller()->GetVisibleRect().bottom());
 }
 
-TEST_F(NewUnifiedMessageCenterViewTest, StackingCounterLayout) {
+TEST_F(UnifiedMessageCenterViewTest, StackingCounterLayout) {
   for (size_t i = 0; i < 6; ++i)
     AddNotification();
   CreateMessageCenterView();
@@ -331,7 +331,7 @@
   EXPECT_EQ(0, GetScroller()->bounds().y());
 }
 
-TEST_F(NewUnifiedMessageCenterViewTest,
+TEST_F(UnifiedMessageCenterViewTest,
        StackingCounterNotAffectingMessageViewBounds) {
   for (size_t i = 0; i < 6; ++i)
     AddNotification();
@@ -364,8 +364,7 @@
   EXPECT_FALSE(GetStackingCounter()->visible());
 }
 
-TEST_F(NewUnifiedMessageCenterViewTest,
-       StackingCounterRemovedWithNotifications) {
+TEST_F(UnifiedMessageCenterViewTest, StackingCounterRemovedWithNotifications) {
   std::vector<std::string> ids;
   for (size_t i = 0; i < 6; ++i)
     ids.push_back(AddNotification());
@@ -384,7 +383,7 @@
   EXPECT_FALSE(GetStackingCounter()->visible());
 }
 
-TEST_F(NewUnifiedMessageCenterViewTest, HeightBelowScroll) {
+TEST_F(UnifiedMessageCenterViewTest, HeightBelowScroll) {
   for (size_t i = 0; i < 6; ++i)
     AddNotification();
   CreateMessageCenterView();
diff --git a/ash/system/message_center/unified_message_list_view.cc b/ash/system/message_center/unified_message_list_view.cc
index 15d5d41..0e5d6cf 100644
--- a/ash/system/message_center/unified_message_list_view.cc
+++ b/ash/system/message_center/unified_message_list_view.cc
@@ -4,8 +4,8 @@
 
 #include "ash/system/message_center/unified_message_list_view.h"
 
-#include "ash/system/message_center/new_unified_message_center_view.h"
 #include "ash/system/message_center/notification_swipe_control_view.h"
+#include "ash/system/message_center/unified_message_center_view.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/unified_system_tray_model.h"
 #include "base/auto_reset.h"
@@ -145,7 +145,7 @@
 };
 
 UnifiedMessageListView::UnifiedMessageListView(
-    NewUnifiedMessageCenterView* message_center_view,
+    UnifiedMessageCenterView* message_center_view,
     UnifiedSystemTrayModel* model)
     : message_center_view_(message_center_view),
       model_(model),
@@ -376,17 +376,23 @@
 }
 
 void UnifiedMessageListView::DeleteRemovedNotifications() {
+  std::vector<MessageViewContainer*> removed_views;
   for (int i = 0; i < child_count(); ++i) {
     auto* view = GetContainer(i);
-    if (view->is_removed()) {
-      model_->RemoveNotificationExpanded(view->GetNotificationId());
-      delete view;
-    }
+    if (view->is_removed())
+      removed_views.push_back(view);
+  }
+
+  for (auto* view : removed_views) {
+    model_->RemoveNotificationExpanded(view->GetNotificationId());
+    delete view;
   }
 }
 
 double UnifiedMessageListView::GetCurrentValue() const {
-  return gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT,
+  return gfx::Tween::CalculateValue(state_ == State::SLIDE_OUT
+                                        ? gfx::Tween::EASE_IN
+                                        : gfx::Tween::FAST_OUT_SLOW_IN,
                                     animation_->GetCurrentValue());
 }
 
diff --git a/ash/system/message_center/unified_message_list_view.h b/ash/system/message_center/unified_message_list_view.h
index 350228af..6331e71 100644
--- a/ash/system/message_center/unified_message_list_view.h
+++ b/ash/system/message_center/unified_message_list_view.h
@@ -22,11 +22,11 @@
 
 namespace ash {
 
-class NewUnifiedMessageCenterView;
+class UnifiedMessageCenterView;
 class UnifiedSystemTrayModel;
 
 // Manages list of notifications. The class doesn't know about the ScrollView
-// it's enclosed. This class is used only from NewUnifiedMessageCenterView.
+// it's enclosed. This class is used only from UnifiedMessageCenterView.
 class ASH_EXPORT UnifiedMessageListView
     : public views::View,
       public message_center::MessageCenterObserver,
@@ -34,7 +34,7 @@
       public gfx::AnimationDelegate {
  public:
   // |message_center_view| can be null in unit tests.
-  UnifiedMessageListView(NewUnifiedMessageCenterView* message_center_view,
+  UnifiedMessageListView(UnifiedMessageCenterView* message_center_view,
                          UnifiedSystemTrayModel* model);
   ~UnifiedMessageListView() override;
 
@@ -79,7 +79,7 @@
       const message_center::Notification& notification);
 
  private:
-  friend class NewUnifiedMessageCenterViewTest;
+  friend class UnifiedMessageCenterViewTest;
   friend class UnifiedMessageListViewTest;
   class MessageViewContainer;
 
@@ -123,7 +123,7 @@
   // Deletes all the MessageViewContainer marked as |is_removed|.
   void DeleteRemovedNotifications();
 
-  NewUnifiedMessageCenterView* const message_center_view_;
+  UnifiedMessageCenterView* const message_center_view_;
   UnifiedSystemTrayModel* const model_;
 
   // If true, ChildPreferredSizeChanged() will be ignored. This is used in
diff --git a/ash/system/status_area_widget_unittest.cc b/ash/system/status_area_widget_unittest.cc
index 63cf084e..533677e 100644
--- a/ash/system/status_area_widget_unittest.cc
+++ b/ash/system/status_area_widget_unittest.cc
@@ -23,7 +23,6 @@
 #include "ash/system/virtual_keyboard/virtual_keyboard_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "base/command_line.h"
-#include "base/test/scoped_feature_list.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/network/network_handler.h"
 #include "components/prefs/testing_pref_service.h"
@@ -299,8 +298,6 @@
 
   // AshTestBase:
   void SetUp() override {
-    scoped_feature_list_.InitAndEnableFeature(features::kSystemTrayUnified);
-
     chromeos::DBusThreadManager::Initialize();
     // Initializing NetworkHandler before ash is more like production.
     chromeos::NetworkHandler::Initialize();
@@ -320,8 +317,6 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   TestingPrefServiceSimple profile_prefs_;
   TestingPrefServiceSimple local_state_;
 
diff --git a/ash/system/tracing_notification_controller_unittest.cc b/ash/system/tracing_notification_controller_unittest.cc
index 3bbaa73..5dff50c 100644
--- a/ash/system/tracing_notification_controller_unittest.cc
+++ b/ash/system/tracing_notification_controller_unittest.cc
@@ -4,11 +4,9 @@
 
 #include "ash/system/tracing_notification_controller.h"
 
-#include "ash/public/cpp/ash_features.h"
 #include "ash/shell.h"
 #include "ash/system/model/system_tray_model.h"
 #include "ash/test/ash_test_base.h"
-#include "base/test/scoped_feature_list.h"
 #include "ui/message_center/message_center.h"
 
 namespace ash {
@@ -18,11 +16,6 @@
   TracingNotificationControllerTest() = default;
   ~TracingNotificationControllerTest() override = default;
 
-  void SetUp() override {
-    scoped_feature_list_.InitAndEnableFeature(features::kSystemTrayUnified);
-    AshTestBase::SetUp();
-  }
-
  protected:
   bool HasNotification() {
     return message_center::MessageCenter::Get()->FindVisibleNotificationById(
@@ -30,8 +23,6 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   DISALLOW_COPY_AND_ASSIGN(TracingNotificationControllerTest);
 };
 
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index dbbcf1b5..f4bf597 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -459,8 +459,8 @@
 
     system_bubble_ = std::make_unique<SystemBubbleWrapper>();
     system_bubble_->InitView(
-        this, shelf()->GetSystemTrayAnchor()->GetBubbleAnchor(),
-        shelf()->GetSystemTrayAnchor()->GetBubbleAnchorInsets(), items,
+        this, shelf()->GetSystemTrayAnchorView()->GetBubbleAnchor(),
+        shelf()->GetSystemTrayAnchorView()->GetBubbleAnchorInsets(), items,
         system_tray_type, &init_params, persistent);
 
     // Record metrics for the system menu when the default view is invoked.
diff --git a/ash/system/tray/tray_bubble_view.cc b/ash/system/tray/tray_bubble_view.cc
index 162dfc02d..1ae91dd 100644
--- a/ash/system/tray/tray_bubble_view.cc
+++ b/ash/system/tray/tray_bubble_view.cc
@@ -213,7 +213,9 @@
       mouse_actively_entered_(false) {
   DCHECK(delegate_);
   DCHECK(params_.parent_window);
-  DCHECK(anchor_widget());  // Computed by BubbleDialogDelegateView().
+  // anchor_widget() is computed by BubbleDialogDelegateView().
+  DCHECK(((init_params.anchor_mode != TrayBubbleView::AnchorMode::kView) ||
+          anchor_widget()));
   bubble_border_->set_use_theme_background_color(!init_params.bg_color);
   if (init_params.corner_radius)
     bubble_border_->SetCornerRadius(init_params.corner_radius.value());
@@ -231,6 +233,11 @@
   auto layout = std::make_unique<BottomAlignedBoxLayout>(this);
   layout->SetDefaultFlex(1);
   layout_ = SetLayoutManager(std::move(layout));
+
+  if (init_params.anchor_mode == AnchorMode::kRect) {
+    SetAnchorView(nullptr);
+    SetAnchorRect(init_params.anchor_rect);
+  }
 }
 
 TrayBubbleView::~TrayBubbleView() {
@@ -306,9 +313,20 @@
 }
 
 void TrayBubbleView::ChangeAnchorView(views::View* anchor_view) {
+  DCHECK_EQ(AnchorMode::kView, params_.anchor_mode);
   BubbleDialogDelegateView::SetAnchorView(anchor_view);
 }
 
+void TrayBubbleView::ChangeAnchorRect(const gfx::Rect& rect) {
+  DCHECK_EQ(AnchorMode::kRect, params_.anchor_mode);
+  BubbleDialogDelegateView::SetAnchorRect(rect);
+}
+
+void TrayBubbleView::ChangeAnchorAlignment(
+    TrayBubbleView::AnchorAlignment alignment) {
+  SetArrow(GetArrowAlignment(alignment));
+}
+
 int TrayBubbleView::GetDialogButtons() const {
   return ui::DIALOG_BUTTON_NONE;
 }
diff --git a/ash/system/tray/tray_bubble_view.h b/ash/system/tray/tray_bubble_view.h
index 22954c9..f3c78eabf 100644
--- a/ash/system/tray/tray_bubble_view.h
+++ b/ash/system/tray/tray_bubble_view.h
@@ -12,6 +12,7 @@
 #include "base/optional.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/events/event.h"
+#include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/mouse_watcher.h"
@@ -85,12 +86,23 @@
     DISALLOW_COPY_AND_ASSIGN(Delegate);
   };
 
+  // Anchor mode being set at creation.
+  enum class AnchorMode {
+    // Anchor to |anchor_view|. This is the default.
+    kView,
+    // Anchor to |anchor_rect|. Used for anchoring to the shelf.
+    kRect
+  };
+
   struct ASH_EXPORT InitParams {
     InitParams();
     InitParams(const InitParams& other);
     Delegate* delegate = nullptr;
     gfx::NativeWindow parent_window = nullptr;
     View* anchor_view = nullptr;
+    AnchorMode anchor_mode = AnchorMode::kView;
+    // Only used if anchor_mode == AnchorMode::kRect.
+    gfx::Rect anchor_rect;
     AnchorAlignment anchor_alignment = ANCHOR_ALIGNMENT_BOTTOM;
     int min_width = 0;
     int max_width = 0;
@@ -135,8 +147,16 @@
   void ResetDelegate();
 
   // Anchors the bubble to |anchor_view|.
+  // Only eligible if anchor_mode == AnchorMode::kView.
   void ChangeAnchorView(views::View* anchor_view);
 
+  // Anchors the bubble to |anchor_rect|. Exclusive with ChangeAnchorView().
+  // Only eligible if anchor_mode == AnchorMode::kRect.
+  void ChangeAnchorRect(const gfx::Rect& anchor_rect);
+
+  // Change anchor alignment mode when anchoring either the rect or view.
+  void ChangeAnchorAlignment(AnchorAlignment alignment);
+
   Delegate* delegate() { return delegate_; }
 
   void set_gesture_dragging(bool dragging) { is_gesture_dragging_ = dragging; }
diff --git a/ash/system/unified/unified_slider_bubble_controller.cc b/ash/system/unified/unified_slider_bubble_controller.cc
index 3ee8d940..79150c5 100644
--- a/ash/system/unified/unified_slider_bubble_controller.cc
+++ b/ash/system/unified/unified_slider_bubble_controller.cc
@@ -165,7 +165,7 @@
   init_params.delegate = this;
   init_params.parent_window = tray_->GetBubbleWindowContainer();
   init_params.anchor_view =
-      tray_->shelf()->GetSystemTrayAnchor()->GetBubbleAnchor();
+      tray_->shelf()->GetSystemTrayAnchorView()->GetBubbleAnchor();
   init_params.corner_radius = kUnifiedTrayCornerRadius;
   init_params.has_shadow = false;
 
diff --git a/ash/system/unified/unified_system_tray_bubble.cc b/ash/system/unified/unified_system_tray_bubble.cc
index a6ca4b62..c48a5a44 100644
--- a/ash/system/unified/unified_system_tray_bubble.cc
+++ b/ash/system/unified/unified_system_tray_bubble.cc
@@ -65,7 +65,7 @@
     UnifiedSystemTray* tray,
     TrayBubbleView* bubble_view) {
   gfx::Insets anchor_insets =
-      tray->shelf()->GetSystemTrayAnchor()->GetBubbleAnchorInsets();
+      tray->shelf()->GetSystemTrayAnchorView()->GetBubbleAnchorInsets();
   gfx::Insets bubble_insets = bubble_view->GetBorderInsets();
   if (tray->shelf()->IsHorizontalAlignment()) {
     anchor_insets -=
@@ -92,8 +92,9 @@
   init_params.max_width = kTrayMenuWidth;
   init_params.delegate = tray;
   init_params.parent_window = tray->GetBubbleWindowContainer();
-  init_params.anchor_view =
-      tray->shelf()->GetSystemTrayAnchor()->GetBubbleAnchor();
+  init_params.anchor_view = nullptr;
+  init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
+  init_params.anchor_rect = tray->shelf()->GetSystemTrayAnchorRect();
   init_params.corner_radius = kUnifiedTrayCornerRadius;
   init_params.has_shadow = false;
   init_params.show_by_click = show_by_click;
@@ -240,7 +241,7 @@
 
 int UnifiedSystemTrayBubble::CalculateMaxHeight() const {
   gfx::Rect anchor_bounds =
-      tray_->shelf()->GetSystemTrayAnchor()->GetBoundsInScreen();
+      tray_->shelf()->GetSystemTrayAnchorView()->GetBoundsInScreen();
   int bottom = tray_->shelf()->IsHorizontalAlignment() ? anchor_bounds.y()
                                                        : anchor_bounds.bottom();
   int free_space_height_above_anchor =
@@ -301,22 +302,8 @@
   int max_height = CalculateMaxHeight();
   unified_view_->SetMaxHeight(max_height);
   bubble_view_->SetMaxHeight(max_height);
-  // If the bubble is open while switching to and from tablet mode, change the
-  // bubble anchor if needed. The new anchor view may also have a translation
-  // applied to it so shift the bubble bounds so that it appears in the correct
-  // location.
-  bubble_view_->ChangeAnchorView(
-      tray_->shelf()->GetSystemTrayAnchor()->GetBubbleAnchor());
-  gfx::Rect bounds =
-      bubble_view_->GetWidget()->GetNativeWindow()->GetBoundsInScreen();
-  const gfx::Vector2dF translation = tray_->shelf()
-                                         ->GetSystemTrayAnchor()
-                                         ->layer()
-                                         ->transform()
-                                         .To2dTranslation();
-  bounds.set_x(bounds.x() - translation.x());
-  bounds.set_y(bounds.y() - translation.y());
-  bubble_view_->GetWidget()->GetNativeWindow()->SetBounds(bounds);
+  bubble_view_->ChangeAnchorAlignment(tray_->GetAnchorAlignment());
+  bubble_view_->ChangeAnchorRect(tray_->shelf()->GetSystemTrayAnchorRect());
 }
 
 void UnifiedSystemTrayBubble::CreateBlurLayerForAnimation() {
diff --git a/ash/system/unified/unified_system_tray_view.cc b/ash/system/unified/unified_system_tray_view.cc
index 73d8c06..88e57301 100644
--- a/ash/system/unified/unified_system_tray_view.cc
+++ b/ash/system/unified/unified_system_tray_view.cc
@@ -8,7 +8,7 @@
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/system/message_center/ash_message_center_lock_screen_controller.h"
-#include "ash/system/message_center/new_unified_message_center_view.h"
+#include "ash/system/message_center/unified_message_center_view.h"
 #include "ash/system/tray/interacted_by_tap_recorder.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/feature_pod_button.h"
@@ -225,7 +225,7 @@
       system_tray_container_(new SystemTrayContainer()),
       detailed_view_container_(new DetailedViewContainer()),
       message_center_view_(
-          new NewUnifiedMessageCenterView(this, controller->model())),
+          new UnifiedMessageCenterView(this, controller->model())),
       focus_search_(std::make_unique<FocusSearch>(this)),
       interacted_by_tap_recorder_(
           std::make_unique<InteractedByTapRecorder>(this)) {
diff --git a/ash/system/unified/unified_system_tray_view.h b/ash/system/unified/unified_system_tray_view.h
index 18d8ee87..448056d 100644
--- a/ash/system/unified/unified_system_tray_view.h
+++ b/ash/system/unified/unified_system_tray_view.h
@@ -14,7 +14,7 @@
 class FeaturePodButton;
 class FeaturePodsContainerView;
 class TopShortcutsView;
-class NewUnifiedMessageCenterView;
+class UnifiedMessageCenterView;
 class UnifiedSystemInfoView;
 class UnifiedSystemTrayController;
 
@@ -124,7 +124,7 @@
   UnifiedSystemInfoView* const system_info_view_;
   views::View* const system_tray_container_;
   views::View* const detailed_view_container_;
-  NewUnifiedMessageCenterView* const message_center_view_;
+  UnifiedMessageCenterView* const message_center_view_;
 
   const std::unique_ptr<FocusSearch> focus_search_;
   const std::unique_ptr<ui::EventHandler> interacted_by_tap_recorder_;
diff --git a/ash/system/update/update_notification_controller_unittest.cc b/ash/system/update/update_notification_controller_unittest.cc
index 2b028f53..038f593 100644
--- a/ash/system/update/update_notification_controller_unittest.cc
+++ b/ash/system/update/update_notification_controller_unittest.cc
@@ -9,7 +9,6 @@
 #include "ash/system/model/system_tray_model.h"
 #include "ash/test/ash_test_base.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "ui/message_center/message_center.h"
 
 #if defined(GOOGLE_CHROME_BUILD)
@@ -25,11 +24,6 @@
   UpdateNotificationControllerTest() = default;
   ~UpdateNotificationControllerTest() override = default;
 
-  void SetUp() override {
-    scoped_feature_list_.InitAndEnableFeature(features::kSystemTrayUnified);
-    AshTestBase::SetUp();
-  }
-
  protected:
   bool HasNotification() {
     return message_center::MessageCenter::Get()->FindVisibleNotificationById(
@@ -78,8 +72,6 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   DISALLOW_COPY_AND_ASSIGN(UpdateNotificationControllerTest);
 };
 
diff --git a/ash/system/user/tray_user_unittest.cc b/ash/system/user/tray_user_unittest.cc
index b994a96..fce4ff4 100644
--- a/ash/system/user/tray_user_unittest.cc
+++ b/ash/system/user/tray_user_unittest.cc
@@ -225,7 +225,6 @@
 
   // Move the mouse over the status area and click to open the status menu.
   ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->set_async(false);
 
   // Verify that nothing is shown.
   EXPECT_FALSE(tray()->IsSystemBubbleVisible());
diff --git a/ash/wm/base_state.cc b/ash/wm/base_state.cc
index 512d3e3..60812ac 100644
--- a/ash/wm/base_state.cc
+++ b/ash/wm/base_state.cc
@@ -24,6 +24,8 @@
 void BaseState::OnWMEvent(WindowState* window_state, const WMEvent* event) {
   if (event->IsWorkspaceEvent()) {
     HandleWorkspaceEvents(window_state, event);
+    if (window_state->IsPip())
+      window_state->UpdatePipBounds();
     return;
   }
   if ((window_state->IsTrustedPinned() || window_state->IsPinned()) &&
@@ -149,8 +151,13 @@
       window->SetProperty(aura::client::kPreMinimizedShowStateKey,
                           ToWindowShowState(previous_state_type));
     }
+    // Count minimizing a PIP window as dismissing it. Android apps in PIP mode
+    // don't exit when they are dismissed, they just go back to being a regular
+    // app, but minimized.
     ::wm::SetWindowVisibilityAnimationType(
-        window, WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
+        window, previous_state_type == mojom::WindowStateType::PIP
+                    ? WINDOW_VISIBILITY_ANIMATION_TYPE_SLIDE_OUT
+                    : WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
 
     window->Hide();
     if (window_state->IsActive())
diff --git a/ash/wm/client_controlled_state.cc b/ash/wm/client_controlled_state.cc
index 725b97d..fceceff3 100644
--- a/ash/wm/client_controlled_state.cc
+++ b/ash/wm/client_controlled_state.cc
@@ -259,6 +259,9 @@
     Shell::Get()->screen_pinning_controller()->SetPinnedWindow(
         window_state->window());
   }
+
+  window_state->UpdatePipState();
+
   return true;
 }
 
diff --git a/ash/wm/default_state.cc b/ash/wm/default_state.cc
index c4cbb6f7..69094d0 100644
--- a/ash/wm/default_state.cc
+++ b/ash/wm/default_state.cc
@@ -460,6 +460,8 @@
     Shell::Get()->screen_pinning_controller()->SetPinnedWindow(
         window_state->window());
   }
+
+  window_state->UpdatePipState();
 }
 
 void DefaultState::ReenterToCurrentState(
diff --git a/ash/wm/pip/pip_positioner.cc b/ash/wm/pip/pip_positioner.cc
index 07a09858..9361b3d 100644
--- a/ash/wm/pip/pip_positioner.cc
+++ b/ash/wm/pip/pip_positioner.cc
@@ -20,6 +20,7 @@
 
 namespace {
 const int kPipWorkAreaInsetsDp = 8;
+const float kPipDismissMovementProportion = 1.5f;
 
 enum { GRAVITY_LEFT, GRAVITY_RIGHT, GRAVITY_TOP, GRAVITY_BOTTOM };
 
@@ -45,6 +46,38 @@
   return bounds;
 }
 
+// Returns the gravity that would make |bounds| fall to the closest edge of
+// |region|. If |bounds| is outside of |region| then it will return the gravity
+// as if |bounds| had fallen outside of |region|. See the below diagram for what
+// the gravity regions look like for a point.
+//  \  TOP /
+//   \____/ R
+// L |\  /| I
+// E | \/ | G
+// F | /\ | H
+// T |/__\| T
+//   /    \
+//  /BOTTOM
+int GetGravityToClosestEdge(const gfx::Rect& bounds, const gfx::Rect& region) {
+  const int left_edge_dist = bounds.x() - region.x();
+  const int right_edge_dist = region.right() - bounds.right();
+  const int top_edge_dist = bounds.y() - region.y();
+  const int bottom_edge_dist = region.bottom() - bounds.bottom();
+  int minimum_edge_dist = std::min(left_edge_dist, right_edge_dist);
+  minimum_edge_dist = std::min(minimum_edge_dist, top_edge_dist);
+  minimum_edge_dist = std::min(minimum_edge_dist, bottom_edge_dist);
+
+  if (left_edge_dist == minimum_edge_dist) {
+    return GRAVITY_LEFT;
+  } else if (right_edge_dist == minimum_edge_dist) {
+    return GRAVITY_RIGHT;
+  } else if (top_edge_dist == minimum_edge_dist) {
+    return GRAVITY_TOP;
+  } else {
+    return GRAVITY_BOTTOM;
+  }
+}
+
 }  // namespace
 
 gfx::Rect PipPositioner::GetMovementArea(const display::Display& display) {
@@ -75,17 +108,37 @@
   gfx::Rect resting_bounds = bounds;
   gfx::Rect area = GetMovementArea(display);
   resting_bounds.AdjustToFit(area);
-  gfx::Point pip_center = resting_bounds.CenterPoint();
-  gfx::Point area_center = area.CenterPoint();
 
-  gfx::Vector2d direction = pip_center - area_center;
-  int gravity = 0;
-  if (std::abs(direction.x()) > std::abs(direction.y())) {
-    gravity = direction.x() < 0 ? GRAVITY_LEFT : GRAVITY_RIGHT;
-  } else {
-    gravity = direction.y() < 0 ? GRAVITY_TOP : GRAVITY_BOTTOM;
-  }
+  const int gravity = GetGravityToClosestEdge(resting_bounds, area);
   return GetAdjustedBoundsByGravity(resting_bounds, area, gravity);
 }
 
+gfx::Rect PipPositioner::GetDismissedPosition(const display::Display& display,
+                                              const gfx::Rect& bounds) {
+  gfx::Rect work_area = GetMovementArea(display);
+  const int gravity = GetGravityToClosestEdge(bounds, work_area);
+  // Allow the bounds to move at most |kPipDismissMovementProportion| of the
+  // length of the bounds in the direction of movement.
+  gfx::Rect bounds_movement_area = bounds;
+  bounds_movement_area.Inset(-bounds.width() * kPipDismissMovementProportion,
+                             -bounds.height() * kPipDismissMovementProportion);
+  gfx::Rect dismissed_bounds =
+      GetAdjustedBoundsByGravity(bounds, bounds_movement_area, gravity);
+
+  // If the PIP window isn't close enough to the edge of the screen, don't slide
+  // it out.
+  return work_area.Intersects(dismissed_bounds) ? bounds : dismissed_bounds;
+}
+
+gfx::Rect PipPositioner::GetPositionAfterMovementAreaChange(
+    wm::WindowState* window_state) {
+  // Restore to previous bounds if we have them. This lets us move the PIP
+  // window back to its original bounds after transient movement area changes,
+  // like the keyboard popping up and pushing the PIP window up.
+  const gfx::Rect bounds = window_state->HasRestoreBounds()
+                               ? window_state->GetRestoreBoundsInScreen()
+                               : window_state->window()->GetBoundsInScreen();
+  return GetRestingPosition(window_state->GetDisplay(), bounds);
+}
+
 }  // namespace ash
diff --git a/ash/wm/pip/pip_positioner.h b/ash/wm/pip/pip_positioner.h
index 32afd5c3..2a3ac51 100644
--- a/ash/wm/pip/pip_positioner.h
+++ b/ash/wm/pip/pip_positioner.h
@@ -13,10 +13,16 @@
 
 namespace ash {
 
+namespace wm {
+class WindowState;
+}  // namespace wm
+
 class PipPositionerTest;
 
 class ASH_EXPORT PipPositioner {
  public:
+  static const int kPipDismissTimeMs = 300;
+
   PipPositioner() = delete;
   ~PipPositioner() = delete;
 
@@ -36,6 +42,19 @@
   static gfx::Rect GetBoundsForDrag(const display::Display& display,
                                     const gfx::Rect& bounds);
 
+  // Based on the current PIP window position, finds a final location of where
+  // the PIP window should be animated to to show a dismissal off the side
+  // of the screen. Note that this may return somewhere not off-screen if
+  // animating the PIP window off-screen would travel too far.
+  static gfx::Rect GetDismissedPosition(const display::Display& display,
+                                        const gfx::Rect& bounds);
+
+  // Gets the position the PIP window should be moved to after a movement area
+  // change. For example, if the shelf is changed from auto-hidden to always
+  // shown, the PIP window should move up to not intersect it.
+  static gfx::Rect GetPositionAfterMovementAreaChange(
+      wm::WindowState* window_state);
+
  private:
   friend class PipPositionerTest;
 
diff --git a/ash/wm/pip/pip_positioner_unittest.cc b/ash/wm/pip/pip_positioner_unittest.cc
index 11aa1c4..93613ce2 100644
--- a/ash/wm/pip/pip_positioner_unittest.cc
+++ b/ash/wm/pip/pip_positioner_unittest.cc
@@ -91,7 +91,7 @@
 
 TEST_F(PipPositionerTest, PipMovementAreaIsInset) {
   gfx::Rect area = PipPositioner::GetMovementArea(window_state()->GetDisplay());
-  EXPECT_EQ("8,8 384x384", area.ToString());
+  EXPECT_EQ(gfx::Rect(8, 8, 384, 384), area);
 }
 
 TEST_F(PipPositionerTest, PipMovementAreaIncludesKeyboardIfKeyboardIsShown) {
@@ -110,72 +110,83 @@
   auto display = window_state()->GetDisplay();
 
   // Snap near top edge to top.
-  EXPECT_EQ("100,8 100x100", PipPositioner::GetRestingPosition(
-                                 display, gfx::Rect(100, 50, 100, 100))
-                                 .ToString());
+  EXPECT_EQ(
+      gfx::Rect(100, 8, 100, 100),
+      PipPositioner::GetRestingPosition(display, gfx::Rect(100, 50, 100, 100)));
 
   // Snap near bottom edge to bottom.
-  EXPECT_EQ("100,292 100x100", PipPositioner::GetRestingPosition(
-                                   display, gfx::Rect(100, 250, 100, 100))
-                                   .ToString());
+  EXPECT_EQ(gfx::Rect(100, 292, 100, 100),
+            PipPositioner::GetRestingPosition(display,
+                                              gfx::Rect(100, 250, 100, 100)));
 
   // Snap near left edge to left.
-  EXPECT_EQ("8,100 100x100", PipPositioner::GetRestingPosition(
-                                 display, gfx::Rect(50, 100, 100, 100))
-                                 .ToString());
+  EXPECT_EQ(
+      gfx::Rect(8, 100, 100, 100),
+      PipPositioner::GetRestingPosition(display, gfx::Rect(50, 100, 100, 100)));
 
   // Snap near right edge to right.
-  EXPECT_EQ("292,100 100x100", PipPositioner::GetRestingPosition(
-                                   display, gfx::Rect(250, 100, 100, 100))
-                                   .ToString());
+  EXPECT_EQ(gfx::Rect(292, 100, 100, 100),
+            PipPositioner::GetRestingPosition(display,
+                                              gfx::Rect(250, 100, 100, 100)));
 }
 
 TEST_F(PipPositionerTest, PipRestingPositionSnapsInsideDisplay) {
   auto display = window_state()->GetDisplay();
 
   // Snap near top edge outside movement area to top.
-  EXPECT_EQ("100,8 100x100", PipPositioner::GetRestingPosition(
-                                 display, gfx::Rect(100, -50, 100, 100))
-                                 .ToString());
+  EXPECT_EQ(gfx::Rect(100, 8, 100, 100),
+            PipPositioner::GetRestingPosition(display,
+                                              gfx::Rect(100, -50, 100, 100)));
 
   // Snap near bottom edge outside movement area to bottom.
-  EXPECT_EQ("100,292 100x100", PipPositioner::GetRestingPosition(
-                                   display, gfx::Rect(100, 450, 100, 100))
-                                   .ToString());
+  EXPECT_EQ(gfx::Rect(100, 292, 100, 100),
+            PipPositioner::GetRestingPosition(display,
+                                              gfx::Rect(100, 450, 100, 100)));
 
   // Snap near left edge outside movement area to left.
-  EXPECT_EQ("8,100 100x100", PipPositioner::GetRestingPosition(
-                                 display, gfx::Rect(-50, 100, 100, 100))
-                                 .ToString());
+  EXPECT_EQ(gfx::Rect(8, 100, 100, 100),
+            PipPositioner::GetRestingPosition(display,
+                                              gfx::Rect(-50, 100, 100, 100)));
 
   // Snap near right edge outside movement area to right.
-  EXPECT_EQ("292,100 100x100", PipPositioner::GetRestingPosition(
-                                   display, gfx::Rect(450, 100, 100, 100))
-                                   .ToString());
+  EXPECT_EQ(gfx::Rect(292, 100, 100, 100),
+            PipPositioner::GetRestingPosition(display,
+                                              gfx::Rect(450, 100, 100, 100)));
+}
+
+TEST_F(PipPositionerTest,
+       PipRestingPositionSnapsInDisplayWithLargeAspectRatio) {
+  UpdateDisplay("1600x400");
+  auto display = window_state()->GetDisplay();
+
+  // Snap to the top edge instead of the far left edge.
+  EXPECT_EQ(gfx::Rect(500, 8, 100, 100),
+            PipPositioner::GetRestingPosition(display,
+                                              gfx::Rect(500, 100, 100, 100)));
 }
 
 TEST_F(PipPositionerTest, PipAdjustPositionForDragClampsToMovementArea) {
   auto display = window_state()->GetDisplay();
 
   // Adjust near top edge outside movement area.
-  EXPECT_EQ("100,8 100x100", PipPositioner::GetBoundsForDrag(
-                                 display, gfx::Rect(100, -50, 100, 100))
-                                 .ToString());
+  EXPECT_EQ(
+      gfx::Rect(100, 8, 100, 100),
+      PipPositioner::GetBoundsForDrag(display, gfx::Rect(100, -50, 100, 100)));
 
   // Adjust near bottom edge outside movement area.
-  EXPECT_EQ("100,292 100x100", PipPositioner::GetBoundsForDrag(
-                                   display, gfx::Rect(100, 450, 100, 100))
-                                   .ToString());
+  EXPECT_EQ(
+      gfx::Rect(100, 292, 100, 100),
+      PipPositioner::GetBoundsForDrag(display, gfx::Rect(100, 450, 100, 100)));
 
   // Adjust near left edge outside movement area.
-  EXPECT_EQ("8,100 100x100", PipPositioner::GetBoundsForDrag(
-                                 display, gfx::Rect(-50, 100, 100, 100))
-                                 .ToString());
+  EXPECT_EQ(
+      gfx::Rect(8, 100, 100, 100),
+      PipPositioner::GetBoundsForDrag(display, gfx::Rect(-50, 100, 100, 100)));
 
   // Adjust near right edge outside movement area.
-  EXPECT_EQ("292,100 100x100", PipPositioner::GetBoundsForDrag(
-                                   display, gfx::Rect(450, 100, 100, 100))
-                                   .ToString());
+  EXPECT_EQ(
+      gfx::Rect(292, 100, 100, 100),
+      PipPositioner::GetBoundsForDrag(display, gfx::Rect(450, 100, 100, 100)));
 }
 
 TEST_F(PipPositionerTest, PipRestingPositionWorksIfKeyboardIsDisabled) {
@@ -183,9 +194,94 @@
   auto display = window_state()->GetDisplay();
 
   // Snap near top edge to top.
-  EXPECT_EQ("100,8 100x100", PipPositioner::GetRestingPosition(
-                                 display, gfx::Rect(100, 50, 100, 100))
-                                 .ToString());
+  EXPECT_EQ(
+      gfx::Rect(100, 8, 100, 100),
+      PipPositioner::GetRestingPosition(display, gfx::Rect(100, 50, 100, 100)));
+}
+
+TEST_F(PipPositionerTest, PipDismissedPositionDoesNotMoveAnExcessiveDistance) {
+  auto display = window_state()->GetDisplay();
+
+  EXPECT_EQ(gfx::Rect(100, 100, 100, 100),
+            PipPositioner::GetDismissedPosition(display,
+                                                gfx::Rect(100, 100, 100, 100)));
+}
+
+TEST_F(PipPositionerTest, PipDismissedPositionChosesClosestEdge) {
+  auto display = window_state()->GetDisplay();
+
+  // Dismiss near top edge outside movement area towards top.
+  EXPECT_EQ(gfx::Rect(100, -100, 100, 100),
+            PipPositioner::GetDismissedPosition(display,
+                                                gfx::Rect(100, 50, 100, 100)));
+
+  // Dismiss near bottom edge outside movement area towards bottom.
+  EXPECT_EQ(gfx::Rect(100, 400, 100, 100),
+            PipPositioner::GetDismissedPosition(display,
+                                                gfx::Rect(100, 250, 100, 100)));
+
+  // Dismiss near left edge outside movement area towards left.
+  EXPECT_EQ(gfx::Rect(-100, 100, 100, 100),
+            PipPositioner::GetDismissedPosition(display,
+                                                gfx::Rect(50, 100, 100, 100)));
+
+  // Dismiss near right edge outside movement area towards right.
+  EXPECT_EQ(gfx::Rect(400, 100, 100, 100),
+            PipPositioner::GetDismissedPosition(display,
+                                                gfx::Rect(250, 100, 100, 100)));
+}
+
+// Verify that if two edges are equally close, the PIP window prefers dismissing
+// out horizontally.
+TEST_F(PipPositionerTest, PipDismissedPositionPrefersHorizontal) {
+  auto display = window_state()->GetDisplay();
+
+  // Top left corner.
+  EXPECT_EQ(
+      gfx::Rect(-150, 0, 100, 100),
+      PipPositioner::GetDismissedPosition(display, gfx::Rect(0, 0, 100, 100)));
+
+  // Top right corner.
+  EXPECT_EQ(gfx::Rect(450, 0, 100, 100),
+            PipPositioner::GetDismissedPosition(display,
+                                                gfx::Rect(300, 0, 100, 100)));
+
+  // Bottom left corner.
+  EXPECT_EQ(gfx::Rect(-150, 300, 100, 100),
+            PipPositioner::GetDismissedPosition(display,
+                                                gfx::Rect(0, 300, 100, 100)));
+
+  // Bottom right corner.
+  EXPECT_EQ(gfx::Rect(450, 300, 100, 100),
+            PipPositioner::GetDismissedPosition(display,
+                                                gfx::Rect(300, 300, 100, 100)));
+}
+
+TEST_F(PipPositionerTest,
+       PipRestoresToPreviousBoundsOnMovementAreaChangeIfTheyExist) {
+  // Position the PIP window on the side of the screen where it will be next
+  // to an edge and therefore in a resting position for the whole test.
+  const gfx::Rect bounds = gfx::Rect(292, 200, 100, 100);
+  // Set restore position to where the window currently is.
+  window()->SetBounds(bounds);
+  window_state()->SetRestoreBoundsInScreen(bounds);
+  EXPECT_TRUE(window_state()->HasRestoreBounds());
+
+  // Update the work area so that the PIP window should be pushed upward.
+  UpdateWorkArea("400x200");
+
+  // Set PIP to the updated constrained bounds.
+  const gfx::Rect constrained_bounds =
+      PipPositioner::GetPositionAfterMovementAreaChange(window_state());
+  EXPECT_EQ(gfx::Rect(292, 92, 100, 100), constrained_bounds);
+  window()->SetBounds(constrained_bounds);
+
+  // Restore the original work area.
+  UpdateWorkArea("400x400");
+
+  // Expect that the PIP window is put back to where it was before.
+  EXPECT_EQ(gfx::Rect(292, 200, 100, 100),
+            PipPositioner::GetPositionAfterMovementAreaChange(window_state()));
 }
 
 }  // namespace ash
diff --git a/ash/wm/window_animations.cc b/ash/wm/window_animations.cc
index 0375683..38afe6a 100644
--- a/ash/wm/window_animations.cc
+++ b/ash/wm/window_animations.cc
@@ -15,6 +15,7 @@
 #include "ash/public/cpp/window_animation_types.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
+#include "ash/wm/pip/pip_positioner.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/workspace_controller.h"
 #include "base/i18n/rtl.h"
@@ -263,6 +264,29 @@
   return false;
 }
 
+void AnimateHideWindow_SlideOut(aura::Window* window) {
+  base::TimeDelta duration =
+      base::TimeDelta::FromMilliseconds(PipPositioner::kPipDismissTimeMs);
+
+  ::wm::ScopedHidingAnimationSettings settings(window);
+  settings.layer_animation_settings()->SetTransitionDuration(duration);
+  window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
+  window->layer()->SetVisible(false);
+
+  gfx::Rect bounds = window->GetBoundsInScreen();
+  display::Display display =
+      display::Screen::GetScreen()->GetDisplayNearestWindow(window);
+  gfx::Rect dismissed_bounds =
+      PipPositioner::GetDismissedPosition(display, bounds);
+  window->layer()->SetBounds(dismissed_bounds);
+
+  // For Android PIP windows, they become minimized app windows after
+  // dismissal, so make sure to reset their animation type back to
+  // default.
+  ::wm::SetWindowVisibilityAnimationType(
+      window, ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT);
+}
+
 bool AnimateShowWindow(aura::Window* window) {
   if (!::wm::HasWindowVisibilityAnimationTransition(window,
                                                     ::wm::ANIMATE_SHOW)) {
@@ -278,6 +302,10 @@
       return true;
     case wm::WINDOW_VISIBILITY_ANIMATION_TYPE_SLIDE_DOWN:
       return AnimateShowWindow_SlideDown(window);
+      return true;
+    case wm::WINDOW_VISIBILITY_ANIMATION_TYPE_SLIDE_OUT:
+      // Slide out is exclusively a hide animation.
+      return false;
     default:
       NOTREACHED();
       return false;
@@ -299,6 +327,9 @@
       return true;
     case wm::WINDOW_VISIBILITY_ANIMATION_TYPE_SLIDE_DOWN:
       return AnimateHideWindow_SlideDown(window);
+    case wm::WINDOW_VISIBILITY_ANIMATION_TYPE_SLIDE_OUT:
+      AnimateHideWindow_SlideOut(window);
+      return true;
     default:
       NOTREACHED();
       return false;
diff --git a/ash/wm/window_animations_unittest.cc b/ash/wm/window_animations_unittest.cc
index 5617b9b..ef32353 100644
--- a/ash/wm/window_animations_unittest.cc
+++ b/ash/wm/window_animations_unittest.cc
@@ -6,9 +6,11 @@
 
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_animation_types.h"
+#include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/workspace_controller.h"
+#include "base/command_line.h"
 #include "base/time/time.h"
 #include "ui/aura/test/test_windows.h"
 #include "ui/aura/window.h"
@@ -17,6 +19,7 @@
 #include "ui/compositor/layer_animator.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/keyboard/keyboard_switches.h"
 
 using aura::Window;
 using ui::Layer;
@@ -27,6 +30,12 @@
  public:
   WindowAnimationsTest() = default;
 
+  void SetUp() override {
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        keyboard::switches::kEnableVirtualKeyboard);
+    AshTestBase::SetUp();
+  }
+
   void TearDown() override { AshTestBase::TearDown(); }
 
  private:
@@ -272,4 +281,25 @@
   }
 }
 
+// Test that a slide out animation slides the window off the screen while
+// modifying the opacity.
+TEST_F(WindowAnimationsTest, SlideOutAnimation) {
+  ui::ScopedAnimationDurationScaleMode test_duration_mode(
+      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
+
+  std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
+  window->SetBounds(gfx::Rect(0, 0, 100, 100));
+  window->Show();
+  EXPECT_TRUE(window->layer()->visible());
+
+  ::wm::SetWindowVisibilityAnimationType(
+      window.get(), wm::WINDOW_VISIBILITY_ANIMATION_TYPE_SLIDE_OUT);
+  AnimateOnChildWindowVisibilityChanged(window.get(), false);
+
+  EXPECT_EQ(0.0f, window->layer()->GetTargetOpacity());
+  EXPECT_FALSE(window->layer()->GetTargetVisibility());
+  EXPECT_FALSE(window->layer()->visible());
+  EXPECT_EQ("-150,0 100x100", window->layer()->GetTargetBounds().ToString());
+}
+
 }  // namespace ash
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index ba2b881..9b5e1bc 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/public/cpp/window_animation_types.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/public/cpp/window_state_type.h"
 #include "ash/public/interfaces/window_pin_type.mojom.h"
@@ -14,6 +15,7 @@
 #include "ash/screen_util.h"
 #include "ash/shell.h"
 #include "ash/wm/default_state.h"
+#include "ash/wm/pip/pip_positioner.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_animations.h"
 #include "ash/wm/window_positioning_utils.h"
@@ -517,6 +519,7 @@
       ignore_property_change_(false),
       current_state_(new DefaultState(ToWindowStateType(GetShowState()))) {
   window_->AddObserver(this);
+  UpdatePipState();
 }
 
 bool WindowState::GetAlwaysOnTop() const {
@@ -671,7 +674,8 @@
 void WindowState::UpdatePipRoundedCorners() {
   auto* layer = window()->layer();
   if (!IsPip()) {
-    if (layer)
+    // Only remove the mask layer if it is from the existing PIP mask.
+    if (layer && pip_mask_ && layer->layer_mask_layer() == pip_mask_->layer())
       layer->SetMaskLayer(nullptr);
     pip_mask_.reset();
     return;
@@ -690,6 +694,22 @@
   }
 }
 
+void WindowState::UpdatePipState() {
+  ::wm::SetWindowVisibilityAnimationType(
+      window(), IsPip() ? WINDOW_VISIBILITY_ANIMATION_TYPE_SLIDE_OUT
+                        : ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT);
+}
+
+void WindowState::UpdatePipBounds() {
+  gfx::Rect new_bounds =
+      PipPositioner::GetPositionAfterMovementAreaChange(this);
+  if (window()->GetBoundsInScreen() != new_bounds) {
+    wm::SetBoundsEvent event(wm::WM_EVENT_SET_BOUNDS, new_bounds,
+                             /*animate=*/true);
+    OnWMEvent(&event);
+  }
+}
+
 WindowState* GetActiveWindowState() {
   aura::Window* active = GetActiveWindow();
   return active ? GetWindowState(active) : nullptr;
diff --git a/ash/wm/window_state.h b/ash/wm/window_state.h
index ced5186..d4f8044a 100644
--- a/ash/wm/window_state.h
+++ b/ash/wm/window_state.h
@@ -414,6 +414,15 @@
   // for non-PIP window states.
   void UpdatePipRoundedCorners();
 
+  // Update PIP related state, such as next window animation type, upon
+  // state change.
+  void UpdatePipState();
+
+  // Update the PIP bounds if necessary. This may need to happen when the
+  // display work area changes, or if system ui regions like the virtual
+  // keyboard position changes.
+  void UpdatePipBounds();
+
   // aura::WindowObserver:
   void OnWindowPropertyChanged(aura::Window* window,
                                const void* key,
diff --git a/base/test/fontconfig_util_linux.cc b/base/test/fontconfig_util_linux.cc
index 6848893..9ed03c4e 100644
--- a/base/test/fontconfig_util_linux.cc
+++ b/base/test/fontconfig_util_linux.cc
@@ -36,19 +36,6 @@
     </edit>
   </match>
 
-  <!-- TODO(thomasanderson): Figure out why this is necessary. -->
-  <match target="pattern">
-    <test name="family" compare="eq">
-      <string>Tinos</string>
-    </test>
-    <test name="prgname" compare="eq">
-      <string>chromevox_tests</string>
-    </test>
-    <edit name="hintstyle" mode="assign">
-      <const>hintslight</const>
-    </edit>
-  </match>
-
   <match target="pattern">
     <test qual="any" name="family">
       <string>Times</string>
diff --git a/cc/base/rtree.h b/cc/base/rtree.h
index d6a1e8d..840eaab 100644
--- a/cc/base/rtree.h
+++ b/cc/base/rtree.h
@@ -61,12 +61,12 @@
 
   // Given a query rect, returns elements that intersect the rect. Elements are
   // returned in the order they appeared in the initial container.
-  std::vector<T> Search(const gfx::Rect& query) const;
+  void Search(const gfx::Rect& query, std::vector<T>* results) const;
 
   // Given a query rect, returns non-owning pointers to elements that intersect
   // the rect. Elements are returned in the order they appeared in the initial
   // container.
-  std::vector<const T*> SearchRefs(const gfx::Rect& query) const;
+  void SearchRefs(const gfx::Rect& query, std::vector<const T*>* results) const;
 
   // Returns the total bounds of all items in this rtree.
   gfx::Rect GetBounds() const;
@@ -288,19 +288,18 @@
 }
 
 template <typename T>
-std::vector<T> RTree<T>::Search(const gfx::Rect& query) const {
-  std::vector<T> results;
+void RTree<T>::Search(const gfx::Rect& query, std::vector<T>* results) const {
+  results->clear();
   if (num_data_elements_ > 0 && query.Intersects(root_.bounds))
-    SearchRecursive(root_.subtree, query, &results);
-  return results;
+    SearchRecursive(root_.subtree, query, results);
 }
 
 template <typename T>
-std::vector<const T*> RTree<T>::SearchRefs(const gfx::Rect& query) const {
-  std::vector<const T*> results;
+void RTree<T>::SearchRefs(const gfx::Rect& query,
+                          std::vector<const T*>* results) const {
+  results->clear();
   if (num_data_elements_ > 0 && query.Intersects(root_.bounds))
-    SearchRefsRecursive(root_.subtree, query, &results);
-  return results;
+    SearchRefsRecursive(root_.subtree, query, results);
 }
 
 template <typename T>
diff --git a/cc/base/rtree_perftest.cc b/cc/base/rtree_perftest.cc
index e7c70d5b..70d44bc 100644
--- a/cc/base/rtree_perftest.cc
+++ b/cc/base/rtree_perftest.cc
@@ -58,7 +58,9 @@
 
     timer_.Reset();
     do {
-      Accumulate(rtree.Search(queries[query_index]));
+      std::vector<size_t> results;
+      rtree.Search(queries[query_index], &results);
+      Accumulate(results);
       query_index = (query_index + 1) % queries.size();
       timer_.NextLap();
     } while (!timer_.HasTimeLimitExpired());
diff --git a/cc/base/rtree_unittest.cc b/cc/base/rtree_unittest.cc
index d5242a1..e5b9379 100644
--- a/cc/base/rtree_unittest.cc
+++ b/cc/base/rtree_unittest.cc
@@ -35,20 +35,21 @@
   RTree<size_t> rtree;
   rtree.Build(rects);
 
-  std::vector<size_t> results = rtree.Search(gfx::Rect(0, 0, 50, 50));
+  std::vector<size_t> results;
+  rtree.Search(gfx::Rect(0, 0, 50, 50), &results);
   ASSERT_EQ(2500u, results.size());
   // Note that the results have to be sorted.
   for (size_t i = 0; i < 2500; ++i) {
     ASSERT_EQ(results[i], i);
   }
 
-  results = rtree.Search(gfx::Rect(0, 0, 50, 49));
+  rtree.Search(gfx::Rect(0, 0, 50, 49), &results);
   ASSERT_EQ(2450u, results.size());
   for (size_t i = 0; i < 2450; ++i) {
     ASSERT_EQ(results[i], i);
   }
 
-  results = rtree.Search(gfx::Rect(5, 6, 1, 1));
+  rtree.Search(gfx::Rect(5, 6, 1, 1), &results);
   ASSERT_EQ(1u, results.size());
   EXPECT_EQ(6u * 50 + 5u, results[0]);
 }
@@ -64,14 +65,15 @@
   RTree<size_t> rtree;
   rtree.Build(rects);
 
-  std::vector<size_t> results = rtree.Search(gfx::Rect(0, 0, 1, 1));
+  std::vector<size_t> results;
+  rtree.Search(gfx::Rect(0, 0, 1, 1), &results);
   ASSERT_EQ(2500u, results.size());
   // Both the checks for the elements assume elements are sorted.
   for (size_t i = 0; i < 2500; ++i) {
     ASSERT_EQ(results[i], i);
   }
 
-  results = rtree.Search(gfx::Rect(0, 49, 1, 1));
+  rtree.Search(gfx::Rect(0, 49, 1, 1), &results);
   ASSERT_EQ(50u, results.size());
   for (size_t i = 0; i < 50; ++i) {
     EXPECT_EQ(results[i], 2450u + i);
@@ -100,9 +102,13 @@
 
   for (int y = 0; y < 50; ++y) {
     for (int x = 0; x < 50; ++x) {
-      VerifySorted(rtree.Search(gfx::Rect(x, y, 1, 1)));
-      VerifySorted(rtree.Search(gfx::Rect(x, y, 50, 1)));
-      VerifySorted(rtree.Search(gfx::Rect(x, y, 1, 50)));
+      std::vector<size_t> results;
+      rtree.Search(gfx::Rect(x, y, 1, 1), &results);
+      VerifySorted(results);
+      rtree.Search(gfx::Rect(x, y, 50, 1), &results);
+      VerifySorted(results);
+      rtree.Search(gfx::Rect(x, y, 1, 50), &results);
+      VerifySorted(results);
     }
   }
 }
@@ -172,11 +178,12 @@
       [](const Container& items, size_t index) { return items[index].first; },
       [](const Container& items, size_t index) { return items[index].second; });
 
-  auto results = rtree.Search(gfx::Rect(0, 0, 1, 1));
+  std::vector<float> results;
+  rtree.Search(gfx::Rect(0, 0, 1, 1), &results);
   ASSERT_EQ(1u, results.size());
   EXPECT_FLOAT_EQ(10.f, results[0]);
 
-  results = rtree.Search(gfx::Rect(5, 5, 10, 10));
+  rtree.Search(gfx::Rect(5, 5, 10, 10), &results);
   ASSERT_EQ(4u, results.size());
   // Items returned should be in the order they were inserted.
   EXPECT_FLOAT_EQ(40.f, results[0]);
diff --git a/cc/paint/discardable_image_map.cc b/cc/paint/discardable_image_map.cc
index e42f80c0..c9462f1 100644
--- a/cc/paint/discardable_image_map.cc
+++ b/cc/paint/discardable_image_map.cc
@@ -452,7 +452,7 @@
 void DiscardableImageMap::GetDiscardableImagesInRect(
     const gfx::Rect& rect,
     std::vector<const DrawImage*>* images) const {
-  *images = images_rtree_.SearchRefs(rect);
+  images_rtree_.SearchRefs(rect, images);
 }
 
 const DiscardableImageMap::Rects& DiscardableImageMap::GetRectsForImage(
diff --git a/cc/paint/discardable_image_map_unittest.cc b/cc/paint/discardable_image_map_unittest.cc
index 72d608b..0c88e50 100644
--- a/cc/paint/discardable_image_map_unittest.cc
+++ b/cc/paint/discardable_image_map_unittest.cc
@@ -65,7 +65,9 @@
           *image, 1.f, PaintImage::kDefaultFrameIndex, target_color_space));
 
     std::vector<PositionScaleDrawImage> position_draw_images;
-    for (DrawImage& image : image_map.images_rtree_.Search(rect)) {
+    std::vector<DrawImage> results;
+    image_map.images_rtree_.Search(rect, &results);
+    for (DrawImage& image : results) {
       auto image_id = image.paint_image().stable_id();
       position_draw_images.push_back(PositionScaleDrawImage(
           image.paint_image(),
diff --git a/cc/paint/display_item_list.cc b/cc/paint/display_item_list.cc
index cc8db47..f2e5259 100644
--- a/cc/paint/display_item_list.cc
+++ b/cc/paint/display_item_list.cc
@@ -51,7 +51,8 @@
   if (!GetCanvasClipBounds(canvas, &canvas_playback_rect))
     return;
 
-  std::vector<size_t> offsets = rtree_.Search(canvas_playback_rect);
+  std::vector<size_t> offsets;
+  rtree_.Search(canvas_playback_rect, &offsets);
   paint_op_buffer_.Playback(canvas, PlaybackParams(image_provider), &offsets);
 }
 
@@ -196,7 +197,7 @@
   std::vector<size_t>* offsets_to_use = nullptr;
   std::vector<size_t> offsets;
   if (!rect.Contains(rtree_.GetBounds())) {
-    offsets = rtree_.Search(rect);
+    rtree_.Search(rect, &offsets);
     offsets_to_use = &offsets;
   }
 
diff --git a/cc/paint/oop_pixeltest.cc b/cc/paint/oop_pixeltest.cc
index 07cb7a9a..40a326e 100644
--- a/cc/paint/oop_pixeltest.cc
+++ b/cc/paint/oop_pixeltest.cc
@@ -21,6 +21,7 @@
 #include "cc/tiles/gpu_image_decode_cache.h"
 #include "gpu/command_buffer/client/gles2_implementation.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
+#include "gpu/command_buffer/client/raster_implementation.h"
 #include "gpu/command_buffer/client/raster_implementation_gles.h"
 #include "gpu/command_buffer/client/shared_memory_limits.h"
 #include "gpu/command_buffer/common/context_creation_attribs.h"
@@ -1605,10 +1606,53 @@
   ExpectEquals(actual, expected);
 }
 
+class OopPathPixelTest : public OopPixelTest,
+                         public ::testing::WithParamInterface<bool> {
+ public:
+  bool AllowInlining() const { return GetParam(); }
+  void RunTest() {
+    auto* ri = static_cast<gpu::raster::RasterImplementation*>(
+        raster_context_provider_->RasterInterface());
+    size_t max_inlined_entry_size =
+        AllowInlining() ? std::numeric_limits<size_t>::max() : 0u;
+    ri->set_max_inlined_entry_size_for_testing(max_inlined_entry_size);
+
+    RasterOptions options;
+    options.resource_size = gfx::Size(100, 100);
+    options.content_size = options.resource_size;
+    options.full_raster_rect = gfx::Rect(options.content_size);
+    options.playback_rect = options.full_raster_rect;
+    options.color_space = gfx::ColorSpace::CreateSRGB();
+
+    auto display_item_list = base::MakeRefCounted<DisplayItemList>();
+    display_item_list->StartPaint();
+    display_item_list->push<DrawColorOp>(SK_ColorWHITE, SkBlendMode::kSrc);
+    PaintFlags flags;
+    flags.setStyle(PaintFlags::kFill_Style);
+    flags.setColor(SK_ColorGREEN);
+    SkPath path;
+    path.addCircle(20, 20, 10);
+    display_item_list->push<DrawPathOp>(path, flags);
+    flags.setColor(SK_ColorBLUE);
+    display_item_list->push<DrawRectOp>(SkRect::MakeWH(10, 10), flags);
+    display_item_list->EndPaintOfUnpaired(options.full_raster_rect);
+    display_item_list->Finalize();
+
+    auto expected = RasterExpectedBitmap(display_item_list, options);
+    auto actual = Raster(display_item_list, options);
+    ExpectEquals(actual, expected);
+  }
+};
+
+TEST_P(OopPathPixelTest, Basic) {
+  RunTest();
+}
+
 INSTANTIATE_TEST_CASE_P(P, OopImagePixelTest, ::testing::Bool());
 INSTANTIATE_TEST_CASE_P(P, OopClearPixelTest, ::testing::Bool());
 INSTANTIATE_TEST_CASE_P(P, OopRecordShaderPixelTest, ::testing::Bool());
 INSTANTIATE_TEST_CASE_P(P, OopRecordFilterPixelTest, ::testing::Bool());
+INSTANTIATE_TEST_CASE_P(P, OopPathPixelTest, ::testing::Bool());
 
 }  // namespace
 }  // namespace cc
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h
index 0ec38b0d..da65ad27 100644
--- a/cc/paint/paint_op_buffer.h
+++ b/cc/paint/paint_op_buffer.h
@@ -164,6 +164,9 @@
     SkColorSpace* color_space = nullptr;
     bool can_use_lcd_text = false;
     bool context_supports_distance_field_text = true;
+    // If true, will calculate a fixed scale for PaintRecord-backed PaintShaders
+    // based on the current canvas scale.  May be turned off for tests.
+    bool scale_paint_record_shaders = true;
     int max_texture_size = 0;
     size_t max_texture_bytes = 0.f;
     SkMatrix original_ctm = SkMatrix::I();
diff --git a/cc/paint/paint_op_buffer_eq_fuzzer.cc b/cc/paint/paint_op_buffer_eq_fuzzer.cc
index 02c1a5c60..54bb52f6 100644
--- a/cc/paint/paint_op_buffer_eq_fuzzer.cc
+++ b/cc/paint/paint_op_buffer_eq_fuzzer.cc
@@ -40,6 +40,12 @@
   SkNoDrawCanvas canvas(100, 100);
   cc::TestOptionsProvider test_options_provider;
 
+  // This test is trying to verify that serializing and deserializing doesn't
+  // change the results.  Scaling paint shaders and changed them to a fixed
+  // scale when serializing defeats this purpose, so turn that off.
+  test_options_provider.mutable_serialize_options().scale_paint_record_shaders =
+      false;
+
   // Need 4 bytes to be able to read the type/skip.
   if (size < 4)
     return 0;
diff --git a/cc/paint/paint_op_reader.cc b/cc/paint/paint_op_reader.cc
index fa13ff7..60472b0 100644
--- a/cc/paint/paint_op_reader.cc
+++ b/cc/paint/paint_op_reader.cc
@@ -207,6 +207,18 @@
   } else {
     valid_ = false;
   }
+
+  size_t bytes_to_skip = 0u;
+  ReadSize(&bytes_to_skip);
+  if (!valid_)
+    return;
+
+  if (bytes_to_skip > remaining_bytes_) {
+    valid_ = false;
+    return;
+  }
+  memory_ += bytes_to_skip;
+  remaining_bytes_ -= bytes_to_skip;
 }
 
 void PaintOpReader::Read(PaintFlags* flags) {
diff --git a/cc/paint/paint_op_writer.cc b/cc/paint/paint_op_writer.cc
index 1faa90a..b8a573d 100644
--- a/cc/paint/paint_op_writer.cc
+++ b/cc/paint/paint_op_writer.cc
@@ -164,13 +164,28 @@
 
 void PaintOpWriter::Write(const SkPath& path) {
   auto id = path.getGenerationID();
+  Write(id);
+
+  uint64_t* bytes_to_skip = WriteSize(0u);
+  if (!valid_)
+    return;
+
   auto locked =
       options_.transfer_cache->LockEntry(TransferCacheEntryType::kPath, id);
+  uint64_t bytes_written = 0u;
   if (!locked) {
-    options_.transfer_cache->CreateEntry(ClientPathTransferCacheEntry(path));
+    // Note that it is not necessary to pass the remaining size for |memory_|
+    // here because the transfer cache implementation (in RasterImplementation)
+    // should have this information about the memory being written to here.
+    bytes_written = options_.transfer_cache->CreateEntry(
+        ClientPathTransferCacheEntry(path), memory_);
     options_.transfer_cache->AssertLocked(TransferCacheEntryType::kPath, id);
   }
-  Write(id);
+
+  DCHECK_LE(bytes_written, remaining_bytes_);
+  *bytes_to_skip = bytes_written;
+  memory_ += bytes_written;
+  remaining_bytes_ -= bytes_written;
 }
 
 void PaintOpWriter::Write(const PaintFlags& flags) {
@@ -335,7 +350,8 @@
                                         &quality, paint_image_needs_mips);
   }
 
-  if (type == PaintShader::Type::kPaintRecord) {
+  if (type == PaintShader::Type::kPaintRecord &&
+      options_.scale_paint_record_shaders) {
     return original->CreateScaledPaintRecord(ctm, paint_record_post_scale);
   }
 
diff --git a/cc/paint/path_transfer_cache_entry.cc b/cc/paint/path_transfer_cache_entry.cc
index c149010..6f15184 100644
--- a/cc/paint/path_transfer_cache_entry.cc
+++ b/cc/paint/path_transfer_cache_entry.cc
@@ -22,10 +22,10 @@
 }
 
 bool ClientPathTransferCacheEntry::Serialize(base::span<uint8_t> data) const {
-  DCHECK_EQ(data.size(), size_);
+  DCHECK_GE(data.size(), size_);
 
   size_t bytes_written = path_.writeToMemory(data.data());
-  CHECK_LE(bytes_written, size_);
+  DCHECK_LE(bytes_written, size_);
   return true;
 }
 
@@ -47,7 +47,6 @@
   if (read_bytes > data.size())
     return false;
   size_ = read_bytes;
-
   return true;
 }
 
diff --git a/cc/paint/raw_memory_transfer_cache_entry.cc b/cc/paint/raw_memory_transfer_cache_entry.cc
index 2c41fabd..aa6fb64 100644
--- a/cc/paint/raw_memory_transfer_cache_entry.cc
+++ b/cc/paint/raw_memory_transfer_cache_entry.cc
@@ -27,10 +27,10 @@
 
 bool ClientRawMemoryTransferCacheEntry::Serialize(
     base::span<uint8_t> data) const {
-  if (data.size() != data_.size())
+  if (data.size() < data_.size())
     return false;
 
-  memcpy(data.data(), data_.data(), data.size());
+  memcpy(data.data(), data_.data(), data_.size());
   return true;
 }
 
diff --git a/cc/paint/transfer_cache_serialize_helper.cc b/cc/paint/transfer_cache_serialize_helper.cc
index 1baec7dd..f50f150f 100644
--- a/cc/paint/transfer_cache_serialize_helper.cc
+++ b/cc/paint/transfer_cache_serialize_helper.cc
@@ -27,13 +27,14 @@
   return true;
 }
 
-void TransferCacheSerializeHelper::CreateEntry(
-    const ClientTransferCacheEntry& entry) {
+size_t TransferCacheSerializeHelper::CreateEntry(
+    const ClientTransferCacheEntry& entry,
+    char* memory) {
   // We shouldn't be creating entries if they were already created or locked.
   EntryKey key(entry.Type(), entry.Id());
   DCHECK_EQ(added_entries_.count(key), 0u);
-  CreateEntryInternal(entry);
   added_entries_.insert(key);
+  return CreateEntryInternal(entry, memory);
 }
 
 void TransferCacheSerializeHelper::FlushEntries() {
diff --git a/cc/paint/transfer_cache_serialize_helper.h b/cc/paint/transfer_cache_serialize_helper.h
index 4682fcb..1a204407 100644
--- a/cc/paint/transfer_cache_serialize_helper.h
+++ b/cc/paint/transfer_cache_serialize_helper.h
@@ -19,18 +19,20 @@
   virtual ~TransferCacheSerializeHelper();
 
   bool LockEntry(TransferCacheEntryType type, uint32_t id);
-  void CreateEntry(const ClientTransferCacheEntry& entry);
+  // The PaintOpWriter passes the address where the transfer cache may inline
+  // this entry. The size returned is the memory used if the entry is inlined,
+  // or 0u if no data is inlined.
+  size_t CreateEntry(const ClientTransferCacheEntry& entry, char* memory);
   void FlushEntries();
 
   void AssertLocked(TransferCacheEntryType type, uint32_t id);
 
-  virtual void SubmitDeferredEntries() {}
-
  protected:
   using EntryKey = std::pair<TransferCacheEntryType, uint32_t>;
 
   virtual bool LockEntryInternal(const EntryKey& key) = 0;
-  virtual void CreateEntryInternal(const ClientTransferCacheEntry& entry) = 0;
+  virtual size_t CreateEntryInternal(const ClientTransferCacheEntry& entry,
+                                     char* memory) = 0;
   virtual void FlushEntriesInternal(std::set<EntryKey> keys) = 0;
 
  private:
diff --git a/cc/test/transfer_cache_test_helper.cc b/cc/test/transfer_cache_test_helper.cc
index 8613c422..deb3fd3 100644
--- a/cc/test/transfer_cache_test_helper.cc
+++ b/cc/test/transfer_cache_test_helper.cc
@@ -99,8 +99,9 @@
   return true;
 }
 
-void TransferCacheTestHelper::CreateEntryInternal(
-    const ClientTransferCacheEntry& client_entry) {
+size_t TransferCacheTestHelper::CreateEntryInternal(
+    const ClientTransferCacheEntry& client_entry,
+    char* memory) {
   auto key = std::make_pair(client_entry.Type(), client_entry.Id());
   DCHECK(entries_.find(key) == entries_.end());
 
@@ -111,6 +112,7 @@
   bool success = client_entry.Serialize(span);
   DCHECK(success);
   CreateEntryDirect(key, span);
+  return 0u;
 }
 
 void TransferCacheTestHelper::FlushEntriesInternal(std::set<EntryKey> keys) {
diff --git a/cc/test/transfer_cache_test_helper.h b/cc/test/transfer_cache_test_helper.h
index fc86362..a6e0d8b 100644
--- a/cc/test/transfer_cache_test_helper.h
+++ b/cc/test/transfer_cache_test_helper.h
@@ -46,7 +46,8 @@
  protected:
   // Serialization helpers.
   bool LockEntryInternal(const EntryKey& key) override;
-  void CreateEntryInternal(const ClientTransferCacheEntry& entry) override;
+  size_t CreateEntryInternal(const ClientTransferCacheEntry& entry,
+                             char* memory) override;
   void FlushEntriesInternal(std::set<EntryKey> keys) override;
 
  private:
diff --git a/chrome/VERSION b/chrome/VERSION
index e77bb1a..fba884e 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=72
 MINOR=0
-BUILD=3591
+BUILD=3592
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java
index 67425879..184676f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoController.java
@@ -45,6 +45,7 @@
 import org.chromium.chrome.browser.preferences.website.ContentSetting;
 import org.chromium.chrome.browser.preferences.website.SingleWebsitePreferences;
 import org.chromium.chrome.browser.previews.PreviewsAndroidBridge;
+import org.chromium.chrome.browser.previews.PreviewsUma;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.ssl.SecurityStateModel;
 import org.chromium.chrome.browser.tab.Tab;
@@ -340,7 +341,10 @@
             viewParams.connectionMessageShown = false;
 
             viewParams.previewShowOriginalClickCallback = () -> {
-                runAfterDismiss(() -> { bridge.loadOriginal(mTab.getWebContents()); });
+                runAfterDismiss(() -> {
+                    PreviewsUma.recordOptOut(bridge.getPreviewsType(mTab.getWebContents()));
+                    bridge.loadOriginal(mTab.getWebContents());
+                });
             };
             final String previewOriginalHost = bridge.getOriginalHost(mTab.getWebContents());
             final String loadOriginalText = mContext.getString(
@@ -558,11 +562,13 @@
 
         @PreviewPageState
         int previewPageState = PreviewPageState.NOT_PREVIEW;
-        if (PreviewsAndroidBridge.getInstance().shouldShowPreviewUI(tab.getWebContents())) {
+        final PreviewsAndroidBridge bridge = PreviewsAndroidBridge.getInstance();
+        if (bridge.shouldShowPreviewUI(tab.getWebContents())) {
             previewPageState = securityLevel == ConnectionSecurityLevel.SECURE
                     ? PreviewPageState.SECURE_PAGE_PREVIEW
                     : PreviewPageState.INSECURE_PAGE_PREVIEW;
 
+            PreviewsUma.recordPageInfoOpened(bridge.getPreviewsType(tab.getWebContents()));
             Tracker tracker = TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile());
             tracker.notifyEvent(EventConstants.PREVIEWS_VERBOSE_STATUS_OPENED);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java
index 3340cb8..f5d939b7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java
@@ -55,6 +55,13 @@
         nativeLoadOriginal(mNativePreviewsAndroidBridge, webContents);
     }
 
+    /**
+     * Returns the committed preview type as a String.
+     */
+    public String getPreviewsType(WebContents webContents) {
+        return nativeGetPreviewsType(mNativePreviewsAndroidBridge, webContents);
+    }
+
     private native long nativeInit();
     private native boolean nativeShouldShowPreviewUI(
             long nativePreviewsAndroidBridge, WebContents webContents);
@@ -64,4 +71,6 @@
             long nativePreviewsAndroidBridge, WebContents webContents);
     private native void nativeLoadOriginal(
             long nativePreviewsAndroidBridge, WebContents webContents);
+    private native String nativeGetPreviewsType(
+            long nativePreviewsAndroidBridge, WebContents webContents);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/previews/PreviewsUma.java b/chrome/android/java/src/org/chromium/chrome/browser/previews/PreviewsUma.java
new file mode 100644
index 0000000..e8386a3
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/previews/PreviewsUma.java
@@ -0,0 +1,78 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.previews;
+
+import org.chromium.base.metrics.RecordHistogram;
+
+/**
+ * Central place to record UMA for Previews in Android.
+ */
+public final class PreviewsUma {
+    // Don't allow this class to be instantiated.
+    private PreviewsUma() {}
+
+    // The base histogram name. The current previews type will be added as a suffix.
+    private static final String BASE_HISTOGRAM_NAME = "Previews.OmniboxAction.%s";
+
+    // This must remain in sync with PreviewsUserOmniboxAction in
+    // //tools/metrics/histograms/enums.xml.
+
+    // User opted out of the preview.
+    private static final int ACTION_OPT_OUT = 0;
+    // User opened the page info dialog.
+    private static final int ACTION_PAGE_INFO_OPENED = 1;
+    // The Lite Page badge was displayed at commit.
+    private static final int ACTION_LITE_PAGE_AT_COMMIT = 2;
+    // The Lite Page badge was displayed at page load finish.
+    private static final int ACTION_LITE_PAGE_AT_FINISH = 3;
+    private static final int ACTION_INDEX_BOUNDARY = 4;
+
+    /**
+     * Records the given action on the BASE_HISTOGRAM with the committed previews type suffix.
+     * @param webContents the active WebContents
+     * @param action the action to record
+     */
+    private static final void recordHistogram(final String previewType, final int action) {
+        assert action >= 0 && action < ACTION_INDEX_BOUNDARY;
+        if (previewType == null || previewType.length() == 0) return;
+
+        final String histogram = String.format(BASE_HISTOGRAM_NAME, previewType);
+        RecordHistogram.recordEnumeratedHistogram(histogram, action, ACTION_INDEX_BOUNDARY);
+    }
+
+    /**
+     * Records that the user opted out of the displayed preview.
+     * @param previewType the committed preview type
+     */
+    public static void recordOptOut(final String previewType) {
+        recordHistogram(previewType, ACTION_OPT_OUT);
+    }
+
+    /**
+     * Records that the user opened the page info dialog.
+     * @param previewType the committed preview type
+     */
+    public static void recordPageInfoOpened(final String previewType) {
+        recordHistogram(previewType, ACTION_PAGE_INFO_OPENED);
+    }
+
+    /**
+     * Records that the lite page badge was displayed at commit.
+     * @param previewType the committed preview type
+     */
+    public static void recordLitePageAtCommit(
+            final String previewType, final boolean isInMainFrame) {
+        if (!isInMainFrame) return;
+        recordHistogram(previewType, ACTION_LITE_PAGE_AT_COMMIT);
+    }
+
+    /**
+     * Records that the lite page badge was displayed at page load finish.
+     * @param previewType the committed preview type
+     */
+    public static void recordLitePageAtLoadFinish(final String previewType) {
+        recordHistogram(previewType, ACTION_LITE_PAGE_AT_FINISH);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 4dd1afb0..29eb5f2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -70,6 +70,8 @@
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager.HomepageStateListener;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.chrome.browser.previews.PreviewsAndroidBridge;
+import org.chromium.chrome.browser.previews.PreviewsUma;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.search_engines.TemplateUrl;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
@@ -415,6 +417,9 @@
                     // also update the verbose status view to make sure the "Lite" badge is
                     // displayed.
                     mLocationBar.updateSecurityIcon();
+                    PreviewsUma.recordLitePageAtLoadFinish(
+                            PreviewsAndroidBridge.getInstance().getPreviewsType(
+                                    tab.getWebContents()));
                 }
 
                 handleIPHForSuccessfulPageLoad(tab);
@@ -557,6 +562,10 @@
                     // is a preview, update the security icon which will also update the verbose
                     // status view to make sure the "Lite" badge is displayed.
                     mLocationBar.updateSecurityIcon();
+                    PreviewsUma.recordLitePageAtCommit(
+                            PreviewsAndroidBridge.getInstance().getPreviewsType(
+                                    tab.getWebContents()),
+                            isInMainFrame);
                 }
 
                 // If the load failed due to a different navigation, there is no need to reset the
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 2652b86..04727a0 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1155,6 +1155,7 @@
   "java/src/org/chromium/chrome/browser/payments/UriUtils.java",
   "java/src/org/chromium/chrome/browser/payments/ui/ContactDetailsSection.java",
   "java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java",
+  "java/src/org/chromium/chrome/browser/previews/PreviewsUma.java",
   "java/src/org/chromium/chrome/browser/widget/prefeditor/Completable.java",
   "java/src/org/chromium/chrome/browser/widget/prefeditor/DropdownFieldAdapter.java",
   "java/src/org/chromium/chrome/browser/widget/prefeditor/EditableOption.java",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 9ff3fec..afe4fbd 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1692,10 +1692,6 @@
      flag_descriptions::kEnableNotificationScrollBarName,
      flag_descriptions::kEnableNotificationScrollBarDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kNotificationScrollBar)},
-    {"ash-enable-new-message-list-view",
-     flag_descriptions::kEnableNewMessageListViewName,
-     flag_descriptions::kEnableNewMessageListViewDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kNewMessageListView)},
     {"allow-touchpad-three-finger-click",
      flag_descriptions::kAllowTouchpadThreeFingerClickName,
      flag_descriptions::kAllowTouchpadThreeFingerClickDescription, kOsCrOS,
@@ -2160,9 +2156,6 @@
 #endif  // ENABLE_EXTENSIONS
 #if !defined(OS_ANDROID)
 #if defined(OS_CHROMEOS)
-    {"enable-system-tray-unified", flag_descriptions::kSystemTrayUnifiedName,
-     flag_descriptions::kSystemTrayUnifiedDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kSystemTrayUnified)},
     {"enable-lock-screen-notification",
      flag_descriptions::kLockScreenNotificationName,
      flag_descriptions::kLockScreenNotificationDescription, kOsCrOS,
@@ -3180,10 +3173,6 @@
      ENABLE_DISABLE_VALUE_TYPE(
          chromeos::switches::kShowAndroidFilesInFilesApp,
          chromeos::switches::kHideAndroidFilesInFilesApp)},
-    {"disable-my-files-navigation",
-     flag_descriptions::kFilesAppDisableMyFilesNavigationName,
-     flag_descriptions::kFilesAppDisableMyFilesNavigationDescription, kOsCrOS,
-     SINGLE_VALUE_TYPE(chromeos::switches::kFilesAppDisableMyFilesNavigation)},
     {"crostini-files", flag_descriptions::kCrostiniFilesName,
      flag_descriptions::kCrostiniFilesDescription, kOsCrOS,
      SINGLE_VALUE_TYPE(chromeos::switches::kCrostiniFiles)},
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 43dd214..84890b07 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -295,7 +295,7 @@
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kDownloadHomeV2{"DownloadHomeV2",
-                                    base::FEATURE_ENABLED_BY_DEFAULT};
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kDownloadHomeShowStorageInfo{
     "DownloadHomeShowStorageInfo", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/chrome_browser_application_mac.h b/chrome/browser/chrome_browser_application_mac.h
index 286f9da..bbb5a86b 100644
--- a/chrome/browser/chrome_browser_application_mac.h
+++ b/chrome/browser/chrome_browser_application_mac.h
@@ -17,17 +17,12 @@
                                                 CrAppControlProtocol> {
  @private
   BOOL handlingSendEvent_;
-  BOOL cyclingWindows_;
 }
 
 // Our implementation of |-terminate:| only attempts to terminate the
 // application, i.e., begins a process which may lead to termination. This
 // method cancels that process.
 - (void)cancelTerminate:(id)sender;
-
-// Keep track of whether windows are being cycled for use in determining whether
-// a Panel window can become the key window.
-- (BOOL)isCyclingWindows;
 @end
 
 #endif  // __OBJC__
diff --git a/chrome/browser/chrome_browser_application_mac.mm b/chrome/browser/chrome_browser_application_mac.mm
index 90f14bb..9167312e 100644
--- a/chrome/browser/chrome_browser_application_mac.mm
+++ b/chrome/browser/chrome_browser_application_mac.mm
@@ -4,7 +4,6 @@
 
 #import "chrome/browser/chrome_browser_application_mac.h"
 
-#include "base/auto_reset.h"
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/mac/call_with_eh_frame.h"
@@ -101,12 +100,6 @@
 
 }  // namespace
 
-// Method exposed for the purposes of overriding.
-// Used to determine when a Panel window can become the key window.
-@interface NSApplication (PanelsCanBecomeKey)
-- (void)_cycleWindowsReversed:(BOOL)arg1;
-@end
-
 @interface BrowserCrApplication ()<NativeEventProcessor> {
   base::ObserverList<content::NativeEventProcessorObserver>::Unchecked
       observers_;
@@ -134,14 +127,6 @@
     }
   }
 
-  // Sanity check to alert if overridden methods are not supported.
-  DCHECK([NSApplication
-      instancesRespondToSelector:@selector(_cycleWindowsReversed:)]);
-  DCHECK([NSApplication
-      instancesRespondToSelector:@selector(_removeWindow:)]);
-  DCHECK([NSApplication
-      instancesRespondToSelector:@selector(_setKeyWindow:)]);
-
   return self;
 }
 
@@ -368,15 +353,6 @@
   return [super accessibilitySetValue:value forAttribute:attribute];
 }
 
-- (void)_cycleWindowsReversed:(BOOL)arg1 {
-  base::AutoReset<BOOL> pin(&cyclingWindows_, YES);
-  [super _cycleWindowsReversed:arg1];
-}
-
-- (BOOL)isCyclingWindows {
-  return cyclingWindows_;
-}
-
 - (void)addNativeEventProcessorObserver:
     (content::NativeEventProcessorObserver*)observer {
   observers_.AddObserver(observer);
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 0e67cdb..82b943dd 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -264,6 +264,7 @@
 #include "content/public/browser/url_loader_request_interceptor.h"
 #include "content/public/browser/vpn_service_proxy.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_ui_url_loader_factory.h"
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/content_descriptors.h"
@@ -4966,6 +4967,16 @@
     content::PreviewsState initial_state,
     content::NavigationHandle* navigation_handle) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // TODO(ryansturm): Support re-evaluation of PreviewsState for redirects.
+  // https://crbug.com/892253.
+  content::WebContents* web_contents = navigation_handle->GetWebContents();
+  content::WebContentsDelegate* delegate = web_contents->GetDelegate();
+
+  if (delegate) {
+    delegate->AdjustPreviewsStateForNavigation(web_contents, &initial_state);
+  }
+
   // If the embedder does not want previews, do not provide any.
   if (initial_state & content::PREVIEWS_OFF ||
       initial_state & content::PREVIEWS_NO_TRANSFORM) {
@@ -4979,8 +4990,7 @@
     return initial_state;
   }
 
-  auto* browser_context =
-      navigation_handle->GetWebContents()->GetBrowserContext();
+  auto* browser_context = web_contents->GetBrowserContext();
 
   PreviewsService* previews_service = PreviewsServiceFactory::GetForProfile(
       Profile::FromBrowserContext(browser_context));
@@ -4995,7 +5005,7 @@
   }
 
   PreviewsUITabHelper* ui_tab_helper =
-      PreviewsUITabHelper::FromWebContents(navigation_handle->GetWebContents());
+      PreviewsUITabHelper::FromWebContents(web_contents);
   // If this tab does not have a PreviewsUITabHelper, no preview should be
   // served.
   if (!ui_tab_helper)
@@ -5151,17 +5161,20 @@
       previews_service->previews_ui_service()->previews_decider_impl();
   DCHECK(previews_decider_impl);
 
-  // ChromeNavigationData should be null after network s13n is turned on.
-  data_reduction_proxy::DataReductionProxyData* drp_data = nullptr;
-  ChromeNavigationData* chrome_navigation_data =
-      static_cast<ChromeNavigationData*>(
-          navigation_handle->GetNavigationData());
-  if (chrome_navigation_data)
-    drp_data = chrome_navigation_data->GetDataReductionProxyData();
+  std::unique_ptr<data_reduction_proxy::DataReductionProxyData> drp_data;
+  auto* settings =
+      DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
+          navigation_handle->GetWebContents()->GetBrowserContext());
+  if (settings) {
+    // TODO(898326): |drp_data| may be incomplete because |navigation_handle|
+    // does not yet have all the response information.
+    drp_data = settings->CreateDataFromNavigationHandle(navigation_handle,
+                                                        response_headers);
+  }
 
   // Determine effective PreviewsState for this committed main frame response.
   content::PreviewsState committed_state = DetermineCommittedPreviewsForURL(
-      navigation_handle->GetURL(), drp_data, previews_user_data,
+      navigation_handle->GetURL(), drp_data.get(), previews_user_data,
       previews_decider_impl, initial_state);
 
   // Double check that we never serve a preview when we have a
diff --git a/chrome/browser/chromeos/accessibility/sticky_keys_browsertest.cc b/chrome/browser/chromeos/accessibility/sticky_keys_browsertest.cc
index 814be94..b41fdcf 100644
--- a/chrome/browser/chromeos/accessibility/sticky_keys_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/sticky_keys_browsertest.cc
@@ -28,19 +28,11 @@
 #include "components/prefs/pref_service.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/events/keycodes/keyboard_codes.h"
-#include "ui/events/test/event_generator.h"
 #include "ui/gfx/native_widget_types.h"
 
 namespace chromeos {
 
 class StickyKeysBrowserTest : public InProcessBrowserTest {
- public:
-  void SetUpOnMainThread() override {
-    content::BrowserTestBase::SetUpOnMainThread();
-    event_generator_.reset(
-        new ui::test::EventGenerator(browser()->window()->GetNativeWindow()));
-  }
-
  protected:
   StickyKeysBrowserTest() = default;
   ~StickyKeysBrowserTest() override = default;
@@ -74,14 +66,11 @@
   }
 
   void SendKeyPress(ui::KeyboardCode key) {
-    event_generator_->PressKey(key, ui::EF_NONE);
-    content::RunAllPendingInMessageLoop();
-    event_generator_->ReleaseKey(key, ui::EF_NONE);
-    content::RunAllPendingInMessageLoop();
+    EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), key, false, false,
+                                                false, false));
   }
 
   content::NotificationRegistrar registrar_;
-  std::unique_ptr<ui::test::EventGenerator> event_generator_;
 
   DISALLOW_COPY_AND_ASSIGN(StickyKeysBrowserTest);
 };
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
index b3642845..c9c24f4 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <utility>
 
+#include "ash/public/cpp/ash_pref_names.h"
 #include "base/logging.h"
 #include "base/memory/singleton.h"
 #include "base/stl_util.h"
@@ -193,7 +194,7 @@
   ~TabletModeObserver() override = default;
 
   void OnTabletModeToggled(bool enabled) override {
-    owner_->SetArcIMEAllowed(enabled);
+    owner_->UpdateArcIMEAllowed();
     owner_->NotifyInputMethodManagerObservers(enabled);
   }
 
@@ -243,6 +244,18 @@
   // PreProfileInit() and this service is created in PostProfileInit().
   DCHECK(TabletModeClient::Get());
   TabletModeClient::Get()->AddObserver(tablet_mode_observer_.get());
+
+  chromeos::AccessibilityManager* accessibility_manager =
+      chromeos::AccessibilityManager::Get();
+  if (accessibility_manager) {
+    // accessibility_status_subscription_ ensures the callback is removed when
+    // ArcInputMethodManagerService is destroyed, so it's safe to use
+    // base::Unretained(this) here.
+    accessibility_status_subscription_ =
+        accessibility_manager->RegisterCallback(base::BindRepeating(
+            &ArcInputMethodManagerService::OnAccessibilityStatusChanged,
+            base::Unretained(this)));
+  }
 }
 
 ArcInputMethodManagerService::~ArcInputMethodManagerService() {
@@ -339,7 +352,7 @@
                                   base::JoinString(active_ime_list, ","));
 
   // Refresh allowed IME list.
-  SetArcIMEAllowed(TabletModeClient::Get()->tablet_mode_enabled());
+  UpdateArcIMEAllowed();
 }
 
 void ArcInputMethodManagerService::OnConnectionClosed() {
@@ -427,6 +440,18 @@
   }
 }
 
+void ArcInputMethodManagerService::OnAccessibilityStatusChanged(
+    const chromeos::AccessibilityStatusEventDetails& event_details) {
+  if (event_details.notification_type !=
+      chromeos::ACCESSIBILITY_TOGGLE_VIRTUAL_KEYBOARD) {
+    // This class is not interested in a11y events except toggling virtual
+    // keyboard event.
+    return;
+  }
+
+  UpdateArcIMEAllowed();
+}
+
 InputConnectionImpl*
 ArcInputMethodManagerService::GetInputConnectionForTesting() {
   return active_connection_.get();
@@ -533,7 +558,9 @@
                                   base::JoinString(ime_id_list, ","));
 }
 
-void ArcInputMethodManagerService::SetArcIMEAllowed(bool allowed) {
+void ArcInputMethodManagerService::UpdateArcIMEAllowed() {
+  const bool allowed = ShouldArcIMEAllowed();
+
   auto* manager = chromeos::input_method::InputMethodManager::Get();
   std::set<std::string> allowed_method_ids_set;
   {
@@ -604,6 +631,12 @@
     manager->GetActiveIMEState()->EnableInputMethod(id);
 }
 
+bool ArcInputMethodManagerService::ShouldArcIMEAllowed() const {
+  return !profile_->GetPrefs()->GetBoolean(
+             ash::prefs::kAccessibilityVirtualKeyboardEnabled) &&
+         TabletModeClient::Get()->tablet_mode_enabled();
+}
+
 void ArcInputMethodManagerService::NotifyInputMethodManagerObservers(
     bool is_tablet_mode) {
   // Togging the mode may enable or disable all the ARC IMEs. To dynamically
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.h b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.h
index daa38753..65f60fd 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.h
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_bridge.h"
 #include "chrome/browser/chromeos/arc/input_method_manager/input_connection_impl.h"
 #include "chrome/browser/chromeos/input_method/input_method_engine.h"
@@ -67,6 +68,11 @@
                           Profile* profile,
                           bool show_message) override;
 
+  // Called when a11y keyboard option changed and disables ARC IME while a11y
+  // keyboard option is enabled.
+  void OnAccessibilityStatusChanged(
+      const chromeos::AccessibilityStatusEventDetails& event_details);
+
   InputConnectionImpl* GetInputConnectionForTesting();
 
  private:
@@ -90,8 +96,12 @@
   void RemoveArcIMEFromPrefs();
   void RemoveArcIMEFromPref(const char* pref_name);
 
-  // Calls InputMethodManager.SetAllowedInputMethods according to the argument.
-  void SetArcIMEAllowed(bool allowed);
+  // Calls InputMethodManager.SetAllowedInputMethods according to the return
+  // value of ShouldArcImeAllowed().
+  void UpdateArcIMEAllowed();
+  // Returns whether ARC IMEs should be allowed now or not.
+  // It depends on tablet mode state and a11y keyboard option.
+  bool ShouldArcIMEAllowed() const;
 
   // Notifies InputMethodManager's observers of possible ARC IME state changes.
   void NotifyInputMethodManagerObservers(bool is_tablet_mode);
@@ -122,6 +132,9 @@
 
   std::unique_ptr<InputMethodObserver> input_method_observer_;
 
+  std::unique_ptr<chromeos::AccessibilityStatusSubscription>
+      accessibility_status_subscription_;
+
   DISALLOW_COPY_AND_ASSIGN(ArcInputMethodManagerService);
 };
 
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc
index 215e5da..0bc42bdd 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "ash/keyboard/ash_keyboard_controller.h"
+#include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/interfaces/constants.mojom.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
@@ -683,6 +684,67 @@
   EXPECT_FALSE(imm()->state()->IsInputMethodAllowed(arc_ime_id));
 }
 
+TEST_F(ArcInputMethodManagerServiceTest,
+       DisallowArcIMEsWhenAccessibilityKeyboardEnabled) {
+  namespace ceiu = chromeos::extension_ime_util;
+  using crx_file::id_util::GenerateId;
+
+  base::test::ScopedFeatureList feature;
+  feature.InitAndEnableFeature(kEnableInputMethodFeature);
+
+  const std::string extension_ime_id =
+      ceiu::GetInputMethodID(GenerateId("test.extension.ime"), "us");
+  const std::string component_extension_ime_id =
+      ceiu::GetComponentInputMethodID(
+          GenerateId("test.component.extension.ime"), "us");
+  const std::string arc_ime_id =
+      ceiu::GetArcInputMethodID(GenerateId("test.arc.ime"), "us");
+
+  // Start from tablet mode.
+  ToggleTabletMode(true);
+
+  // Activate 3 IMEs.
+  imm()->state()->AddActiveInputMethodId(extension_ime_id);
+  imm()->state()->AddActiveInputMethodId(component_extension_ime_id);
+  imm()->state()->AddActiveInputMethodId(arc_ime_id);
+
+  // Update the prefs because the testee checks them. Note that toggling the
+  // mode never changes the prefs.
+  profile()->GetPrefs()->SetString(
+      prefs::kLanguageEnabledImes,
+      base::StringPrintf("%s,%s,%s", extension_ime_id.c_str(),
+                         component_extension_ime_id.c_str(),
+                         arc_ime_id.c_str()));
+
+  // All IMEs are allowed to use.
+  EXPECT_TRUE(imm()->state()->IsInputMethodAllowed(extension_ime_id));
+  EXPECT_TRUE(imm()->state()->IsInputMethodAllowed(component_extension_ime_id));
+  EXPECT_TRUE(imm()->state()->IsInputMethodAllowed(arc_ime_id));
+
+  // Enable a11y keyboard option.
+  profile()->GetPrefs()->SetBoolean(
+      ash::prefs::kAccessibilityVirtualKeyboardEnabled, true);
+  // Notify ArcInputMethodManagerService.
+  service()->OnAccessibilityStatusChanged(
+      {chromeos::ACCESSIBILITY_TOGGLE_VIRTUAL_KEYBOARD, true});
+
+  // ARC IME is not allowed.
+  EXPECT_TRUE(imm()->state()->IsInputMethodAllowed(extension_ime_id));
+  EXPECT_TRUE(imm()->state()->IsInputMethodAllowed(component_extension_ime_id));
+  EXPECT_FALSE(imm()->state()->IsInputMethodAllowed(arc_ime_id));
+
+  // Disable a11y keyboard option.
+  profile()->GetPrefs()->SetBoolean(
+      ash::prefs::kAccessibilityVirtualKeyboardEnabled, false);
+  // Notify ArcInputMethodManagerService.
+  service()->OnAccessibilityStatusChanged(
+      {chromeos::ACCESSIBILITY_TOGGLE_VIRTUAL_KEYBOARD, false});
+
+  EXPECT_TRUE(imm()->state()->IsInputMethodAllowed(extension_ime_id));
+  EXPECT_TRUE(imm()->state()->IsInputMethodAllowed(component_extension_ime_id));
+  EXPECT_TRUE(imm()->state()->IsInputMethodAllowed(arc_ime_id));
+}
+
 TEST_F(ArcInputMethodManagerServiceTest, FocusAndBlur) {
   base::test::ScopedFeatureList feature;
   feature.InitAndEnableFeature(kEnableInputMethodFeature);
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index 2c917f4..907d899 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -362,18 +362,6 @@
     if (is_aborted_)
       return;
 
-    // Share folders from Downloads, etc with container.
-    // TODO(joelhockey): SharePersistedPaths is getting called on every Restart,
-    // even if the VM is already running.  Only call SharePersistedPaths when
-    // VM starts for the first time.
-    SharePersistedPaths(
-        profile_,
-        base::BindOnce(&CrostiniRestarter::OnPersistedPathsShared, this));
-  }
-
-  void OnPersistedPathsShared(bool success, std::string failure_reason) {
-    // Sharing paths is not critical, FinishRestart(SUCCESS) regardless.
-    // Errors will be logged.
     FinishRestart(CrostiniResult::SUCCESS);
   }
 
@@ -1295,6 +1283,11 @@
       vm_name, base::BindOnce(&CrostiniManager::OnStartTremplin,
                               weak_ptr_factory_.GetWeakPtr(), vm_name,
                               std::move(callback), CrostiniResult::SUCCESS));
+
+  // Share folders from Downloads, etc with default VM.
+  if (vm_name == kCrostiniDefaultVmName) {
+    SharePersistedPaths(profile_, base::DoNothing());
+  }
 }
 
 void CrostiniManager::OnStartTremplin(std::string vm_name,
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index 122904ca..bfed26d 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -1069,7 +1069,12 @@
 }
 
 void WizardController::OnSyncConsentFinished() {
-  ShowDiscoverScreen();
+  if (chromeos::quick_unlock::IsFingerprintEnabled(
+          ProfileManager::GetActiveUserProfile())) {
+    ShowFingerprintSetupScreen();
+  } else {
+    ShowDiscoverScreen();
+  }
 }
 
 void WizardController::OnDiscoverScreenFinished() {
@@ -1077,16 +1082,11 @@
 }
 
 void WizardController::OnMarketingOptInFinished() {
-  if (chromeos::quick_unlock::IsFingerprintEnabled(
-          ProfileManager::GetActiveUserProfile())) {
-    ShowFingerprintSetupScreen();
-  } else {
-    ShowArcTermsOfServiceScreen();
-  }
+  ShowArcTermsOfServiceScreen();
 }
 
 void WizardController::OnFingerprintSetupFinished() {
-  ShowArcTermsOfServiceScreen();
+  ShowDiscoverScreen();
 }
 
 void WizardController::OnArcTermsOfServiceSkipped() {
diff --git a/chrome/browser/extensions/bookmark_app_helper.cc b/chrome/browser/extensions/bookmark_app_helper.cc
index 0a04b25..1a6ea3f0 100644
--- a/chrome/browser/extensions/bookmark_app_helper.cc
+++ b/chrome/browser/extensions/bookmark_app_helper.cc
@@ -388,6 +388,13 @@
   if (contents_->IsBeingDestroyed())
     return;
 
+  if (require_manifest_ && !data.valid_manifest) {
+    LOG(WARNING) << "Did not install " << web_app_info_.app_url.spec()
+                 << " because it didn't have a manifest";
+    callback_.Run(nullptr, web_app_info_);
+    return;
+  }
+
   for_installable_site_ =
       data.error_code == NO_ERROR_DETECTED && !shortcut_app_requested_
           ? ForInstallableSite::kYes
diff --git a/chrome/browser/extensions/bookmark_app_helper.h b/chrome/browser/extensions/bookmark_app_helper.h
index 5ef059e..9a6f068 100644
--- a/chrome/browser/extensions/bookmark_app_helper.h
+++ b/chrome/browser/extensions/bookmark_app_helper.h
@@ -112,6 +112,12 @@
     bypass_service_worker_check_ = true;
   }
 
+  // If called, the installation will only succeed if a manifest is found.
+  void set_require_manifest() {
+    DCHECK(is_default_app());
+    require_manifest_ = true;
+  }
+
   // If called, the installed app will launch in |launch_type|. User might still
   // be able to change the launch type depending on the type of app.
   void set_forced_launch_type(LaunchType launch_type) {
@@ -191,6 +197,8 @@
 
   bool bypass_service_worker_check_ = false;
 
+  bool require_manifest_ = false;
+
   // The mechanism via which the app creation was triggered.
   WebappInstallSource install_source_;
 
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 53c0040e..9256c30 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -77,10 +77,6 @@
 const char kEnableBloatedRendererDetectionDescription[] =
     "Enable bloated renderer detection";
 
-const char kSystemTrayUnifiedName[] = "New system menu";
-const char kSystemTrayUnifiedDescription[] =
-    "Enable the experimental system menu.";
-
 const char kAsyncImageDecodingName[] = "AsyncImageDecoding";
 const char kAsyncImageDecodingDescription[] =
     "Enables asynchronous decoding of images from raster for web content";
@@ -674,10 +670,6 @@
 const char kEnableNotificationScrollBarDescription[] =
     "Enable the scroll bar of the notification list in Unified System Tray.";
 
-const char kEnableNewMessageListViewName[] = "Enable new message list view";
-const char kEnableNewMessageListViewDescription[] =
-    "Enable new implementation of message list view.";
-
 const char kEnableNupPrintingName[] = "Enable N-up printing";
 const char kEnableNupPrintingDescription[] =
     "Enable N-up printing in the print preview panel.";
@@ -1642,20 +1634,11 @@
     "Shows previews of the open windows for a given running app when hovering "
     "over the shelf.";
 
-const char kShelfNewUiName[] = "Newest shelf redesign.";
-const char kShelfNewUiDescription[] = "Enables the newest shelf appearance.";
-
 const char kShowAndroidFilesInFilesAppName[] =
     "Show Android files in Files app";
 const char kShowAndroidFilesInFilesAppDescription[] =
     "Show Android files in Files app if Android is enabled on the device.";
 
-const char kFilesAppDisableMyFilesNavigationName[] =
-    "Disable the My Files on the navigation in the Files app";
-const char kFilesAppDisableMyFilesNavigationDescription[] =
-    "Disables the My Files on the  navigation in the Files app, which displays "
-    R"("My Files" root for Downloads, Linux and Play files)";
-
 const char kShowAutofillSignaturesName[] = "Show autofill signatures.";
 const char kShowAutofillSignaturesDescription[] =
     "Annotates web forms with Autofill signatures as HTML attributes. Also "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index d703b80..910dbe9 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -73,9 +73,6 @@
 extern const char kEnableBloatedRendererDetectionName[];
 extern const char kEnableBloatedRendererDetectionDescription[];
 
-extern const char kSystemTrayUnifiedName[];
-extern const char kSystemTrayUnifiedDescription[];
-
 extern const char kAsyncImageDecodingName[];
 extern const char kAsyncImageDecodingDescription[];
 
@@ -431,9 +428,6 @@
 extern const char kEnableNotificationScrollBarName[];
 extern const char kEnableNotificationScrollBarDescription[];
 
-extern const char kEnableNewMessageListViewName[];
-extern const char kEnableNewMessageListViewDescription[];
-
 extern const char kEnableNupPrintingName[];
 extern const char kEnableNupPrintingDescription[];
 
@@ -996,15 +990,9 @@
 extern const char kShelfHoverPreviewsName[];
 extern const char kShelfHoverPreviewsDescription[];
 
-extern const char kShelfNewUiName[];
-extern const char kShelfNewUiDescription[];
-
 extern const char kShowAndroidFilesInFilesAppName[];
 extern const char kShowAndroidFilesInFilesAppDescription[];
 
-extern const char kFilesAppDisableMyFilesNavigationName[];
-extern const char kFilesAppDisableMyFilesNavigationDescription[];
-
 extern const char kShowAutofillSignaturesName[];
 extern const char kShowAutofillSignaturesDescription[];
 
diff --git a/chrome/browser/media/cast_remoting_connector.cc b/chrome/browser/media/cast_remoting_connector.cc
index ba39f521..4abe021 100644
--- a/chrome/browser/media/cast_remoting_connector.cc
+++ b/chrome/browser/media/cast_remoting_connector.cc
@@ -145,8 +145,11 @@
               if (media_router::ShouldUseViewsDialog()) {
                 media_router::MediaRemotingDialogView::GetPermission(
                     contents, std::move(result_callback));
-                return base::BindOnce(
-                    &media_router::MediaRemotingDialogView::HideDialog);
+                return media_router::MediaRemotingDialogView::IsShowing()
+                           ? base::BindOnce(
+                                 &media_router::MediaRemotingDialogView::
+                                     HideDialog)
+                           : CancelPermissionRequestCallback();
               } else {
                 std::move(result_callback).Run(true);
                 return CancelPermissionRequestCallback();
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.cc
index 5b903ab..c3e8243 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.cc
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.cc
@@ -17,6 +17,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/data_use_measurement/chrome_data_use_measurement.h"
+#include "chrome/browser/loader/chrome_navigation_data.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/previews/previews_service.h"
 #include "chrome/browser/previews/previews_service_factory.h"
@@ -28,8 +29,8 @@
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
 #include "components/data_reduction_proxy/core/browser/data_store.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -37,6 +38,8 @@
 #include "components/proxy_config/proxy_config_pref_names.h"
 #include "components/proxy_config/proxy_prefs.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/network_service_instance.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/proxy_server.h"
 #include "net/proxy_resolution/proxy_config.h"
@@ -222,6 +225,7 @@
               data_reduction_proxy::DataReductionProxyPingbackClientImpl>(
               url_loader_factory, ui_task_runner),
           g_browser_process->network_quality_tracker(),
+          content::GetNetworkConnectionTracker(),
           g_browser_process->data_use_measurement(), ui_task_runner,
           io_data->io_task_runner(), db_task_runner, commit_delay);
   data_reduction_proxy::DataReductionProxySettings::
@@ -251,6 +255,63 @@
   }
 }
 
+std::unique_ptr<data_reduction_proxy::DataReductionProxyData>
+DataReductionProxyChromeSettings::CreateDataFromNavigationHandle(
+    content::NavigationHandle* handle,
+    const net::HttpResponseHeaders* headers) {
+  ChromeNavigationData* chrome_navigation_data =
+      static_cast<ChromeNavigationData*>(handle->GetNavigationData());
+  if (chrome_navigation_data) {
+    if (chrome_navigation_data->GetDataReductionProxyData())
+      return chrome_navigation_data->GetDataReductionProxyData()->DeepCopy();
+    return nullptr;
+  }
+
+  // Some unit tests don't have data_reduction_proxy_service() set.
+  if (!data_reduction_proxy_service())
+    return nullptr;
+
+  // TODO(721403): Need to fill in:
+  //  - client_lofi_requestd_
+  //  - session_key_
+  //  - page_id_
+  //  - request_info_
+  auto data = std::make_unique<data_reduction_proxy::DataReductionProxyData>();
+  data->set_request_url(handle->GetURL());
+  data->set_effective_connection_type(
+      data_reduction_proxy_service()->GetEffectiveConnectionType());
+  data->set_connection_type(net::NetworkChangeNotifier::ConnectionType(
+      data_reduction_proxy_service()->GetConnectionType()));
+  data->set_used_data_reduction_proxy(
+      IsConfiguredDataReductionProxy(handle->GetProxyServer()));
+
+  if (!headers || headers->IsRedirect(nullptr))
+    return data;
+
+  if (handle->WasResponseCached() &&
+      headers->HasHeader(data_reduction_proxy::chrome_proxy_header())) {
+    data->set_was_cached_data_reduction_proxy_response(true);
+  }
+
+  switch (data_reduction_proxy::ParseResponseTransform(*headers)) {
+    case data_reduction_proxy::TRANSFORM_LITE_PAGE:
+      data->set_lite_page_received(true);
+      break;
+    case data_reduction_proxy::TRANSFORM_PAGE_POLICIES_EMPTY_IMAGE:
+      data->set_lofi_policy_received(true);
+      break;
+    case data_reduction_proxy::TRANSFORM_EMPTY_IMAGE:
+      data->set_lofi_received(true);
+      break;
+    case data_reduction_proxy::TRANSFORM_IDENTITY:
+    case data_reduction_proxy::TRANSFORM_COMPRESSED_VIDEO:
+    case data_reduction_proxy::TRANSFORM_NONE:
+    case data_reduction_proxy::TRANSFORM_UNKNOWN:
+      break;
+  }
+  return data;
+}
+
 // static
 data_reduction_proxy::Client DataReductionProxyChromeSettings::GetClient() {
 #if defined(OS_ANDROID)
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h
index 2fa3d69..5eb7858 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h
@@ -21,6 +21,10 @@
 class SingleThreadTaskRunner;
 }
 
+namespace content {
+class NavigationHandle;
+}
+
 namespace data_reduction_proxy {
 class DataReductionProxyIOData;
 class DataStore;
@@ -95,6 +99,12 @@
   void SetIgnoreLongTermBlackListRules(
       bool ignore_long_term_black_list_rules) override;
 
+  // Builds an instance of DataReductionProxyData from the given |handle| and
+  // |headers|.
+  std::unique_ptr<data_reduction_proxy::DataReductionProxyData>
+  CreateDataFromNavigationHandle(content::NavigationHandle* handle,
+                                 const net::HttpResponseHeaders* headers);
+
  private:
   // Helper method for migrating the Data Reduction Proxy away from using the
   // proxy pref. Returns the ProxyPrefMigrationResult value indicating the
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_unittest.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_unittest.cc
index 23dd45b..32224b58 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_unittest.cc
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_unittest.cc
@@ -9,38 +9,64 @@
 
 #include "base/message_loop/message_loop.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
+#include "components/data_reduction_proxy/proto/client_config.pb.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/proxy_config/proxy_config_pref_names.h"
+#include "content/public/browser/navigation_handle.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/test/test_network_connection_tracker.h"
+#include "services/network/test/test_network_quality_tracker.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace {
 using testing::_;
 using testing::Return;
 
-class DataReductionProxyChromeSettingsTest : public testing::Test {
+constexpr char kUrl[] = "http://example.com";
+constexpr char kProxyPac[] = "PROXY proxy.net";
+}  // namespace
+
+class DataReductionProxyChromeSettingsTest
+    : public ChromeRenderViewHostTestHarness {
  public:
   void SetUp() override {
-    drp_chrome_settings_ = std::make_unique<DataReductionProxyChromeSettings>();
+    ChromeRenderViewHostTestHarness::SetUp();
+    network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
+        network::mojom::ConnectionType::CONNECTION_4G);
+    auto settings = std::make_unique<DataReductionProxyChromeSettings>();
+    drp_chrome_settings_ = settings.get();
     test_context_ =
         data_reduction_proxy::DataReductionProxyTestContext::Builder()
             .WithMockConfig()
-            .SkipSettingsInitialization()
+            .WithSettings(std::move(settings))
             .Build();
+    net::ProxyList proxies;
+    proxies.SetFromPacString(kProxyPac);
+    test_context_->data_reduction_proxy_service()->SetConfiguredProxiesOnUI(
+        proxies);
+    test_context_->test_network_quality_tracker()
+        ->ReportEffectiveConnectionTypeForTesting(
+            net::EFFECTIVE_CONNECTION_TYPE_4G);
     config_ = test_context_->mock_config();
-    drp_chrome_settings_->ResetConfigForTest(config_);
     dict_ = std::make_unique<base::DictionaryValue>();
 
     PrefRegistrySimple* registry = test_context_->pref_service()->registry();
     registry->RegisterDictionaryPref(proxy_config::prefs::kProxy);
   }
 
-  base::MessageLoopForIO message_loop_;
-  std::unique_ptr<DataReductionProxyChromeSettings> drp_chrome_settings_;
+  void TearDown() override {
+    // Make sure |test_context_| is destroyed before message loop is destroyed.
+    test_context_.reset();
+    ChromeRenderViewHostTestHarness::TearDown();
+  }
+
+  DataReductionProxyChromeSettings* drp_chrome_settings_;
   std::unique_ptr<base::DictionaryValue> dict_;
   std::unique_ptr<data_reduction_proxy::DataReductionProxyTestContext>
       test_context_;
@@ -354,3 +380,110 @@
         DataReductionProxyChromeSettings::PROXY_PREF_NOT_CLEARED, 1);
   }
 }
+
+TEST_F(DataReductionProxyChromeSettingsTest, CreateDataBasic) {
+  std::unique_ptr<content::NavigationHandle> handle =
+      content::NavigationHandle::CreateNavigationHandleForTesting(GURL(kUrl),
+                                                                  main_rfh());
+  std::string headers = "HTTP/1.0 200 OK\n";
+  handle->CallWillProcessResponseForTesting(
+      main_rfh(),
+      net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()), false,
+      net::ProxyServer::Direct());
+  auto data = drp_chrome_settings_->CreateDataFromNavigationHandle(
+      handle.get(), handle->GetResponseHeaders());
+
+  EXPECT_EQ(data->request_url(), GURL(kUrl));
+  EXPECT_EQ(data->effective_connection_type(),
+            net::EFFECTIVE_CONNECTION_TYPE_4G);
+  EXPECT_EQ(data->connection_type(),
+            net::NetworkChangeNotifier::ConnectionType::CONNECTION_4G);
+  EXPECT_FALSE(data->used_data_reduction_proxy());
+}
+
+TEST_F(DataReductionProxyChromeSettingsTest, CreateDataUsedDataReductionProxy) {
+  std::unique_ptr<content::NavigationHandle> handle =
+      content::NavigationHandle::CreateNavigationHandleForTesting(GURL(kUrl),
+                                                                  main_rfh());
+  std::string headers = "HTTP/1.0 200 OK\n";
+  handle->CallWillProcessResponseForTesting(
+      main_rfh(),
+      net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()), false,
+      net::ProxyServer::FromPacString(kProxyPac));
+  auto data = drp_chrome_settings_->CreateDataFromNavigationHandle(
+      handle.get(), handle->GetResponseHeaders());
+
+  EXPECT_TRUE(data->used_data_reduction_proxy());
+}
+
+TEST_F(DataReductionProxyChromeSettingsTest, CreateDataCachedResponse) {
+  std::unique_ptr<content::NavigationHandle> handle =
+      content::NavigationHandle::CreateNavigationHandleForTesting(GURL(kUrl),
+                                                                  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_TRUE(data->was_cached_data_reduction_proxy_response());
+}
+
+TEST_F(DataReductionProxyChromeSettingsTest, CreateDataWithLitePage) {
+  std::unique_ptr<content::NavigationHandle> handle =
+      content::NavigationHandle::CreateNavigationHandleForTesting(GURL(kUrl),
+                                                                  main_rfh());
+  std::string headers =
+      "HTTP/1.0 200 OK\n"
+      "chrome-proxy-content-transform: lite-page\n";
+  handle->CallWillProcessResponseForTesting(
+      main_rfh(),
+      net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()), false,
+      net::ProxyServer::Direct());
+  auto data = drp_chrome_settings_->CreateDataFromNavigationHandle(
+      handle.get(), handle->GetResponseHeaders());
+
+  EXPECT_TRUE(data->lite_page_received());
+  EXPECT_FALSE(data->lofi_received());
+  EXPECT_FALSE(data->lofi_policy_received());
+}
+
+TEST_F(DataReductionProxyChromeSettingsTest, CreateDataWithLofiPolicyReceived) {
+  std::unique_ptr<content::NavigationHandle> handle =
+      content::NavigationHandle::CreateNavigationHandleForTesting(GURL(kUrl),
+                                                                  main_rfh());
+  std::string headers =
+      "HTTP/1.0 200 OK\n"
+      "chrome-proxy: page-policies=empty-image\n";
+  handle->CallWillProcessResponseForTesting(
+      main_rfh(),
+      net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()), false,
+      net::ProxyServer::Direct());
+  auto data = drp_chrome_settings_->CreateDataFromNavigationHandle(
+      handle.get(), handle->GetResponseHeaders());
+
+  EXPECT_FALSE(data->lite_page_received());
+  EXPECT_FALSE(data->lofi_received());
+  EXPECT_TRUE(data->lofi_policy_received());
+}
+
+TEST_F(DataReductionProxyChromeSettingsTest, CreateDataWithLofiReceived) {
+  std::unique_ptr<content::NavigationHandle> handle =
+      content::NavigationHandle::CreateNavigationHandleForTesting(GURL(kUrl),
+                                                                  main_rfh());
+  std::string headers =
+      "HTTP/1.0 200 OK\n"
+      "chrome-proxy-content-transform: empty-image\n";
+  handle->CallWillProcessResponseForTesting(
+      main_rfh(),
+      net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()), false,
+      net::ProxyServer::Direct());
+  auto data = drp_chrome_settings_->CreateDataFromNavigationHandle(
+      handle.get(), handle->GetResponseHeaders());
+
+  EXPECT_FALSE(data->lite_page_received());
+  EXPECT_TRUE(data->lofi_received());
+  EXPECT_FALSE(data->lofi_policy_received());
+}
diff --git a/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc b/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
index 29c8528..6875aa3a 100644
--- a/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
+++ b/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
@@ -1309,6 +1309,10 @@
   network::ResourceRequest request =
       CreateResourceRequest(url, method, extra_headers, is_main_frame);
 
+  request.previews_state = test_base_->allow_preview()
+                               ? content::OFFLINE_PAGE_ON
+                               : content::PREVIEWS_OFF;
+
   url_loader_ = OfflinePageURLLoader::Create(
       navigation_ui_data_.get(),
       test_base_->web_contents()->GetMainFrame()->GetFrameTreeNodeId(), request,
@@ -1320,9 +1324,6 @@
     return;
 
   url_loader_->SetTabIdGetterForTesting(base::BindRepeating(&GetTabId, kTabId));
-  url_loader_->SetShouldAllowPreviewCallbackForTesting(
-      base::BindRepeating(&OfflinePageRequestHandlerTestBase::allow_preview,
-                          base::Unretained(test_base_)));
 }
 
 void OfflinePageURLLoaderBuilder::InterceptRequest(
diff --git a/chrome/browser/offline_pages/offline_page_url_loader.cc b/chrome/browser/offline_pages/offline_page_url_loader.cc
index 03eb2b69..3f6186b 100644
--- a/chrome/browser/offline_pages/offline_page_url_loader.cc
+++ b/chrome/browser/offline_pages/offline_page_url_loader.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/offline_pages/offline_page_url_loader.h"
 
+#include "base/bind.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
@@ -12,6 +13,7 @@
 #include "components/offline_pages/core/offline_page_item.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/previews_state.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
 #include "net/base/io_buffer.h"
 #include "net/url_request/url_request.h"
@@ -86,6 +88,8 @@
       transition_type_(tentative_resource_request.transition_type),
       loader_callback_(std::move(callback)),
       binding_(this),
+      is_offline_preview_allowed_(tentative_resource_request.previews_state &
+                                  content::OFFLINE_PAGE_ON),
       weak_ptr_factory_(this) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
@@ -106,11 +110,6 @@
   tab_id_getter_ = tab_id_getter;
 }
 
-void OfflinePageURLLoader::SetShouldAllowPreviewCallbackForTesting(
-    ShouldAllowPreviewCallback should_allow_preview_callback) {
-  should_allow_preview_callback_ = should_allow_preview_callback;
-}
-
 void OfflinePageURLLoader::FollowRedirect(
     const base::Optional<std::vector<std::string>>&
         to_be_removed_request_headers,
@@ -210,11 +209,7 @@
 }
 
 bool OfflinePageURLLoader::ShouldAllowPreview() const {
-  // TODO(jianli): This is a temporary hack to make the tests pass. The real
-  // logic needs to be implemented.
-  if (!should_allow_preview_callback_.is_null())
-    return should_allow_preview_callback_.Run();
-  return false;
+  return is_offline_preview_allowed_;
 }
 
 int OfflinePageURLLoader::GetPageTransition() const {
diff --git a/chrome/browser/offline_pages/offline_page_url_loader.h b/chrome/browser/offline_pages/offline_page_url_loader.h
index e19fe18..b43705a 100644
--- a/chrome/browser/offline_pages/offline_page_url_loader.h
+++ b/chrome/browser/offline_pages/offline_page_url_loader.h
@@ -48,10 +48,6 @@
   void SetTabIdGetterForTesting(
       OfflinePageRequestHandler::Delegate::TabIdGetter tab_id_getter);
 
-  using ShouldAllowPreviewCallback = base::RepeatingCallback<bool(void)>;
-  void SetShouldAllowPreviewCallbackForTesting(
-      ShouldAllowPreviewCallback should_allow_preview_callback);
-
  private:
   OfflinePageURLLoader(
       content::NavigationUIData* navigation_ui_data,
@@ -116,7 +112,7 @@
   std::unique_ptr<mojo::SimpleWatcher> handle_watcher_;
 
   OfflinePageRequestHandler::Delegate::TabIdGetter tab_id_getter_;
-  ShouldAllowPreviewCallback should_allow_preview_callback_;
+  bool is_offline_preview_allowed_;
 
   base::WeakPtrFactory<OfflinePageURLLoader> weak_ptr_factory_;
 
diff --git a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer.cc
index 2f7d259a..b94b5634 100644
--- a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer.cc
@@ -167,18 +167,19 @@
   // ResourceDispatcherHostDelegate::GetNavigationData during commit.
   // Because ChromeResourceDispatcherHostDelegate always returns a
   // ChromeNavigationData, it is safe to static_cast here.
-  ChromeNavigationData* chrome_navigation_data =
-      static_cast<ChromeNavigationData*>(
-          navigation_handle->GetNavigationData());
-  if (!chrome_navigation_data)
-    return STOP_OBSERVING;
-  data_reduction_proxy::DataReductionProxyData* data =
-      chrome_navigation_data->GetDataReductionProxyData();
+  std::unique_ptr<DataReductionProxyData> data;
+  auto* settings =
+      DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
+          browser_context_);
+  if (settings) {
+    data = settings->CreateDataFromNavigationHandle(
+        navigation_handle, navigation_handle->GetResponseHeaders());
+  }
   if (!data || !(data->used_data_reduction_proxy() ||
                  data->was_cached_data_reduction_proxy_response())) {
     return STOP_OBSERVING;
   }
-  data_ = data->DeepCopy();
+  data_ = std::move(data);
 
   PreviewsUITabHelper* ui_tab_helper =
       PreviewsUITabHelper::FromWebContents(navigation_handle->GetWebContents());
diff --git a/chrome/browser/plugins/pdf_iframe_navigation_throttle_unittest.cc b/chrome/browser/plugins/pdf_iframe_navigation_throttle_unittest.cc
index 695c3ab2..8308b77 100644
--- a/chrome/browser/plugins/pdf_iframe_navigation_throttle_unittest.cc
+++ b/chrome/browser/plugins/pdf_iframe_navigation_throttle_unittest.cc
@@ -88,7 +88,8 @@
           GURL(kExampleURL), main_rfh());
 
   handle->CallWillProcessResponseForTesting(
-      main_rfh(), net::HttpUtil::AssembleRawHeaders(kHeader, strlen(kHeader)));
+      main_rfh(), net::HttpUtil::AssembleRawHeaders(kHeader, strlen(kHeader)),
+      false, net::ProxyServer::Direct());
 
   std::unique_ptr<content::NavigationThrottle> throttle =
       PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(handle.get());
@@ -99,7 +100,8 @@
       GURL(kExampleURL), subframe());
 
   handle->CallWillProcessResponseForTesting(
-      subframe(), net::HttpUtil::AssembleRawHeaders(kHeader, strlen(kHeader)));
+      subframe(), net::HttpUtil::AssembleRawHeaders(kHeader, strlen(kHeader)),
+      false, net::ProxyServer::Direct());
 
   throttle = PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(handle.get());
   ASSERT_NE(nullptr, throttle);
@@ -117,7 +119,8 @@
   std::string header = GetHeaderWithMimeType("application/pdf");
   handle->CallWillProcessResponseForTesting(
       subframe(),
-      net::HttpUtil::AssembleRawHeaders(header.c_str(), header.size()));
+      net::HttpUtil::AssembleRawHeaders(header.c_str(), header.size()), false,
+      net::ProxyServer::Direct());
 
   std::unique_ptr<content::NavigationThrottle> throttle =
       PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(handle.get());
@@ -129,7 +132,8 @@
   // Verify that we PROCEED for other mime types.
   // Blank mime type
   handle->CallWillProcessResponseForTesting(
-      subframe(), net::HttpUtil::AssembleRawHeaders(kHeader, strlen(kHeader)));
+      subframe(), net::HttpUtil::AssembleRawHeaders(kHeader, strlen(kHeader)),
+      false, net::ProxyServer::Direct());
 
   throttle = PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(handle.get());
 
@@ -141,7 +145,8 @@
   header = GetHeaderWithMimeType("text/html");
   handle->CallWillProcessResponseForTesting(
       subframe(),
-      net::HttpUtil::AssembleRawHeaders(header.c_str(), header.size()));
+      net::HttpUtil::AssembleRawHeaders(header.c_str(), header.size()), false,
+      net::ProxyServer::Direct());
 
   throttle = PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(handle.get());
 
@@ -153,7 +158,8 @@
   header = GetHeaderWithMimeType("image/png");
   handle->CallWillProcessResponseForTesting(
       subframe(),
-      net::HttpUtil::AssembleRawHeaders(header.c_str(), header.size()));
+      net::HttpUtil::AssembleRawHeaders(header.c_str(), header.size()), false,
+      net::ProxyServer::Direct());
 
   throttle = PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(handle.get());
 
@@ -178,7 +184,8 @@
       "content-disposition: attachment\r\n";
   handle->CallWillProcessResponseForTesting(
       subframe(),
-      net::HttpUtil::AssembleRawHeaders(header.c_str(), header.size()));
+      net::HttpUtil::AssembleRawHeaders(header.c_str(), header.size()), false,
+      net::ProxyServer::Direct());
 
   std::unique_ptr<content::NavigationThrottle> throttle =
       PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(handle.get());
@@ -198,7 +205,8 @@
   std::string header = GetHeaderWithMimeType("application/pdf");
   handle->CallWillProcessResponseForTesting(
       subframe(),
-      net::HttpUtil::AssembleRawHeaders(header.c_str(), header.size()));
+      net::HttpUtil::AssembleRawHeaders(header.c_str(), header.size()), false,
+      net::ProxyServer::Direct());
 
   // Test PDF Viewer enabled.
   SetAlwaysOpenPdfExternallyForTests(false);
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_factory.cc b/chrome/browser/policy/cloud/user_policy_signin_service_factory.cc
index e326c6e5..7259e11 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service_factory.cc
+++ b/chrome/browser/policy/cloud/user_policy_signin_service_factory.cc
@@ -36,11 +36,6 @@
 
 }  // namespace
 
-// static
-base::LazyInstance<scoped_refptr<network::SharedURLLoaderFactory>>::Leaky
-    UserPolicySigninServiceFactory::system_url_loader_factory_for_tests_ =
-        LAZY_INSTANCE_INITIALIZER;
-
 UserPolicySigninServiceFactory::UserPolicySigninServiceFactory()
     : BrowserContextKeyedServiceFactory(
         "UserPolicySigninService",
@@ -70,12 +65,6 @@
   g_device_management_service = device_management_service;
 }
 
-// static
-void UserPolicySigninServiceFactory::SetSystemURLLoaderFactoryForTesting(
-    scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory) {
-  system_url_loader_factory_for_tests_.Get() = system_url_loader_factory;
-}
-
 KeyedService* UserPolicySigninServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
   Profile* profile = static_cast<Profile*>(context);
@@ -85,20 +74,11 @@
       g_device_management_service ? g_device_management_service
                                   : connector->device_management_service();
 
-  scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory =
-      system_url_loader_factory_for_tests_.Get();
-  if (!system_url_loader_factory &&
-      g_browser_process->system_network_context_manager()) {
-    system_url_loader_factory =
-        g_browser_process->system_network_context_manager()
-            ->GetSharedURLLoaderFactory();
-  }
-
   UserPolicySigninService* service = new UserPolicySigninService(
       profile, g_browser_process->local_state(), device_management_service,
       UserCloudPolicyManagerFactory::GetForBrowserContext(context),
       IdentityManagerFactory::GetForProfile(profile),
-      std::move(system_url_loader_factory),
+      g_browser_process->shared_url_loader_factory(),
       ProfileOAuth2TokenServiceFactory::GetForProfile(profile));
   return service;
 }
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_factory.h b/chrome/browser/policy/cloud/user_policy_signin_service_factory.h
index 0e15762..dcef00d 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service_factory.h
+++ b/chrome/browser/policy/cloud/user_policy_signin_service_factory.h
@@ -5,9 +5,7 @@
 #ifndef CHROME_BROWSER_POLICY_CLOUD_USER_POLICY_SIGNIN_SERVICE_FACTORY_H_
 #define CHROME_BROWSER_POLICY_CLOUD_USER_POLICY_SIGNIN_SERVICE_FACTORY_H_
 
-#include "base/lazy_instance.h"
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
 #include "base/memory/singleton.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 
@@ -17,10 +15,6 @@
 class PrefRegistrySyncable;
 }
 
-namespace network {
-class SharedURLLoaderFactory;
-}
-
 namespace policy {
 
 class DeviceManagementService;
@@ -44,9 +38,6 @@
   static void SetDeviceManagementServiceForTesting(
       DeviceManagementService* device_management_service);
 
-  static void SetSystemURLLoaderFactoryForTesting(
-      scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory);
-
  protected:
   // BrowserContextKeyedServiceFactory implementation.
   KeyedService* BuildServiceInstanceFor(
@@ -62,9 +53,6 @@
  private:
   friend struct base::DefaultSingletonTraits<UserPolicySigninServiceFactory>;
 
-  static base::LazyInstance<scoped_refptr<network::SharedURLLoaderFactory>>::
-      Leaky system_url_loader_factory_for_tests_;
-
   UserPolicySigninServiceFactory();
   ~UserPolicySigninServiceFactory() override;
 
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc b/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc
index 597347a..0d55bf31 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc
+++ b/chrome/browser/policy/cloud/user_policy_signin_service_unittest.cc
@@ -151,12 +151,12 @@
   void SetUp() override {
     UserPolicySigninServiceFactory::SetDeviceManagementServiceForTesting(
         &device_management_service_);
-    UserPolicySigninServiceFactory::SetSystemURLLoaderFactoryForTesting(
-        test_system_shared_loader_factory_);
 
     local_state_.reset(new TestingPrefServiceSimple);
     RegisterLocalState(local_state_->registry());
     TestingBrowserProcess::GetGlobal()->SetLocalState(local_state_.get());
+    TestingBrowserProcess::GetGlobal()->SetSharedURLLoaderFactory(
+        test_system_shared_loader_factory_);
 
     g_browser_process->browser_policy_connector()->Init(
         local_state_.get(), test_system_shared_loader_factory_);
diff --git a/chrome/browser/previews/android/previews_android_bridge.cc b/chrome/browser/previews/android/previews_android_bridge.cc
index 37c649df..3621686f 100644
--- a/chrome/browser/previews/android/previews_android_bridge.cc
+++ b/chrome/browser/previews/android/previews_android_bridge.cc
@@ -9,6 +9,8 @@
 #include "base/android/jni_android.h"
 #include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
 #include "chrome/browser/previews/previews_ui_tab_helper.h"
+#include "components/previews/content/previews_user_data.h"
+#include "components/previews/core/previews_experiments.h"
 #include "content/public/browser/web_contents.h"
 #include "jni/PreviewsAndroidBridge_jni.h"
 
@@ -101,3 +103,28 @@
 
   tab_helper->ReloadWithoutPreviews();
 }
+
+base::android::ScopedJavaLocalRef<jstring>
+PreviewsAndroidBridge::GetPreviewsType(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj,
+    const base::android::JavaParamRef<jobject>& j_web_contents) {
+  content::WebContents* web_contents =
+      content::WebContents::FromJavaWebContents(j_web_contents);
+  if (!web_contents)
+    return base::android::ScopedJavaLocalRef<jstring>();
+
+  PreviewsUITabHelper* tab_helper =
+      PreviewsUITabHelper::FromWebContents(web_contents);
+  if (!tab_helper)
+    return base::android::ScopedJavaLocalRef<jstring>();
+
+  previews::PreviewsUserData* data = tab_helper->previews_user_data();
+  if (!data || !data->HasCommittedPreviewsType())
+    return base::android::ScopedJavaLocalRef<jstring>();
+
+  return base::android::ScopedJavaLocalRef<jstring>(
+      base::android::ConvertUTF8ToJavaString(
+          env,
+          previews::GetStringNameForType(data->committed_previews_type())));
+}
diff --git a/chrome/browser/previews/android/previews_android_bridge.h b/chrome/browser/previews/android/previews_android_bridge.h
index b7dc027a..6e028e8 100644
--- a/chrome/browser/previews/android/previews_android_bridge.h
+++ b/chrome/browser/previews/android/previews_android_bridge.h
@@ -37,6 +37,11 @@
                     const base::android::JavaParamRef<jobject>& obj,
                     const base::android::JavaParamRef<jobject>& j_web_contents);
 
+  base::android::ScopedJavaLocalRef<jstring> GetPreviewsType(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj,
+      const base::android::JavaParamRef<jobject>& j_web_contents);
+
  private:
   base::WeakPtrFactory<PreviewsAndroidBridge> weak_factory_;
 
diff --git a/chrome/browser/previews/previews_ui_tab_helper.cc b/chrome/browser/previews/previews_ui_tab_helper.cc
index b8bb21b..774a5586 100644
--- a/chrome/browser/previews/previews_ui_tab_helper.cc
+++ b/chrome/browser/previews/previews_ui_tab_helper.cc
@@ -240,6 +240,7 @@
       !navigation_handle->HasCommitted() || navigation_handle->IsSameDocument())
     return;
 
+  previews_freshness_ = base::Time();
   previews_user_data_.reset();
 #if defined(OS_ANDROID)
   should_display_android_omnibox_badge_ = false;
diff --git a/chrome/browser/previews/previews_ui_tab_helper_unittest.cc b/chrome/browser/previews/previews_ui_tab_helper_unittest.cc
index c8e27664..f4584a52 100644
--- a/chrome/browser/previews/previews_ui_tab_helper_unittest.cc
+++ b/chrome/browser/previews/previews_ui_tab_helper_unittest.cc
@@ -106,7 +106,8 @@
     std::string headers("HTTP/1.1 200 OK\n\n");
     test_handle_->CallWillProcessResponseForTesting(
         main_rfh(),
-        net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()));
+        net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()),
+        false, net::ProxyServer::Direct());
     SimulateCommit();
   }
 
diff --git a/chrome/browser/resource_coordinator/tab_activity_watcher.cc b/chrome/browser/resource_coordinator/tab_activity_watcher.cc
index b14dfee..00a2e3a 100644
--- a/chrome/browser/resource_coordinator/tab_activity_watcher.cc
+++ b/chrome/browser/resource_coordinator/tab_activity_watcher.cc
@@ -7,6 +7,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/resource_coordinator/tab_manager_features.h"
 #include "chrome/browser/resource_coordinator/tab_metrics_logger.h"
 #include "chrome/browser/resource_coordinator/tab_ranker/mru_features.h"
 #include "chrome/browser/resource_coordinator/tab_ranker/tab_features.h"
@@ -56,6 +57,17 @@
     if (web_contents()->IsBeingDestroyed() || backgrounded_time_.is_null())
       return base::nullopt;
 
+    // Only Scores Oldest N tabs (based on least recently used index calculated
+    // as mru.total - mru.index - 1).
+    const auto mru = GetMRUFeatures();
+    const int lru_index = mru.total - mru.index - 1;
+
+    // If the least recently used index is greater than or equals to N, which
+    // means the tab is not in the oldest N list, we should simply skip it.
+    // The N is defaulted as kMaxInt so that all tabs are scored.
+    if (lru_index >= GetNumOldestTabsToScoreWithTabRanker())
+      return base::nullopt;
+
     const Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
     if (!browser)
       return base::nullopt;
@@ -67,8 +79,8 @@
 
     float score;
     tab_ranker::TabRankerResult result =
-        TabActivityWatcher::GetInstance()->predictor_.ScoreTab(
-            tab, window, GetMRUFeatures(), &score);
+        TabActivityWatcher::GetInstance()->predictor_.ScoreTab(tab, window, mru,
+                                                               &score);
     if (result == tab_ranker::TabRankerResult::kSuccess)
       return score;
     return base::nullopt;
diff --git a/chrome/browser/resource_coordinator/tab_activity_watcher_browsertest.cc b/chrome/browser/resource_coordinator/tab_activity_watcher_browsertest.cc
index be098cb..ab7591fd 100644
--- a/chrome/browser/resource_coordinator/tab_activity_watcher_browsertest.cc
+++ b/chrome/browser/resource_coordinator/tab_activity_watcher_browsertest.cc
@@ -7,9 +7,11 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h"
+#include "chrome/browser/resource_coordinator/tab_manager_features.h"
 #include "chrome/browser/resource_coordinator/time.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
@@ -103,6 +105,39 @@
   CloseBrowserSynchronously(browser());
 }
 
+// Tests only oldest N tabs are scored.
+IN_PROC_BROWSER_TEST_F(TabActivityWatcherTest,
+                       OnlyCalculateReactivationScoreForOldestN) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      features::kTabRanker,
+      {{"number_of_oldest_tabs_to_score_with_TabRanker", "1"}});
+  // Use test clock so tabs have non-zero backgrounded times.
+  base::SimpleTestTickClock test_clock;
+  ScopedSetTickClockForTesting scoped_set_tick_clock_for_testing(&test_clock);
+  test_clock.Advance(base::TimeDelta::FromMinutes(1));
+
+  AddTabAtIndex(1, test_urls_[0], ui::PAGE_TRANSITION_LINK);
+  test_clock.Advance(base::TimeDelta::FromMinutes(1));
+  AddTabAtIndex(2, test_urls_[0], ui::PAGE_TRANSITION_LINK);
+  test_clock.Advance(base::TimeDelta::FromMinutes(1));
+  browser()->tab_strip_model()->ActivateTabAt(0, kIsUserGesture);
+  test_clock.Advance(base::TimeDelta::FromMinutes(1));
+
+  // tab@1 is scored successfully.
+  base::Optional<float> tab_1 =
+      TabActivityWatcher::GetInstance()->CalculateReactivationScore(
+          browser()->tab_strip_model()->GetWebContentsAt(1));
+  EXPECT_TRUE(tab_1.has_value());
+
+  // tab@2 is not scored successfully since it's not in the OldestN.
+  base::Optional<float> tab_2 =
+      TabActivityWatcher::GetInstance()->CalculateReactivationScore(
+          browser()->tab_strip_model()->GetWebContentsAt(2));
+  EXPECT_FALSE(tab_2.has_value());
+
+  CloseBrowserSynchronously(browser());
+}
 // Tests UKM entries generated by TabActivityWatcher/TabMetricsLogger as tabs
 // are backgrounded and foregrounded.
 // Modeled after the TabActivityWatcherTest unit tests, these browser tests
diff --git a/chrome/browser/resource_coordinator/tab_manager_features.cc b/chrome/browser/resource_coordinator/tab_manager_features.cc
index 2126f42..f1e66a1 100644
--- a/chrome/browser/resource_coordinator/tab_manager_features.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_features.cc
@@ -289,4 +289,10 @@
   return params;
 }
 
+int GetNumOldestTabsToScoreWithTabRanker() {
+  return base::GetFieldTrialParamByFeatureAsInt(
+      features::kTabRanker, "number_of_oldest_tabs_to_score_with_TabRanker",
+      std::numeric_limits<int>::max());
+}
+
 }  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/tab_manager_features.h b/chrome/browser/resource_coordinator/tab_manager_features.h
index 9de4063..0f7fe0548 100644
--- a/chrome/browser/resource_coordinator/tab_manager_features.h
+++ b/chrome/browser/resource_coordinator/tab_manager_features.h
@@ -328,6 +328,9 @@
 // Gets parameters for the infinite session restore feature.
 InfiniteSessionRestoreParams GetInfiniteSessionRestoreParams();
 
+// Gets number of oldest tab that should be scored by TabRanker.
+int GetNumOldestTabsToScoreWithTabRanker();
+
 }  // namespace resource_coordinator
 
 #endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_MANAGER_FEATURES_H_
diff --git a/chrome/browser/resources/chromeos/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/chromevox/BUILD.gn
index 9bc9585..9e91ae9 100644
--- a/chrome/browser/resources/chromeos/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/chromevox/BUILD.gn
@@ -485,46 +485,16 @@
   }
 }
 
-test("chromevox_tests") {
-  sources = [
-    "//chrome/browser/extensions/browsertest_util.cc",
-    "//chrome/browser/extensions/browsertest_util.h",
-    "//chrome/browser/ui/webui/web_ui_test_handler.cc",
-    "//chrome/browser/ui/webui/web_ui_test_handler.h",
-    "//chrome/test/base/extension_js_browser_test.cc",
-    "//chrome/test/base/extension_js_browser_test.h",
-    "//chrome/test/base/extension_load_waiter_one_shot.cc",
-    "//chrome/test/base/extension_load_waiter_one_shot.h",
-    "//chrome/test/base/javascript_browser_test.cc",
-    "//chrome/test/base/javascript_browser_test.h",
-    "//chrome/test/base/test_chrome_web_ui_controller_factory.cc",
-    "//chrome/test/base/test_chrome_web_ui_controller_factory.h",
-    "//chrome/test/base/web_ui_browser_test.cc",
-    "//chrome/test/base/web_ui_browser_test.h",
-  ]
+source_set("browser_tests") {
+  testonly = true
+  assert(enable_extensions)
 
   deps = [
     ":chromevox_extjs_tests",
     ":chromevox_unitjs_tests",
-    "//base",
-    "//base:i18n",
-    "//base/test:test_support",
-    "//chrome:browser_tests_pak",
-    "//chrome:packed_resources",
-    "//chrome:resources",
-    "//chrome:strings",
-    "//chrome/browser",
-    "//chrome/renderer",
-    "//chrome/test:browser_tests_runner",
-    "//chrome/test:test_support",
-    "//chrome/test:test_support_ui",
-    "//content/test:test_support",
-    "//services/service_manager/background:lib",
-    "//testing/gmock",
-    "//testing/gtest",
-    "//ui/keyboard:resources",
   ]
 
+  # TODO(jamescook): Figure out which of these are really necessary.
   data = [
     "$root_out_dir/chrome_100_percent.pak",
     "$root_out_dir/chrome_200_percent.pak",
@@ -661,6 +631,14 @@
     "testing/chromevox_next_e2e_test_base.js",
     "testing/mock_feedback.js",
   ]
+
+  # The test base classes generate C++ code with these deps.
+  deps = [
+    "//ash",
+    "//base",
+    "//chrome/browser/chromeos",
+    "//chrome/common",
+  ]
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 }
 
diff --git a/chrome/browser/resources/chromeos/chromevox/braille/braille_display_manager_test.unitjs b/chrome/browser/resources/chromeos/chromevox/braille/braille_display_manager_test.unitjs
index b13608c..de1a2d00 100644
--- a/chrome/browser/resources/chromeos/chromevox/braille/braille_display_manager_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/braille/braille_display_manager_test.unitjs
@@ -11,9 +11,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxBrailleDisplayManagerUnitTest() {}
+function ChromeVoxBrailleDisplayManagerUnitTest() {}
 
-CvoxBrailleDisplayManagerUnitTest.prototype = {
+ChromeVoxBrailleDisplayManagerUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -179,7 +179,7 @@
 };
 
 
-TEST_F('CvoxBrailleDisplayManagerUnitTest', 'NoApi', function() {
+TEST_F('ChromeVoxBrailleDisplayManagerUnitTest', 'NoApi', function() {
   var manager = new cvox.BrailleDisplayManager(this.translatorManager);
   manager.setContent(this.NAV_BRAILLE);
   this.translatorManager.setTranslator(this.translator);
@@ -190,7 +190,7 @@
  * Test that we don't write to the display when the API is available, but
  * the display is not.
  */
-TEST_F('CvoxBrailleDisplayManagerUnitTest', 'NoDisplay', function() {
+TEST_F('ChromeVoxBrailleDisplayManagerUnitTest', 'NoDisplay', function() {
   this.addFakeApi();
   this.displayState = {available: false};
 
@@ -204,7 +204,8 @@
 /**
  * Tests the typical sequence: setContent, setTranslator, setContent.
  */
-TEST_F('CvoxBrailleDisplayManagerUnitTest', 'BasicSetContent', function() {
+TEST_F('ChromeVoxBrailleDisplayManagerUnitTest', 'BasicSetContent',
+       function() {
   this.addFakeApi();
   this.displayAvailable();
   var manager = new cvox.BrailleDisplayManager(this.translatorManager);
@@ -220,8 +221,8 @@
 /**
  * Tests that setting empty content clears the display.
  */
-TEST_F('CvoxBrailleDisplayManagerUnitTest', 'SetEmptyContentWithTranslator',
-       function() {
+TEST_F('ChromeVoxBrailleDisplayManagerUnitTest',
+       'SetEmptyContentWithTranslator', function() {
   this.addFakeApi();
   this.displayAvailable();
 
@@ -236,7 +237,8 @@
 });
 
 
-TEST_F('CvoxBrailleDisplayManagerUnitTest', 'CursorAndPanning', function() {
+TEST_F('ChromeVoxBrailleDisplayManagerUnitTest', 'CursorAndPanning',
+       function() {
   var text = 'This is a test string';
   function createNavBrailleWithCursor(start, end) {
     return new cvox.NavBraille({ text: text, startIndex: start,
@@ -276,7 +278,7 @@
  * Tests that the grouping algorithm works with one text character that maps
  * to one braille cell.
  */
-TEST_F('CvoxBrailleDisplayManagerUnitTest', 'BasicGroup', function() {
+TEST_F('ChromeVoxBrailleDisplayManagerUnitTest', 'BasicGroup', function() {
   var text = 'a';
   var translated = '1';
   var mapping = [0];
@@ -292,7 +294,7 @@
  * Tests that the grouping algorithm works with one text character that maps
  * to multiple braille cells.
  */
-TEST_F('CvoxBrailleDisplayManagerUnitTest', 'OneRtoManyB', function() {
+TEST_F('ChromeVoxBrailleDisplayManagerUnitTest', 'OneRtoManyB', function() {
   var text = 'A';
   var translated = '11';
   var mapping = [0,0];
@@ -308,7 +310,7 @@
  * Tests that the grouping algorithm works with one braille cell that maps
  * to multiple text characters.
  */
-TEST_F('CvoxBrailleDisplayManagerUnitTest', 'OneBtoManyR', function() {
+TEST_F('ChromeVoxBrailleDisplayManagerUnitTest', 'OneBtoManyR', function() {
   var text = 'knowledge';
   var translated = '1';
   var mapping = [0];
@@ -324,7 +326,8 @@
  * Tests that the grouping algorithm works with one string that on both ends,
  * have text characters that map to multiple braille cells.
  */
-TEST_F('CvoxBrailleDisplayManagerUnitTest', 'OneRtoManyB_BothEnds', function() {
+TEST_F('ChromeVoxBrailleDisplayManagerUnitTest', 'OneRtoManyB_BothEnds',
+       function() {
   var text = 'AbbC';
   var translated = 'X122X3';
   var mapping = [0,0,1,2,3,3];
@@ -340,7 +343,8 @@
  * Tests that the grouping algorithm works with one string that on both ends,
  * have braille cells that map to multiple text characters.
  */
-TEST_F('CvoxBrailleDisplayManagerUnitTest', 'OneBtoManyR_BothEnds', function() {
+TEST_F('ChromeVoxBrailleDisplayManagerUnitTest', 'OneBtoManyR_BothEnds',
+       function() {
   var text = 'knowledgehappych';
   var translated = '1234456';
   var mapping = [0, 9, 10, 11, 12, 13, 14];
@@ -357,7 +361,7 @@
  * Tests that the grouping algorithm works with one  string that has both types
  * of mapping.
  */
-TEST_F('CvoxBrailleDisplayManagerUnitTest', 'RandB_Random', function() {
+TEST_F('ChromeVoxBrailleDisplayManagerUnitTest', 'RandB_Random', function() {
   var text = 'knowledgeIsPower';
   var translated = '1X23X45678';
   var mapping = [0, 9, 9, 10, 11, 11, 12, 13, 14, 15];
diff --git a/chrome/browser/resources/chromeos/chromevox/braille/braille_input_handler_test.unitjs b/chrome/browser/resources/chromeos/chromevox/braille/braille_input_handler_test.unitjs
index 5e3375c7..789fd66 100644
--- a/chrome/browser/resources/chromeos/chromevox/braille/braille_input_handler_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/braille/braille_input_handler_test.unitjs
@@ -430,9 +430,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxBrailleInputHandlerUnitTest() {}
+function ChromeVoxBrailleInputHandlerUnitTest() {}
 
-CvoxBrailleInputHandlerUnitTest.prototype = {
+ChromeVoxBrailleInputHandlerUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -534,7 +534,7 @@
   }
 };
 
-TEST_F('CvoxBrailleInputHandlerUnitTest', 'ConnectFromUnknownExtension',
+TEST_F('ChromeVoxBrailleInputHandlerUnitTest', 'ConnectFromUnknownExtension',
   function() {
   this.port.sender.id = 'your unknown friend';
   chrome.runtime.onConnectExternal.getListener()(this.port);
@@ -542,7 +542,7 @@
 });
 
 
-TEST_F('CvoxBrailleInputHandlerUnitTest', 'NoTranslator', function() {
+TEST_F('ChromeVoxBrailleInputHandlerUnitTest', 'NoTranslator', function() {
   var editor = this.createEditor();
   editor.setContent('blah', 0);
   editor.setActive(true);
@@ -554,7 +554,7 @@
 });
 
 
-TEST_F('CvoxBrailleInputHandlerUnitTest', 'InputUncontracted', function() {
+TEST_F('ChromeVoxBrailleInputHandlerUnitTest', 'InputUncontracted', function() {
   this.translatorManager.setTranslators(this.uncontractedTranslator, null);
   var editor = this.createEditor();
   editor.setActive(true);
@@ -581,7 +581,7 @@
 });
 
 
-TEST_F('CvoxBrailleInputHandlerUnitTest', 'InputContracted', function() {
+TEST_F('ChromeVoxBrailleInputHandlerUnitTest', 'InputContracted', function() {
   var editor = this.createEditor();
   this.translatorManager.setTranslators(this.contractedTranslator,
                                         this.uncontractedTranslator);
@@ -641,7 +641,7 @@
 });
 
 
-TEST_F('CvoxBrailleInputHandlerUnitTest', 'TypingUrlWithContracted',
+TEST_F('ChromeVoxBrailleInputHandlerUnitTest', 'TypingUrlWithContracted',
        function() {
   var editor = this.createEditor();
   this.translatorManager.setTranslators(this.contractedTranslator,
@@ -665,7 +665,7 @@
 });
 
 
-TEST_F('CvoxBrailleInputHandlerUnitTest', 'Backspace', function() {
+TEST_F('ChromeVoxBrailleInputHandlerUnitTest', 'Backspace', function() {
   var editor = this.createEditor();
   this.translatorManager.setTranslators(this.contractedTranslator,
                                         this.uncontractedTranslator);
@@ -695,7 +695,7 @@
 });
 
 
-TEST_F('CvoxBrailleInputHandlerUnitTest', 'KeysImeNotActive', function() {
+TEST_F('ChromeVoxBrailleInputHandlerUnitTest', 'KeysImeNotActive', function() {
   var editor = this.createEditor();
   this.sendKeyEvent('Enter');
   this.sendKeyEvent('ArrowUp');
diff --git a/chrome/browser/resources/chromeos/chromevox/braille/braille_table_test.extjs b/chrome/browser/resources/chromeos/chromevox/braille/braille_table_test.extjs
index 6adae04..c778bd7 100644
--- a/chrome/browser/resources/chromeos/chromevox/braille/braille_table_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/braille/braille_table_test.extjs
@@ -13,11 +13,11 @@
  * @constructor
  * @extends {ChromeVoxE2ETest}
  */
-function CvoxBrailleTableTest() {
+function ChromeVoxBrailleTableTest() {
   ChromeVoxE2ETest.call(this);
 }
 
-CvoxBrailleTableTest.prototype = {
+ChromeVoxBrailleTableTest.prototype = {
   __proto__: ChromeVoxE2ETest.prototype,
 };
 
@@ -25,7 +25,7 @@
  * Tests that {@code getAll} can fetch and parse the tables file.
  * NOTE: This will need to be adjusted when more tables are added.
  */
-TEST_F('CvoxBrailleTableTest', 'testGetAllAndValidate', function() {
+TEST_F('ChromeVoxBrailleTableTest', 'testGetAllAndValidate', function() {
   cvox.BrailleTable.getAll(this.newCallback(function(tables) {
     expectEquals(68, tables.length);
     assertNotNullNorUndefined(
@@ -40,7 +40,7 @@
 });
 
 /** Tests getDisplayName for some specific representative cases. */
-TEST_F('CvoxBrailleTableTest', 'testGetDisplayName', function() {
+TEST_F('ChromeVoxBrailleTableTest', 'testGetDisplayName', function() {
   cvox.BrailleTable.getAll(this.newCallback(function(tables) {
     var table = cvox.BrailleTable.forId(tables, 'bg-comp8');
     expectEquals('Bulgarian', cvox.BrailleTable.getDisplayName(table));
@@ -55,7 +55,7 @@
 /**
  * Tests the getUncontracted function.
  */
-TEST_F('CvoxBrailleTableTest', 'testGetUncontracted', function() {
+TEST_F('ChromeVoxBrailleTableTest', 'testGetUncontracted', function() {
   cvox.BrailleTable.getAll(this.newCallback(function(tables) {
     function expectUncontracted(uncontractedId, idToCheck) {
       var checkedTable = cvox.BrailleTable.forId(tables, idToCheck);
diff --git a/chrome/browser/resources/chromeos/chromevox/braille/braille_translator_manager_test.extjs b/chrome/browser/resources/chromeos/chromevox/braille/braille_translator_manager_test.extjs
index ea5dbdda..ec4ce849 100644
--- a/chrome/browser/resources/chromeos/chromevox/braille/braille_translator_manager_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/braille/braille_translator_manager_test.extjs
@@ -13,11 +13,11 @@
  * @constructor
  * @extends {ChromeVoxE2ETest}
  */
-function CvoxBrailleTranslatorManagerTest() {
+function ChromeVoxBrailleTranslatorManagerTest() {
   ChromeVoxE2ETest.call(this);
 }
 
-CvoxBrailleTranslatorManagerTest.prototype = {
+ChromeVoxBrailleTranslatorManagerTest.prototype = {
   __proto__: ChromeVoxE2ETest.prototype,
 
   /** @override */
@@ -78,7 +78,7 @@
   };
 }
 
-TEST_F('CvoxBrailleTranslatorManagerTest', 'testInitial', function() {
+TEST_F('ChromeVoxBrailleTranslatorManagerTest', 'testInitial', function() {
   assertEquals(null, this.manager.getExpandingTranslator());
   assertEquals(null, this.manager.getDefaultTranslator());
   assertEquals(null, this.manager.getUncontractedTranslator());
@@ -89,7 +89,7 @@
   });
 });
 
-TEST_F('CvoxBrailleTranslatorManagerTest', 'testRefreshWithoutChange',
+TEST_F('ChromeVoxBrailleTranslatorManagerTest', 'testRefreshWithoutChange',
        function() {
   this.addChangeListener(function() {
     assertNotEquals(null, this.manager.getExpandingTranslator());
@@ -101,7 +101,7 @@
   });
 });
 
-TEST_F('CvoxBrailleTranslatorManagerTest', 'testRefreshWithChange',
+TEST_F('ChromeVoxBrailleTranslatorManagerTest', 'testRefreshWithChange',
        function() {
   this.addChangeListener(function() {
     assertNotEquals(null, this.manager.getExpandingTranslator());
diff --git a/chrome/browser/resources/chromeos/chromevox/braille/expanding_braille_translator_test.unitjs b/chrome/browser/resources/chromeos/chromevox/braille/expanding_braille_translator_test.unitjs
index 0b772a51..64541cc 100644
--- a/chrome/browser/resources/chromeos/chromevox/braille/expanding_braille_translator_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/braille/expanding_braille_translator_test.unitjs
@@ -11,9 +11,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxExpandingBrailleTranslatorUnitTest() {}
+function ChromeVoxExpandingBrailleTranslatorUnitTest() {}
 
-CvoxExpandingBrailleTranslatorUnitTest.prototype = {
+ChromeVoxExpandingBrailleTranslatorUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -78,7 +78,7 @@
   }
 }
 
-TEST_F('CvoxExpandingBrailleTranslatorUnitTest', 'TranslationError',
+TEST_F('ChromeVoxExpandingBrailleTranslatorUnitTest', 'TranslationError',
     function() {
   var text = new Spannable('error ok', new cvox.ValueSpan());
   text.setSpan(new cvox.ValueSelectionSpan, 0, 0);
@@ -255,7 +255,7 @@
 
 var TEXT = 'Hello, world!';
 
-TEST_F('CvoxExpandingBrailleTranslatorUnitTest', 'successfulTranslations',
+TEST_F('ChromeVoxExpandingBrailleTranslatorUnitTest', 'successfulTranslations',
        function() {
   /**
    * Dictionary of test strings, keyed on a descriptive name for the
@@ -313,7 +313,7 @@
   assertEquals(totalExpectedTranslationTests, totalRunTranslationTests);
 });
 
-TEST_F('CvoxExpandingBrailleTranslatorUnitTest', 'StyleTranslations',
+TEST_F('ChromeVoxExpandingBrailleTranslatorUnitTest', 'StyleTranslations',
        function() {
   var formTypeMap = {};
   formTypeMap[cvox.LibLouis.FormType.BOLD] = 'b';
diff --git a/chrome/browser/resources/chromeos/chromevox/braille/liblouis_test.extjs b/chrome/browser/resources/chromeos/chromevox/braille/liblouis_test.extjs
index ee2533c0..c5e88e3 100644
--- a/chrome/browser/resources/chromeos/chromevox/braille/liblouis_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/braille/liblouis_test.extjs
@@ -15,11 +15,11 @@
  * @constructor
  * @extends {ChromeVoxE2ETest}
  */
-function CvoxLibLouisTest() {
+function ChromeVoxLibLouisTest() {
   ChromeVoxE2ETest.call(this);
 }
 
-CvoxLibLouisTest.prototype = {
+ChromeVoxLibLouisTest.prototype = {
   __proto__: ChromeVoxE2ETest.prototype,
 
   createLiblouis: function() {
@@ -48,7 +48,7 @@
   assertEqualsJSON(expected, as_array);
 }
 
-TEST_F('CvoxLibLouisTest', 'checkAllTables', function() {
+TEST_F('ChromeVoxLibLouisTest', 'checkAllTables', function() {
   var liblouis = this.createAndAttachLiblouis();
   cvox.BrailleTable.getAll(this.newCallback(function(tables) {
     var i = 0;
@@ -66,7 +66,7 @@
   }.bind(this)));
 });
 
-TEST_F('CvoxLibLouisTest', 'testTranslateComputerBraille', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testTranslateComputerBraille', function() {
   var liblouis = this.createAndAttachLiblouis();
   this.withTranslator(liblouis, 'en-us-comp8.ctb', function(translator) {
     translator.translate('Hello!', [], this.newCallback(
@@ -78,7 +78,7 @@
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testBackTranslateComputerBraille', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testBackTranslateComputerBraille', function() {
   var liblouis = this.createAndAttachLiblouis();
   this.withTranslator(liblouis, 'en-us-comp8.ctb', function(translator) {
     var cells = new Uint8Array([0x53, 0x11, 0x07, 0x07, 0x15, 0x2e]);
@@ -88,7 +88,7 @@
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testTranslateGermanGrade2Braille', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testTranslateGermanGrade2Braille', function() {
   var liblouis = this.createAndAttachLiblouis();
   // This is one of the moderately large tables.
   this.withTranslator(liblouis, 'de-de-g2.ctb', function(translator) {
@@ -101,7 +101,7 @@
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testBackTranslateGermanComputerBraille', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testBackTranslateGermanComputerBraille', function() {
   var liblouis = this.createAndAttachLiblouis();
   this.withTranslator(liblouis, 'de-de-comp8.ctb', function(translator) {
     var cells = new Uint8Array([0xb3]);
@@ -111,7 +111,7 @@
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testBackTranslateEmptyCells', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testBackTranslateEmptyCells', function() {
   var liblouis = this.createAndAttachLiblouis();
   this.withTranslator(liblouis, 'de-de-comp8.ctb', function(translator) {
        translator.backTranslate(
@@ -123,7 +123,7 @@
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testGetTranslatorBeforeAttach', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testGetTranslatorBeforeAttach', function() {
   var liblouis = this.createLiblouis();
   assertFalse(liblouis.isAttached());
   this.withTranslator(liblouis, 'en-us-comp8.ctb', function(translator) {
@@ -131,14 +131,14 @@
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testGetInvalidTranslator', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testGetInvalidTranslator', function() {
   var liblouis = this.createAndAttachLiblouis();
   this.withTranslator(liblouis, 'nonexistant-table', function(translator) {
     assertEquals(null, translator);
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testTranslateAfterDetach', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testTranslateAfterDetach', function() {
   var liblouis = this.createAndAttachLiblouis();
   this.withTranslator(liblouis, 'de-de-comp8.ctb', function(translator) {
     liblouis.detach();
@@ -151,7 +151,7 @@
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testDetachWithOutstandingCallbacks', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testDetachWithOutstandingCallbacks', function() {
   var liblouis = this.createAndAttachLiblouis();
   this.withTranslator(liblouis, 'de-de-comp8.ctb', function(translator) {
     var called = false;
@@ -167,7 +167,7 @@
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testKeyEventStaticData', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testKeyEventStaticData', function() {
   var liblouis = this.createAndAttachLiblouis();
   this.withTranslator(liblouis, 'en-us-comp8.ctb', function(translator) {
     translator.translate('abcdefghijklmnopqrstuvwxyz 0123456789', [],
diff --git a/chrome/browser/resources/chromeos/chromevox/braille/pan_strategy_test.unitjs b/chrome/browser/resources/chromeos/chromevox/braille/pan_strategy_test.unitjs
index 161d4fa3..0ce7de2 100644
--- a/chrome/browser/resources/chromeos/chromevox/braille/pan_strategy_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/braille/pan_strategy_test.unitjs
@@ -9,9 +9,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxPanStrategyUnitTest() {}
+function ChromeVoxPanStrategyUnitTest() {}
 
-CvoxPanStrategyUnitTest.prototype = {
+ChromeVoxPanStrategyUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -36,7 +36,7 @@
   return result;
 }
 
- TEST_F('CvoxPanStrategyUnitTest', 'FixedPanning', function() {
+ TEST_F('ChromeVoxPanStrategyUnitTest', 'FixedPanning', function() {
   var panner = new cvox.PanStrategy();
   panner.setPanStrategy(false);
 
@@ -89,7 +89,8 @@
   assertEqualsJSON({firstRow: 0, lastRow: 1}, panner.viewPort);
 });
 
-TEST_F('CvoxPanStrategyUnitTest', 'WrappedPanningSingleLine', function() {
+TEST_F('ChromeVoxPanStrategyUnitTest', 'WrappedPanningSingleLine',
+       function() {
   var panner = new cvox.PanStrategy();
   panner.setPanStrategy(true);
 
@@ -142,7 +143,7 @@
         panner.getCurrentBrailleViewportContents());
 });
 
-TEST_F('CvoxPanStrategyUnitTest', 'WrappedPanningMultiline', function() {
+TEST_F('ChromeVoxPanStrategyUnitTest', 'WrappedPanningMultiline', function() {
   var panner = new cvox.PanStrategy();
   panner.setPanStrategy(true);
 
@@ -172,7 +173,7 @@
         panner.getCurrentBrailleViewportContents());
 });
 
-TEST_F('CvoxPanStrategyUnitTest', 'FixedSetContent', function() {
+TEST_F('ChromeVoxPanStrategyUnitTest', 'FixedSetContent', function() {
   var panner = new cvox.PanStrategy();
   panner.setPanStrategy(false);
 
@@ -188,7 +189,7 @@
   assertArraysEquals(expectedMappingValue, panner.brailleToText);
 });
 
-TEST_F('CvoxPanStrategyUnitTest', 'WrappedSetContent', function() {
+TEST_F('ChromeVoxPanStrategyUnitTest', 'WrappedSetContent', function() {
   var panner = new cvox.PanStrategy();
   panner.setPanStrategy(true);
 
@@ -245,7 +246,8 @@
   assertArraysEquals(expectedMappingValue, panner.brailleToText);
 });
 
-TEST_F('CvoxPanStrategyUnitTest', 'getCurrentTextViewportContents', function() {
+TEST_F('ChromeVoxPanStrategyUnitTest', 'getCurrentTextViewportContents',
+       function() {
   var panner = new cvox.PanStrategy();
   panner.setPanStrategy(true);
 
@@ -269,7 +271,7 @@
   assertEquals('789', panner.getCurrentTextViewportContents());
 });
 
-TEST_F('CvoxPanStrategyUnitTest', 'WrappedUnwrappedCursors', function() {
+TEST_F('ChromeVoxPanStrategyUnitTest', 'WrappedUnwrappedCursors', function() {
   var panner = new cvox.PanStrategy();
   panner.setPanStrategy(true);
 
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher_test.unitjs b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher_test.unitjs
index 6582e12..23184e43 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher_test.unitjs
@@ -10,11 +10,11 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxEventWatcherUnitTest() {
+function ChromeVoxEventWatcherUnitTest() {
   ChromeVoxUnitTestBase.call(this);
 }
 
-CvoxEventWatcherUnitTest.prototype = {
+ChromeVoxEventWatcherUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -84,7 +84,7 @@
   }
 };
 
-TEST_F('CvoxEventWatcherUnitTest', 'ButtonFocusFeedback', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'ButtonFocusFeedback', function() {
   this.loadHtml('<div> <button id="alpha">Alpha</button> </div>');
   this.setFocus('alpha');
   this.waitForCalm(this.assertSpoken, 'Alpha Button');
@@ -93,7 +93,7 @@
 /**
  * Test feedback when focusing links backwards (like shift-tabbing).
  */
-TEST_F('CvoxEventWatcherUnitTest', 'FocusLinksBackwards', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'FocusLinksBackwards', function() {
   this.loadHtml('<div> <p>before</p>' +
       '<p><a href="#" id="l1">1</a></p>' +
       '<p><a href="#" id="l2">2</a></p>' +
@@ -113,7 +113,7 @@
 /**
  * Test feedback when an editable text field gets focus.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'TextFocusFeedback', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'TextFocusFeedback', function() {
   this.loadHtml('<div>' +
       '<label for="mytext">Label</label>' +
       '<input id="mytext" value="Value" title="Title" />' +
@@ -126,7 +126,8 @@
 /**
  * Test feedback when a contenteditable field gets focus.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'ContentEditableFocusFeedback', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'ContentEditableFocusFeedback',
+       function() {
   this.loadHtml('<div>' +
       '<label for="mytext">Label</label>' +
       '<div id="mytext" contentEditable>This is editable</div>' +
@@ -140,7 +141,7 @@
  * Test feedback when an item in an dialog receives focus and then focus
  *     leaves the dialog.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'DialogFeedback', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'DialogFeedback', function() {
   this.loadHtml('<div>' +
       '<button id="show">Show</button>' +
       '<div aria-label="compose message" role="dialog">' +
@@ -166,7 +167,7 @@
 /**
  * Test feedback when an item in an alert dialog receives focus.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'AlertDialogFeedback', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'AlertDialogFeedback', function() {
   this.loadHtml('<div>' +
       '<div role="alertdialog">' +
       '  <p>Are you sure you want to install Windows?</p>' +
@@ -187,7 +188,7 @@
  * quickly - make sure the notification that we entered the dialog
  * isn't interrupted.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'DoubleFocusAlertDialogFeedback',
+TEST_F('ChromeVoxEventWatcherUnitTest', 'DoubleFocusAlertDialogFeedback',
     function() {
   this.loadHtml('<div>' +
       '<div role="alertdialog">' +
@@ -216,7 +217,7 @@
 /**
  * Test recovery when a dialog box closes and the user sends a tab event.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'CloseDialogTabRecovery', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'CloseDialogTabRecovery', function() {
   this.loadHtml('<div id="container">' +
       '<p id="first">first node</p>' +
       '<button id="button">valid button before</button>' +
@@ -247,7 +248,7 @@
 /**
  * Test feedback when a list box with an active descendant receives focus.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'ListBoxFeedback', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'ListBoxFeedback', function() {
   this.loadHtml('<div>' +
       '<p id="before">My listbox</p>' +
       '<div id="listbox" role="listbox" tabindex="0"' +
@@ -278,7 +279,7 @@
 /**
  * Test feedback when the items of a list box receive focus.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'ListBoxOptionFeedback', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'ListBoxOptionFeedback', function() {
   this.loadHtml('<div>' +
       '<p id="before">My listbox</p>' +
       '<div id="listbox" role="listbox">' +
@@ -302,7 +303,8 @@
  * Test feedback when the list box is setting focus in response to arrow
  * (or some other) keypress and the user is also using ChromeVox navigation.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'ListBoxOptionFeedbackWithFocus', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'ListBoxOptionFeedbackWithFocus',
+       function() {
   this.loadHtml('<div>' +
       '<p id="before">My listbox</p>' +
       '<div id="listbox" role="listbox">' +
@@ -333,7 +335,7 @@
  * The low-level details are tested in editable_text_test.js, this is
  * a higher-level test of how that code interacts with the event watcher.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'EditableText', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'EditableText', function() {
   cvox.ChromeVoxEditableTextBase.eventTypingEcho = false;
   this.loadHtml('<div>' +
       '<button id="before">Before</button>' +
@@ -369,7 +371,7 @@
  * The low-level details are tested in editable_text_test.js, this is
  * a higher-level test of how that code interacts with the event watcher.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'EditableTextListbox', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'EditableTextListbox', function() {
   this.loadHtml('<div>' +
       '<button id="before">Before</button>' +
       '<label for="input">Query</label>' +
@@ -402,7 +404,7 @@
  * The low-level details are tested in editable_text_test.js, this is
  * a higher-level test of how that code interacts with the event watcher.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'EditableTextListboxUpdatingInput',
+TEST_F('ChromeVoxEventWatcherUnitTest', 'EditableTextListboxUpdatingInput',
     function() {
   cvox.ChromeVoxEditableTextBase.shouldSpeakInsertions = true;
   this.loadHtml('<div>' +
@@ -434,7 +436,7 @@
 /**
  * Tests navigating through a multiline text area.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'MultilineNavigation', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'MultilineNavigation', function() {
   this.loadHtml('<div> <textarea id="area">' +
       'one' +
       '\n\n' +
@@ -474,7 +476,7 @@
       .waitForCalm(this.assertSpoken, 'three');
 });
 
-SYNC_TEST_F('CvoxEventWatcherUnitTest', 'ShouldWaitToProcess', function() {
+SYNC_TEST_F('ChromeVoxEventWatcherUnitTest', 'ShouldWaitToProcess', function() {
   // The focus event just happened, wait.
   assertTrue(
       cvox.ChromeVoxEventWatcherUtil.shouldWaitToProcess(100, 100, 100));
@@ -491,7 +493,7 @@
  * Test that no feedback is received for events that fire on elements
  * that are hidden (or the descendant of a hidden element).
  */
-TEST_F('CvoxEventWatcherUnitTest', 'AriaHiddenFeedback', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'AriaHiddenFeedback', function() {
   this.loadHtml('<div>' +
       '<div>' +
       '  <button id="button1">Button 1</button>' +
@@ -523,7 +525,7 @@
  * Test that key down events don't cause excessive value and state announcements
  * when arrowing around radiobuttons.
  */
-TEST_F('CvoxEventWatcherUnitTest',
+TEST_F('ChromeVoxEventWatcherUnitTest',
        'DISABLED_RadioButtonAnnouncements',
        function() {
   this.loadHtml(
@@ -561,7 +563,7 @@
  * Test time widget.
  * Disabled because test relies on removed behavior; see crbug.com/520519.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'DISABLED_TimeWidget', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'DISABLED_TimeWidget', function() {
   this.loadHtml(
     '<label for="timewidget">Set alarm for:</label>' +
     '<input id="timewidget" type="time" value="12:00">');
@@ -619,7 +621,7 @@
  * Test date widget.
  * Disabled because test relies on removed behavior; see crbug.com/520519.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'DISABLED_DateWidget', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'DISABLED_DateWidget', function() {
   this.loadHtml(
     '<label for="datewidget">Set birthdate:</label>' +
     '<input id="datewidget" type="date" value="1998-09-04"/>');
@@ -674,7 +676,7 @@
  * Test that ChromeVox speaks the correct state when a focused control
  * changes as the result of a key up, not just key down.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'ToggleOnKeyUp', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'ToggleOnKeyUp', function() {
   this.loadHtml('<div>' +
       '<div tabIndex=0 id="pressable" role="button" aria-pressed="false">' +
       'Toggle' +
@@ -711,7 +713,8 @@
 /**
  * Exiting dialog message should not interrupt a live region.
  */
-TEST_F('CvoxEventWatcherUnitTest', 'DISABLED_ExitDialogWithLiveRegion', function() {
+TEST_F('ChromeVoxEventWatcherUnitTest', 'DISABLED_ExitDialogWithLiveRegion',
+       function() {
   this.loadHtml(
       '<div role="dialog" aria-label="MyAlert">' +
       '  <h1>Heading</h1>' +
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/live_regions_test.unitjs b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/live_regions_test.unitjs
index e43e82a4..bf7256d 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/live_regions_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/live_regions_test.unitjs
@@ -10,11 +10,11 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxLiveRegionsUnitTest() {
+function ChromeVoxLiveRegionsUnitTest() {
   ChromeVoxUnitTestBase.call(this);
 }
 
-CvoxLiveRegionsUnitTest.prototype = {
+ChromeVoxLiveRegionsUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -37,7 +37,7 @@
   }
 };
 
-TEST_F('CvoxLiveRegionsUnitTest', 'InsertNonLiveRegion', function() {
+TEST_F('ChromeVoxLiveRegionsUnitTest', 'InsertNonLiveRegion', function() {
   var region = document.createElement('div');
   region.innerHTML = '<div role="button">Alpha</div>';
   document.body.appendChild(region);
@@ -52,7 +52,8 @@
  * Test inserting an 'alert' live region.
  */
 // Flaky on Chromium OS: crbug.com/498881.
-TEST_F('CvoxLiveRegionsUnitTest', 'DISABLED_InsertAlertLiveRegion', function() {
+TEST_F('ChromeVoxLiveRegionsUnitTest', 'DISABLED_InsertAlertLiveRegion',
+       function() {
   var region = document.createElement('div');
   region.innerHTML = '<div role="alert">Alpha</div>';
   document.body.appendChild(region);
@@ -69,7 +70,7 @@
  * Test making text appear inside an 'alert' live region by setting its
  * display to something other than 'none'.
  */
-TEST_F('CvoxLiveRegionsUnitTest', 'RevealAlertLiveRegion', function() {
+TEST_F('ChromeVoxLiveRegionsUnitTest', 'RevealAlertLiveRegion', function() {
   this.loadDoc(function() {/*!
     <div role="alert">
       <style>
@@ -95,7 +96,7 @@
  * Test inserting a 'polite' live region.
  */
 // Flaky on Chromium OS: crbug.com/498881.
-TEST_F('CvoxLiveRegionsUnitTest', 'DISABLED_InsertPoliteLiveRegion', function() {
+TEST_F('ChromeVoxLiveRegionsUnitTest', 'DISABLED_InsertPoliteLiveRegion', function() {
   var region = document.createElement('div');
   region.innerHTML = '<div aria-live="polite">Beta</div>';
   document.body.appendChild(region);
@@ -110,7 +111,7 @@
 /**
  * Test modifying an existing status live region.
  */
-TEST_F('CvoxLiveRegionsUnitTest', 'ModifyStatusLiveRegion', function() {
+TEST_F('ChromeVoxLiveRegionsUnitTest', 'ModifyStatusLiveRegion', function() {
   var region = document.createElement('div');
   region.innerHTML = '<div id="status" role="status">Gamma</div>';
   document.body.appendChild(region);
@@ -130,7 +131,7 @@
 /**
  * Test adding element to a atomic and non-atomic live regions.
  */
-TEST_F('CvoxLiveRegionsUnitTest', 'AddToLiveRegion', function() {
+TEST_F('ChromeVoxLiveRegionsUnitTest', 'AddToLiveRegion', function() {
   this.loadDoc(function() {/*!
     <div>
       <div id="non_atomic_buddylist" aria-live="polite">
@@ -163,7 +164,7 @@
  * Test removing elements from live regions.
  */
 // Flaky on Chromium OS: crbug.com/498881.
-TEST_F('CvoxLiveRegionsUnitTest', 'DISABLED_RemoveFromLiveRegion', function() {
+TEST_F('ChromeVoxLiveRegionsUnitTest', 'DISABLED_RemoveFromLiveRegion', function() {
   this.loadDoc(function() {/*!
     <div>
       <div id="buddylist2" aria-relevant="removals">
@@ -187,7 +188,7 @@
 /**
  * Test live region that's a progress bar through the event watcher.
  */
-TEST_F('CvoxLiveRegionsUnitTest', 'ProgressBarLiveRegionEvents', function() {
+TEST_F('ChromeVoxLiveRegionsUnitTest', 'ProgressBarLiveRegionEvents', function() {
   this.loadDoc(function() {/*!
     <div id="progress" role="progressbar" aria-live="polite" aria-valuenow="1">
       <div id="ptext">
@@ -209,7 +210,7 @@
  * Test 'alert' live region inserted as a result of focus change, like
  *   when there's an error message when filling out a form.
  */
-TEST_F('CvoxLiveRegionsUnitTest', 'FocusTriggeredAlertLiveRegion', function() {
+TEST_F('ChromeVoxLiveRegionsUnitTest', 'FocusTriggeredAlertLiveRegion', function() {
   this.loadDoc(function() {/*!
     <form id="form">
       <label>
@@ -256,7 +257,7 @@
  * Test focus followed by live region change, make sure both are spoken.
  */
 // Flaky on Chromium OS: crbug.com/498881.
-TEST_F('CvoxLiveRegionsUnitTest', 'DISABLED_FocusThenLiveRegion', function() {
+TEST_F('ChromeVoxLiveRegionsUnitTest', 'DISABLED_FocusThenLiveRegion', function() {
   this.loadDoc(function() {/*!
     <button id="button_to_focus">Button To Focus</button>
     <div id="live" aria-live="polite"></div>
@@ -277,7 +278,7 @@
  * Test live region change followed by focus, make sure both are spoken.
  */
 // Flaky on Chromium OS: crbug.com/498881.
-TEST_F('CvoxLiveRegionsUnitTest', 'DISABLED_LiveRegionThenFocus', function() {
+TEST_F('ChromeVoxLiveRegionsUnitTest', 'DISABLED_LiveRegionThenFocus', function() {
   this.loadDoc(function() {/*!
     <button id="button_to_focus">Button To Focus</button>
     <div id="live" aria-live="polite"></div>
@@ -301,7 +302,7 @@
  * one utterance until this bug is fixed: http://crbug.com/415679
  */
 // Flaky on Chromium OS: crbug.com/498881.
-TEST_F('CvoxLiveRegionsUnitTest', 'DISABLED_TwoElementsInLiveRegion', function() {
+TEST_F('ChromeVoxLiveRegionsUnitTest', 'DISABLED_TwoElementsInLiveRegion', function() {
   this.loadDoc(function() {/*!
     <div id="live" aria-live="polite">
       <div id="hidden" style="display:none">
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/navigation_manager_test.unitjs b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/navigation_manager_test.unitjs
index f807f25..0294711f 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/navigation_manager_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/navigation_manager_test.unitjs
@@ -10,11 +10,11 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxNavigationManagerUnitTest() {
+function ChromeVoxNavigationManagerUnitTest() {
   ChromeVoxUnitTestBase.call(this);
 }
 
-CvoxNavigationManagerUnitTest.prototype = {
+ChromeVoxNavigationManagerUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -55,7 +55,7 @@
     this.waitForCalm(cvox.ChromeVoxTester.setStrategy, strategy)
         .waitForCalm(cvox.ChromeVoxTester.syncToFirstNode);
 
-    var depth = 0;    
+    var depth = 0;
     commandsAndExpectations.forEach((function(ce) {
       this.waitForCalm(function() {
         if (ce.command) {
@@ -119,7 +119,7 @@
   }
 };
 
-TEST_F('CvoxNavigationManagerUnitTest', 'SimpleStaticHTML', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'SimpleStaticHTML', function() {
   this.loadHtml(
       '<div>' +
         '<p id="before">Before</p>' +
@@ -145,7 +145,7 @@
  * Test navigation of simple static HTML with some control elements to validate
  *      the text and annotation returned when doing navigation.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'ControlElements', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'ControlElements', function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -182,7 +182,8 @@
  *     fieldset to validate the text and annotation returned when doing
  *     navigation.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'ControlElementsWithFieldset', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'ControlElementsWithFieldset',
+       function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -222,7 +223,7 @@
 /**
  * Test skip to next/prev element navigation.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'SkipNavigation', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'SkipNavigation', function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -261,7 +262,7 @@
 /**
  * Test finding the next heading.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'FindNextHeading', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'FindNextHeading', function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -330,7 +331,7 @@
 /**
  * Test navigation of HTML and ARIA lists.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'ListLinearNav', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'ListLinearNav', function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -395,7 +396,7 @@
 /**
  * Test navigation of HTML and ARIA lists.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'ListSmartNav', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'ListSmartNav', function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -450,7 +451,8 @@
 /**
  * Test smart navigation of link collections.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'LinkCollectionSmartNav', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'LinkCollectionSmartNav',
+       function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -507,7 +509,7 @@
 /**
  * Test navigation of a control followed by its label.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'ControlThenLabel', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'ControlThenLabel', function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -541,7 +543,7 @@
 /**
  * Test navigation of a label followed by its control.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'LabelThenControl', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'LabelThenControl', function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -576,7 +578,7 @@
  * Test navigation of a control inside a label element (yes, this is
  *     valid HTML and should be supported).
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'ControlInsideLabel', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'ControlInsideLabel', function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -628,7 +630,8 @@
  * Test navigation of two controls inside a single label element - this
  *     is nonstandard but we should make sure we don't totally fail!
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'TwoControlsInsideLabel', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'TwoControlsInsideLabel',
+       function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -676,7 +679,7 @@
  * Test invalid labels - if a label doesn't point to a control, it
  *     should just be read as if it wasn't a label.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'InvalidLabels', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'InvalidLabels', function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -718,7 +721,7 @@
 /**
  * Test scrollbar value readout.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'Scrollbar', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'Scrollbar', function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -742,7 +745,8 @@
 /**
  * Test ARIA listbox where the whole box gets focus.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'AriaListboxActiveDescendant', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'AriaListboxActiveDescendant',
+       function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -803,7 +807,7 @@
 /**
  * Test ARIA listbox where each option gets focus.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'AriaListboxOption', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'AriaListboxOption', function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -898,7 +902,8 @@
  * Test ARIA listbox where each option gets focus and the outer container has
  * tabindex="-1"
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'AriaListboxOptionOuterFocus', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'AriaListboxOptionOuterFocus',
+       function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -937,7 +942,7 @@
 /**
  * Test ARIA menus.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'AriaMenus', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'AriaMenus', function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -977,7 +982,8 @@
 /**
  * Test left and right navigation at the group level.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'LeftRightGroupNavigation', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'LeftRightGroupNavigation',
+       function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -1019,7 +1025,8 @@
 /**
  * Test left and right navigation at the sentence level.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'LeftRightSentenceNavigation', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'LeftRightSentenceNavigation',
+       function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -1073,7 +1080,8 @@
 /**
  * Test left and right navigation at the word level.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'LeftRightWordNavigation', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'LeftRightWordNavigation',
+       function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -1131,7 +1139,8 @@
 /**
  * Test left and right navigation at the character level.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'LeftRightCharacterNavigation', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'LeftRightCharacterNavigation',
+       function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -1205,7 +1214,7 @@
 /**
  * Test whether fullyDescribe works
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'FullyDescribe', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'FullyDescribe', function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -1252,7 +1261,7 @@
 /**
  * Test continuous reading mode.
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'ContinuousReading', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'ContinuousReading', function() {
   this.loadHtml(
     '<div id="continuousTest">' +
       '<p id="before">Before</p>' +
@@ -1274,7 +1283,7 @@
 /**
  * Test HTML5 semantic elements
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'SemanticElts', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'SemanticElts', function() {
   this.loadHtml(
     '<p id="before">Before</p>' +
     '<article>' +
@@ -1331,7 +1340,7 @@
 /**
  * Test aria-haspopup
  */
-TEST_F('CvoxNavigationManagerUnitTest', 'AriaHasPopup', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'AriaHasPopup', function() {
   this.loadHtml(
     '<div>' +
       '<p id="before">Before</p>' +
@@ -1374,7 +1383,7 @@
 
 
 /** Test Aria Math Roles. */
-TEST_F('CvoxNavigationManagerUnitTest', 'AriaMathRoles', function() {
+TEST_F('ChromeVoxNavigationManagerUnitTest', 'AriaMathRoles', function() {
   this.loadHtml(
     '<p><div role="math"' +
     'aria-label="a times x squared plus b times x plus c equals 0">' +
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/user_commands_test.unitjs b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/user_commands_test.unitjs
index f425d10..a96283b2 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/user_commands_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/user_commands_test.unitjs
@@ -10,11 +10,11 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxUserCommandsUnitTest() {
+function ChromeVoxUserCommandsUnitTest() {
   ChromeVoxUnitTestBase.call(this);
 }
 
-CvoxUserCommandsUnitTest.prototype = {
+ChromeVoxUserCommandsUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -36,7 +36,7 @@
   }
 };
 
-TEST_F('CvoxUserCommandsUnitTest', 'TabHandling', function() {
+TEST_F('ChromeVoxUserCommandsUnitTest', 'TabHandling', function() {
   this.loadDoc(function() {/*!
     <div>
       <p id="before">Before</p>
@@ -65,7 +65,7 @@
  * Tests everything in the find next map to make sure there are no
  * typos and everything is valid.
  */
-SYNC_TEST_F('CvoxUserCommandsUnitTest', 'FindNextMap', function() {
+SYNC_TEST_F('ChromeVoxUserCommandsUnitTest', 'FindNextMap', function() {
   var findMap = cvox.ChromeVoxUserCommands.FIND_NEXT_MAP_;
   for (var find in findMap) {
     assertTrue(
@@ -87,7 +87,7 @@
  * is valid.
  * @export
  */
-SYNC_TEST_F('CvoxUserCommandsUnitTest', 'CommandsMap', function() {
+SYNC_TEST_F('ChromeVoxUserCommandsUnitTest', 'CommandsMap', function() {
   var cmdMap = cvox.ChromeVoxUserCommands.CMD_WHITELIST_;
   var findMap = cvox.ChromeVoxUserCommands.FIND_NEXT_MAP_;
   for (var cmd in cmdMap) {
diff --git a/chrome/browser/resources/chromeos/chromevox/common/aria_util_test.unitjs b/chrome/browser/resources/chromeos/chromevox/common/aria_util_test.unitjs
index bf13830..17fed82 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/aria_util_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/common/aria_util_test.unitjs
@@ -10,9 +10,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxAriaUtilUnitTest() {}
+function ChromeVoxAriaUtilUnitTest() {}
 
-CvoxAriaUtilUnitTest.prototype = {
+ChromeVoxAriaUtilUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -22,7 +22,7 @@
     'cvox.DomUtil',]
 };
 
-TEST_F('CvoxAriaUtilUnitTest', 'GetStateGridWithActiveCell', function() {
+TEST_F('ChromeVoxAriaUtilUnitTest', 'GetStateGridWithActiveCell', function() {
   this.loadDoc(function() {/*!
     <div id="grid" role="grid" aria-activedescendant="cell">
       <div role="row">
@@ -35,7 +35,7 @@
       eqJSON([['role_gridcell_pos', 1, 1]]));
 });
 
-TEST_F('CvoxAriaUtilUnitTest', 'GetActiveDescendant', function() {
+TEST_F('ChromeVoxAriaUtilUnitTest', 'GetActiveDescendant', function() {
   this.loadDoc(function() {/*!
     <div id="top" aria-activedescendant="child">
       <div id="child" />
@@ -75,7 +75,7 @@
   assertEquals(null, cvox.AriaUtil.getActiveDescendant(circleAElt));
 });
 
-TEST_F('CvoxAriaUtilUnitTest', 'ListIndexAndState', function() {
+TEST_F('ChromeVoxAriaUtilUnitTest', 'ListIndexAndState', function() {
   this.loadDoc(function() {/*!
     <div id="l" role="listbox" tabindex="0" aria-activedescendant="l2">
       <div id="l1" role="option">A</div>
@@ -110,7 +110,7 @@
       eqJSON([['list_position', 1, 3]]));
 });
 
-TEST_F('CvoxAriaUtilUnitTest', 'GetLiveRegions', function() {
+TEST_F('ChromeVoxAriaUtilUnitTest', 'GetLiveRegions', function() {
   this.loadDoc(function() {/*!
    <div id="outer">
     <div id="progress" role="progressbar" aria-live="polite" aria-valuenow="1">
diff --git a/chrome/browser/resources/chromeos/chromevox/common/braille_text_handler_test.unitjs b/chrome/browser/resources/chromeos/chromevox/common/braille_text_handler_test.unitjs
index 3f53422..61a00af 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/braille_text_handler_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/common/braille_text_handler_test.unitjs
@@ -39,9 +39,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxBrailleTextHandlerUnitTest() {}
+function ChromeVoxBrailleTextHandlerUnitTest() {}
 
-CvoxBrailleTextHandlerUnitTest.prototype = {
+ChromeVoxBrailleTextHandlerUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -61,7 +61,7 @@
   }
 };
 
-TEST_F('CvoxBrailleTextHandlerUnitTest', 'UpdateByUser', function() {
+TEST_F('ChromeVoxBrailleTextHandlerUnitTest', 'UpdateByUser', function() {
   var navBraille = new cvox.NavBraille({ text: 'Hello, world!' });
   this.navigationManager.setNavBraille(navBraille);
 
diff --git a/chrome/browser/resources/chromeos/chromevox/common/braille_util_test.unitjs b/chrome/browser/resources/chromeos/chromevox/common/braille_util_test.unitjs
index 44ef24f..4cff1f3 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/braille_util_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/common/braille_util_test.unitjs
@@ -10,9 +10,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxBrailleUtilUnitTest() {}
+function ChromeVoxBrailleUtilUnitTest() {}
 
-CvoxBrailleUtilUnitTest.prototype = {
+ChromeVoxBrailleUtilUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -64,7 +64,7 @@
   }
 };
 
-TEST_F('CvoxBrailleUtilUnitTest', 'BrailleName', function() {
+TEST_F('ChromeVoxBrailleUtilUnitTest', 'BrailleName', function() {
   this.loadHtml(
       '<div id="navbar">' +
       '<a id="1" href="one.com">one</a>' +
@@ -111,7 +111,7 @@
 /**
  * @export
  */
-TEST_F('CvoxBrailleUtilUnitTest', 'NameTemplate', function() {
+TEST_F('ChromeVoxBrailleUtilUnitTest', 'NameTemplate', function() {
   this.loadHtml(
       '<button id="1">Submit</button>' +
       '<input id="2" type="text" aria-label="Search">'
@@ -146,7 +146,7 @@
 /**
  * @export
  */
-TEST_F('CvoxBrailleUtilUnitTest', 'TextField', function() {
+TEST_F('ChromeVoxBrailleUtilUnitTest', 'TextField', function() {
   this.loadHtml(
       '<input id="1" type="text" aria-label="Search" value="larry">'
       );
@@ -175,7 +175,7 @@
 /**
  * @export
  */
-TEST_F('CvoxBrailleUtilUnitTest', 'TextFieldEmpty', function() {
+TEST_F('ChromeVoxBrailleUtilUnitTest', 'TextFieldEmpty', function() {
   this.loadHtml(
       '<input id="1" type="text">'
       );
@@ -200,7 +200,7 @@
 /**
  * @export
  */
-TEST_F('CvoxBrailleUtilUnitTest', 'TextFieldSelection', function() {
+TEST_F('ChromeVoxBrailleUtilUnitTest', 'TextFieldSelection', function() {
   this.loadHtml(
       '<input id="1" type="text" value="strawberry">'
       );
@@ -232,7 +232,7 @@
 /**
  * @export
  */
-TEST_F('CvoxBrailleUtilUnitTest', 'StateTemplate', function() {
+TEST_F('ChromeVoxBrailleUtilUnitTest', 'StateTemplate', function() {
   this.loadHtml(
       '<input id="1" type="checkbox" aria-label="Save">');
 
@@ -257,7 +257,7 @@
 /**
  * @export
  */
-TEST_F('CvoxBrailleUtilUnitTest', 'AccessKey', function() {
+TEST_F('ChromeVoxBrailleUtilUnitTest', 'AccessKey', function() {
   this.loadHtml(
       '<a href="http://www.google.com" id="1" accesskey="g">Google</a>');
 
@@ -274,7 +274,7 @@
 /**
  * @export
  */
-TEST_F('CvoxBrailleUtilUnitTest', 'ContainerTemplate', function() {
+TEST_F('ChromeVoxBrailleUtilUnitTest', 'ContainerTemplate', function() {
   this.loadHtml(
       '<h1>' +
       '<a id="1" href="#menu">Skip To Menu</a>' +
@@ -296,7 +296,7 @@
 /**
  * @export
  */
-TEST_F('CvoxBrailleUtilUnitTest', 'LinkSpans', function() {
+TEST_F('ChromeVoxBrailleUtilUnitTest', 'LinkSpans', function() {
   this.loadHtml('<p><a id="1" href="#1">Hello</a> from' +
       ' <a id="2" href="//www.google.com/">ChromeVox</a>');
   var link1 = $('1');
@@ -314,7 +314,7 @@
 });
 
 
-TEST_F('CvoxBrailleUtilUnitTest', 'VisitedLink', function() {
+TEST_F('ChromeVoxBrailleUtilUnitTest', 'VisitedLink', function() {
   this.loadHtml('<p><a id="1" href="http://visited.link">Hello</a> there.');
   var link = $('1');
   var navBraille = this.getBraille_(
@@ -336,7 +336,7 @@
 /**
  * @export
  */
-TEST_F('CvoxBrailleUtilUnitTest', 'NestedElements', function() {
+TEST_F('ChromeVoxBrailleUtilUnitTest', 'NestedElements', function() {
   this.loadHtml('<h1 id="test-h1">Larry, ' +
       '<a href="#batman" id="batman-link">Sergey</a> and Eric</h1>');
   var h1 = $('test-h1');
@@ -355,7 +355,7 @@
 /**
  * @export
  */
-TEST_F('CvoxBrailleUtilUnitTest', 'GetTemplatedOverride', function() {
+TEST_F('ChromeVoxBrailleUtilUnitTest', 'GetTemplatedOverride', function() {
   assertEquals('Menu mnu',
                cvox.BrailleUtil.getTemplated(null, null,
                                              { 'name': 'Menu',
@@ -381,7 +381,7 @@
 /**
  * @export
  */
-TEST_F('CvoxBrailleUtilUnitTest', 'CreateValue', function() {
+TEST_F('ChromeVoxBrailleUtilUnitTest', 'CreateValue', function() {
   var s;
   var valueSpan;
   var selectiponSpan;
diff --git a/chrome/browser/resources/chromeos/chromevox/common/command_store_test.unitjs b/chrome/browser/resources/chromeos/chromevox/common/command_store_test.unitjs
index a1f9cfdc..0d8ffef 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/command_store_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/common/command_store_test.unitjs
@@ -10,9 +10,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxCommandStoreUnitTest() {}
+function ChromeVoxCommandStoreUnitTest() {}
 
-CvoxCommandStoreUnitTest.prototype = {
+ChromeVoxCommandStoreUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -22,7 +22,7 @@
   ]
 };
 
-TEST_F('CvoxCommandStoreUnitTest', 'TableData', function() {
+TEST_F('ChromeVoxCommandStoreUnitTest', 'TableData', function() {
   var categories = cvox.CommandStore.categories();
   assertEquals(11, categories.length);
   assertEquals('modifier_keys', categories[0]);
@@ -52,7 +52,7 @@
 
 
 /** Tests that undefined is returned for bad queries. */
-TEST_F('CvoxCommandStoreUnitTest', 'InvalidQueries', function() {
+TEST_F('ChromeVoxCommandStoreUnitTest', 'InvalidQueries', function() {
   assertThat(cvox.CommandStore.commandsForCategory('foo'), eqJSON([]));
   assertTrue(undefined == cvox.CommandStore.categoryForCommand('foo'));
   assertTrue(undefined == cvox.CommandStore.messageForCommand('foo'));
@@ -60,7 +60,7 @@
 
 
 /** Tests the validity of every command. */
-TEST_F('CvoxCommandStoreUnitTest', 'CommandValidity', function() {
+TEST_F('ChromeVoxCommandStoreUnitTest', 'CommandValidity', function() {
   var categories = cvox.CommandStore.categories();
   for (var i = 0; i < categories.length; i++) {
     var commands = cvox.CommandStore.commandsForCategory(categories[i]);
diff --git a/chrome/browser/resources/chromeos/chromevox/common/content_editable_extractor_test.unitjs b/chrome/browser/resources/chromeos/chromevox/common/content_editable_extractor_test.unitjs
index c5f0fa3..1dabf7d 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/content_editable_extractor_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/common/content_editable_extractor_test.unitjs
@@ -10,9 +10,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxContentEditableExtractorUnitTest() {}
+function ChromeVoxContentEditableExtractorUnitTest() {}
 
-CvoxContentEditableExtractorUnitTest.prototype = {
+ChromeVoxContentEditableExtractorUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -37,7 +37,7 @@
   sel.addRange(r);
 }
 
-TEST_F('CvoxContentEditableExtractorUnitTest', 'EmptyElement', function() {
+TEST_F('ChromeVoxContentEditableExtractorUnitTest', 'EmptyElement', function() {
   this.loadDoc(function() {/*!
     <div>
       <div id="textbox" contentEditable="true"></div>
@@ -58,7 +58,8 @@
 /**
  * Test getting text and selections from a single contenteditable node.
  */
-TEST_F('CvoxContentEditableExtractorUnitTest', 'SingleTextNode', function() {
+TEST_F('ChromeVoxContentEditableExtractorUnitTest', 'SingleTextNode',
+       function() {
   this.loadDoc(function() {/*!
     <div>
       <div id="textbox" contentEditable="true">Hello</div>
@@ -102,7 +103,7 @@
  * Test getting text and selections from a contenteditable node with
  * nonprinted whitespace.
  */
-TEST_F('CvoxContentEditableExtractorUnitTest', 'TextWithWhitespace',
+TEST_F('ChromeVoxContentEditableExtractorUnitTest', 'TextWithWhitespace',
     function() {
   this.loadDoc(function() {/*!
     <div>
@@ -152,7 +153,7 @@
  * Test getting text and selections from a contenteditable node with
  * preformatted text.
  */
-TEST_F('CvoxContentEditableExtractorUnitTest', 'Preformatted', function() {
+TEST_F('ChromeVoxContentEditableExtractorUnitTest', 'Preformatted', function() {
   this.loadDoc(function() {/*!
     <div>
       <pre id="textbox" contentEditable="true">aaaaaaaaaa
@@ -185,7 +186,7 @@
  * Test getting text and selections from a contenteditable node with
  * wrapping.
  */
-TEST_F('CvoxContentEditableExtractorUnitTest', 'WordWrap', function() {
+TEST_F('ChromeVoxContentEditableExtractorUnitTest', 'WordWrap', function() {
   this.loadDoc(function() {/*!
     <div>
       <div id="textbox"
@@ -218,7 +219,7 @@
  * Test getting text and lines from a contenteditable region
  * containing two paragraphs and an explicit line break.
  */
-TEST_F('CvoxContentEditableExtractorUnitTest', 'TwoParas', function() {
+TEST_F('ChromeVoxContentEditableExtractorUnitTest', 'TwoParas', function() {
   this.loadDoc(function() {/*!
     <div>
       <div id="textbox" contentEditable="true">
@@ -245,7 +246,7 @@
  * Test getting text and lines from a contenteditable region
  * containing two paragraphs, this time with added whitespace.
  */
-TEST_F('CvoxContentEditableExtractorUnitTest', 'TwoParasWithWhitespace',
+TEST_F('ChromeVoxContentEditableExtractorUnitTest', 'TwoParasWithWhitespace',
     function() {
   this.loadDoc(function() {/*!
     <div>
@@ -273,7 +274,8 @@
  * Test getting text and lines from a contenteditable region
  * containing some raw text and then some text in a block-level element.
  */
-TEST_F('CvoxContentEditableExtractorUnitTest', 'NodePlusElement', function() {
+TEST_F('ChromeVoxContentEditableExtractorUnitTest', 'NodePlusElement',
+       function() {
   this.loadDoc(function() {/*!
     <div>
       <div id="textbox"
diff --git a/chrome/browser/resources/chromeos/chromevox/common/cursor_selection_test.unitjs b/chrome/browser/resources/chromeos/chromevox/common/cursor_selection_test.unitjs
index d4a72beb..9a3663c 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/cursor_selection_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/common/cursor_selection_test.unitjs
@@ -10,9 +10,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxCursorSelectionUnitTest() {}
+function ChromeVoxCursorSelectionUnitTest() {}
 
-CvoxCursorSelectionUnitTest.prototype = {
+ChromeVoxCursorSelectionUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -21,7 +21,7 @@
   ]
 };
 
-TEST_F('CvoxCursorSelectionUnitTest', 'Reverse', function() {
+TEST_F('ChromeVoxCursorSelectionUnitTest', 'Reverse', function() {
   this.loadDoc(function() {/*!
      <div>
        <p id="a">a</p>
@@ -65,7 +65,7 @@
 
 
 /** Tests correctness of collapsing selections.  */
-TEST_F('CvoxCursorSelectionUnitTest', 'Collapse', function() {
+TEST_F('ChromeVoxCursorSelectionUnitTest', 'Collapse', function() {
   this.loadDoc(function() {/*!
     <p id='1'>This is a test.</p>
   */});
diff --git a/chrome/browser/resources/chromeos/chromevox/common/dom_util_test.unitjs b/chrome/browser/resources/chromeos/chromevox/common/dom_util_test.unitjs
index b77b3667..1ac6876 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/dom_util_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/common/dom_util_test.unitjs
@@ -10,9 +10,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxDomUtilUnitTest() {}
+function ChromeVoxDomUtilUnitTest() {}
 
-CvoxDomUtilUnitTest.prototype = {
+ChromeVoxDomUtilUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -101,7 +101,7 @@
   },
 };
 
-TEST_F('CvoxDomUtilUnitTest', 'IsVisible', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'IsVisible', function() {
   this.loadDomUtilTestDoc_();
 
   // Simple tests.
@@ -150,7 +150,7 @@
 });
 
 /** Test determining if a node is a leaf node or not. @export */
-TEST_F('CvoxDomUtilUnitTest', 'IsLeafNode', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'IsLeafNode', function() {
   this.loadDomUtilTestDoc_();
 
   var node = $('normal_node');
@@ -180,7 +180,7 @@
 });
 
 /** Test determining if a node has content or not. @export */
-TEST_F('CvoxDomUtilUnitTest', 'HasContent', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'HasContent', function() {
   this.loadDomUtilTestDoc_();
 
   var node = $('normal_node');
@@ -220,7 +220,7 @@
 });
 
 /** Test getting a node's state. @export */
-TEST_F('CvoxDomUtilUnitTest', 'NodeState', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'NodeState', function() {
   this.loadDomUtilTestDoc_();
   this.appendDoc(function() {/*!
   <input id="state1_enabled">
@@ -271,7 +271,7 @@
 });
 
 /** Test finding the next/previous leaf node. @export */
-TEST_F('CvoxDomUtilUnitTest', 'LeafNodeTraversal', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'LeafNodeTraversal', function() {
   this.loadDomUtilTestDoc_();
 
   var node = $('a');
@@ -290,7 +290,7 @@
 });
 
 /** Test finding the label for controls. @export */
-TEST_F('CvoxDomUtilUnitTest', 'GetLabel', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'GetLabel', function() {
   this.loadDoc(function() {/*!
     <fieldset id="Fieldset">
     <legend>This is a legend inside a fieldset</legend>
@@ -357,7 +357,7 @@
 });
 
 /** Test finding the label for controls with a more complex setup. @export */
-TEST_F('CvoxDomUtilUnitTest', 'GetLabelComplex', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'GetLabelComplex', function() {
   this.loadDoc(function() {/*!
   <table class="bug-report-table">
   <tbody><tr>
@@ -413,7 +413,7 @@
 
 /**************************************************************/
 
-TEST_F('CvoxDomUtilUnitTest', 'EscapedNames', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'EscapedNames', function() {
   this.loadDoc(function() {/*!
      <p id="en-title" title="&lt;&gt;"></p>
      <p id="en-arialabel" aria-label="&lt;&gt;"></p>
@@ -431,7 +431,7 @@
 });
 
 /** Test a paragraph with plain text. @export */
-TEST_F('CvoxDomUtilUnitTest', 'SimplePara', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'SimplePara', function() {
   this.loadDoc(function() {/*!
     <p id="simplepara">This is a simple paragraph.</p>
   */});
@@ -441,7 +441,7 @@
 });
 
 /** Test a paragraph with nested tags. @export */
-TEST_F('CvoxDomUtilUnitTest', 'NestedPara', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'NestedPara', function() {
   this.loadDoc(function() {/*!
     <p id="nestedpara">This is a <b>paragraph</b> with <i>nested</i> tags.</p>
   */});
@@ -454,7 +454,7 @@
  * Test a paragraph with nested tags and varying visibility.
  * @export
  */
-TEST_F('CvoxDomUtilUnitTest', 'NestedVisibilityPara', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'NestedVisibilityPara', function() {
   this.loadDoc(function() {/*!
     <style type="text/css">
       #nested_visibility_paragraph { }
@@ -475,7 +475,7 @@
 });
 
 /** Test getting text from an IMG node. @export */
-TEST_F('CvoxDomUtilUnitTest', 'Image', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'Image', function() {
   this.loadDoc(function() {/*!
     <img id="img">
     <img id="img_noalt" src="rose.png">
@@ -498,7 +498,7 @@
 });
 
 /** Test getting text from a select box. @export */
-TEST_F('CvoxDomUtilUnitTest', 'Select', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'Select', function() {
   this.loadDoc(function() {/*!
     <select id="select_noneselected">
       <option>Apple</option>
@@ -520,7 +520,7 @@
 });
 
 /** Test whether funky html causes getName to go into infinite loop. */
-TEST_F('CvoxDomUtilUnitTest', 'GetNameInfiniteLoop', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'GetNameInfiniteLoop', function() {
   this.loadDoc(function() {/*!
     <div>
       <label for="a">
@@ -535,7 +535,7 @@
 });
 
 /** Test getting text from an INPUT control. @export */
-TEST_F('CvoxDomUtilUnitTest', 'Input', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'Input', function() {
   this.loadDoc(function() {/*!
     <form action="">
       <input id="hidden" type="hidden" value="hidden1">
@@ -599,7 +599,7 @@
 
 
 /** Test checking if something is a control. @export */
-TEST_F('CvoxDomUtilUnitTest', 'IsControl', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'IsControl', function() {
   this.loadDoc(function() {/*!
   <table width="100%" border="0" cellpadding="0" cellspacing="0">
     <tbody>
@@ -1000,7 +1000,7 @@
 });
 
 /** Test if something is an ARIA control. @export */
-TEST_F('CvoxDomUtilUnitTest', 'IsAriaControl', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'IsAriaControl', function() {
   this.loadDoc(function() {/*!
   <li id="cb1" role="checkbox" tabindex="0" aria-checked="false"
   aria-describedby="cond desc1">
@@ -1022,7 +1022,7 @@
 });
 
 /** Test if something is an focusable. @export */
-TEST_F('CvoxDomUtilUnitTest', 'IsFocusable', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'IsFocusable', function() {
   this.loadDoc(function() {/*!
   <a id="focus_link" href="#">Link</a>
   <a id="focus_anchor">Unfocusable anchor</a>
@@ -1075,7 +1075,7 @@
 });
 
 /** Some additional tests for getName function. */
-TEST_F('CvoxDomUtilUnitTest', 'GetName', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'GetName', function() {
   this.loadDoc(function() {/*!
   <span id="test-span" aria-labelledby="fake-id">Some text</span>
   <label id="label1">One</label>
@@ -1092,7 +1092,7 @@
 });
 
 /** Test for getLinkURL. */
-TEST_F('CvoxDomUtilUnitTest', 'GetLinkURL', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'GetLinkURL', function() {
   this.loadDoc(function() {/*!
   <a id="l1" name="nohref">Anchor</a>
   <a id="l2" href="">Empty link</a>
@@ -1116,7 +1116,7 @@
 });
 
 /** Test for isDisabled. */
-TEST_F('CvoxDomUtilUnitTest', 'IsDisabled', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'IsDisabled', function() {
   this.loadDoc(function() {/*!
   <input id="button1" type="button" value="Press me!"/>
   <input id="button2" type="button" value="Don't touch me!" disabled/>
@@ -1128,7 +1128,7 @@
 });
 
 /** Test for a tree with aria-expanded attribute. */
-TEST_F('CvoxDomUtilUnitTest', 'Tree', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'Tree', function() {
   this.loadDoc(function() {/*!
   <div id=":0" role="tree" aria-selected="false" aria-expanded="true"
     aria-level="0" aria-labelledby=":0.label" tabindex="0"
@@ -1188,7 +1188,7 @@
 });
 
 /** Test for tables with different border specifications */
-TEST_F('CvoxDomUtilUnitTest', 'TableBorders', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'TableBorders', function() {
   this.loadDoc(function() {/*!
   <table id=":0" border="1">
     <tr>
@@ -1289,7 +1289,7 @@
 });
 
 /** Tests for shallowChildlessClone */
-TEST_F('CvoxDomUtilUnitTest', 'ShallowChildlessClone', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'ShallowChildlessClone', function() {
   this.loadDoc(function() {/*!
     <div id='simple'>asdf</div>
     <div id='expectedSimpleClone'>asdf</div>
@@ -1317,7 +1317,7 @@
 });
 
 /** Tests for deepClone */
-TEST_F('CvoxDomUtilUnitTest', 'DeepClone', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'DeepClone', function() {
   this.loadDoc(function() {/*!
     <div id='simple'>asdf</div>
   */});
@@ -1332,7 +1332,7 @@
 });
 
 /** Tests for findNode */
-TEST_F('CvoxDomUtilUnitTest', 'FindNode', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'FindNode', function() {
   this.loadDoc(function() {/*!
     <div id="root">
       <p id="a">a</p>
@@ -1345,7 +1345,7 @@
 });
 
 /** Tests for getState for a list */
-TEST_F('CvoxDomUtilUnitTest', 'ListLength', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'ListLength', function() {
   this.loadDoc(function() {/*!
     <ul id="ul1">
       <li>A
@@ -1368,7 +1368,7 @@
 });
 
 /** Tests for hasLongDesc */
-TEST_F('CvoxDomUtilUnitTest', 'HasLongDesc', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'HasLongDesc', function() {
   this.loadDoc(function() {/*!
     <img id="img0" longdesc="desc.html" src="img0.jpg"></img>
     <img id="img1" src="img1.jpg"></img>
@@ -1381,7 +1381,7 @@
 });
 
 /** Tests for various link leaf types. */
-TEST_F('CvoxDomUtilUnitTest', 'LinkLeaf', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'LinkLeaf', function() {
   this.loadDoc(function() {/*!
     <a id='leaf' href='google.com'><strong>Click</strong><div>here</div></a>
     <a id='non-leaf' href='google.com'>Click <h2>here</h2></a>
@@ -1394,7 +1394,7 @@
 
 
 /** Test the value and state of a multiple select. */
-TEST_F('CvoxDomUtilUnitTest', 'MultipleSelectValue', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'MultipleSelectValue', function() {
   this.loadDoc(function() {/*!
     <select id='cars' multiple>
       <option value="volvo">Volvo</option>
@@ -1415,7 +1415,7 @@
  * Absolute positioning of the container is used to avoid the window of the
  * browser being too small to contain the test elements.
  */
-TEST_F('CvoxDomUtilUnitTest', 'ElementToPoint', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'ElementToPoint', function() {
   this.loadDoc(function() {/*!
     <div style="position: absolute; top: 0; left: 0">
       <a id='one' href='#a'>First</a>
@@ -1438,7 +1438,7 @@
 });
 
 /** Tests we compute the correct name for hidden aria labelledby nodes. */
-TEST_F('CvoxDomUtilUnitTest', 'HiddenAriaLabelledby', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'HiddenAriaLabelledby', function() {
   this.loadDoc(function() {/*!
     <span id="acc_name" style="display: none">
       hello world!
@@ -1450,7 +1450,7 @@
 });
 
 /** Tests that we compute the correct state for accesskeys. */
-TEST_F('CvoxDomUtilUnitTest', 'AccessKey', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'AccessKey', function() {
   this.loadDoc(function() {/*!
     <a id='accessKey' href="#f" title="Next page" accesskey="n">Next page</a>
   */});
@@ -1460,7 +1460,7 @@
 
 
 /** Tests that we compute the correct name for ordered listitems. */
-TEST_F('CvoxDomUtilUnitTest', 'OrderedListitem', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'OrderedListitem', function() {
   this.loadDoc(function() {/*!
     <ol id="fruits_ol">
       <li id='ol_li1'>apple
@@ -1497,7 +1497,7 @@
 });
 
 /** Tests a node with title, and textContent containing only whitespace. */
-TEST_F('CvoxDomUtilUnitTest', 'TitleOverridesInnerWhitespace', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'TitleOverridesInnerWhitespace', function() {
   this.loadDoc(function() {/*!
     <button id="btn1" title="Remove from Chrome">
       <span class="lid"></span>
@@ -1509,7 +1509,7 @@
 });
 
 /** Test memoization. **/
-TEST_F('CvoxDomUtilUnitTest', 'Memoization', function() {
+TEST_F('ChromeVoxDomUtilUnitTest', 'Memoization', function() {
   this.loadDoc(function() {/*!
     <div id="container">
     </div>
diff --git a/chrome/browser/resources/chromeos/chromevox/common/editable_text_area_shadow_test.unitjs b/chrome/browser/resources/chromeos/chromevox/common/editable_text_area_shadow_test.unitjs
index ce87422..075aa69 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/editable_text_area_shadow_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/common/editable_text_area_shadow_test.unitjs
@@ -10,9 +10,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxShadowUnitTest() {}
+function ChromeVoxShadowUnitTest() {}
 
-CvoxShadowUnitTest.prototype = {
+ChromeVoxShadowUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -21,7 +21,7 @@
   ]
 };
 
-TEST_F('CvoxShadowUnitTest', 'MultilineLines', function() {
+TEST_F('ChromeVoxShadowUnitTest', 'MultilineLines', function() {
   this.loadDoc(function() {/*!
     <div><textarea id="area">
 one
@@ -51,7 +51,7 @@
  * Test disabled due to not being reliable if font size changes.
  * See https://codereview.chromium.org/549303004/
  */
-TEST_F('CvoxShadowUnitTest', 'DISABLED_MultilineWrap', function() {
+TEST_F('ChromeVoxShadowUnitTest', 'DISABLED_MultilineWrap', function() {
   this.loadDoc(function() {/*!
     <div><textarea id="area"
           cols=4 rows=20>One two thr fou fiv six sev eig</textarea>
diff --git a/chrome/browser/resources/chromeos/chromevox/common/editable_text_test.unitjs b/chrome/browser/resources/chromeos/chromevox/common/editable_text_test.unitjs
index 7a78aa1..c72d7d5 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/editable_text_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/common/editable_text_test.unitjs
@@ -88,9 +88,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxEditableTextUnitTest() {}
+function ChromeVoxEditableTextUnitTest() {}
 
-CvoxEditableTextUnitTest.prototype = {
+ChromeVoxEditableTextUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -174,7 +174,7 @@
   }
 };
 
-TEST_F('CvoxEditableTextUnitTest', 'CursorNavigation', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'CursorNavigation', function() {
   var tts = new TestTts();
   var obj = new cvox.ChromeVoxEditableTextBase('Hello', 0, 0, false, tts);
 
@@ -193,7 +193,7 @@
 });
 
 /** Test typing words. */
-TEST_F('CvoxEditableTextUnitTest', 'TypingWords', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'TypingWords', function() {
   var tts = new TestTts();
   var obj = new cvox.ChromeVoxEditableTextBase('', 0, 0, false, tts);
   obj.changed(new cvox.TextChangeEvent('H', 1, 1));
@@ -252,7 +252,7 @@
 });
 
 /** Test selection. */
-TEST_F('CvoxEditableTextUnitTest', 'Selection', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'Selection', function() {
   var tts = new TestTts();
   var obj =
       new cvox.ChromeVoxEditableTextBase('Hello, world.', 0, 0, false, tts);
@@ -285,7 +285,7 @@
 
 
 /** Test multi-line text. */
-TEST_F('CvoxEditableTextUnitTest', 'MultiLineText', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'MultiLineText', function() {
   var str = 'This string\nspans\nfive lines.\n  \n';
   var tts = new TestTts();
   var obj = new cvox.ChromeVoxEditableElement(null, str, 0, 0, false, tts);
@@ -345,7 +345,7 @@
  * address bar, and it's being autocompleted. Sometimes it's autocompleted
  * as they type, sometimes there's a short delay.
  */
-TEST_F('CvoxEditableTextUnitTest', 'Autocomplete', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'Autocomplete', function() {
   var tts = new TestTts();
   var obj = new cvox.ChromeVoxEditableTextBase('', 0, 0, false, tts);
 
@@ -395,7 +395,7 @@
 /**
  * Test a few common scenarios where text is replaced.
  */
-TEST_F('CvoxEditableTextUnitTest', 'ReplacingText', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'ReplacingText', function() {
   // Initial value is Alabama.
   var tts = new TestTts();
   var obj = new cvox.ChromeVoxEditableTextBase('Alabama', 0, 0, false, tts);
@@ -434,7 +434,7 @@
 /**
  * Test feedback when text changes in a long sentence.
  */
-TEST_F('CvoxEditableTextUnitTest', 'ReplacingLongText', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'ReplacingLongText', function() {
   var tts = new TestTts();
   var obj = new cvox.ChromeVoxEditableTextBase(
     'I love deadlines. I like the whooshing sound they make as they fly by.',
@@ -449,7 +449,7 @@
 });
 
 /** Tests character echo. */
-TEST_F('CvoxEditableTextUnitTest', 'CharacterEcho', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'CharacterEcho', function() {
   cvox.ChromeVox.typingEcho = cvox.TypingEcho.CHARACTER;
   var tts = new TestTts();
   var obj = new cvox.ChromeVoxEditableTextBase('', 0, 0, false, tts);
@@ -473,7 +473,7 @@
 
 
 /** Tests word echo. */
-TEST_F('CvoxEditableTextUnitTest', 'WordEcho', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'WordEcho', function() {
   cvox.ChromeVox.typingEcho = cvox.TypingEcho.WORD;
   var tts = new TestTts();
   var obj = new cvox.ChromeVoxEditableTextBase('', 0, 0, false, tts);
@@ -497,7 +497,7 @@
 
 
 /** Tests no echo. */
-TEST_F('CvoxEditableTextUnitTest', 'NoEcho', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'NoEcho', function() {
   cvox.ChromeVox.typingEcho = cvox.TypingEcho.NONE;
   var tts = new TestTts();
   var obj = new cvox.ChromeVoxEditableTextBase('', 0, 0, false, tts);
@@ -520,7 +520,8 @@
 });
 
 /** Tests cursor movement in an input field by character. */
-TEST_F('CvoxEditableTextUnitTest', 'CursorMovementByCharacter', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'CursorMovementByCharacter',
+       function() {
   var test = this.setUpForCursorTest_('input');
   var editable = test.editable, prepare = test.prepare, expect = test.expect;
   try {
@@ -549,7 +550,7 @@
 });
 
 /** Tests cursor movement in an input field by word. */
-TEST_F('CvoxEditableTextUnitTest', 'CursorMovementByWord', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'CursorMovementByWord', function() {
   var test = this.setUpForCursorTest_('input');
   var editable = test.editable, prepare = test.prepare, expect = test.expect;
   try {
@@ -584,7 +585,7 @@
 });
 
 /** Tests that character and word movement still work in <textarea>. */
-TEST_F('CvoxEditableTextUnitTest', 'CursorMovementTextArea', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'CursorMovementTextArea', function() {
   var test = this.setUpForCursorTest_('textarea');
   var editable = test.editable, prepare = test.prepare, expect = test.expect;
   try {
@@ -609,7 +610,7 @@
 });
 
 /** Tests that line navigation works. */
-TEST_F('CvoxEditableTextUnitTest', 'CursorMovementByLine', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'CursorMovementByLine', function() {
   var test = this.setUpForCursorTest_('textarea');
   var editable = test.editable, prepare = test.prepare, expect = test.expect;
   try {
@@ -644,7 +645,7 @@
 });
 
 /** Tests that paragraph navigation works. */
-TEST_F('CvoxEditableTextUnitTest', 'CursorMovementByParagraph', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'CursorMovementByParagraph', function() {
   var test = this.setUpForCursorTest_('textarea');
   var editable = test.editable, prepare = test.prepare, expect = test.expect;
   try {
@@ -667,7 +668,7 @@
 });
 
 /** Tests normalization of TextChangeEvent's */
-TEST_F('CvoxEditableTextUnitTest', 'TextChangeEvent', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'TextChangeEvent', function() {
   var event1 = new cvox.TextChangeEvent('foo', 0, 1, true);
   var event2 = new cvox.TextChangeEvent('foo', 1, 0, true);
   var event3 = new cvox.TextChangeEvent('foo', 1, 1, true);
@@ -682,7 +683,7 @@
   assertEquals(1, event3.end);
 });
 
-TEST_F('CvoxEditableTextUnitTest', 'ContentEditableBraille', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'ContentEditableBraille', function() {
   this.loadDoc(function() {/*!
     <div id='1' contenteditable='true'>
       Some &nbsp; text.<br><br>
@@ -704,7 +705,7 @@
   TestBraille.assertContent('', 0, 0);
 });
 
-TEST_F('CvoxEditableTextUnitTest', 'TypingNonBreakingSpaces', function() {
+TEST_F('ChromeVoxEditableTextUnitTest', 'TypingNonBreakingSpaces', function() {
   var tts = new TestTts();
   var obj = new cvox.ChromeVoxEditableTextBase('Hello', 0, 0, false, tts);
 
diff --git a/chrome/browser/resources/chromeos/chromevox/common/find_util_test.unitjs b/chrome/browser/resources/chromeos/chromevox/common/find_util_test.unitjs
index 707fdcb3..62651c7 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/find_util_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/common/find_util_test.unitjs
@@ -10,9 +10,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxFindUtilUnitTest() {}
+function ChromeVoxFindUtilUnitTest() {}
 
-CvoxFindUtilUnitTest.prototype = {
+ChromeVoxFindUtilUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -21,7 +21,7 @@
   ]
 };
 
-TEST_F('CvoxFindUtilUnitTest', 'Links', function() {
+TEST_F('ChromeVoxFindUtilUnitTest', 'Links', function() {
   this.loadDoc(function() {/*!
     <div>
       <p id="before">Before</p>
diff --git a/chrome/browser/resources/chromeos/chromevox/common/key_sequence_test.unitjs b/chrome/browser/resources/chromeos/chromevox/common/key_sequence_test.unitjs
index 077ceeb..512eec3c 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/key_sequence_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/common/key_sequence_test.unitjs
@@ -10,9 +10,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxKeySequenceUnitTest() {}
+function ChromeVoxKeySequenceUnitTest() {}
 
-CvoxKeySequenceUnitTest.prototype = {
+ChromeVoxKeySequenceUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -156,7 +156,7 @@
   }
 };
 
-TEST_F('CvoxKeySequenceUnitTest', 'SimpleSequenceNoModifier', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'SimpleSequenceNoModifier', function() {
   var downKey = new cvox.KeySequence(this.downArrowEvent, false);
 
   assertEqualsJSON([40], downKey.keys.keyCode);
@@ -176,7 +176,8 @@
 
 
 /** Test another key sequence, this time with the modifier */
-TEST_F('CvoxKeySequenceUnitTest', 'SimpleSequenceWithModifier', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'SimpleSequenceWithModifier',
+       function() {
   var downKey = new cvox.KeySequence(this.downArrowEvent, true);
 
   assertEqualsJSON([40], downKey.keys.keyCode);
@@ -196,7 +197,7 @@
 
 
 /** Test a key sequence that includes the modifier */
-TEST_F('CvoxKeySequenceUnitTest', 'ModifiedSequence', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'ModifiedSequence', function() {
   var cvoxDownKey = new cvox.KeySequence(this.altDownArrowEvent, true);
 
   assertEqualsJSON([40], cvoxDownKey.keys.keyCode);
@@ -220,7 +221,7 @@
  * These should be equal because Ctrl should still function even with
  * sticky mode on.
 */
-TEST_F('CvoxKeySequenceUnitTest', 'StickyEquality', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'StickyEquality', function() {
   var ctrlKey = new cvox.KeySequence(this.ctrlEvent, false);
   var ctrlSticky = new cvox.KeySequence(this.ctrlStickyEvent, false);
 
@@ -233,7 +234,7 @@
  * modifier.
  * These should not be equal because they do not have the same modifiers.
 */
-TEST_F('CvoxKeySequenceUnitTest', 'ShiftEquality', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'ShiftEquality', function() {
   var aKey = new cvox.KeySequence(this.aEvent, false);
   var shiftA = new cvox.KeySequence(this.shiftAEvent, false);
 
@@ -246,7 +247,7 @@
  * on, 'a' with prefix key, and 'a' with ChromeVox modifier held down. These
  * should all be equal to each other.
  */
-TEST_F('CvoxKeySequenceUnitTest', 'FourWayEquality', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'FourWayEquality', function() {
   var commandSequence = new cvox.KeySequence(this.aEvent, true);
   var stickySequence = new cvox.KeySequence(this.aEventSticky, false);
   var prefixSequence = new cvox.KeySequence(this.aEventPrefix, false);
@@ -276,7 +277,7 @@
  * modifier specified vs. 'a' key with ChromeVox modifier held down.
  * These should all be equal to each other..
 */
-TEST_F('CvoxKeySequenceUnitTest', 'ShiftPrefixEquality', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'ShiftPrefixEquality', function() {
   var shiftAWithModifier = new cvox.KeySequence(this.shiftAEvent, true);
   var shiftAWithPrefix = new cvox.KeySequence(this.shiftAPrefixEvent, false);
   var shiftASticky = new cvox.KeySequence(this.shiftAStickyEvent, false);
@@ -304,7 +305,7 @@
  * Test inequality - 'a' with modifier key vs. 'a' without modifier key.
  * These should not be equal.
 */
-TEST_F('CvoxKeySequenceUnitTest', 'Inequality', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'Inequality', function() {
   var aNoModifier = new cvox.KeySequence(this.aEvent, false);
   var aWithModifier = new cvox.KeySequence(this.aEvent, true);
 
@@ -316,7 +317,7 @@
 /**
  * Test equality - adding an additional key onto a sequence.
  */
-TEST_F('CvoxKeySequenceUnitTest', 'CvoxCtrl', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'CvoxCtrl', function() {
   var cvoxCtrlSequence = new cvox.KeySequence(this.ctrlEvent, true);
   assertTrue(cvoxCtrlSequence.addKeyEvent(this.rightArrowEvent));
 
@@ -347,7 +348,7 @@
 /**
  * Test for inequality - key sequences in different orders.
  */
-TEST_F('CvoxKeySequenceUnitTest', 'DifferentSequences', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'DifferentSequences', function() {
   var cvoxBSequence = new cvox.KeySequence(this.bEvent, true);
   assertTrue(cvoxBSequence.addKeyEvent(this.cEvent));
 
@@ -363,7 +364,7 @@
  * Tests modifiers (ctrl, alt, etc) - if two sequences have different modifiers
  * held down then they aren't equal.
  */
-TEST_F('CvoxKeySequenceUnitTest', 'MoreModifiers', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'MoreModifiers', function() {
   var ctrlASequence = new cvox.KeySequence(this.ctrlAEvent, false);
   var ctrlModifierKeyASequence = new cvox.KeySequence(this.ctrlAEvent, true);
 
@@ -381,7 +382,7 @@
  * Tests modifier (ctrl, alt, etc) order - if two sequences have the same
  * modifiers but held down in a different order then they aren't equal.
  */
-TEST_F('CvoxKeySequenceUnitTest', 'ModifierOrder', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'ModifierOrder', function() {
   var ctrlShiftSequence = new cvox.KeySequence(this.ctrlShiftEvent, false);
   var shiftCtrlSequence = new cvox.KeySequence(this.shiftCtrlEvent, true);
 
@@ -392,7 +393,7 @@
 /**
  * Tests converting from a string to a KeySequence object.
  */
-TEST_F('CvoxKeySequenceUnitTest', 'FromStr', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'FromStr', function() {
   var ctrlString = cvox.KeySequence.fromStr('Ctrl');
   assertEqualsJSON(ctrlString.keys.ctrlKey, [true]);
   assertEqualsJSON(ctrlString.keys.keyCode, [17]);
@@ -428,7 +429,7 @@
 /**
  * Tests converting from a JSON string to a KeySequence object.
  */
-TEST_F('CvoxKeySequenceUnitTest', 'Deserialize', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'Deserialize', function() {
   var forwardSequence = cvox.KeySequence.deserialize({'cvoxModifier': true,
       'stickyMode': false, 'prefixKey': false, 'keys': {'ctrlKey': [false],
       'searchKeyHeld': [false], 'altKey': [false], 'altGraphKey': [false],
@@ -444,7 +445,8 @@
   assertEqualsJSON(ctrlSequence.keys.keyCode, [17]);
 });
 
-TEST_F('CvoxKeySequenceUnitTest', 'DeserializeAltShiftCvoxMod', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'DeserializeAltShiftCvoxMod',
+       function() {
   cvox.ChromeVox.modKeyStr = 'Alt+Shift';
 
   // Build a key sequence that does not strip modifiers when deserializing.
@@ -463,7 +465,8 @@
   assertTrue(prevHeadingSeq.keys.shiftKey[0]);
 });
 
-TEST_F('CvoxKeySequenceUnitTest', 'DeserializeSearchCvoxMod', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'DeserializeSearchCvoxMod',
+       function() {
   // Test the case when we do want to strip modifiers when deserializing. This
   // is important when the key sequence in the key map and the key sequence at
   // runtime both contain the bare cvox modifier as a key code such as in the
@@ -487,7 +490,7 @@
   assertFalse(stickySeq.keys.searchKeyHeld[0]);
 });
 
-TEST_F('CvoxKeySequenceUnitTest', 'RequireStickyMode', function() {
+TEST_F('ChromeVoxKeySequenceUnitTest', 'RequireStickyMode', function() {
   var oneFromMap = cvox.KeySequence.deserialize({requireStickyMode: true,
                                           keys: {keyCode: [49]}});
 
diff --git a/chrome/browser/resources/chromeos/chromevox/common/page_selection_test.unitjs b/chrome/browser/resources/chromeos/chromevox/common/page_selection_test.unitjs
index 413b9292..9e9263ae 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/page_selection_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/common/page_selection_test.unitjs
@@ -10,9 +10,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxPageSelectionUnitTest() {}
+function ChromeVoxPageSelectionUnitTest() {}
 
-CvoxPageSelectionUnitTest.prototype = {
+ChromeVoxPageSelectionUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -49,7 +49,7 @@
   }
 };
 
-TEST_F('CvoxPageSelectionUnitTest', 'BasicExtend', function() {
+TEST_F('ChromeVoxPageSelectionUnitTest', 'BasicExtend', function() {
   var pageSel = new cvox.PageSelection(this.pSel);
   pageSel.extend(this.hSel);
   this.assertSelection_('The quick\n\nbrown fox\njumped over');
@@ -61,7 +61,7 @@
 
 
 /** Tests a reverse extension. */
-TEST_F('CvoxPageSelectionUnitTest', 'ReverseExtend', function() {
+TEST_F('ChromeVoxPageSelectionUnitTest', 'ReverseExtend', function() {
   var pageSel = new cvox.PageSelection(this.pSel);
   this.assertSelection_('The quick');
   pageSel.extend(this.hSel);
@@ -81,7 +81,7 @@
 /** Tests all possible configurations of PageSelection's and extending
  * CursorSelection's.
  */
-TEST_F('CvoxPageSelectionUnitTest', 'ExtendDirections', function() {
+TEST_F('ChromeVoxPageSelectionUnitTest', 'ExtendDirections', function() {
   // A normal page selection, with a normal extension.
   var pageSel = new cvox.PageSelection(this.aSel);
   assertTrue(pageSel.extend(this.hSel));
@@ -99,7 +99,7 @@
 
 
 /** Tests degenerate extensions. */
-TEST_F('CvoxPageSelectionUnitTest', 'DegenerateExtensions', function() {
+TEST_F('ChromeVoxPageSelectionUnitTest', 'DegenerateExtensions', function() {
   var pageSel = new cvox.PageSelection(this.aSel);
 
   // A normal page selection, with a normal extension not in document order.
diff --git a/chrome/browser/resources/chromeos/chromevox/common/selection_util_test.unitjs b/chrome/browser/resources/chromeos/chromevox/common/selection_util_test.unitjs
index 7adbc57..8b78dc9 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/selection_util_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/common/selection_util_test.unitjs
@@ -10,9 +10,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxSelectionUtilUnitTest() {}
+function ChromeVoxSelectionUtilUnitTest() {}
 
-CvoxSelectionUtilUnitTest.prototype = {
+ChromeVoxSelectionUtilUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -21,7 +21,7 @@
   ]
 };
 
-TEST_F('CvoxSelectionUtilUnitTest', 'SimpleFindPos', function() {
+TEST_F('ChromeVoxSelectionUtilUnitTest', 'SimpleFindPos', function() {
   this.loadDoc(function() {/*!
     <div id="foo" style="position:absolute;top:50px">
     </div>
diff --git a/chrome/browser/resources/chromeos/chromevox/common/spannable_test.unitjs b/chrome/browser/resources/chromeos/chromevox/common/spannable_test.unitjs
index 22ab46a6..ac4895d8 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/spannable_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/common/spannable_test.unitjs
@@ -60,9 +60,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxSpannableUnitTest() {}
+function ChromeVoxSpannableUnitTest() {}
 
-CvoxSpannableUnitTest.prototype = {
+ChromeVoxSpannableUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -82,20 +82,20 @@
   }
 };
 
-TEST_F('CvoxSpannableUnitTest', 'ToStringUnannotated', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'ToStringUnannotated', function() {
   assertEquals('', new Spannable().toString());
   assertEquals('hello world', new Spannable('hello world').toString());
 });
 
 /** Tests that toString works correctly on annotated strings. */
-TEST_F('CvoxSpannableUnitTest', 'ToStringAnnotated', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'ToStringAnnotated', function() {
   var spannable = new Spannable('Hello Google');
   spannable.setSpan('http://www.google.com/', 6, 12);
   assertEquals('Hello Google', spannable.toString());
 });
 
 /** Tests the length calculation. */
-TEST_F('CvoxSpannableUnitTest', 'LengthProperty', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'LengthProperty', function() {
   var spannable = new Spannable('Hello');
   spannable.setSpan({}, 0, 3);
   assertEquals(5, spannable.length);
@@ -106,7 +106,7 @@
 });
 
 /** Tests that a span can be added and retrieved at the beginning. */
-TEST_F('CvoxSpannableUnitTest', 'SpanBeginning', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'SpanBeginning', function() {
   var annotation = {};
   var spannable = new Spannable('Hello world');
   spannable.setSpan(annotation, 0, 5);
@@ -118,7 +118,7 @@
 });
 
 /** Tests that a span can be added and retrieved at the beginning. */
-TEST_F('CvoxSpannableUnitTest', 'SpanEnd', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'SpanEnd', function() {
   var annotation = {};
   var spannable = new Spannable('Hello world');
   spannable.setSpan(annotation, 6, 11);
@@ -130,7 +130,7 @@
 });
 
 /** Tests that a zero-length span is not retrieved. */
-TEST_F('CvoxSpannableUnitTest', 'SpanZeroLength', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'SpanZeroLength', function() {
   var annotation = {};
   var spannable = new Spannable('Hello world');
   spannable.setSpan(annotation, 3, 3);
@@ -141,7 +141,7 @@
 });
 
 /** Tests that a removed span is not returned. */
-TEST_F('CvoxSpannableUnitTest', 'RemoveSpan', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'RemoveSpan', function() {
   var annotation = {};
   var spannable = new Spannable('Hello world');
   spannable.setSpan(annotation, 0, 3);
@@ -152,7 +152,7 @@
 });
 
 /** Tests that adding a span in one place removes it from another. */
-TEST_F('CvoxSpannableUnitTest', 'SetSpanMoves', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'SetSpanMoves', function() {
   var annotation = {};
   var spannable = new Spannable('Hello world');
   spannable.setSpan(annotation, 0, 3);
@@ -164,7 +164,7 @@
 });
 
 /** Tests that setSpan objects to out-of-range arguments. */
-TEST_F('CvoxSpannableUnitTest', 'SetSpanRangeError', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'SetSpanRangeError', function() {
   var spannable = new Spannable('Hello world');
 
   // Start index out of range.
@@ -187,7 +187,7 @@
  * Tests that multiple spans can be retrieved at one point.
  * The first one added which applies should be returned by getSpan.
  */
-TEST_F('CvoxSpannableUnitTest', 'MultipleSpans', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'MultipleSpans', function() {
   var annotation1 = { number: 1 };
   var annotation2 = { number: 2 };
   assertNotSame(annotation1, annotation2);
@@ -203,7 +203,7 @@
 });
 
 /** Tests that appending appends the strings. */
-TEST_F('CvoxSpannableUnitTest', 'AppendToString', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'AppendToString', function() {
   var spannable = new Spannable('Google');
   assertEquals('Google', spannable.toString());
   spannable.append(' Chrome');
@@ -215,7 +215,7 @@
 /**
  * Tests that appending Spannables combines annotations.
  */
-TEST_F('CvoxSpannableUnitTest', 'AppendAnnotations', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'AppendAnnotations', function() {
   var annotation1 = { number: 1 };
   var annotation2 = { number: 2 };
   assertNotSame(annotation1, annotation2);
@@ -231,7 +231,7 @@
 /**
  * Tests that a span's bounds can be retrieved.
  */
-TEST_F('CvoxSpannableUnitTest', 'GetSpanStartAndEndAndLength', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'GetSpanStartAndEndAndLength', function() {
   var annotation = {};
   var spannable = new Spannable('potato wedges');
   spannable.setSpan(annotation, 8, 12);
@@ -243,7 +243,8 @@
 /**
  * Tests that an absent span's bounds are reported correctly.
  */
-TEST_F('CvoxSpannableUnitTest', 'GetSpanStartAndEndAndLengthAbsent', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'GetSpanStartAndEndAndLengthAbsent',
+       function() {
   var annotation = {};
   var spannable = new Spannable('potato wedges');
   assertSpanNotFound(spannable, annotation);
@@ -252,7 +253,7 @@
 /**
  * Test that a zero length span can still be found.
  */
-TEST_F('CvoxSpannableUnitTest', 'GetSpanStartAndEndAndLengthZeroLength',
+TEST_F('ChromeVoxSpannableUnitTest', 'GetSpanStartAndEndAndLengthZeroLength',
        function() {
   var annotation = {};
   var spannable = new Spannable('potato wedges');
@@ -266,7 +267,7 @@
  * Tests that == (but not ===) objects are treated distinctly when getting
  * span bounds.
  */
-TEST_F('CvoxSpannableUnitTest', 'GetSpanStartAndEndEquality', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'GetSpanStartAndEndEquality', function() {
   // Note that 0 == '' and '' == 0 in JavaScript.
   var spannable = new Spannable('wat');
   spannable.setSpan(0, 0, 0);
@@ -280,7 +281,7 @@
 /**
  * Tests that substrings have the correct character sequence.
  */
-TEST_F('CvoxSpannableUnitTest', 'Substring', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'Substring', function() {
   var assertSubstringResult = function(expected, initial, start, opt_end) {
     var spannable = new Spannable(initial);
     var substring = spannable.substring(start, opt_end);
@@ -295,7 +296,7 @@
 /**
  * Tests that substring arguments are validated properly.
  */
-TEST_F('CvoxSpannableUnitTest', 'SubstringRangeError', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'SubstringRangeError', function() {
   var assertRangeError = function(initial, start, opt_end) {
     var spannable = new Spannable(initial);
     assertException('expected range error', function() {
@@ -310,7 +311,7 @@
 /**
  * Tests that spans in the substring range are preserved.
  */
-TEST_F('CvoxSpannableUnitTest', 'SubstringSpansIncluded', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'SubstringSpansIncluded', function() {
   var assertSpanIncluded = function(expectedSpanStart, expectedSpanEnd,
       initial, initialSpanStart, initialSpanEnd, start, opt_end) {
     var annotation = {};
@@ -343,7 +344,7 @@
  * Tests that spans outside the range are omitted.
  * It's fine to keep zero-length spans at the ends, though.
  */
-TEST_F('CvoxSpannableUnitTest', 'SubstringSpansExcluded', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'SubstringSpansExcluded', function() {
   var assertSpanExcluded = function(initial, spanStart, spanEnd,
       start, opt_end) {
     var annotation = {};
@@ -361,7 +362,7 @@
 /**
  * Tests that spans which cross the boundary are clipped.
  */
-TEST_F('CvoxSpannableUnitTest', 'SubstringSpansClipped', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'SubstringSpansClipped', function() {
   var assertSpanIncluded = function(expectedSpanStart, expectedSpanEnd,
       initial, initialSpanStart, initialSpanEnd, start, opt_end) {
     var annotation = {};
@@ -383,7 +384,7 @@
 /**
  * Tests that whitespace is trimmed.
  */
-TEST_F('CvoxSpannableUnitTest', 'Trim', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'Trim', function() {
   var assertTrimResult = function(expected, initial) {
     assertEquals(expected, new Spannable(initial).trim().toString());
   };
@@ -398,7 +399,7 @@
 /**
  * Tests that trim keeps, drops and clips spans.
  */
-TEST_F('CvoxSpannableUnitTest', 'TrimSpans', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'TrimSpans', function() {
   var spannable = new Spannable(' \t Kennedy\n');
   spannable.setSpan('tab', 1, 2);
   spannable.setSpan('jfk', 3, 10);
@@ -414,7 +415,7 @@
 /**
  * Tests that when a string is all whitespace, we trim off the *end*.
  */
-TEST_F('CvoxSpannableUnitTest', 'TrimAllWhitespace', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'TrimAllWhitespace', function() {
   var spannable = new Spannable('    ');
   spannable.setSpan('cursor 1', 0, 0);
   spannable.setSpan('cursor 2', 2, 2);
@@ -427,7 +428,7 @@
 /**
  * Tests finding a span which is an instance of a given class.
  */
-TEST_F('CvoxSpannableUnitTest', 'GetSpanInstanceOf', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'GetSpanInstanceOf', function() {
   function ExampleConstructorBase() {}
   function ExampleConstructor1() {}
   function ExampleConstructor2() {}
@@ -447,7 +448,7 @@
 });
 
 /** Tests trimming only left or right. */
-TEST_F('CvoxSpannableUnitTest', 'TrimLeftOrRight', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'TrimLeftOrRight', function() {
   var spannable = new Spannable('    ');
   spannable.setSpan('cursor 1', 0, 0);
   spannable.setSpan('cursor 2', 2, 2);
@@ -483,7 +484,7 @@
   assertEquals(0, trimmed3.getSpanEnd('cursor 2'));
 });
 
-TEST_F('CvoxSpannableUnitTest', 'Serialize', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'Serialize', function() {
   var fresh = new Spannable('text');
   var freshStatelessSerializable = new StatelessSerializableSpan();
   var freshNonStatelessSerializable = new NonStatelessSerializableSpan(14);
@@ -507,7 +508,7 @@
              eqJSON(thawnNonStatelessSerializable));
 });
 
-TEST_F('CvoxSpannableUnitTest', 'GetSpanIntervals', function() {
+TEST_F('ChromeVoxSpannableUnitTest', 'GetSpanIntervals', function() {
   function Foo() {}
   function Bar() {}
   var ms = new MultiSpannable('f12b45f78b01');
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util_test.extjs
index 8f983de..f5b58342 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util_test.extjs
@@ -13,11 +13,11 @@
  * @constructor
  * @extends {ChromeVoxE2ETestBase}
  */
-function AutomationUtilE2ETest() {
+function ChromeVoxAutomationUtilE2ETest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-AutomationUtilE2ETest.prototype = {
+ChromeVoxAutomationUtilE2ETest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   /** @override */
@@ -63,7 +63,7 @@
   */}
 };
 
-TEST_F('AutomationUtilE2ETest', 'GetAncestors', function() {
+TEST_F('ChromeVoxAutomationUtilE2ETest', 'GetAncestors', function() {
   this.runWithLoadedTree(this.basicDoc, function(root) {
     var expectedLength = 1;
     while (root) {
@@ -74,7 +74,7 @@
   });
 });
 
-TEST_F('AutomationUtilE2ETest', 'GetUniqueAncestors', function() {
+TEST_F('ChromeVoxAutomationUtilE2ETest', 'GetUniqueAncestors', function() {
   this.runWithLoadedTree(this.basicDoc, function(root) {
     var leftmost = root, rightmost = root;
     while (leftmost.firstChild)
@@ -117,7 +117,7 @@
   }.bind(this));
 });
 
-TEST_F('AutomationUtilE2ETest', 'GetDirection', function() {
+TEST_F('ChromeVoxAutomationUtilE2ETest', 'GetDirection', function() {
   this.runWithLoadedTree(this.basicDoc, function(root) {
     var left = root, right = root;
 
@@ -138,7 +138,7 @@
   });
 });
 
-TEST_F('AutomationUtilE2ETest', 'VisitContainer', function() {
+TEST_F('ChromeVoxAutomationUtilE2ETest', 'VisitContainer', function() {
   this.runWithLoadedTree(toolbarDoc, function(r) {
     var pred = function(n) { return n.role != 'rootWebArea'; };
 
@@ -157,7 +157,7 @@
   });
 });
 
-TEST_F('AutomationUtilE2ETest', 'HitTest', function() {
+TEST_F('ChromeVoxAutomationUtilE2ETest', 'HitTest', function() {
   this.runWithLoadedTree(headingDoc, function(r) {
     // Gets the center point of a rect.
     function getCP(node) {
@@ -179,7 +179,7 @@
   });
 });
 
-TEST_F('AutomationUtilE2ETest', 'FindLastNodeSimple', function() {
+TEST_F('ChromeVoxAutomationUtilE2ETest', 'FindLastNodeSimple', function() {
   this.runWithLoadedTree(function() {/*!
     <p aria-label=" "><div aria-label="x"></div></p>
   */}, function(r) {
@@ -190,7 +190,7 @@
   });
 });
 
-TEST_F('AutomationUtilE2ETest', 'FindLastNodeNonLeaf', function() {
+TEST_F('ChromeVoxAutomationUtilE2ETest', 'FindLastNodeNonLeaf', function() {
   this.runWithLoadedTree(function() {/*!
     <div aria-label="x"><div aria-label=" "></div></div>
   */}, function(r) {
@@ -201,7 +201,7 @@
   });
 });
 
-TEST_F('AutomationUtilE2ETest', 'FindLastNodeComplex', function() {
+TEST_F('ChromeVoxAutomationUtilE2ETest', 'FindLastNodeComplex', function() {
   this.runWithLoadedTree(function() {/*!
     <p>start</p>
     <div aria-label="x"><div aria-label=" "></div></div>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
index cd9f58a..18b806f 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
@@ -13,11 +13,11 @@
  * @constructor
  * @extends {ChromeVoxNextE2ETest}
  */
-function BackgroundTest() {
+function ChromeVoxBackgroundTest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-BackgroundTest.prototype = {
+ChromeVoxBackgroundTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   /** @override */
@@ -132,18 +132,18 @@
 };
 
 /** Tests that ChromeVox classic is in this context. */
-SYNC_TEST_F('BackgroundTest', 'ClassicNamespaces', function() {
+SYNC_TEST_F('ChromeVoxBackgroundTest', 'ClassicNamespaces', function() {
   assertEquals('object', typeof(cvox));
   assertEquals('function', typeof(cvox.ChromeVoxBackground));
 });
 
 /** Tests that ChromeVox next is in this context. */
-SYNC_TEST_F('BackgroundTest', 'NextNamespaces', function() {
+SYNC_TEST_F('ChromeVoxBackgroundTest', 'NextNamespaces', function() {
   assertEquals('function', typeof(Background));
 });
 
 /** Tests consistency of navigating forward and backward. */
-TEST_F('BackgroundTest', 'ForwardBackwardNavigation', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ForwardBackwardNavigation', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.linksAndHeadingsDoc, function() {
     mockFeedback.expectSpeech('start').expectBraille('start');
@@ -202,7 +202,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'CaretNavigation', function() {
+TEST_F('ChromeVoxBackgroundTest', 'CaretNavigation', function() {
   // TODO(plundblad): Add braille expectaions when crbug.com/523285 is fixed.
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.linksAndHeadingsDoc, function() {
@@ -250,7 +250,7 @@
 });
 
 /** Tests that individual buttons are stops for move-by-word functionality. */
-TEST_F('BackgroundTest', 'CaretNavigationTreatsButtonAsWord', function() {
+TEST_F('ChromeVoxBackgroundTest', 'CaretNavigationTreatsButtonAsWord', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.buttonDoc, function() {
     mockFeedback.expectSpeech('start');
@@ -272,7 +272,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'SelectSingleBasic', function() {
+TEST_F('ChromeVoxBackgroundTest', 'SelectSingleBasic', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.formsDoc, function() {
     var incrementSelectedIndex =
@@ -289,7 +289,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'ContinuousRead', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ContinuousRead', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.linksAndHeadingsDoc, function() {
     mockFeedback.expectSpeech('start')
@@ -303,7 +303,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'InitialFocus', function() {
+TEST_F('ChromeVoxBackgroundTest', 'InitialFocus', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree('<a href="a">a</a>',
     function(rootNode) {
@@ -313,7 +313,7 @@
     });
 });
 
-TEST_F('BackgroundTest', 'AriaLabel', function() {
+TEST_F('ChromeVoxBackgroundTest', 'AriaLabel', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree('<a aria-label="foo" href="a">a</a>',
     function(rootNode) {
@@ -327,7 +327,7 @@
   );
 });
 
-TEST_F('BackgroundTest', 'ShowContextMenu', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ShowContextMenu', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree('<p>before</p><a href="a">a</a>',
     function(rootNode) {
@@ -342,7 +342,7 @@
     }.bind(this));
 });
 
-TEST_F('BackgroundTest', 'BrailleRouting', function() {
+TEST_F('ChromeVoxBackgroundTest', 'BrailleRouting', function() {
   var mockFeedback = this.createMockFeedback();
   var route = function(position) {
     assertTrue(ChromeVoxState.instance.onBrailleKeyEvent(
@@ -385,7 +385,7 @@
       });
 });
 
-TEST_F('BackgroundTest', 'FocusInputElement', function() {
+TEST_F('ChromeVoxBackgroundTest', 'FocusInputElement', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
     function() {/*!
@@ -410,7 +410,7 @@
 });
 
 // Flaky, see https://crbug.com/622387.
-TEST_F('BackgroundTest', 'DISABLED_UseEditableState', function() {
+TEST_F('ChromeVoxBackgroundTest', 'DISABLED_UseEditableState', function() {
   this.runWithLoadedTree(
     function() {/*!
       <input type="text"></input>
@@ -441,7 +441,7 @@
     }.bind(this));
 });
 
-TEST_F('BackgroundTest', 'EarconsForControls', function() {
+TEST_F('ChromeVoxBackgroundTest', 'EarconsForControls', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
     function() {/*!
@@ -489,7 +489,7 @@
     }.bind(this));
 });
 
-SYNC_TEST_F('BackgroundTest', 'GlobsToRegExp', function() {
+SYNC_TEST_F('ChromeVoxBackgroundTest', 'GlobsToRegExp', function() {
   assertEquals('/^()$/', Background.globsToRegExp_([]).toString());
   assertEquals(
       '/^(http:\\/\\/host\\/path\\+here)$/',
@@ -500,7 +500,7 @@
 });
 
 // Flaky, see https://crbug.com/622387.
-TEST_F('BackgroundTest', 'DISABLED_ActiveOrInactive', function() {
+TEST_F('ChromeVoxBackgroundTest', 'DISABLED_ActiveOrInactive', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
       <a href="a">a</a>
@@ -538,7 +538,7 @@
     });
 });
 
-TEST_F('BackgroundTest', 'ShouldNotFocusIframe', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ShouldNotFocusIframe', function() {
   this.runWithLoadedTree(  function() {/*!
     <iframe tabindex=0 src="data:text/html,<p>Inside</p>"></iframe>
     <button>outside</button>
@@ -560,7 +560,7 @@
   }.bind(this));
 });
 
-TEST_F('BackgroundTest', 'ShouldFocusLink', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ShouldFocusLink', function() {
   this.runWithLoadedTree(  function() {/*!
     <div><a href="#">mylink</a></div>
     <button>after</button>
@@ -581,7 +581,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'NoisySlider', function() {
+TEST_F('ChromeVoxBackgroundTest', 'NoisySlider', function() {
   var mockFeedback = this.createMockFeedback();
   // Slider aria-valuetext must change otherwise blink suppresses event.
   this.runWithLoadedTree(  function() {/*!
@@ -610,7 +610,7 @@
   }.bind(this));
 });
 
-TEST_F('BackgroundTest', 'Checkbox', function() {
+TEST_F('ChromeVoxBackgroundTest', 'Checkbox', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go" role="checkbox">go</div>
@@ -645,7 +645,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'MixedCheckbox', function() {
+TEST_F('ChromeVoxBackgroundTest', 'MixedCheckbox', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go" role="checkbox" aria-checked="mixed">go</div>
@@ -655,7 +655,7 @@
 });
 
 /** Tests navigating into and out of iframes using nextButton */
-TEST_F('BackgroundTest', 'ForwardNavigationThroughIframeButtons', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ForwardNavigationThroughIframeButtons', function() {
   var mockFeedback = this.createMockFeedback();
 
   var running = false;
@@ -697,7 +697,7 @@
 });
 
 /** Tests navigating into and out of iframes using nextObject */
-TEST_F('BackgroundTest', 'ForwardObjectNavigationThroughIframes', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ForwardObjectNavigationThroughIframes', function() {
   var mockFeedback = this.createMockFeedback();
 
   var running = false;
@@ -746,7 +746,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'SelectOptionSelected', function() {
+TEST_F('ChromeVoxBackgroundTest', 'SelectOptionSelected', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <select>
@@ -770,7 +770,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'ToggleButton', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ToggleButton', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div aria-pressed="mixed" role="button">boldface</div>
@@ -803,7 +803,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'EditText', function() {
+TEST_F('ChromeVoxBackgroundTest', 'EditText', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <input type="text"></input>
@@ -819,7 +819,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'BackwardForwardSync', function() {
+TEST_F('ChromeVoxBackgroundTest', 'BackwardForwardSync', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div aria-label="Group" role="group" tabindex=0>
@@ -852,7 +852,7 @@
 });
 
 /** Tests that navigation works when the current object disappears. */
-TEST_F('BackgroundTest', 'DisappearingObject', function() {
+TEST_F('ChromeVoxBackgroundTest', 'DisappearingObject', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.disappearingObjectDoc, function(rootNode) {
     var deleteButton = rootNode.find({role: RoleType.BUTTON,
@@ -891,7 +891,7 @@
 });
 
 /** Tests that focus jumps to details properly when indicated. */
-TEST_F('BackgroundTest', 'JumpToDetails', function() {
+TEST_F('ChromeVoxBackgroundTest', 'JumpToDetails', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.detailsDoc, function(rootNode) {
     mockFeedback.call(doCmd('jumpToDetails'))
@@ -901,7 +901,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'ButtonNameValueDescription', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ButtonNameValueDescription', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <input type="submit" aria-label="foo" value="foo"></input>
@@ -914,7 +914,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'NameFromHeadingLink', function() {
+TEST_F('ChromeVoxBackgroundTest', 'NameFromHeadingLink', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <p>before</p>
@@ -929,7 +929,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'OptionChildIndexCount', function() {
+TEST_F('ChromeVoxBackgroundTest', 'OptionChildIndexCount', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <div role="listbox">
@@ -950,7 +950,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'ListMarkerIsIgnored', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ListMarkerIsIgnored', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <ul><li>apple</ul>
@@ -962,7 +962,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'SymetricComplexHeading', function() {
+TEST_F('ChromeVoxBackgroundTest', 'SymetricComplexHeading', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <h4><p>NW</p><p>NE</p></h4>
@@ -978,7 +978,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'ContentEditableJumpSyncsRange', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ContentEditableJumpSyncsRange', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <p>start</p>
@@ -1010,7 +1010,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'Selection', function() {
+TEST_F('ChromeVoxBackgroundTest', 'Selection', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <p>simple</p>
@@ -1035,7 +1035,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'BasicTableCommands', function() {
+TEST_F('ChromeVoxBackgroundTest', 'BasicTableCommands', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
   <table border=1>
@@ -1105,7 +1105,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'MissingTableCells', function() {
+TEST_F('ChromeVoxBackgroundTest', 'MissingTableCells', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
   <table border=1>
@@ -1147,7 +1147,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'DisabledState', function() {
+TEST_F('ChromeVoxBackgroundTest', 'DisabledState', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <button aria-disabled="true">ok</button>
@@ -1156,7 +1156,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'HeadingLevels', function() {
+TEST_F('ChromeVoxBackgroundTest', 'HeadingLevels', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <h1>1</h1><h2>2</h2><h3>3</h3><h4>4</h4><h5>5</h5><h6>6</h6>
@@ -1176,7 +1176,7 @@
 });
 
 // Flaky, see https://crbug.com/622387.
-TEST_F('BackgroundTest', 'DISABLED_EditableNavigation', function() {
+TEST_F('ChromeVoxBackgroundTest', 'DISABLED_EditableNavigation', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <div contenteditable>this is a test</div>
@@ -1193,7 +1193,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'NavigationMovesFocus', function() {
+TEST_F('ChromeVoxBackgroundTest', 'NavigationMovesFocus', function() {
   this.runWithLoadedTree(function(root) {/*!
     <p>start</p>
     <input type="text"></input>
@@ -1208,7 +1208,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'BrailleCaretNavigation', function() {
+TEST_F('ChromeVoxBackgroundTest', 'BrailleCaretNavigation', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <p>This is a<em>test</em> of inline braille<br>with a second line</p>
@@ -1229,7 +1229,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'InPageLinks', function() {
+TEST_F('ChromeVoxBackgroundTest', 'InPageLinks', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <a href="#there">hi</a>
@@ -1242,7 +1242,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'ListItem', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ListItem', function() {
   this.resetContextualOutput();
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
@@ -1273,7 +1273,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'BusyHeading', function() {
+TEST_F('ChromeVoxBackgroundTest', 'BusyHeading', function() {
   this.resetContextualOutput();
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
@@ -1290,7 +1290,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'NodeVsSubnode', function() {
+TEST_F('ChromeVoxBackgroundTest', 'NodeVsSubnode', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <a href="#">test</a>
@@ -1320,7 +1320,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'NativeFind', function() {
+TEST_F('ChromeVoxBackgroundTest', 'NativeFind', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <a href="#">grape</a>
@@ -1337,7 +1337,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'EditableKeyCommand', function() {
+TEST_F('ChromeVoxBackgroundTest', 'EditableKeyCommand', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <input type="text"></input>
@@ -1370,7 +1370,7 @@
 });
 
 // Failing consistently, please see https://crbug.com/857382
-TEST_F('BackgroundTest', 'DISABLED_TextSelectionAndLiveRegion', function() {
+TEST_F('ChromeVoxBackgroundTest', 'DISABLED_TextSelectionAndLiveRegion', function() {
   DesktopAutomationHandler.announceActions = true;
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
@@ -1408,7 +1408,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'TableColumnHeaders', function() {
+TEST_F('ChromeVoxBackgroundTest', 'TableColumnHeaders', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <div role="grid">
@@ -1449,7 +1449,7 @@
 });
 
 // Flaky, see https://crbug.com/622387.
-TEST_F('BackgroundTest', 'DISABLED_ActiveDescendantUpdates', function() {
+TEST_F('ChromeVoxBackgroundTest', 'DISABLED_ActiveDescendantUpdates', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <div aria-label="container" tabindex=0 role="group" id="active"
@@ -1481,7 +1481,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'NavigationEscapesEdit', function() {
+TEST_F('ChromeVoxBackgroundTest', 'NavigationEscapesEdit', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <p>before content editable</p>
@@ -1556,7 +1556,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'NavigationSyncsSelect', function() {
+TEST_F('ChromeVoxBackgroundTest', 'NavigationSyncsSelect', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <select>
@@ -1578,7 +1578,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'NavigationIgnoresLabels', function() {
+TEST_F('ChromeVoxBackgroundTest', 'NavigationIgnoresLabels', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <p>before</p>
@@ -1610,7 +1610,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'NavigationIgnoresDescriptions', function() {
+TEST_F('ChromeVoxBackgroundTest', 'NavigationIgnoresDescriptions', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <p>before</p>
@@ -1642,7 +1642,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'MathContentViaInnerHtml', function() {
+TEST_F('ChromeVoxBackgroundTest', 'MathContentViaInnerHtml', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*
     <div role="math">
@@ -1685,7 +1685,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'GestureGranularity', function() {
+TEST_F('ChromeVoxBackgroundTest', 'GestureGranularity', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*
     <p>This is a test</p>
@@ -1726,7 +1726,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'LinesFilterWhitespace', function() {
+TEST_F('ChromeVoxBackgroundTest', 'LinesFilterWhitespace', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*
     <p>start</p>
@@ -1747,7 +1747,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'TabSwitchAndRefreshRecovery', function() {
+TEST_F('ChromeVoxBackgroundTest', 'TabSwitchAndRefreshRecovery', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*
     <p>tab1</p>
@@ -1775,7 +1775,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'ListName', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ListName', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*
     <div id="_md-chips-wrapper-76" tabindex="-1" class="md-chips md-readonly" aria-setsize="4" aria-label="Favorite Sports" role="list" aria-describedby="chipsNote">
@@ -1790,7 +1790,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'LayoutTable', function() {
+TEST_F('ChromeVoxBackgroundTest', 'LayoutTable', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*
     <table><tr><td>start</td></tr></table>
@@ -1803,7 +1803,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'ReinsertedNodeRecovery', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ReinsertedNodeRecovery', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*
     <div>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
index f5f42d72..9a84912 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
@@ -10,11 +10,11 @@
  * @constructor
  * @extends {ChromeVoxNextE2ETest}
  */
-function CursorsTest() {
+function ChromeVoxCursorsTest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-CursorsTest.prototype = {
+ChromeVoxCursorsTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   /** Test cursors.Cursor. @const {string} */
@@ -129,7 +129,7 @@
   */}
 };
 
-TEST_F('CursorsTest', 'CharacterCursor', function() {
+TEST_F('ChromeVoxCursorsTest', 'CharacterCursor', function() {
   this.runCursorMovesOnDocument(this.simpleDoc, [
     [CHARACTER, DIRECTIONAL, FORWARD, {index: 1, value: 'start '}],
     [CHARACTER, DIRECTIONAL, BACKWARD, {index: 0, value: 'start '}],
@@ -149,7 +149,7 @@
     [CHARACTER, DIRECTIONAL, BACKWARD, {index: 5, value: 'start '}],]);
 });
 
-TEST_F('CursorsTest', 'WordCursor', function() {
+TEST_F('ChromeVoxCursorsTest', 'WordCursor', function() {
   this.runCursorMovesOnDocument(this.simpleDoc, [
     // Word (BOUND).
     [WORD, BOUND, BACKWARD, {index: 0, value: 'start '}],
@@ -171,7 +171,7 @@
     [WORD, DIRECTIONAL, BACKWARD, {index: 0, value: undefined}]]);
 });
 
-TEST_F('CursorsTest', 'CharacterWordCursor', function() {
+TEST_F('ChromeVoxCursorsTest', 'CharacterWordCursor', function() {
   this.runCursorMovesOnDocument(this.simpleDoc, [
     [CHARACTER, DIRECTIONAL, FORWARD, {index: 1, value: 'start '}],
 
@@ -187,7 +187,7 @@
     [WORD, DIRECTIONAL, BACKWARD, {index: 0, value: 'start '}]]);
 });
 
-TEST_F('CursorsTest', 'LineCursor', function() {
+TEST_F('ChromeVoxCursorsTest', 'LineCursor', function() {
   this.runCursorMovesOnDocument(this.simpleDoc, [
     // Line (BOUND).
     [LINE, BOUND, FORWARD, {value: 'same line'}],
@@ -204,7 +204,7 @@
     [LINE, BOUND, BACKWARD, {value: 'start '}]]);
 });
 
-TEST_F('CursorsTest', 'CharacterRange', function() {
+TEST_F('ChromeVoxCursorsTest', 'CharacterRange', function() {
   this.runCursorMovesOnDocument(this.simpleDoc, [
       [CHARACTER, FORWARD,
           {value: 'start ', index: 1}, {value: 'start ', index: 2}],
@@ -237,7 +237,7 @@
   ], this.RANGE);
 });
 
-TEST_F('CursorsTest', 'WordRange', function() {
+TEST_F('ChromeVoxCursorsTest', 'WordRange', function() {
   this.runCursorMovesOnDocument(this.simpleDoc, [
       [WORD, FORWARD,
           {value: 'same line', index: 0}, {value: 'same line', index: 4}],
@@ -262,7 +262,7 @@
 });
 
 
-TEST_F('CursorsTest', 'LineRange', function() {
+TEST_F('ChromeVoxCursorsTest', 'LineRange', function() {
   this.runCursorMovesOnDocument(this.simpleDoc, [
         [LINE, FORWARD, {value: 'end', index: 0}, {value: 'end', index: 3}],
       [LINE, FORWARD, {value: 'end', index: 0}, {value: 'end', index: 3}],
@@ -277,7 +277,7 @@
   ], this.RANGE);
 });
 
-TEST_F('CursorsTest', 'DontSplitOnNodeNavigation', function() {
+TEST_F('ChromeVoxCursorsTest', 'DontSplitOnNodeNavigation', function() {
   this.runWithLoadedTree(this.multiInlineDoc, function(root) {
     var para = root.firstChild;
     assertEquals('paragraph', para.role);
@@ -297,7 +297,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'WrappingCursors', function() {
+TEST_F('ChromeVoxCursorsTest', 'WrappingCursors', function() {
   this.runWithLoadedTree(this.multiInlineDoc, function(root) {
     var first = root;
     var last = root.lastChild.firstChild;
@@ -313,7 +313,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'IsInWebRange', function() {
+TEST_F('ChromeVoxCursorsTest', 'IsInWebRange', function() {
   this.runWithLoadedTree(this.simpleDoc, function(root) {
     var para = root.firstChild;
     var webRange = new cursors.Range.fromNode(para);
@@ -323,7 +323,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'SingleDocSelection', function() {
+TEST_F('ChromeVoxCursorsTest', 'SingleDocSelection', function() {
   this.runWithLoadedTree(function() {/*!
     <span>start</span>
     <p><a href="google.com">google home page</a></p>
@@ -364,7 +364,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'MultiLineOffsetSelection', function() {
+TEST_F('ChromeVoxCursorsTest', 'MultiLineOffsetSelection', function() {
   this.runWithLoadedTree(this.multiInlineDoc, function(root) {
     var secondLine = root.firstChild.firstChild.firstChild.nextSibling;
     assertEquals('inlineTextBox', secondLine.role);
@@ -393,7 +393,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'InlineElementOffset', function() {
+TEST_F('ChromeVoxCursorsTest', 'InlineElementOffset', function() {
   this.runWithLoadedTree(function() {/*!
     <span>start</span>
     <p>This<br> is a<a href="#g">test</a>of selection</p>
@@ -431,7 +431,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'ContentEquality', function() {
+TEST_F('ChromeVoxCursorsTest', 'ContentEquality', function() {
   this.runWithLoadedTree(function() {/*!
     <div role="region">this is a test</button>
   */}, function(root) {
@@ -463,7 +463,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'DeepEquivalency', function() {
+TEST_F('ChromeVoxCursorsTest', 'DeepEquivalency', function() {
   this.runWithLoadedTree(function() {/*!
     <p style="word-spacing:100000px">this is a test</p>
   */}, function(root) {
@@ -514,7 +514,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'SelectionAdjustmentsRichText', function() {
+TEST_F('ChromeVoxCursorsTest', 'SelectionAdjustmentsRichText', function() {
   this.runWithLoadedTree(function() {/*!
     <div contenteditable><p>test</p><p>123</p></div>
   */}, function(root) {
@@ -563,7 +563,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'SelectionAdjustmentsNonRichText', function() {
+TEST_F('ChromeVoxCursorsTest', 'SelectionAdjustmentsNonRichText', function() {
   this.runWithLoadedTree(function() {/*!
     <input type="text"></input>
     <textarea></textarea>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing_test.extjs
index 36a626a..b0e93ebb 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing_test.extjs
@@ -13,12 +13,12 @@
  * @constructor
  * @extends {ChromeVoxNextE2ETest}
  */
-function EditingTest() {
+function ChromeVoxEditingTest() {
   ChromeVoxNextE2ETest.call(this);
   window.RoleType = chrome.automation.RoleType;
 }
 
-EditingTest.prototype = {
+ChromeVoxEditingTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   /**
@@ -50,7 +50,7 @@
 </textarea>
 */};
 
-TEST_F('EditingTest', 'Focus', function() {
+TEST_F('ChromeVoxEditingTest', 'Focus', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(doc, function(root) {
     var singleLine = root.find({role: RoleType.TEXT_FIELD,
@@ -73,7 +73,7 @@
   });
 });
 
-TEST_F('EditingTest', 'Multiline', function() {
+TEST_F('ChromeVoxEditingTest', 'Multiline', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(doc, function(root) {
     var textarea = root.find({role: RoleType.TEXT_FIELD,
@@ -102,7 +102,7 @@
   });
 });
 
-TEST_F('EditingTest', 'TextButNoSelectionChange', function() {
+TEST_F('ChromeVoxEditingTest', 'TextButNoSelectionChange', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
     function() {/*!
@@ -137,7 +137,7 @@
     });
 });
 
-TEST_F('EditingTest', 'RichTextMoveByLine', function() {
+TEST_F('ChromeVoxEditingTest', 'RichTextMoveByLine', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go" role="textbox" contenteditable>
@@ -184,7 +184,7 @@
   });
 });
 
-TEST_F('EditingTest', 'RichTextMoveByCharacter', function() {
+TEST_F('ChromeVoxEditingTest', 'RichTextMoveByCharacter', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go" role="textbox" contenteditable>This <b>is</b> a test.</div>
@@ -254,7 +254,7 @@
 });
 
 // Tests specifically for cursor workarounds.
-TEST_F('EditingTest', 'RichTextMoveByCharacterNodeWorkaround', function() {
+TEST_F('ChromeVoxEditingTest', 'RichTextMoveByCharacterNodeWorkaround', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go" role="textbox" contenteditable>hello <b>world</b></div>
@@ -295,7 +295,7 @@
   });
 });
 
-TEST_F('EditingTest', 'RichTextMoveByCharacterEndOfLine', function() {
+TEST_F('ChromeVoxEditingTest', 'RichTextMoveByCharacterEndOfLine', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go" role="textbox" contenteditable>Test</div>
@@ -331,7 +331,7 @@
   });
 });
 
-TEST_F('EditingTest', 'RichTextLinkOutput', function() {
+TEST_F('ChromeVoxEditingTest', 'RichTextLinkOutput', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go" role="textbox" contenteditable>a <a href="#">test</a></div>
@@ -375,7 +375,7 @@
   });
 });
 
-TEST_F('EditingTest', 'RichTextExtendByCharacter', function() {
+TEST_F('ChromeVoxEditingTest', 'RichTextExtendByCharacter', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go" role="textbox" contenteditable>Te<br>st</div>
@@ -409,7 +409,7 @@
   });
 });
 
-TEST_F('EditingTest', 'RichTextImageByCharacter', function() {
+TEST_F('ChromeVoxEditingTest', 'RichTextImageByCharacter', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <p id="go" contenteditable>
@@ -470,13 +470,17 @@
   });
 });
 
-TEST_F('EditingTest', 'RichTextSelectByLine', function() {
+TEST_F('ChromeVoxEditingTest', 'RichTextSelectByLine', function() {
   var mockFeedback = this.createMockFeedback();
+  // Use digit strings like "11111" and "22222" because the character widths
+  // of digits are always the same. This means the test can move down one line
+  // middle of "11111" and reliably hit a given character position in "22222",
+  // regardless of font configuration. https://crbug.com/898213
   this.runWithLoadedTree(function() {/*!
     <p id="go" contenteditable>
-      first line<br>
-      second line<br>
-      third line<br>
+      11111 line<br>
+      22222 line<br>
+      33333 line<br>
     </p>
     <script>
       var commands = [
@@ -513,11 +517,11 @@
 
       // By character.
       mockFeedback.call(move)
-          .expectSpeech('f', 'selected')
-          .expectBraille('first line\nmled', {startIndex: 0, endIndex: 1})
+          .expectSpeech('1', 'selected')
+          .expectBraille('11111 line\nmled', {startIndex: 0, endIndex: 1})
           .call(move)
-          .expectSpeech('i', 'added to selection')
-          .expectBraille('first line\nmled', {startIndex: 0, endIndex: 2})
+          .expectSpeech('1', 'added to selection')
+          .expectBraille('11111 line\nmled', {startIndex: 0, endIndex: 2})
 
           // Forward selection.
 
@@ -526,54 +530,54 @@
           // By line (notice the partial selections from the first and second
           // lines).
           .call(move)
-          .expectSpeech('rst line \n se', 'selected')
-          .expectBraille('second line\n', {startIndex: 0, endIndex: 2})
+          .expectSpeech('111 line \n 22', 'selected')
+          .expectBraille('22222 line\n', {startIndex: 0, endIndex: 2})
 
           .call(move)
-          .expectSpeech('cond line \n th', 'selected')
-          .expectBraille('third line\n', {startIndex: 0, endIndex: 2})
+          .expectSpeech('222 line \n 33', 'selected')
+          .expectBraille('33333 line\n', {startIndex: 0, endIndex: 2})
 
           // Shrinking.
           .call(move)
-          .expectSpeech('cond line \n th', 'unselected')
-          .expectBraille('second line\n', {startIndex: 0, endIndex: 2})
+          .expectSpeech('222 line \n 33', 'unselected')
+          .expectBraille('22222 line\n', {startIndex: 0, endIndex: 2})
 
           .call(move)
-          .expectSpeech('fi', 'selected')
-          .expectBraille('first line\nmled', {startIndex: 0, endIndex: 2})
+          .expectSpeech('11', 'selected')
+          .expectBraille('11111 line\nmled', {startIndex: 0, endIndex: 2})
 
           // Document boundary.
           .call(move)
-          .expectSpeech('rst line \n second line \n third line \n \n',
+          .expectSpeech('111 line \n 22222 line \n 33333 line \n \n',
                         'selected')
-          .expectBraille('third line\n', {startIndex: 0, endIndex: 10})
+          .expectBraille('33333 line\n', {startIndex: 0, endIndex: 10})
 
           // The script repositions the caret to the 'n' of the third line.
           .call(move)
-          .expectSpeech('third line')
-          .expectBraille('third line\n', {startIndex: 10, endIndex: 10})
+          .expectSpeech('33333 line')
+          .expectBraille('33333 line\n', {startIndex: 10, endIndex: 10})
           .call(move)
           .expectSpeech('e')
-          .expectBraille('third line\n', {startIndex: 9, endIndex: 9})
+          .expectBraille('33333 line\n', {startIndex: 9, endIndex: 9})
           .call(move)
           .expectSpeech('n')
-          .expectBraille('third line\n', {startIndex: 8, endIndex: 8})
+          .expectBraille('33333 line\n', {startIndex: 8, endIndex: 8})
 
           // Backward selection.
 
           // Growing.
           .call(move)
-          .expectSpeech(' line \n third li', 'selected')
-          .expectBraille('second line\n', {startIndex: 6, endIndex: 12})
+          .expectSpeech('ne \n 33333 li', 'selected')
+          .expectBraille('22222 line\n', {startIndex: 8, endIndex: 11})
 
           .call(move)
-          .expectSpeech('e \n second', 'selected')
-          .expectBraille('first line\n', {startIndex: 9, endIndex: 11})
+          .expectSpeech('ne \n 22222 li', 'selected')
+          .expectBraille('11111 line\n', {startIndex: 8, endIndex: 11})
 
           // Shrinking.
           .call(move)
-          .expectSpeech('e \n second', 'unselected')
-          .expectBraille('second line\n', {startIndex: 6, endIndex: 12})
+          .expectSpeech('ne \n 22222 li', 'unselected')
+          .expectBraille('22222 line\n', {startIndex: 8, endIndex: 11})
 
           .replay();
     });
@@ -581,7 +585,7 @@
   });
 });
 
-TEST_F('EditingTest', 'EditableLineOneStaticText', function() {
+TEST_F('ChromeVoxEditingTest', 'EditableLineOneStaticText', function() {
   this.runWithLoadedTree(function() {/*!
     <p contenteditable style="word-spacing:100000px">this is a test</p>
   */}, function(root) {
@@ -633,7 +637,7 @@
   });
 });
 
-TEST_F('EditingTest', 'EditableLineTwoStaticTexts', function() {
+TEST_F('ChromeVoxEditingTest', 'EditableLineTwoStaticTexts', function() {
   this.runWithLoadedTree(function() {/*!
     <p contenteditable>hello <b>world</b></p>
   */}, function(root) {
@@ -686,7 +690,7 @@
   });
 });
 
-TEST_F('EditingTest', 'EditableLineEquality', function() {
+TEST_F('ChromeVoxEditingTest', 'EditableLineEquality', function() {
   this.runWithLoadedTree(function() {/*!
     <div contenteditable role="textbox">
       <p style="word-spacing:100000px">this is a test</p>
@@ -763,7 +767,7 @@
   });
 });
 
-TEST_F('EditingTest', 'EditableLineStrictEquality', function() {
+TEST_F('ChromeVoxEditingTest', 'EditableLineStrictEquality', function() {
   this.runWithLoadedTree(function() {/*!
     <div contenteditable role="textbox">
       <p style="word-spacing:100000px">this is a test</p>
@@ -818,7 +822,7 @@
   });
 });
 
-TEST_F('EditingTest', 'EditableLineBaseLineAnchorOrFocus', function() {
+TEST_F('ChromeVoxEditingTest', 'EditableLineBaseLineAnchorOrFocus', function() {
   this.runWithLoadedTree(function() {/*!
     <div contenteditable role="textbox">
       <p style="word-spacing:100000px">this is a test</p>
@@ -862,7 +866,7 @@
   })
 });
 
-TEST_F('EditingTest', 'IsValidLine', function() {
+TEST_F('ChromeVoxEditingTest', 'IsValidLine', function() {
   this.runWithLoadedTree(function() {/*!
     <div contenteditable role="textbox">
       <p style="word-spacing:100000px">this is a test</p>
@@ -904,7 +908,7 @@
   })
 });
 
-TEST_F('EditingTest', 'TelTrimsWhitespace', function() {
+TEST_F('ChromeVoxEditingTest', 'TelTrimsWhitespace', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go"></div>
@@ -947,7 +951,7 @@
   });
 });
 
-TEST_F('EditingTest', 'BackwardWordDelete', function() {
+TEST_F('ChromeVoxEditingTest', 'BackwardWordDelete', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div
@@ -978,7 +982,7 @@
   });
 });
 
-TEST_F('EditingTest', 'BackwardWordDeleteAcrossParagraphs', function() {
+TEST_F('ChromeVoxEditingTest', 'BackwardWordDeleteAcrossParagraphs', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/event_stream_logger.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/event_stream_logger.js
index 4e1305ba..c3f392e 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/event_stream_logger.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/event_stream_logger.js
@@ -52,41 +52,15 @@
   },
 
   /**
-   * @param {!AutomationNode} target
-   */
-  isDescendantOfConsole: function(target) {
-    /** Event log should not be written when event is dispatched from console or
-     * chromevox log page.
-     */
-    if (target.docUrl &&
-        (target.docUrl.indexOf('chrome-devtools://') == 0 ||
-         target.docUrl == chrome.runtime.getURL('cvox2/background/log.html')))
-      return true;
-
-    if (!target.parent)
-      return false;
-
-    return this.isDescendantOfConsole(target.parent);
-  },
-
-  /**
    * @param {!AutomationEvent} evt
    */
   eventStreamLogging: function(evt) {
-    /**
-     * If evt is dispatched to console, don't show.
-     * Console event log are unnecessary for developers.
-     */
-    if (this.isDescendantOfConsole(evt.target))
-      return;
-
     var logStr = 'EventType = ' + evt.type;
     logStr += ', TargetName = ' + evt.target.name;
     logStr += ', RootName = ' + evt.target.root.name;
     logStr += ', DocumentURL = ' + evt.target.docUrl;
 
     LogStore.getInstance().writeTextLog(logStr, TextLog.LogType.EVENT);
-    console.log(logStr);
   },
 
   /**
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/i_search_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/i_search_test.extjs
index 27ff448..9f004358 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/i_search_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/i_search_test.extjs
@@ -10,11 +10,11 @@
  * @constructor
  * @extends {ChromeVoxNextE2ETest}
  */
-function ISearchTest() {
+function ChromeVoxISearchTest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-ISearchTest.prototype = {
+ChromeVoxISearchTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   /** @override */
@@ -64,7 +64,7 @@
   }
 };
 
-TEST_F('ISearchTest', 'DISABLED_Simple', function() {
+TEST_F('ChromeVoxISearchTest', 'DISABLED_Simple', function() {
   this.runWithLoadedTree(this.linksAndHeadingsDoc, function(rootNode) {
     var handler = new FakeISearchHandler(this);
     var search = new ISearch(rootNode);
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/live_regions_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/live_regions_test.extjs
index e3312bd0..cf247dd 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/live_regions_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/live_regions_test.extjs
@@ -13,11 +13,11 @@
  * @constructor
  * @extends {ChromeVoxNextE2ETest}
  */
-function LiveRegionsTest() {
+function ChromeVoxLiveRegionsTest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-LiveRegionsTest.prototype = {
+ChromeVoxLiveRegionsTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   /** @override */
@@ -48,7 +48,7 @@
   },
 };
 
-TEST_F('LiveRegionsTest', 'LiveRegionAddElement', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'LiveRegionAddElement', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
     function() {/*!
@@ -69,7 +69,7 @@
     });
 });
 
-TEST_F('LiveRegionsTest', 'LiveRegionRemoveElement', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'LiveRegionRemoveElement', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
     function() {/*!
@@ -91,7 +91,7 @@
     });
 });
 
-TEST_F('LiveRegionsTest', 'LiveRegionChangeAtomic', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'LiveRegionChangeAtomic', function() {
   LiveRegions.LIVE_REGION_QUEUE_TIME_MS = 0;
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
@@ -115,7 +115,7 @@
     });
 });
 
-TEST_F('LiveRegionsTest', 'LiveRegionChangeAtomicText', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'LiveRegionChangeAtomicText', function() {
   LiveRegions.LIVE_REGION_QUEUE_TIME_MS = 0;
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
@@ -136,7 +136,7 @@
     });
 });
 
-TEST_F('LiveRegionsTest', 'LiveRegionChangeImageAlt', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'LiveRegionChangeImageAlt', function() {
   // Note that there is a live region outputted as a result of page load; the
   // test expects a live region announcement after a click on the button, but
   // the LiveRegions module has a half second filter for live region
@@ -165,7 +165,7 @@
     });
 });
 
-TEST_F('LiveRegionsTest', 'LiveRegionThenFocus', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'LiveRegionThenFocus', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
     function() {/*!
@@ -190,7 +190,7 @@
     });
 });
 
-TEST_F('LiveRegionsTest', 'FocusThenLiveRegion', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'FocusThenLiveRegion', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
     function() {/*!
@@ -215,7 +215,7 @@
     });
 });
 
-TEST_F('LiveRegionsTest', 'LiveRegionCategoryFlush', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'LiveRegionCategoryFlush', function() {
   // Adjust the live region queue time to be shorter (i.e. flushes happen for
   // live regions coming 1 ms in time). Also, can help with flakeyness.
   LiveRegions.LIVE_REGION_QUEUE_TIME_MS = 1;
@@ -244,7 +244,7 @@
     });
 });
 
-TEST_F('LiveRegionsTest', 'SilentOnNodeChange', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'SilentOnNodeChange', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <p>start</p>
@@ -273,7 +273,7 @@
   });
 });
 
-TEST_F('LiveRegionsTest', 'SimulateTreeChanges', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'SimulateTreeChanges', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <button></button>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/log_store.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/log_store.js
index 458eac0e..d57e6b4 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/log_store.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/log_store.js
@@ -160,6 +160,9 @@
  * @param {!TextLog.LogType} logType
  */
 LogStore.prototype.writeTextLog = function(logContent, logType) {
+  if (this.shouldSkipOutput_())
+    return;
+
   var log = new TextLog(logContent, logType);
   this.logs_[this.startIndex_] = log;
   this.startIndex_ += 1;
@@ -173,6 +176,9 @@
  * @param {!TreeDumper} logContent
  */
 LogStore.prototype.writeTreeLog = function(logContent) {
+  if (this.shouldSkipOutput_())
+    return;
+
   var log = new TreeLog(logContent);
   this.logs_[this.startIndex_] = log;
   this.startIndex_ += 1;
@@ -189,6 +195,19 @@
   this.startIndex_ = 0;
 };
 
+/** @private @return {boolean} */
+LogStore.prototype.shouldSkipOutput_ = function() {
+  var ChromeVoxState = chrome.extension.getBackgroundPage()['ChromeVoxState'];
+  if (ChromeVoxState.instance.currentRange &&
+      ChromeVoxState.instance.currentRange.start &&
+      ChromeVoxState.instance.currentRange.start.node &&
+      ChromeVoxState.instance.currentRange.start.node.root) {
+    return ChromeVoxState.instance.currentRange.start.node.root.docUrl.indexOf(
+               chrome.extension.getURL('cvox2/background/log.html')) == 0;
+  }
+  return false;
+};
+
 /**
  * Global instance.
  * @type {LogStore}
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/log_store_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/log_store_test.extjs
index af7a2ec..10c462d 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/log_store_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/log_store_test.extjs
@@ -10,15 +10,15 @@
  * @constructor
  * @extends {ChromeVoxE2ETestBase}
  */
-function LogStoreTest() {
+function ChromeVoxLogStoreTest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-LogStoreTest.prototype = {
+ChromeVoxLogStoreTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 };
 
-SYNC_TEST_F('LogStoreTest', 'ShortLogs', function() {
+SYNC_TEST_F('ChromeVoxLogStoreTest', 'ShortLogs', function() {
   var logStore = new LogStore();
   for (var i = 0; i < 100; i++)
     logStore.writeTextLog('test' + i, 'speech');
@@ -29,7 +29,7 @@
     assertEquals(logs[i].toString(), 'test' + i);
 });
 
-SYNC_TEST_F('LogStoreTest', 'LongLogs', function() {
+SYNC_TEST_F('ChromeVoxLogStoreTest', 'LongLogs', function() {
   var logStore = new LogStore();
   for (var i = 0; i < LogStore.LOG_LIMIT + 500; i++)
     logStore.writeTextLog('test' + i, 'speech');
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
index fb46b5b..7d321048 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
@@ -90,11 +90,11 @@
  * @constructor
  * @extends {ChromeVoxNextE2ETestBase}
  */
-function OutputE2ETest() {
+function ChromeVoxOutputE2ETest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-OutputE2ETest.prototype = {
+ChromeVoxOutputE2ETest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   /** @override */
@@ -105,7 +105,7 @@
   }
 };
 
-TEST_F('OutputE2ETest', 'Links', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'Links', function() {
   this.runWithLoadedTree('<a href="#">Click here</a>',
     function(root) {
       var el = root.firstChild.firstChild;
@@ -128,7 +128,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'Checkbox', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'Checkbox', function() {
   this.runWithLoadedTree('<input type="checkbox">',
     function(root) {
       var el = root.firstChild.firstChild;
@@ -147,7 +147,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'InLineTextBoxValueGetsIgnored', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'InLineTextBoxValueGetsIgnored', function() {
   this.runWithLoadedTree('<p>OK',
     function(root) {
       var el = root.firstChild.firstChild.firstChild;
@@ -178,7 +178,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'Headings', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'Headings', function() {
   this.runWithLoadedTree(function() {/*!
       <h1>a</h1><h2>b</h2><h3>c</h3><h4>d</h4><h5>e</h5><h6>f</h6>
       <h1><a href="a.com">b</a></h1> */},
@@ -220,7 +220,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'Audio', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'Audio', function() {
   this.runWithLoadedTree('<audio src="foo.mp3" controls></audio>',
     function(root) {
       var el = root.find({role: 'button'});
@@ -267,7 +267,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'Input', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'Input', function() {
   this.runWithLoadedTree(
       '<input type="text"></input>' +
       '<input type="email"></input>' +
@@ -373,7 +373,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'List', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'List', function() {
   this.runWithLoadedTree(
       '<ul><li aria-label="a">a<li>b<li>c</ul>',
     function(root) {
@@ -399,7 +399,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'Tree', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'Tree', function() {
   this.runWithLoadedTree(function() {/*!
     <ul role="tree" style="list-style-type:none">
       <li aria-expanded="true" role="treeitem">a
@@ -463,7 +463,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'Menu', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'Menu', function() {
   this.runWithLoadedTree(function() {/*!
     <div role="menu">
       <div role="menuitem">a</div>
@@ -486,7 +486,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'ListBox', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'ListBox', function() {
   this.runWithLoadedTree(function() {/*!
     <select multiple>
       <option>1</option>
@@ -512,7 +512,7 @@
   });
 });
 
-SYNC_TEST_F('OutputE2ETest', 'MessageIdAndEarconValidity', function() {
+SYNC_TEST_F('ChromeVoxOutputE2ETest', 'MessageIdAndEarconValidity', function() {
   const kNoBrailleMessageRequired = new Set([
     'contentDeletion',
     'contentInsertion',
@@ -593,7 +593,7 @@
   }
 });
 
-TEST_F('OutputE2ETest', 'DivOmitsRole', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'DivOmitsRole', function() {
   this.runWithLoadedTree(function() {/*!
     <div>that has content</div>
     <div></div>
@@ -612,7 +612,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'LessVerboseAncestry', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'LessVerboseAncestry', function() {
   this.runWithLoadedTree(function() {/*!
     <div role="banner"><p>inside</p></div>
     <div role="banner"><p>inside</p></div>
@@ -642,7 +642,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'Brief', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'Brief', function() {
   this.runWithLoadedTree(function() {/*!
     <div role="article"><p>inside</p></div>
   */},
@@ -656,7 +656,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'AuralStyledHeadings', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'AuralStyledHeadings', function() {
   function toFixed(num) {
     return parseFloat(Number(num).toFixed(1));
   }
@@ -683,7 +683,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'ToggleButton', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'ToggleButton', function() {
   this.runWithLoadedTree(function() {/*!
       <div role="button" aria-pressed="true">Subscribe</div>*/},
     function(root) {
@@ -700,7 +700,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'JoinDescendants', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'JoinDescendants', function() {
   this.runWithLoadedTree(function() {/*!
       <p>This</p>
       <p>fragment</p>
@@ -718,7 +718,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'ComplexDiv', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'ComplexDiv', function() {
   this.runWithLoadedTree(function() {/*!
       <div><button>ok</button></div>
     */},
@@ -729,7 +729,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'ContainerFocus', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'ContainerFocus', function() {
   this.runWithLoadedTree(function() {/*!
       <div role="row" tabindex=0 aria-label="start"></div>
       <div role="row" tabindex=0 aria-label="end"></div>
@@ -742,7 +742,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'BraileWhitespace', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'BraileWhitespace', function() {
   this.runWithLoadedTree(function() {/*!
     <p>this is a <em>test</em>of emphasized text</p>
   */},
@@ -765,7 +765,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'BrailleAncestry', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'BrailleAncestry', function() {
   this.runWithLoadedTree(function() {/*!
     <ul><li><a href="#">test</a></li></ul>
   */},
@@ -789,7 +789,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'RangeOutput', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'RangeOutput', function() {
   this.runWithLoadedTree(function(root) {/*!
     <div role="slider" aria-valuemin="1" aria-valuemax="10" aria-valuenow="2"
                        aria-label="volume"></div>
@@ -853,7 +853,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'RoleDescription', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'RoleDescription', function() {
   this.runWithLoadedTree(function(root) {/*!
     <div aria-label="hi" role="button" aria-roledescription="foo"></div>
   */}, function(root) {
@@ -871,7 +871,7 @@
   });
 });
 
-SYNC_TEST_F('OutputE2ETest', 'ValidateCommonProperties', function() {
+SYNC_TEST_F('ChromeVoxOutputE2ETest', 'ValidateCommonProperties', function() {
   var stateStr = '$state';
   var restrictionStr = '$restriction';
   var descStr = '$description';
@@ -941,7 +941,7 @@
   // descriptions somewhere in the output.
 });
 
-TEST_F('OutputE2ETest', 'InlineBraille', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'InlineBraille', function() {
   this.runWithLoadedTree(function(root) {/*!
     <table border=1>
       <tr><td>Name</td><td id="active">Age</td><td>Address</td></tr>
@@ -956,7 +956,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'TextFieldObeysRoleDescription', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'TextFieldObeysRoleDescription', function() {
   this.runWithLoadedTree(function(root) {/*!
     <div role="textbox" aria-roledescription="square"></div>
     <div role="region" aria-roledescription="circle"></div>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_test.extjs
index b7cd489..667bb35 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_test.extjs
@@ -12,11 +12,11 @@
  * @constructor
  * @extends {ChromeVoxNextE2ETest}
  */
-function PanelTest() {
+function ChromeVoxPanelTest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-PanelTest.prototype = {
+ChromeVoxPanelTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   /** @override */
@@ -87,7 +87,7 @@
   */}
 };
 
-TEST_F('PanelTest', 'MAYBE_ActivateMenu', function() {
+TEST_F('ChromeVoxPanelTest', 'MAYBE_ActivateMenu', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.linksDoc, function(root) {
     var openMenus = new PanelCommand(PanelCommandType.OPEN_MENUS);
@@ -105,7 +105,7 @@
   });
 });
 
-TEST_F('PanelTest', 'MAYBE_LinkMenu', function() {
+TEST_F('ChromeVoxPanelTest', 'MAYBE_LinkMenu', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.linksDoc, function(root) {
     var openMenus = new PanelCommand(PanelCommandType.OPEN_MENUS, 'role_link');
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/recovery_strategy_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/recovery_strategy_test.extjs
index 41d70a1..72e6398 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/recovery_strategy_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/recovery_strategy_test.extjs
@@ -11,16 +11,16 @@
  * @constructor
  * @extends {ChromeVoxNextE2ETest}
  */
-function RecoveryStrategyTest() {
+function ChromeVoxRecoveryStrategyTest() {
   ChromeVoxNextE2ETest.call(this);
   window.RoleType = chrome.automation.RoleType;
 }
 
-RecoveryStrategyTest.prototype = {
+ChromeVoxRecoveryStrategyTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 };
 
-TEST_F('RecoveryStrategyTest', 'ReparentedRecovery', function() {
+TEST_F('ChromeVoxRecoveryStrategyTest', 'ReparentedRecovery', function() {
   this.runWithLoadedTree(function() {/*!
     <input type="text"></input>
     <p id="p">hi</p>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/tree_walker_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/tree_walker_test.extjs
index 3851903..479c936 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/tree_walker_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/tree_walker_test.extjs
@@ -12,11 +12,11 @@
  * @constructor
  * @extends {ChromeVoxE2ETestBase}
  */
-function AutomationTreeWalkerTest() {
+function ChromeVoxAutomationTreeWalkerTest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-AutomationTreeWalkerTest.prototype = {
+ChromeVoxAutomationTreeWalkerTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   flattenTree: function(node, outResult) {
@@ -45,7 +45,7 @@
   }
 };
 
-TEST_F('AutomationTreeWalkerTest', 'Forward', function() {
+TEST_F('ChromeVoxAutomationTreeWalkerTest', 'Forward', function() {
   chrome.automation.getDesktop(this.newCallback(function(d) {
     var resultList = [];
     this.flattenTree(d, resultList);
@@ -72,7 +72,7 @@
   }.bind(this)));
 });
 
-TEST_F('AutomationTreeWalkerTest', 'Backward', function() {
+TEST_F('ChromeVoxAutomationTreeWalkerTest', 'Backward', function() {
   chrome.automation.getDesktop(this.newCallback(function(d) {
     var resultList = [];
     this.flattenTree(d, resultList);
@@ -99,7 +99,7 @@
   }.bind(this)));
 });
 
-TEST_F('AutomationTreeWalkerTest', 'RootLeafRestriction', function() {
+TEST_F('ChromeVoxAutomationTreeWalkerTest', 'RootLeafRestriction', function() {
   this.runWithLoadedTree(function() {/*!
       <div role="group" aria-label="1">
         <div role="group" aria-label="2">
@@ -163,7 +163,7 @@
     });
 });
 
-TEST_F('AutomationTreeWalkerTest', 'LeafPredicateSymmetry', function() {
+TEST_F('ChromeVoxAutomationTreeWalkerTest', 'LeafPredicateSymmetry', function() {
   this.runWithLoadedTree(toolbarDoc, function(r) {
     var d = r.root.parent.root;
     var forwardWalker = new AutomationTreeWalker(d, 'forward');
@@ -185,7 +185,7 @@
   });
 });
 
-TEST_F('AutomationTreeWalkerTest', 'RootPredicateEnding', function() {
+TEST_F('ChromeVoxAutomationTreeWalkerTest', 'RootPredicateEnding', function() {
   this.runWithLoadedTree(toolbarDoc, function(r) {
     var backwardWalker = new AutomationTreeWalker(r.firstChild, 'backward',
         {root: function(node) { return node === r; }});
diff --git a/chrome/browser/resources/chromeos/chromevox/host/chrome/braille_integration_test.unitjs b/chrome/browser/resources/chromeos/chromevox/host/chrome/braille_integration_test.unitjs
index 0870247..e0a93e1 100644
--- a/chrome/browser/resources/chromeos/chromevox/host/chrome/braille_integration_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/host/chrome/braille_integration_test.unitjs
@@ -14,11 +14,11 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxBrailleIntegrationUnitTest() {
+function ChromeVoxBrailleIntegrationUnitTest() {
   ChromeVoxUnitTestBase.call(this);
 }
 
-CvoxBrailleIntegrationUnitTest.prototype = {
+ChromeVoxBrailleIntegrationUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -163,14 +163,15 @@
 
 /** @extends {cvox.BrailleTranslatorManager} */
 function FakeTranslatorManager() {
-}        
+}
 
-SYNC_TEST_F('CvoxBrailleIntegrationUnitTest', 'Write', function() {
+SYNC_TEST_F('ChromeVoxBrailleIntegrationUnitTest', 'Write', function() {
   this.braille.write(this.content1);
   assertEqualsJSON(this.content1, this.displayManager.content);
 });
 
-SYNC_TEST_F('CvoxBrailleIntegrationUnitTest', 'WriteWithSpans', function() {
+SYNC_TEST_F('ChromeVoxBrailleIntegrationUnitTest', 'WriteWithSpans',
+            function() {
   var selectionSpan = new cvox.ValueSelectionSpan();
   var valueSpan = new cvox.ValueSpan(20);
   var toSend = cvox.NavBraille.fromText(
@@ -185,7 +186,7 @@
   assertEqualsJSON(expected, this.displayManager.content);
 });
 
-TEST_F('CvoxBrailleIntegrationUnitTest', 'CommandNoContent', function() {
+TEST_F('ChromeVoxBrailleIntegrationUnitTest', 'CommandNoContent', function() {
   // Commands are only delivered to the content script if the window has focus.
   this.awaitDocumentFocused(function() {
     this.sendCommand(this.command1, null);
@@ -194,7 +195,7 @@
   });
 });
 
-TEST_F('CvoxBrailleIntegrationUnitTest',
+TEST_F('ChromeVoxBrailleIntegrationUnitTest',
        'InterleavedWritesAndCommands',
        function() {
   this.awaitDocumentFocused(function() {
@@ -215,7 +216,7 @@
   });
 });
 
-TEST_F('CvoxBrailleIntegrationUnitTest', 'CommandAfterBackgroundWrite',
+TEST_F('ChromeVoxBrailleIntegrationUnitTest', 'CommandAfterBackgroundWrite',
        function() {
   this.awaitDocumentFocused(function() {
     this.braille.write(this.content1);
@@ -231,7 +232,7 @@
   });
 });
 
-TEST_F('CvoxBrailleIntegrationUnitTest', 'CommandAfterOtherTabWrite',
+TEST_F('ChromeVoxBrailleIntegrationUnitTest', 'CommandAfterOtherTabWrite',
        function() {
   this.awaitDocumentFocused(function() {
     // Ignore the listener of the braille from the second 'tab'.
diff --git a/chrome/browser/resources/chromeos/chromevox/host/chrome/tts_background_test.extjs b/chrome/browser/resources/chromeos/chromevox/host/chrome/tts_background_test.extjs
index 824e3e2..9bc88e39 100644
--- a/chrome/browser/resources/chromeos/chromevox/host/chrome/tts_background_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/host/chrome/tts_background_test.extjs
@@ -13,15 +13,15 @@
  * @constructor
  * @extends {ChromeVoxE2ETest}
  */
-function CvoxTtsBackgroundTest() {
+function ChromeVoxTtsBackgroundTest() {
   ChromeVoxE2ETest.call(this);
 }
 
-CvoxTtsBackgroundTest.prototype = {
+ChromeVoxTtsBackgroundTest.prototype = {
   __proto__: ChromeVoxE2ETest.prototype
 };
 
-SYNC_TEST_F('CvoxTtsBackgroundTest', 'Preprocess', function() {
+SYNC_TEST_F('ChromeVoxTtsBackgroundTest', 'Preprocess', function() {
   var tts = new cvox.TtsBackground(false);
   var preprocess = tts.preprocess.bind(tts);
 
@@ -44,7 +44,7 @@
   assertEquals('bullet 2 bullets', preprocess('\u2022 \u2022\u2022'));
 });
 
-TEST_F('CvoxTtsBackgroundTest', 'UpdateVoice', function() {
+TEST_F('ChromeVoxTtsBackgroundTest', 'UpdateVoice', function() {
   var tts = new cvox.TtsBackground(false);
   var voices = [
     {lang: 'zh-CN', voiceName: 'Chinese'},
@@ -95,8 +95,8 @@
 });
 
 // This test only works if Google tts is installed. Run it locally.
-TEST_F(
-    'CvoxTtsBackgroundTest', 'DISABLED_EmptyStringCallsCallbacks', function() {
+TEST_F('ChromeVoxTtsBackgroundTest',
+       'DISABLED_EmptyStringCallsCallbacks', function() {
   var tts = new cvox.TtsBackground(false);
   var startCalls = 0, endCalls = 0;
   assertCallsCallbacks = function(text, speakCalls) {
diff --git a/chrome/browser/resources/chromeos/chromevox/walkers/character_walker_test.unitjs b/chrome/browser/resources/chromeos/chromevox/walkers/character_walker_test.unitjs
index d93d11a13..67f58ad0 100644
--- a/chrome/browser/resources/chromeos/chromevox/walkers/character_walker_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/walkers/character_walker_test.unitjs
@@ -8,16 +8,17 @@
 /**
  * Test fixture.
  * @constructor
- * @extends {CvoxWalkerTestBase}
+ * @extends {ChromeVoxWalkerTestBase}
  */
-function CvoxCharacterWalkerUnitTest() {}
+function ChromeVoxCharacterWalkerUnitTest() {}
 
-CvoxCharacterWalkerUnitTest.prototype = {
-  __proto__: CvoxWalkerUnitTestBase.prototype,
+ChromeVoxCharacterWalkerUnitTest.prototype = {
+  __proto__: ChromeVoxWalkerUnitTestBase.prototype,
 
   /** @override */
-  closureModuleDeps: CvoxWalkerUnitTestBase.prototype.closureModuleDeps.concat(
-      'cvox.CharacterWalker'),
+  closureModuleDeps:
+      ChromeVoxWalkerUnitTestBase.prototype.closureModuleDeps.concat(
+          'cvox.CharacterWalker'),
 
   /** @override */
   newWalker: function() {
@@ -35,9 +36,9 @@
   }
 };
 
-CvoxWalkerUnitTestBase.addCommonTests('CvoxCharacterWalkerUnitTest');
+ChromeVoxWalkerUnitTestBase.addCommonTests('ChromeVoxCharacterWalkerUnitTest');
 
-TEST_F('CvoxCharacterWalkerUnitTest', 'SimpleForwardSync', function() {
+TEST_F('ChromeVoxCharacterWalkerUnitTest', 'SimpleForwardSync', function() {
   this.setUpSimpleHtml_();
 
   // invalid selection
@@ -55,7 +56,8 @@
   assertTrue(ret2.equals(ret));
 });
 
-TEST_F('CvoxCharacterWalkerUnitTest', 'testSimpleReversedSync', function() {
+TEST_F('ChromeVoxCharacterWalkerUnitTest', 'testSimpleReversedSync',
+       function() {
   this.setUpSimpleHtml_();
 
   // invalid selection
@@ -74,7 +76,7 @@
   assertTrue(ret2.equals(ret));
 });
 
-TEST_F('CvoxCharacterWalkerUnitTest', 'testSimpleForwardNext', function() {
+TEST_F('ChromeVoxCharacterWalkerUnitTest', 'testSimpleForwardNext', function() {
   this.setUpSimpleHtml_();
 
   var sel = cvox.CursorSelection.fromNode($('a'));
@@ -88,7 +90,8 @@
   });
 });
 
-TEST_F('CvoxCharacterWalkerUnitTest', 'testSimpleReversedNext', function() {
+TEST_F('ChromeVoxCharacterWalkerUnitTest', 'testSimpleReversedNext',
+       function() {
   this.setUpSimpleHtml_();
 
   var sel = cvox.CursorSelection.fromNode($('a'));
@@ -105,7 +108,7 @@
 /**
  * Tests for how spaces should be navigated character by character.
  */
-TEST_F('CvoxCharacterWalkerUnitTest', 'testSpaces', function() {
+TEST_F('ChromeVoxCharacterWalkerUnitTest', 'testSpaces', function() {
   this.loadDoc(function() {/*!
     <div id="foo">a <i>b</i> c<input type="text" value="asdf"/></div>
   */});
diff --git a/chrome/browser/resources/chromeos/chromevox/walkers/group_walker_test.unitjs b/chrome/browser/resources/chromeos/chromevox/walkers/group_walker_test.unitjs
index d035f89..d5acd2e8 100644
--- a/chrome/browser/resources/chromeos/chromevox/walkers/group_walker_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/walkers/group_walker_test.unitjs
@@ -8,16 +8,17 @@
 /**
  * Test fixture.
  * @constructor
- * @extends {CvoxWalkerTestBase}
+ * @extends {ChromeVoxWalkerTestBase}
  */
-function CvoxGroupWalkerUnitTest() {}
+function ChromeVoxGroupWalkerUnitTest() {}
 
-CvoxGroupWalkerUnitTest.prototype = {
-  __proto__: CvoxWalkerUnitTestBase.prototype,
+ChromeVoxGroupWalkerUnitTest.prototype = {
+  __proto__: ChromeVoxWalkerUnitTestBase.prototype,
 
   /** @override */
-  closureModuleDeps: CvoxWalkerUnitTestBase.prototype.closureModuleDeps.concat(
-      'cvox.GroupWalker'),
+  closureModuleDeps:
+      ChromeVoxWalkerUnitTestBase.prototype.closureModuleDeps.concat(
+          'cvox.GroupWalker'),
 
   /** @override */
   newWalker: function() {
@@ -43,9 +44,9 @@
   }
 };
 
-CvoxWalkerUnitTestBase.addCommonTests('CvoxGroupWalkerUnitTest');
+ChromeVoxWalkerUnitTestBase.addCommonTests('ChromeVoxGroupWalkerUnitTest');
 
-TEST_F('CvoxGroupWalkerUnitTest', 'testSimpleForwardSync', function() {
+TEST_F('ChromeVoxGroupWalkerUnitTest', 'testSimpleForwardSync', function() {
   this.setUpSimpleHtml_();
 
   // invalid selection
@@ -57,7 +58,7 @@
   assertTrue(ret2.equals(ret));
 });
 
-TEST_F('CvoxGroupWalkerUnitTest', 'testSimpleReversedSync', function() {
+TEST_F('ChromeVoxGroupWalkerUnitTest', 'testSimpleReversedSync', function() {
   this.setUpSimpleHtml_();
 
   // invalid selection
@@ -70,7 +71,7 @@
   assertTrue(ret2.equals(ret));
 });
 
-TEST_F('CvoxGroupWalkerUnitTest', 'testSimpleForwardNext', function() {
+TEST_F('ChromeVoxGroupWalkerUnitTest', 'testSimpleForwardNext', function() {
   this.setUpSimpleHtml_();
 
   // invalid selection
@@ -79,7 +80,7 @@
   var ret = this.go(sel, 'next', {selNodeId: 'b', selReversed: false});
 });
 
-TEST_F('CvoxGroupWalkerUnitTest', 'testSimpleReversedNext', function() {
+TEST_F('ChromeVoxGroupWalkerUnitTest', 'testSimpleReversedNext', function() {
   this.setUpSimpleHtml_();
 
   // invalid selection
diff --git a/chrome/browser/resources/chromeos/chromevox/walkers/layout_line_walker_test.unitjs b/chrome/browser/resources/chromeos/chromevox/walkers/layout_line_walker_test.unitjs
index b257dd94..0015312 100644
--- a/chrome/browser/resources/chromeos/chromevox/walkers/layout_line_walker_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/walkers/layout_line_walker_test.unitjs
@@ -10,9 +10,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxLayoutLineWalkerUnitTest() {}
+function ChromeVoxLayoutLineWalkerUnitTest() {}
 
-CvoxLayoutLineWalkerUnitTest.prototype = {
+ChromeVoxLayoutLineWalkerUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -41,23 +41,23 @@
     );
     Msgs = TestMsgs;
     this.walker = new cvox.LayoutLineWalker();
-  
+
     this.a = cvox.CursorSelection.fromNode($('a'));
     this.aa = cvox.CursorSelection.fromNode($('aa'));
     this.b = cvox.CursorSelection.fromNode($('b'));
     this.bb = cvox.CursorSelection.fromNode($('bb'));
-  
+
     this.line1Text = 'Demonstrating that in-line links like google are' +
         ' considered a single layout line.';
-  
+
     this.line2Text = 'This includes a paragraph that has a lot of text' +
         ' like this one. Wikipedia is a great example of a site that this' +
         ' walker becomes valuable.';
-  
+
     this.line3Text =
         'Braille also benefits greatly from this type of formatting since ' +
         'some displays can handle lots of text like 80 cell displays!';
-  
+
     this.line1Description =
         [{'context': '', 'text': 'Demonstrating that in-line links like',
           'userValue': '', 'annotation': '', 'earcons': [], 'personality': null,
@@ -68,7 +68,7 @@
          {'context': '', 'text': 'are considered a single layout line.',
           'userValue': '', 'annotation': '', 'earcons': [], 'personality': null,
           'hint': '', 'category': null}];
-  
+
     this.line2Description =
         [{'context': '', 'text':
           'This includes a paragraph that has a lot of text like this one.',
@@ -83,13 +83,13 @@
           'hint': '', 'category': null},
          {'context': '', 'text':
           'is a great example of a site that this walker becomes valuable.',
-          'userValue': '',  
+          'userValue': '',
           'annotation': '', 'earcons': [], 'personality': null,
           'hint': '', 'category': null}];
   }
 };
 
-TEST_F('CvoxLayoutLineWalkerUnitTest', 'Sync', function() {
+TEST_F('ChromeVoxLayoutLineWalkerUnitTest', 'Sync', function() {
   var sel = cvox.CursorSelection.fromNode($('1'));
   sel = this.walker.sync(sel);
   assertEquals(this.line1Text, sel.getText());
@@ -121,7 +121,7 @@
 });
 
 /** Tests description of valid selections. */
-TEST_F('CvoxLayoutLineWalkerUnitTest', 'Description', function() {
+TEST_F('ChromeVoxLayoutLineWalkerUnitTest', 'Description', function() {
   var sel = this.walker.sync(this.a);
   assertEqualsJSON(this.line1Description,
                    this.walker.getDescription(sel, sel));
@@ -132,7 +132,7 @@
 
 
 /** Tests back/forward movement. */
-TEST_F('CvoxLayoutLineWalkerUnitTest', 'BackForward', function() {
+TEST_F('ChromeVoxLayoutLineWalkerUnitTest', 'BackForward', function() {
   var sel = this.walker.sync(this.a);
 
   // Beginning of second line.
diff --git a/chrome/browser/resources/chromeos/chromevox/walkers/object_walker_test.unitjs b/chrome/browser/resources/chromeos/chromevox/walkers/object_walker_test.unitjs
index 1b6fe43..aaafa932 100644
--- a/chrome/browser/resources/chromeos/chromevox/walkers/object_walker_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/walkers/object_walker_test.unitjs
@@ -8,16 +8,17 @@
 /**
  * Test fixture.
  * @constructor
- * @extends {CvoxWalkerTestBase}
+ * @extends {ChromeVoxWalkerTestBase}
  */
-function CvoxObjectWalkerUnitTest() {}
+function ChromeVoxObjectWalkerUnitTest() {}
 
-CvoxObjectWalkerUnitTest.prototype = {
-  __proto__: CvoxWalkerUnitTestBase.prototype,
+ChromeVoxObjectWalkerUnitTest.prototype = {
+  __proto__: ChromeVoxWalkerUnitTestBase.prototype,
 
   /** @override */
-  closureModuleDeps: CvoxWalkerUnitTestBase.prototype.closureModuleDeps.concat(
-      'cvox.ObjectWalker'),
+  closureModuleDeps:
+      ChromeVoxWalkerUnitTestBase.prototype.closureModuleDeps.concat(
+          'cvox.ObjectWalker'),
 
   /** @override */
   newWalker: function() {
@@ -38,9 +39,9 @@
   }
 };
 
-CvoxWalkerUnitTestBase.addCommonTests('CvoxObjectWalkerUnitTest');
+ChromeVoxWalkerUnitTestBase.addCommonTests('ChromeVoxObjectWalkerUnitTest');
 
-TEST_F('CvoxObjectWalkerUnitTest', 'testSimpleForwardSync', function() {
+TEST_F('ChromeVoxObjectWalkerUnitTest', 'testSimpleForwardSync', function() {
   this.setUpSimpleHtml_();
 
   // invalid selection
@@ -52,7 +53,7 @@
   assertTrue(ret2.equals(ret));
 });
 
-TEST_F('CvoxObjectWalkerUnitTest', 'testSimpleReversedSync', function() {
+TEST_F('ChromeVoxObjectWalkerUnitTest', 'testSimpleReversedSync', function() {
   this.setUpSimpleHtml_();
 
   // invalid selection
@@ -65,7 +66,7 @@
   assertTrue(ret2.equals(ret));
 });
 
-TEST_F('CvoxObjectWalkerUnitTest', 'testSimpleForwardNext', function() {
+TEST_F('ChromeVoxObjectWalkerUnitTest', 'testSimpleForwardNext', function() {
   this.setUpSimpleHtml_();
 
   var sel = cvox.CursorSelection.fromNode($('a'));
@@ -73,7 +74,7 @@
   var ret = this.go(sel, 'next', {selParentNodeId: 'c', selReversed: false});
 });
 
-TEST_F('CvoxObjectWalkerUnitTest', 'testSimpleReversedNext', function() {
+TEST_F('ChromeVoxObjectWalkerUnitTest', 'testSimpleReversedNext', function() {
   this.setUpSimpleHtml_();
 
   var sel = cvox.CursorSelection.fromNode($('a'));
@@ -82,7 +83,8 @@
 });
 
 /** tests for unbroken anchor link descriptions. */
-TEST_F('CvoxObjectWalkerUnitTest', 'testAnchorLinkDescriptions', function() {
+TEST_F('ChromeVoxObjectWalkerUnitTest', 'testAnchorLinkDescriptions',
+       function() {
   this.loadDoc(function() {/*!
     <a href='a1.html' id='a1'>
       This link <em>has</em> a few <strong>complications.</strong>
diff --git a/chrome/browser/resources/chromeos/chromevox/walkers/sentence_walker_test.unitjs b/chrome/browser/resources/chromeos/chromevox/walkers/sentence_walker_test.unitjs
index f899781..ac34ce1 100644
--- a/chrome/browser/resources/chromeos/chromevox/walkers/sentence_walker_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/walkers/sentence_walker_test.unitjs
@@ -8,16 +8,17 @@
 /**
  * Test fixture.
  * @constructor
- * @extends {CvoxWalkerTestBase}
+ * @extends {ChromeVoxWalkerTestBase}
  */
-function CvoxSentenceWalkerUnitTest() {}
+function ChromeVoxSentenceWalkerUnitTest() {}
 
-CvoxSentenceWalkerUnitTest.prototype = {
-  __proto__: CvoxWalkerUnitTestBase.prototype,
+ChromeVoxSentenceWalkerUnitTest.prototype = {
+  __proto__: ChromeVoxWalkerUnitTestBase.prototype,
 
   /** @override */
-  closureModuleDeps: CvoxWalkerUnitTestBase.prototype.closureModuleDeps.concat(
-      'cvox.SentenceWalker'),
+  closureModuleDeps:
+      ChromeVoxWalkerUnitTestBase.prototype.closureModuleDeps.concat(
+          'cvox.SentenceWalker'),
 
   /** @override */
   newWalker: function() {
@@ -35,9 +36,9 @@
   }
 };
 
-CvoxWalkerUnitTestBase.addCommonTests('CvoxSentenceWalkerUnitTest');
+ChromeVoxWalkerUnitTestBase.addCommonTests('ChromeVoxSentenceWalkerUnitTest');
 
-TEST_F('CvoxSentenceWalkerUnitTest', 'testSimpleForwardSync', function() {
+TEST_F('ChromeVoxSentenceWalkerUnitTest', 'testSimpleForwardSync', function() {
   this.setUpSimpleHtml_();
 
   var sel = cvox.CursorSelection.fromNode($('a'));
@@ -54,7 +55,7 @@
   assertTrue(ret2.equals(ret));
 });
 
-TEST_F('CvoxSentenceWalkerUnitTest', 'testSimpleReversedSync', function() {
+TEST_F('ChromeVoxSentenceWalkerUnitTest', 'testSimpleReversedSync', function() {
   this.setUpSimpleHtml_();
 
   var sel = cvox.CursorSelection.fromNode($('a'));
@@ -72,7 +73,7 @@
   assertTrue(ret2.equals(ret));
 });
 
-TEST_F('CvoxSentenceWalkerUnitTest', 'testSimpleForwardNext', function() {
+TEST_F('ChromeVoxSentenceWalkerUnitTest', 'testSimpleForwardNext', function() {
   this.setUpSimpleHtml_();
 
   var sel = cvox.CursorSelection.fromNode($('a'));
@@ -86,7 +87,7 @@
   });
 });
 
-TEST_F('CvoxSentenceWalkerUnitTest', 'testSimpleReversedNext', function() {
+TEST_F('ChromeVoxSentenceWalkerUnitTest', 'testSimpleReversedNext', function() {
   this.setUpSimpleHtml_();
 
   var sel = cvox.CursorSelection.fromNode($('a'));
@@ -101,7 +102,7 @@
   ret = this.go(ret, 'next', null);
 });
 
-TEST_F('CvoxSentenceWalkerUnitTest', 'testControlElements', function() {
+TEST_F('ChromeVoxSentenceWalkerUnitTest', 'testControlElements', function() {
   this.loadDoc(function() {/*!
       <div id="a"><p>Before</p><input type="text"/><p>After</p></div>
   */});
diff --git a/chrome/browser/resources/chromeos/chromevox/walkers/structural_line_walker_test.unitjs b/chrome/browser/resources/chromeos/chromevox/walkers/structural_line_walker_test.unitjs
index 7e0c59f..012abd1a 100644
--- a/chrome/browser/resources/chromeos/chromevox/walkers/structural_line_walker_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/walkers/structural_line_walker_test.unitjs
@@ -10,9 +10,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxStructuralLineWalkerUnitTest() {}
+function ChromeVoxStructuralLineWalkerUnitTest() {}
 
-CvoxStructuralLineWalkerUnitTest.prototype = {
+ChromeVoxStructuralLineWalkerUnitTest.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -30,14 +30,14 @@
         <a href='google.com'>And here!</a>
       </p>
       */});
-  
+
     Msgs = TestMsgs;
-  
+
     this.walker_ = new cvox.StructuralLineWalker();
   },
 };
 
-TEST_F('CvoxStructuralLineWalkerUnitTest', 'BrailleLine', function() {
+TEST_F('ChromeVoxStructuralLineWalkerUnitTest', 'BrailleLine', function() {
   var aLink = $('1');
   var aLinkSel1 = this.walker_.sync(cvox.CursorSelection.fromNode(aLink));
   assertEquals('Click Here lnk',
@@ -58,7 +58,7 @@
 
 
 /** Tests sync'ing to a line in the middle of a paragraph. */
-TEST_F('CvoxStructuralLineWalkerUnitTest', 'Sync', function() {
+TEST_F('ChromeVoxStructuralLineWalkerUnitTest', 'Sync', function() {
   var p1Sel = this.walker_.sync(
       cvox.CursorSelection.fromNode($('2')));
 
@@ -72,7 +72,7 @@
 });
 
 /** Tests syncing into an element treated as a leaf by TraverseUtil. */
-TEST_F('CvoxStructuralLineWalkerUnitTest', 'SyncTraverseUtil', function() {
+TEST_F('ChromeVoxStructuralLineWalkerUnitTest', 'SyncTraverseUtil', function() {
   this.loadDoc(function() {/*!
     <select id='leaf'>
       <option>apple
@@ -86,7 +86,7 @@
 
 
 /** Tests specialized name calc with listitems with prefixes. */
-TEST_F('CvoxStructuralLineWalkerUnitTest', 'ListitemPrefixes', function() {
+TEST_F('ChromeVoxStructuralLineWalkerUnitTest', 'ListitemPrefixes', function() {
   this.loadDoc(function() {/*!
     <ol>
       <li id='li_orange'>orange
diff --git a/chrome/browser/resources/chromeos/chromevox/walkers/table_walker_test.unitjs b/chrome/browser/resources/chromeos/chromevox/walkers/table_walker_test.unitjs
index f2e9e3d..5a20762 100644
--- a/chrome/browser/resources/chromeos/chromevox/walkers/table_walker_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/walkers/table_walker_test.unitjs
@@ -8,16 +8,17 @@
 /**
  * Test fixture.
  * @constructor
- * @extends {CvoxWalkerTestBase}
+ * @extends {ChromeVoxWalkerTestBase}
  */
-function CvoxTableWalkerUnitTest() {}
+function ChromeVoxTableWalkerUnitTest() {}
 
-CvoxTableWalkerUnitTest.prototype = {
-  __proto__: CvoxWalkerUnitTestBase.prototype,
+ChromeVoxTableWalkerUnitTest.prototype = {
+  __proto__: ChromeVoxWalkerUnitTestBase.prototype,
 
   /** @override */
-  closureModuleDeps: CvoxWalkerUnitTestBase.prototype.closureModuleDeps.concat(
-      'cvox.TableWalker'),
+  closureModuleDeps:
+      ChromeVoxWalkerUnitTestBase.prototype.closureModuleDeps.concat(
+          'cvox.TableWalker'),
 
   /** @override */
   newWalker: function() {
@@ -32,7 +33,7 @@
 /**
  * Simple tests for TableWalker
  */
-TEST_F('CvoxTableWalkerUnitTest', 'testSimpleTableWalker', function() {
+TEST_F('ChromeVoxTableWalkerUnitTest', 'testSimpleTableWalker', function() {
   this.loadDoc(function() {/*!
     <p id="before">Before</p>
     <table id="table">
@@ -48,7 +49,7 @@
 /**
  * Test navigating rows.
  */
-TEST_F('CvoxTableWalkerUnitTest', 'testNavigateRows', function() {
+TEST_F('ChromeVoxTableWalkerUnitTest', 'testNavigateRows', function() {
   this.loadDoc(function() {/*!
     <table id="table">
       <tr><td>A</td><td>1</td></tr>
@@ -68,7 +69,7 @@
 /**
  * Test navigating columns.
  */
-TEST_F('CvoxTableWalkerUnitTest', 'testNavigateCols', function() {
+TEST_F('ChromeVoxTableWalkerUnitTest', 'testNavigateCols', function() {
   this.loadDoc(function() {/*!
     <table id="table">
       <tr><td>A</td><td>1</td></tr>
diff --git a/chrome/browser/resources/chromeos/chromevox/walkers/walker_unittest_base.js b/chrome/browser/resources/chromeos/chromevox/walkers/walker_unittest_base.js
index 6efe7fa..0e0d099 100644
--- a/chrome/browser/resources/chromeos/chromevox/walkers/walker_unittest_base.js
+++ b/chrome/browser/resources/chromeos/chromevox/walkers/walker_unittest_base.js
@@ -9,9 +9,9 @@
  * @constructor
  * @extends {ChromeVoxUnitTestBase}
  */
-function CvoxWalkerUnitTestBase() {}
+function ChromeVoxWalkerUnitTestBase() {}
 
-CvoxWalkerUnitTestBase.prototype = {
+ChromeVoxWalkerUnitTestBase.prototype = {
   __proto__: ChromeVoxUnitTestBase.prototype,
 
   /** @override */
@@ -79,7 +79,8 @@
     if (opt_cmdOrDest instanceof cvox.CursorSelection) {
       var ret = opt_cmdOrDest;
     } else {
-      if (CvoxWalkerUnitTestBase.CMD_WHITELIST.indexOf(opt_cmdOrDest) == -1) {
+      if (ChromeVoxWalkerUnitTestBase.CMD_WHITELIST.indexOf(opt_cmdOrDest) ==
+          -1) {
         // Intentionally fail the test if there's a typo.
         throw 'Got an invalid command: "' + opt_cmdOrDest + '".';
       }
@@ -97,7 +98,7 @@
     }
 
     for (var key in desc) {
-      if (CvoxWalkerUnitTestBase.DESC_WHITELIST.indexOf(key) == -1) {
+      if (ChromeVoxWalkerUnitTestBase.DESC_WHITELIST.indexOf(key) == -1) {
         throw 'Invalid key in desc parameter: "' + key + '".';
       }
     }
@@ -152,14 +153,15 @@
  * @type {Array.string}
  * @const
  */
-CvoxWalkerUnitTestBase.CMD_WHITELIST = ['next', 'sync', 'nextRow', 'nextCol'];
+ChromeVoxWalkerUnitTestBase.CMD_WHITELIST =
+    ['next', 'sync', 'nextRow', 'nextCol'];
 
 /**
  * Whitelist for the properties that can be asserted with go().
  * @type {Array.string}
  * @const
  */
-CvoxWalkerUnitTestBase.DESC_WHITELIST = [
+ChromeVoxWalkerUnitTestBase.DESC_WHITELIST = [
   'selText', 'selNodeId', 'selParentNodeId', 'selStartIndex', 'selEndIndex',
   'selReversed', 'descText', 'descContext', 'descAnnotation', 'descUserValue',
   'descPersonality'
@@ -169,7 +171,7 @@
  * Adds common walker tests
  * @param {string} testFixture Name of the test fixture class.
  */
-CvoxWalkerUnitTestBase.addCommonTests = function(testFixture) {
+ChromeVoxWalkerUnitTestBase.addCommonTests = function(testFixture) {
   /**
    * Ensures that syncing to the beginning and ends of the page return
    * not null.
diff --git a/chrome/browser/resources/chromeos/chromevox/walkers/word_walker_test.unitjs b/chrome/browser/resources/chromeos/chromevox/walkers/word_walker_test.unitjs
index 2da6b52..62ae84e 100644
--- a/chrome/browser/resources/chromeos/chromevox/walkers/word_walker_test.unitjs
+++ b/chrome/browser/resources/chromeos/chromevox/walkers/word_walker_test.unitjs
@@ -8,16 +8,17 @@
 /**
  * Test fixture.
  * @constructor
- * @extends {CvoxWalkerTestBase}
+ * @extends {ChromeVoxWalkerTestBase}
  */
-function CvoxWordWalkerUnitTest() {}
+function ChromeVoxWordWalkerUnitTest() {}
 
-CvoxWordWalkerUnitTest.prototype = {
-  __proto__: CvoxWalkerUnitTestBase.prototype,
+ChromeVoxWordWalkerUnitTest.prototype = {
+  __proto__: ChromeVoxWalkerUnitTestBase.prototype,
 
   /** @override */
-  closureModuleDeps: CvoxWalkerUnitTestBase.prototype.closureModuleDeps.concat(
-      'cvox.WordWalker'),
+  closureModuleDeps:
+      ChromeVoxWalkerUnitTestBase.prototype.closureModuleDeps.concat(
+          'cvox.WordWalker'),
 
   /** @override */
   newWalker: function() {
@@ -35,9 +36,9 @@
   }
 };
 
-CvoxWalkerUnitTestBase.addCommonTests('CvoxWordWalkerUnitTest');
+ChromeVoxWalkerUnitTestBase.addCommonTests('ChromeVoxWordWalkerUnitTest');
 
-TEST_F('CvoxWordWalkerUnitTest', 'testSimpleForwardSync', function() {
+TEST_F('ChromeVoxWordWalkerUnitTest', 'testSimpleForwardSync', function() {
   this.setUpSimpleHtml_();
 
   // invalid selection
@@ -55,7 +56,7 @@
   assertTrue(ret2.equals(ret));
 });
 
-TEST_F('CvoxWordWalkerUnitTest', 'testSimpleReversedSync', function() {
+TEST_F('ChromeVoxWordWalkerUnitTest', 'testSimpleReversedSync', function() {
   this.setUpSimpleHtml_();
 
   // invalid selection
@@ -74,7 +75,7 @@
   assertTrue(ret2.equals(ret));
 });
 
-TEST_F('CvoxWordWalkerUnitTest', 'testSimpleForwardNext', function() {
+TEST_F('ChromeVoxWordWalkerUnitTest', 'testSimpleForwardNext', function() {
   this.setUpSimpleHtml_();
 
   var sel = cvox.CursorSelection.fromNode(document.getElementById('a'));
@@ -88,7 +89,7 @@
   });
 });
 
-TEST_F('CvoxWordWalkerUnitTest', 'testSimpleReversedNext', function() {
+TEST_F('ChromeVoxWordWalkerUnitTest', 'testSimpleReversedNext', function() {
   this.setUpSimpleHtml_();
 
   var sel = cvox.CursorSelection.fromNode(document.getElementById('a'));
diff --git a/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn b/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn
index 2324b147..eca51cf 100644
--- a/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn
+++ b/chrome/browser/resources/chromeos/select_to_speak/BUILD.gn
@@ -98,6 +98,7 @@
   is_guest_manifest = true
 }
 
+# TODO(jamescook): Fold this into browser_tests.
 test("select_to_speak_extension_tests") {
   sources = [
     "//chrome/browser/extensions/browsertest_util.cc",
@@ -170,6 +171,15 @@
     "//chrome/test/data/webui/settings/fake_settings_private.js",
     "//chrome/test/data/webui/fake_chrome_event.js",
   ]
+
+  # The test base classes generate C++ code with these deps.
+  deps = [
+    "//ash",
+    "//base",
+    "//chrome/browser/chromeos",
+    "//chrome/common",
+    "//ui/keyboard",
+  ]
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 }
 
diff --git a/chrome/browser/resources/print_preview/new/app.js b/chrome/browser/resources/print_preview/new/app.js
index 1e433043..73e4fe62 100644
--- a/chrome/browser/resources/print_preview/new/app.js
+++ b/chrome/browser/resources/print_preview/new/app.js
@@ -273,7 +273,8 @@
       }
     }
 
-    if (e.code == 'Enter' && this.state == print_preview_new.State.READY &&
+    if ((e.code === 'Enter' || e.code === 'NumpadEnter') &&
+        this.state === print_preview_new.State.READY &&
         this.openDialogs_.length === 0) {
       const activeElementTag = e.path[0].tagName;
       if (['PAPER-BUTTON', 'BUTTON', 'SELECT', 'A', 'CR-CHECKBOX'].includes(
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chrome/browser/resources/settings/settings_ui/settings_ui.js
index a0d3d044..2f5ebce 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.js
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.js
@@ -70,6 +70,9 @@
     autofillHomeEnabled_: Boolean,
 
     /** @private */
+    enableScrollAnimator_: Boolean,
+
+    /** @private */
     lastSearchQuery_: {
       type: String,
       value: '',
@@ -156,6 +159,8 @@
     this.autofillHomeEnabled_ =
         loadTimeData.valueExists('autofillHomeEnabled') &&
         loadTimeData.getBoolean('autofillHomeEnabled');
+    this.enableScrollAnimator_ =
+        loadTimeData.getBoolean('enableScrollAnimator');
 
     this.addEventListener('show-container', () => {
       this.$.container.style.visibility = 'visible';
@@ -180,8 +185,9 @@
     document.fonts.load('bold 12px Roboto');
     settings.setGlobalScrollTarget(this.$.container);
 
+    const scrollBehavior = this.enableScrollAnimator_ ? 'smooth' : 'auto';
     const scrollToTop = top => new Promise(resolve => {
-      this.$.container.scrollTo({top, behavior: 'smooth'});
+      this.$.container.scrollTo({top, behavior: scrollBehavior});
       const onScroll = () => {
         this.debounce('scrollEnd', () => {
           this.$.container.removeEventListener('scroll', onScroll);
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
index fb79a2b..d68c0e4 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
@@ -930,7 +930,8 @@
             test_handle->CallWillProcessResponseForTesting(
                 main_rfh(),
                 net::HttpUtil::AssembleRawHeaders(
-                    kBasicResponseHeaders, strlen(kBasicResponseHeaders))));
+                    kBasicResponseHeaders, strlen(kBasicResponseHeaders)),
+                false, net::ProxyServer::Direct()));
   test_handle->CallDidCommitNavigationForTesting(redirect_url);
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(test_handle->HasCommitted());
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index 819809e..4d85071 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -322,8 +322,7 @@
                                       const std::string& new_title) {
   if (most_visited_sites_) {
     return most_visited_sites_->UpdateCustomLink(url, new_url,
-                                                 base::UTF8ToUTF16(new_title),
-                                                 /*is_user_action=*/true);
+                                                 base::UTF8ToUTF16(new_title));
   }
   return false;
 }
@@ -490,14 +489,11 @@
     // already timed out.
     if (duration >
         base::TimeDelta::FromSeconds(kCustomLinkDialogTimeoutSeconds)) {
+      GURL::Replacements replacements;
+      replacements.SetSchemeStr(url::kHttpScheme);
+      GURL new_url = url.ReplaceComponents(replacements);
+      UpdateCustomLink(url, new_url, /*new_title=*/std::string());
       timeout = true;
-      if (most_visited_sites_) {
-        GURL::Replacements replacements;
-        replacements.SetSchemeStr(url::kHttpScheme);
-        GURL new_url = url.ReplaceComponents(replacements);
-        most_visited_sites_->UpdateCustomLink(url, new_url, base::string16(),
-                                              /*is_user_action=*/false);
-      }
     }
   }
   std::move(callback).Run(resolves, timeout);
diff --git a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
index a7b8f31..a8c7cad0 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
@@ -360,35 +360,27 @@
   int ssl_version =
       net::SSLConnectionStatusToVersion(security_info.connection_status);
   net::SSLVersionToString(&protocol, ssl_version);
-  EXPECT_EQ(l10n_util::GetStringFUTF8(IDS_STRONG_SSL_SUMMARY,
-                                      base::ASCIIToUTF16(protocol)),
-            explanation.summary);
-
   bool is_aead, is_tls13;
   uint16_t cipher_suite =
       net::SSLConnectionStatusToCipherSuite(security_info.connection_status);
   net::SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, &is_aead,
                                &is_tls13, cipher_suite);
+  // Modern configurations are always AEADs and specify groups.
   EXPECT_TRUE(is_aead);
-  EXPECT_EQ(nullptr, mac);  // The default secure cipher does not have a MAC.
-  EXPECT_FALSE(is_tls13);   // The default secure cipher is not TLS 1.3.
+  EXPECT_EQ(nullptr, mac);
+  ASSERT_NE(0, security_info.key_exchange_group);
+  const char* key_exchange_group =
+      SSL_get_curve_name(security_info.key_exchange_group);
 
-  base::string16 key_exchange_name = base::ASCIIToUTF16(key_exchange);
-  if (security_info.key_exchange_group != 0) {
-    key_exchange_name = l10n_util::GetStringFUTF16(
-        IDS_SSL_KEY_EXCHANGE_WITH_GROUP, key_exchange_name,
-        base::ASCIIToUTF16(
-            SSL_get_curve_name(security_info.key_exchange_group)));
-  }
+  // The description should summarize the settings.
+  EXPECT_NE(std::string::npos, explanation.description.find(protocol));
+  EXPECT_NE(std::string::npos, explanation.description.find(key_exchange));
+  EXPECT_NE(std::string::npos,
+            explanation.description.find(key_exchange_group));
+  EXPECT_NE(std::string::npos, explanation.description.find(cipher));
 
-  std::vector<base::string16> description_replacements;
-  description_replacements.push_back(base::ASCIIToUTF16(protocol));
-  description_replacements.push_back(key_exchange_name);
-  description_replacements.push_back(base::ASCIIToUTF16(cipher));
-  base::string16 secure_description = l10n_util::GetStringFUTF16(
-      IDS_STRONG_SSL_DESCRIPTION, description_replacements, nullptr);
-
-  EXPECT_EQ(secure_description, base::ASCIIToUTF16(explanation.description));
+  // There should be no recommendations to provide.
+  EXPECT_EQ(0u, explanation.recommendations.size());
 }
 
 // Checks that the given |explanation| contains an appropriate
@@ -2577,37 +2569,24 @@
   security_state::SecurityInfo security_info;
   SecurityStateTabHelper::FromWebContents(web_contents)
       ->GetSecurityInfo(&security_info);
-  const char* protocol;
-  int ssl_version =
-      net::SSLConnectionStatusToVersion(security_info.connection_status);
-  net::SSLVersionToString(&protocol, ssl_version);
   for (const auto& explanation :
        observer.latest_explanations().secure_explanations) {
-    EXPECT_NE(l10n_util::GetStringFUTF8(IDS_STRONG_SSL_SUMMARY,
-                                        base::ASCIIToUTF16(protocol)),
+    EXPECT_NE(l10n_util::GetStringUTF8(IDS_SECURE_SSL_SUMMARY),
               explanation.summary);
   }
 
-  // Populate description string replacement with values corresponding
-  // to test constants.
-  std::vector<base::string16> description_replacements;
-  description_replacements.push_back(base::ASCIIToUTF16("TLS 1.1"));
-  description_replacements.push_back(
-      l10n_util::GetStringUTF16(IDS_SSL_AN_OBSOLETE_PROTOCOL));
-  description_replacements.push_back(base::ASCIIToUTF16("ECDHE_RSA"));
-  description_replacements.push_back(
-      l10n_util::GetStringUTF16(IDS_SSL_A_STRONG_KEY_EXCHANGE));
-  description_replacements.push_back(
-      base::ASCIIToUTF16("AES_128_CBC with HMAC-SHA1"));
-  description_replacements.push_back(
-      l10n_util::GetStringUTF16(IDS_SSL_AN_OBSOLETE_CIPHER));
-  base::string16 obsolete_description = l10n_util::GetStringFUTF16(
-      IDS_OBSOLETE_SSL_DESCRIPTION, description_replacements, nullptr);
+  // The description string should include the connection properties.
+  const content::SecurityStyleExplanation& explanation =
+      observer.latest_explanations().info_explanations[0];
+  EXPECT_NE(std::string::npos, explanation.description.find("TLS 1.1"));
+  EXPECT_NE(std::string::npos, explanation.description.find("ECDHE_RSA"));
+  EXPECT_NE(std::string::npos, explanation.description.find("AES_128_CBC"));
+  EXPECT_NE(std::string::npos, explanation.description.find("HMAC-SHA1"));
 
-  EXPECT_EQ(
-      obsolete_description,
-      base::ASCIIToUTF16(
-          observer.latest_explanations().info_explanations[0].description));
+  // There should be recommendations to fix the issues.
+  ASSERT_EQ(2u, explanation.recommendations.size());
+  EXPECT_NE(std::string::npos, explanation.recommendations[0].find("TLS 1.2"));
+  EXPECT_NE(std::string::npos, explanation.recommendations[1].find("GCM"));
 }
 
 // Tests that the Not Secure chip does not show for error pages on http:// URLs.
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index f155800..609b558e 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -357,6 +357,7 @@
          one.cert_status == two.cert_status &&
          one.security_bits == two.security_bits &&
          one.key_exchange_group == two.key_exchange_group &&
+         one.peer_signature_algorithm == two.peer_signature_algorithm &&
          one.connection_status == two.connection_status &&
          one.pkp_bypassed == two.pkp_bypassed;
 }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 11cc868..1ff39b21 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1465,6 +1465,10 @@
       "views/select_file_dialog_extension.h",
       "views/select_file_dialog_extension_factory.cc",
       "views/select_file_dialog_extension_factory.h",
+      "views/touch_selection_menu_chromeos.cc",
+      "views/touch_selection_menu_chromeos.h",
+      "views/touch_selection_menu_runner_chromeos.cc",
+      "views/touch_selection_menu_runner_chromeos.h",
       "webui/chromeos/assistant_optin/assistant_optin_ui.cc",
       "webui/chromeos/assistant_optin/assistant_optin_ui.h",
       "webui/chromeos/assistant_optin/assistant_optin_utils.cc",
@@ -2816,12 +2820,16 @@
       "views/translate/translate_icon_view.h",
       "views/update_recommended_message_box.cc",
       "views/update_recommended_message_box.h",
+      "views/webauthn/authenticator_ble_pin_entry_sheet_view.cc",
+      "views/webauthn/authenticator_ble_pin_entry_sheet_view.h",
       "views/webauthn/authenticator_request_dialog_view.cc",
       "views/webauthn/authenticator_request_dialog_view.h",
       "views/webauthn/authenticator_request_sheet_view.cc",
       "views/webauthn/authenticator_request_sheet_view.h",
       "views/webauthn/authenticator_transport_selector_sheet_view.cc",
       "views/webauthn/authenticator_transport_selector_sheet_view.h",
+      "views/webauthn/ble_pin_entry_view.cc",
+      "views/webauthn/ble_pin_entry_view.h",
       "views/webauthn/hover_list_view.cc",
       "views/webauthn/hover_list_view.h",
       "views/webauthn/sheet_view_factory.cc",
diff --git a/chrome/browser/ui/ash/tab_scrubber.cc b/chrome/browser/ui/ash/tab_scrubber.cc
index 627605b..515982af 100644
--- a/chrome/browser/ui/ash/tab_scrubber.cc
+++ b/chrome/browser/ui/ash/tab_scrubber.cc
@@ -257,8 +257,10 @@
 }
 
 void TabScrubber::ScheduleFinishScrubIfNeeded() {
+  // Tests use a really long delay to ensure RunLoops don't unnecessarily
+  // trigger the timer running.
   const base::TimeDelta delay = base::TimeDelta::FromMilliseconds(
-      use_default_activation_delay_ ? 200 : 0);
+      use_default_activation_delay_ ? 200 : 20000);
   activate_timer_.Start(FROM_HERE, delay,
                         base::BindRepeating(&TabScrubber::FinishScrub,
                                             base::Unretained(this), true));
diff --git a/chrome/browser/ui/ash/tab_scrubber.h b/chrome/browser/ui/ash/tab_scrubber.h
index 5c15892..9b62cc5 100644
--- a/chrome/browser/ui/ash/tab_scrubber.h
+++ b/chrome/browser/ui/ash/tab_scrubber.h
@@ -93,7 +93,7 @@
   // Timer to control a delayed activation of the |highlighted_tab_|.
   base::RetainingOneShotTimer activate_timer_;
   // True if the default activation delay should be used with |activate_timer_|.
-  // A value of false means the |activate_timer_| gets a zero delay.
+  // A value of false means the |activate_timer_| gets a really long delay.
   bool use_default_activation_delay_ = true;
   // Forces the tabs to be revealed if we are in immersive fullscreen.
   std::unique_ptr<ImmersiveRevealedLock> immersive_reveal_lock_;
diff --git a/chrome/browser/ui/ash/tab_scrubber_browsertest.cc b/chrome/browser/ui/ash/tab_scrubber_browsertest.cc
index d779e8bd..5310ff83 100644
--- a/chrome/browser/ui/ash/tab_scrubber_browsertest.cc
+++ b/chrome/browser/ui/ash/tab_scrubber_browsertest.cc
@@ -26,6 +26,7 @@
 #include "content/public/common/url_constants.h"
 #include "content/public/test/test_utils.h"
 #include "ui/aura/window.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/test/event_generator.h"
 
@@ -81,7 +82,7 @@
 class TabScrubberTest : public InProcessBrowserTest,
                         public TabStripModelObserver {
  public:
-  TabScrubberTest() : target_index_(-1) {}
+  TabScrubberTest() = default;
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     command_line->AppendSwitch(chromeos::switches::kNaturalScrollDefault);
@@ -89,7 +90,6 @@
 
   void SetUpOnMainThread() override {
     TabScrubber::GetInstance()->use_default_activation_delay_ = false;
-
     // Disable external monitor scaling of coordinates.
     ash::Shell* shell = ash::Shell::Get();
     shell->event_transformation_handler()->set_transformation_mode(
@@ -109,19 +109,17 @@
     return tab_strip;
   }
 
-  float GetStartX(Browser* browser,
-                  int index,
-                  TabScrubber::Direction direction) {
-    return static_cast<float>(
-        TabScrubber::GetStartPoint(GetTabStrip(browser), index, direction).x());
+  int GetStartX(Browser* browser, int index, TabScrubber::Direction direction) {
+    return TabScrubber::GetStartPoint(GetTabStrip(browser), index, direction)
+        .x();
   }
 
-  float GetTabCenter(Browser* browser, int index) {
-    return static_cast<float>(GetTabStrip(browser)
-                                  ->tab_at(index)
-                                  ->GetMirroredBounds()
-                                  .CenterPoint()
-                                  .x());
+  int GetTabCenter(Browser* browser, int index) {
+    return GetTabStrip(browser)
+        ->tab_at(index)
+        ->GetMirroredBounds()
+        .CenterPoint()
+        .x();
   }
 
   // The simulated scroll event's offsets are calculated in the tests rather
@@ -144,21 +142,19 @@
   // Sends one scroll event synchronously without initial or final
   // fling events.
   void SendScrubEvent(Browser* browser, int index) {
-    aura::Window* window = browser->window()->GetNativeWindow();
-    aura::Window* root = window->GetRootWindow();
-    ui::test::EventGenerator event_generator(root, window);
+    auto event_generator = CreateEventGenerator(browser);
     int active_index = browser->tab_strip_model()->active_index();
     TabScrubber::Direction direction =
         index < active_index ? TabScrubber::LEFT : TabScrubber::RIGHT;
 
     direction = InvertDirectionIfNeeded(direction);
 
-    float offset = GetTabCenter(browser, index) -
-                   GetStartX(browser, active_index, direction);
+    int offset = GetTabCenter(browser, index) -
+                 GetStartX(browser, active_index, direction);
     ui::ScrollEvent scroll_event(ui::ET_SCROLL, gfx::Point(0, 0),
                                  ui::EventTimeForNow(), 0, offset, 0, offset, 0,
                                  3);
-    event_generator.Dispatch(&scroll_event);
+    event_generator->Dispatch(&scroll_event);
   }
 
   enum ScrubType {
@@ -170,10 +166,7 @@
   // Sends asynchronous events and waits for tab at |index| to become
   // active.
   void Scrub(Browser* browser, int index, ScrubType scrub_type) {
-    aura::Window* window = browser->window()->GetNativeWindow();
-    aura::Window* root = window->GetRootWindow();
-    ui::test::EventGenerator event_generator(root, window);
-    event_generator.set_async(true);
+    auto event_generator = CreateEventGenerator(browser);
     activation_order_.clear();
     int active_index = browser->tab_strip_model()->active_index();
     ASSERT_NE(index, active_index);
@@ -192,21 +185,20 @@
 
     if (scrub_type == SKIP_TABS)
       increment *= 2;
-    float last = GetStartX(browser, active_index, direction);
-    std::vector<gfx::PointF> offsets;
+    browser->tab_strip_model()->AddObserver(this);
+    ScrollGenerator scroll_generator(event_generator.get());
+    int last = GetStartX(browser, active_index, direction);
     for (int i = active_index + increment; i != (index + increment);
          i += increment) {
-      float tab_center = GetTabCenter(browser, i);
-      offsets.push_back(gfx::PointF(tab_center - last, 0));
+      int tab_center = GetTabCenter(browser, i);
+      scroll_generator.GenerateScroll(tab_center - last);
       last = GetStartX(browser, i, direction);
       if (scrub_type == REPEAT_TABS) {
-        offsets.push_back(gfx::PointF(static_cast<float>(increment), 0));
+        scroll_generator.GenerateScroll(increment);
         last += increment;
       }
     }
-    event_generator.ScrollSequence(
-        gfx::Point(0, 0), base::TimeDelta::FromMilliseconds(100), offsets, 3);
-    RunUntilTabActive(browser, index);
+    browser->tab_strip_model()->RemoveObserver(this);
   }
 
   // Sends events and waits for tab at |index| to become active
@@ -214,19 +206,11 @@
   // If the active tab is expected to stay the same, send events
   // synchronously (as we don't have anything to wait for).
   void SendScrubSequence(Browser* browser, float x_offset, int index) {
-    aura::Window* window = browser->window()->GetNativeWindow();
-    aura::Window* root = window->GetRootWindow();
-    ui::test::EventGenerator event_generator(root, window);
-    bool wait_for_active = false;
-    if (index != browser->tab_strip_model()->active_index()) {
-      wait_for_active = true;
-      event_generator.set_async(true);
-    }
-    event_generator.ScrollSequence(gfx::Point(0, 0),
-                                   base::TimeDelta::FromMilliseconds(100),
-                                   x_offset, 0, 1, 3);
-    if (wait_for_active)
-      RunUntilTabActive(browser, index);
+    auto event_generator = CreateEventGenerator(browser);
+    browser->tab_strip_model()->AddObserver(this);
+    ScrollGenerator scroll_generator(event_generator.get());
+    scroll_generator.GenerateScroll(x_offset);
+    browser->tab_strip_model()->RemoveObserver(this);
   }
 
   void AddTabs(Browser* browser, int num_tabs) {
@@ -245,26 +229,65 @@
                         int index,
                         int reason) override {
     activation_order_.push_back(index);
-    if (index == target_index_)
-      quit_closure_.Run();
   }
 
   // History of tab activation. Scrub() resets it.
   std::vector<int> activation_order_;
 
  private:
-  void RunUntilTabActive(Browser* browser, int target) {
-    base::RunLoop run_loop;
-    quit_closure_ = content::GetDeferredQuitTaskForRunLoop(&run_loop);
-    browser->tab_strip_model()->AddObserver(this);
-    target_index_ = target;
-    content::RunThisRunLoop(&run_loop);
-    browser->tab_strip_model()->RemoveObserver(this);
-    target_index_ = -1;
+  // Used to generate a sequence of scrolls. Starts with a cancel, is followed
+  // by any number of scrolls and finally a fling-start. After every event this
+  // forces the TabScrubber to complete any pending activation.
+  class ScrollGenerator {
+   public:
+    // TabScrubber reacts to three-finger scrolls.
+    static const int kNumFingers = 3;
+
+    explicit ScrollGenerator(ui::test::EventGenerator* event_generator)
+        : event_generator_(event_generator) {
+      ui::ScrollEvent fling_cancel(ui::ET_SCROLL_FLING_CANCEL, gfx::Point(),
+                                   time_for_next_event_, 0, 0, 0, 0, 0,
+                                   kNumFingers);
+      event_generator->Dispatch(&fling_cancel);
+      if (TabScrubber::GetInstance()->IsActivationPending())
+        TabScrubber::GetInstance()->FinishScrub(true);
+    }
+
+    ~ScrollGenerator() {
+      ui::ScrollEvent fling_start(ui::ET_SCROLL_FLING_START, gfx::Point(),
+                                  time_for_next_event_, 0, last_x_offset_, 0,
+                                  last_x_offset_, 0, kNumFingers);
+      event_generator_->Dispatch(&fling_start);
+      if (TabScrubber::GetInstance()->IsActivationPending())
+        TabScrubber::GetInstance()->FinishScrub(true);
+    }
+
+    void GenerateScroll(int x_offset) {
+      time_for_next_event_ += base::TimeDelta::FromMilliseconds(100);
+      ui::ScrollEvent scroll(ui::ET_SCROLL, gfx::Point(), time_for_next_event_,
+                             0, x_offset, 0, x_offset, 0, kNumFingers);
+      last_x_offset_ = x_offset;
+      event_generator_->Dispatch(&scroll);
+      if (TabScrubber::GetInstance()->IsActivationPending())
+        TabScrubber::GetInstance()->FinishScrub(true);
+    }
+
+   private:
+    ui::test::EventGenerator* event_generator_;
+    base::TimeTicks time_for_next_event_ = ui::EventTimeForNow();
+    int last_x_offset_ = 0;
+
+    DISALLOW_COPY_AND_ASSIGN(ScrollGenerator);
+  };
+
+  std::unique_ptr<ui::test::EventGenerator> CreateEventGenerator(
+      Browser* browser) {
+    aura::Window* window = browser->window()->GetNativeWindow();
+    aura::Window* root = window->GetRootWindow();
+    return std::make_unique<ui::test::EventGenerator>(
+        features::IsUsingWindowService() ? nullptr : root, window);
   }
 
-  base::Closure quit_closure_;
-  int target_index_;
 
   DISALLOW_COPY_AND_ASSIGN(TabScrubberTest);
 };
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.mm
index a15aa3a..8489b9b 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.mm
@@ -43,6 +43,10 @@
   }
 }
 
+NSString* MenuTitleForNode(const BookmarkNode* node) {
+  return base::SysUTF16ToNSString(node->GetTitle());
+}
+
 }  // namespace
 
 BookmarkMenuBridge::BookmarkMenuBridge(Profile* profile, NSMenu* menu_root)
@@ -245,7 +249,7 @@
 void BookmarkMenuBridge::AddNodeAsSubmenu(NSMenu* menu,
                                           const BookmarkNode* node,
                                           NSImage* image) {
-  NSString* title = [BookmarkMenuCocoaController menuTitleForNode:node];
+  NSString* title = MenuTitleForNode(node);
   base::scoped_nsobject<NSMenuItem> items(
       [[NSMenuItem alloc] initWithTitle:title action:nil keyEquivalent:@""]);
   [items setImage:image];
@@ -278,9 +282,8 @@
     if (child->is_folder()) {
       AddNodeAsSubmenu(menu, child, folder_image_);
     } else {
-      NSString* title = [BookmarkMenuCocoaController menuTitleForNode:child];
       base::scoped_nsobject<NSMenuItem> item([[NSMenuItem alloc]
-          initWithTitle:title
+          initWithTitle:MenuTitleForNode(child)
                  action:nil
           keyEquivalent:@""]);
       bookmark_nodes_[child] = item;
@@ -294,7 +297,7 @@
                                            NSMenuItem* item,
                                            bool set_title) {
   if (set_title)
-    [item setTitle:[BookmarkMenuCocoaController menuTitleForNode:node]];
+    [item setTitle:MenuTitleForNode(node)];
   [item setTarget:controller_];
   [item setAction:@selector(openBookmarkMenuItem:)];
   [item setTag:node->id()];
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm
index e457fce..8606563 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm
@@ -208,9 +208,9 @@
   NSString* s = [short_item title];
   EXPECT_NSEQ([NSString stringWithUTF8String:short_url], s);
 
-  // Make sure a super-long title gets trimmed
+  // Long titles are shortened, but only once drawn by AppKit.
   s = [long_item title];
-  EXPECT_TRUE([s length] < strlen(long_url));
+  EXPECT_NSEQ([NSString stringWithUTF8String:long_url], s);
 
   // Confirm tooltips and confirm they are not trimmed (like the item
   // name might be).  Add tolerance for URL fixer-upping;
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.h b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.h
index 02969d8b..89d249fd 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.h
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.h
@@ -20,10 +20,6 @@
 // Unfortunately there is already a C++ class named BookmarkMenuController.
 @interface BookmarkMenuCocoaController : NSObject<NSMenuDelegate>
 
-// Return an autoreleased string to be used as a menu title for the
-// given bookmark node.
-+ (NSString*)menuTitleForNode:(const bookmarks::BookmarkNode*)node;
-
 // Make a relevant tooltip string for node.
 + (NSString*)tooltipForNode:(const bookmarks::BookmarkNode*)node;
 
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.mm
index 2ecf8a6..5845df18 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.mm
@@ -27,10 +27,6 @@
 
 namespace {
 
-// Menus more than this many pixels wide will get trimmed
-// TODO(jrg): ask UI dudes what a good value is.
-const NSUInteger kMaximumMenuPixelsWide = 300;
-
 // Returns the NSMenuItem in |submenu|'s supermenu that holds |submenu|.
 NSMenuItem* GetItemWithSubmenu(NSMenu* submenu) {
   NSArray* parent_items = [[submenu supermenu] itemArray];
@@ -48,13 +44,6 @@
   BookmarkMenuBridge* bridge_;  // Weak. Owns |self|.
 }
 
-+ (NSString*)menuTitleForNode:(const BookmarkNode*)node {
-  base::string16 title =
-      [MenuControllerCocoa elideMenuTitle:node->GetTitle()
-                                  toWidth:kMaximumMenuPixelsWide];
-  return base::SysUTF16ToNSString(title);
-}
-
 + (NSString*)tooltipForNode:(const BookmarkNode*)node {
   NSString* title = base::SysUTF16ToNSString(node->GetTitle());
   if (node->is_folder())
diff --git a/chrome/browser/ui/cocoa/history_menu_bridge.mm b/chrome/browser/ui/cocoa/history_menu_bridge.mm
index a754320..2bf9f18 100644
--- a/chrome/browser/ui/cocoa/history_menu_bridge.mm
+++ b/chrome/browser/ui/cocoa/history_menu_bridge.mm
@@ -23,14 +23,10 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/image/image.h"
-#include "ui/gfx/text_elider.h"
 #include "ui/resources/grit/ui_resources.h"
 
 namespace {
 
-// Maximum number of pixels to use for a menu item title.
-const float kTitlePixelWidth = 400;
-
 // Number of days to consider when getting the number of visited items.
 const int kVisitedScope = 90;
 
@@ -293,12 +289,10 @@
                                              NSInteger tag,
                                              NSInteger index) {
   // Elide the title of the history item, or use the URL if there is none.
-  std::string url = item->url.possibly_invalid_spec();
-  base::string16 full_title = item->title;
-  base::string16 title = gfx::ElideText(
-      full_title.empty() ? base::UTF8ToUTF16(url) : full_title,
-      gfx::FontList(gfx::Font([NSFont menuFontOfSize:0])), kTitlePixelWidth,
-      gfx::ELIDE_MIDDLE, gfx::Typesetter::NATIVE);
+  const std::string& url = item->url.possibly_invalid_spec();
+  const base::string16& full_title = item->title;
+  const base::string16& title =
+      full_title.empty() ? base::UTF8ToUTF16(url) : full_title;
 
   item->menu_item.reset(
       [[NSMenuItem alloc] initWithTitle:base::SysUTF16ToNSString(title)
diff --git a/chrome/browser/ui/media_router/media_router_ui_base.cc b/chrome/browser/ui/media_router/media_router_ui_base.cc
index ab6db59bc..af4f9375 100644
--- a/chrome/browser/ui/media_router/media_router_ui_base.cc
+++ b/chrome/browser/ui/media_router/media_router_ui_base.cc
@@ -540,7 +540,8 @@
                      weak_factory_.GetWeakPtr(), cast_mode));
 
   // There are 3 cases. In cases (1) and (3) the MediaRouterUIBase will need to
-  // be notified. In case (2) the dialog will be closed.
+  // be notified via OnRouteResponseReceived(). In case (2) the dialog will be
+  // closed before that via HandleCreateSessionRequestRouteResponse().
   // (1) Non-presentation route request (e.g., mirroring). No additional
   //     notification necessary.
   // (2) Presentation route request for a PresentationRequest.start() call.
@@ -550,12 +551,11 @@
   //     PresentationServiceDelegateImpl will have to be notified. Note that we
   //     treat subsequent route requests from a Presentation API-initiated
   //     dialogs as browser-initiated.
-  if (!for_presentation_source || !start_presentation_context_) {
-    params.route_result_callbacks.push_back(base::BindOnce(
-        &MediaRouterUIBase::OnRouteResponseReceived, weak_factory_.GetWeakPtr(),
-        current_route_request_->id, sink_id, cast_mode,
-        base::UTF8ToUTF16(GetTruncatedPresentationRequestSourceName())));
-  }
+  // TODO(https://crbug.com/868186): Close the Views dialog in case (2).
+  params.route_result_callbacks.push_back(base::BindOnce(
+      &MediaRouterUIBase::OnRouteResponseReceived, weak_factory_.GetWeakPtr(),
+      current_route_request_->id, sink_id, cast_mode,
+      base::UTF8ToUTF16(GetTruncatedPresentationRequestSourceName())));
   if (for_presentation_source) {
     if (start_presentation_context_) {
       // |start_presentation_context_| will be nullptr after this call, as the
diff --git a/chrome/browser/ui/views/accessibility/invert_bubble_view_browsertest.cc b/chrome/browser/ui/views/accessibility/invert_bubble_view_browsertest.cc
index e0bf71a6..e0a9bbe8 100644
--- a/chrome/browser/ui/views/accessibility/invert_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/accessibility/invert_bubble_view_browsertest.cc
@@ -19,12 +19,10 @@
     // Bubble dialogs' bounds may exceed the display's work area.
     // https://crbug.com/893292.
     set_should_verify_dialog_bounds(false);
-    ShowInvertBubbleView(browser(), &anchor_);
+    ShowInvertBubbleView(browser(), nullptr);
   }
 
  private:
-  views::View anchor_;
-
   DISALLOW_COPY_AND_ASSIGN(InvertBubbleViewBrowserTest);
 };
 
diff --git a/chrome/browser/ui/views/autofill/local_card_migration_bubble_views.cc b/chrome/browser/ui/views/autofill/local_card_migration_bubble_views.cc
index 17b3e40..8f869e0 100644
--- a/chrome/browser/ui/views/autofill/local_card_migration_bubble_views.cc
+++ b/chrome/browser/ui/views/autofill/local_card_migration_bubble_views.cc
@@ -28,6 +28,7 @@
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/fill_layout.h"
 #include "ui/views/style/typography.h"
 
 namespace autofill {
@@ -100,17 +101,28 @@
 
 void LocalCardMigrationBubbleViews::AddedToWidget() {
   auto title_container = std::make_unique<views::View>();
-  title_container->SetLayoutManager(
-      std::make_unique<views::BoxLayout>(views::BoxLayout::kHorizontal));
+  title_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::kVertical, gfx::Insets(),
+      ChromeLayoutProvider::Get()->GetDistanceMetric(
+          DISTANCE_RELATED_CONTROL_VERTICAL_SMALL)));
   gfx::ImageSkia image = gfx::ImageSkiaOperations::CreateTiledImage(
       gfx::CreateVectorIcon(kGooglePayLogoIcon, gfx::kPlaceholderColor),
       /*x=*/0, /*y=*/0, kMigrationBubbleGooglePayLogoWidth,
       kMigrationBubbleGooglePayLogoHeight);
   views::ImageView* icon_view = new views::ImageView();
   icon_view->SetImage(&image);
+  icon_view->SetHorizontalAlignment(views::ImageView::LEADING);
   icon_view->SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_AUTOFILL_GOOGLE_PAY_LOGO_ACCESSIBLE_NAME));
   title_container->AddChildView(icon_view);
+
+  auto* title = new views::Label(
+      l10n_util::GetStringUTF16(IDS_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_TITLE),
+      views::style::CONTEXT_DIALOG_TITLE);
+  title->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  title->SetMultiLine(true);
+  title_container->AddChildView(title);
+
   GetBubbleFrameView()->SetTitleView(std::move(title_container));
 }
 
@@ -128,18 +140,7 @@
 LocalCardMigrationBubbleViews::~LocalCardMigrationBubbleViews() {}
 
 void LocalCardMigrationBubbleViews::Init() {
-  SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::kVertical, gfx::Insets(),
-      ChromeLayoutProvider::Get()->GetDistanceMetric(
-          DISTANCE_RELATED_CONTROL_VERTICAL_SMALL)));
-
-  auto* title = new views::Label(
-      l10n_util::GetStringUTF16(IDS_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_TITLE),
-      views::style::CONTEXT_DIALOG_TITLE);
-  title->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  title->SetMultiLine(true);
-  AddChildView(title);
-
+  SetLayoutManager(std::make_unique<views::FillLayout>());
   auto* explanatory_message = new views::Label(
       l10n_util::GetStringUTF16(
           IDS_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_BODY_TEXT),
diff --git a/chrome/browser/ui/views/chrome_views_delegate.cc b/chrome/browser/ui/views/chrome_views_delegate.cc
index 263ba71e..265e6da 100644
--- a/chrome/browser/ui/views/chrome_views_delegate.cc
+++ b/chrome/browser/ui/views/chrome_views_delegate.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/browser/ui/views/chrome_views_delegate.h"
 
-#include <memory>
-
 #include "base/logging.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
@@ -26,6 +24,10 @@
 #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h"
 #endif
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/ui/views/touch_selection_menu_runner_chromeos.h"
+#endif
+
 // Helpers --------------------------------------------------------------------
 
 namespace {
@@ -57,7 +59,17 @@
 
 // ChromeViewsDelegate --------------------------------------------------------
 
-ChromeViewsDelegate::ChromeViewsDelegate() {}
+ChromeViewsDelegate::ChromeViewsDelegate() {
+#if defined(OS_CHROMEOS)
+  // ViewsDelegate's constructor may have created a menu runner already, and
+  // since TouchSelectionMenuRunner is a singleton with checks to not
+  // initialize it if there is already an existing runner we need to first
+  // destroy runner before we can create the ChromeOS specific instance.
+  SetTouchSelectionMenuRunner(nullptr);
+  SetTouchSelectionMenuRunner(
+      std::make_unique<TouchSelectionMenuRunnerChromeOS>());
+#endif
+}
 
 ChromeViewsDelegate::~ChromeViewsDelegate() {
   DCHECK_EQ(0u, ref_count_);
diff --git a/chrome/browser/ui/views/chrome_views_delegate.h b/chrome/browser/ui/views/chrome_views_delegate.h
index 7e6d5fcc..64510ab 100644
--- a/chrome/browser/ui/views/chrome_views_delegate.h
+++ b/chrome/browser/ui/views/chrome_views_delegate.h
@@ -5,6 +5,10 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_CHROME_VIEWS_DELEGATE_H_
 #define CHROME_BROWSER_UI_VIEWS_CHROME_VIEWS_DELEGATE_H_
 
+#include <map>
+#include <memory>
+#include <string>
+
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/location.h"
diff --git a/chrome/browser/ui/views/touch_selection_menu_chromeos.cc b/chrome/browser/ui/views/touch_selection_menu_chromeos.cc
new file mode 100644
index 0000000..ddaf481
--- /dev/null
+++ b/chrome/browser/ui/views/touch_selection_menu_chromeos.cc
@@ -0,0 +1,83 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/touch_selection_menu_chromeos.h"
+
+#include <utility>
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/views/touch_selection_menu_runner_chromeos.h"
+#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_service_manager.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_skia_operations.h"
+
+namespace {
+
+constexpr size_t kSmallIconSizeInDip = 16;
+
+}  // namespace
+
+TouchSelectionMenuChromeOS::TouchSelectionMenuChromeOS(
+    views::TouchSelectionMenuRunnerViews* owner,
+    ui::TouchSelectionMenuClient* client,
+    aura::Window* context,
+    arc::mojom::TextSelectionActionPtr action)
+    : views::TouchSelectionMenuRunnerViews::Menu(owner, client, context),
+      action_(std::move(action)) {}
+
+void TouchSelectionMenuChromeOS::SetActionsForTesting(
+    std::vector<arc::mojom::TextSelectionActionPtr> actions) {
+  action_ = std::move(actions[0]);
+
+  // Since we are forcing new button entries here, it is very likely that the
+  // default action buttons are already added, we should remove the existent
+  // buttons if any, and then call CreateButtons, this will call the parent
+  // method too.
+  if (has_children())
+    RemoveAllChildViews(/*delete_children=*/true);
+
+  CreateButtons();
+}
+
+void TouchSelectionMenuChromeOS::CreateButtons() {
+  if (action_) {
+    views::LabelButton* button = CreateButton(base::UTF8ToUTF16(action_->title),
+                                              kSmartTextSelectionActionTag);
+
+    if (action_->bitmap_icon) {
+      gfx::ImageSkia original(
+          gfx::ImageSkia::CreateFrom1xBitmap(action_->bitmap_icon.value()));
+      gfx::ImageSkia icon = gfx::ImageSkiaOperations::CreateResizedImage(
+          original, skia::ImageOperations::RESIZE_BEST,
+          gfx::Size(kSmallIconSizeInDip, kSmallIconSizeInDip));
+      button->SetImage(views::Button::ButtonState::STATE_NORMAL, icon);
+    }
+
+    AddChildView(button);
+  }
+
+  views::TouchSelectionMenuRunnerViews::Menu::CreateButtons();
+}
+
+void TouchSelectionMenuChromeOS::ButtonPressed(views::Button* sender,
+                                               const ui::Event& event) {
+  if (sender->tag() != kSmartTextSelectionActionTag) {
+    views::TouchSelectionMenuRunnerViews::Menu::ButtonPressed(sender, event);
+    return;
+  }
+
+  auto* arc_service_manager = arc::ArcServiceManager::Get();
+  if (!arc_service_manager)
+    return;
+  auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
+      arc_service_manager->arc_bridge_service()->intent_helper(), HandleIntent);
+  if (!instance)
+    return;
+
+  instance->HandleIntent(std::move(action_->action_intent),
+                         std::move(action_->activity));
+}
+
+TouchSelectionMenuChromeOS::~TouchSelectionMenuChromeOS() = default;
diff --git a/chrome/browser/ui/views/touch_selection_menu_chromeos.h b/chrome/browser/ui/views/touch_selection_menu_chromeos.h
new file mode 100644
index 0000000..0b329e3b
--- /dev/null
+++ b/chrome/browser/ui/views/touch_selection_menu_chromeos.h
@@ -0,0 +1,44 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_TOUCH_SELECTION_MENU_CHROMEOS_H_
+#define CHROME_BROWSER_UI_VIEWS_TOUCH_SELECTION_MENU_CHROMEOS_H_
+
+#include <vector>
+
+#include "components/arc/common/intent_helper.mojom.h"
+#include "ui/views/touchui/touch_selection_menu_runner_views.h"
+
+// A ChromeOS specific subclass of the the bubble menu. It provides an
+// additional button for a Smart Text Selection action based on the current
+// user's text selection.
+class TouchSelectionMenuChromeOS
+    : public views::TouchSelectionMenuRunnerViews::Menu {
+ public:
+  // Tag used to mark added actions/buttons as generated by the smart text
+  // selection feature.
+  enum { kSmartTextSelectionActionTag = -2 };
+
+  TouchSelectionMenuChromeOS(views::TouchSelectionMenuRunnerViews* owner,
+                             ui::TouchSelectionMenuClient* client,
+                             aura::Window* context,
+                             arc::mojom::TextSelectionActionPtr action);
+
+  void SetActionsForTesting(
+      std::vector<arc::mojom::TextSelectionActionPtr> actions);
+
+ protected:
+  // views:TouchSelectionMenuRunnerViews::Menu
+  void CreateButtons() override;
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+ private:
+  ~TouchSelectionMenuChromeOS() override;
+
+  arc::mojom::TextSelectionActionPtr action_;
+
+  DISALLOW_COPY_AND_ASSIGN(TouchSelectionMenuChromeOS);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_TOUCH_SELECTION_MENU_CHROMEOS_H_
diff --git a/chrome/browser/ui/views/touch_selection_menu_runner_chromeos.cc b/chrome/browser/ui/views/touch_selection_menu_runner_chromeos.cc
new file mode 100644
index 0000000..d6536b8
--- /dev/null
+++ b/chrome/browser/ui/views/touch_selection_menu_runner_chromeos.cc
@@ -0,0 +1,95 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/touch_selection_menu_runner_chromeos.h"
+
+#include <utility>
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/views/touch_selection_menu_chromeos.h"
+#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_features.h"
+#include "components/arc/arc_service_manager.h"
+#include "ui/aura/window.h"
+#include "ui/base/layout.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+
+TouchSelectionMenuRunnerChromeOS::TouchSelectionMenuRunnerChromeOS() = default;
+
+TouchSelectionMenuRunnerChromeOS::~TouchSelectionMenuRunnerChromeOS() = default;
+
+void TouchSelectionMenuRunnerChromeOS::OpenMenuWithTextSelectionAction(
+    ui::TouchSelectionMenuClient* client,
+    const gfx::Rect& anchor_rect,
+    const gfx::Size& handle_image_size,
+    std::unique_ptr<aura::WindowTracker> tracker,
+    std::vector<arc::mojom::TextSelectionActionPtr> actions) {
+  if (tracker->windows().empty())
+    return;
+  if (!client->ShouldShowQuickMenu())
+    return;
+
+  arc::mojom::TextSelectionActionPtr top_action;
+  // Get the first action generated by the Android TextClassifier.
+  for (auto& action : actions) {
+    if (!action->text_classifier_action)
+      continue;
+    std::swap(top_action, action);
+    break;
+  }
+
+  // The menu manages its own lifetime and deletes itself when closed.
+  TouchSelectionMenuChromeOS* menu = new TouchSelectionMenuChromeOS(
+      this, client, tracker->Pop(), std::move(top_action));
+  ShowMenu(menu, anchor_rect, handle_image_size);
+}
+
+void TouchSelectionMenuRunnerChromeOS::OpenMenu(
+    ui::TouchSelectionMenuClient* client,
+    const gfx::Rect& anchor_rect,
+    const gfx::Size& handle_image_size,
+    aura::Window* context) {
+  views::TouchSelectionMenuRunnerViews::CloseMenu();
+
+  if (!views::TouchSelectionMenuRunnerViews::IsMenuAvailable(client))
+    return;
+
+  if (base::FeatureList::IsEnabled(arc::kSmartTextSelectionFeature)) {
+    auto* arc_service_manager = arc::ArcServiceManager::Get();
+    if (arc_service_manager) {
+      arc::mojom::IntentHelperInstance* instance = ARC_GET_INSTANCE_FOR_METHOD(
+          arc_service_manager->arc_bridge_service()->intent_helper(),
+          RequestTextSelectionActions);
+
+      if (instance) {
+        // aura::WindowTracker is used since the newly created menu may need to
+        // know about the parent window.
+        std::unique_ptr<aura::WindowTracker> tracker =
+            std::make_unique<aura::WindowTracker>();
+        tracker->Add(context);
+
+        const display::Screen* screen = display::Screen::GetScreen();
+        DCHECK(screen);
+
+        // Fetch actions for selected text and then show quick menu.
+        instance->RequestTextSelectionActions(
+            base::UTF16ToUTF8(client->GetSelectedText()),
+            arc::mojom::ScaleFactor(
+                screen->GetDisplayNearestWindow(context).device_scale_factor()),
+            base::BindOnce(&TouchSelectionMenuRunnerChromeOS::
+                               OpenMenuWithTextSelectionAction,
+                           weak_ptr_factory_.GetWeakPtr(), client, anchor_rect,
+                           handle_image_size, std::move(tracker)));
+        return;
+      }
+    }
+  }
+
+  // The menu manages its own lifetime and deletes itself when closed.
+  TouchSelectionMenuChromeOS* menu =
+      new TouchSelectionMenuChromeOS(this, client, context,
+                                     /*action=*/nullptr);
+  ShowMenu(menu, anchor_rect, handle_image_size);
+}
diff --git a/chrome/browser/ui/views/touch_selection_menu_runner_chromeos.h b/chrome/browser/ui/views/touch_selection_menu_runner_chromeos.h
new file mode 100644
index 0000000..146b8941
--- /dev/null
+++ b/chrome/browser/ui/views/touch_selection_menu_runner_chromeos.h
@@ -0,0 +1,50 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_TOUCH_SELECTION_MENU_RUNNER_CHROMEOS_H_
+#define CHROME_BROWSER_UI_VIEWS_TOUCH_SELECTION_MENU_RUNNER_CHROMEOS_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "components/arc/common/intent_helper.mojom.h"
+#include "ui/aura/window_tracker.h"
+#include "ui/views/touchui/touch_selection_menu_runner_views.h"
+
+namespace aura {
+class Window;
+}
+
+// A Chrome OS TouchSelectionMenuRunner implementation that queries ARC++
+// for Smart Text Selection actions based on the current text selection. This
+// allows the quick menu to show a new contextual action button.
+class TouchSelectionMenuRunnerChromeOS
+    : public views::TouchSelectionMenuRunnerViews {
+ public:
+  TouchSelectionMenuRunnerChromeOS();
+  ~TouchSelectionMenuRunnerChromeOS() override;
+
+ private:
+  // Called asynchronously with the result from the container.
+  void OpenMenuWithTextSelectionAction(
+      ui::TouchSelectionMenuClient* client,
+      const gfx::Rect& anchor_rect,
+      const gfx::Size& handle_image_size,
+      std::unique_ptr<aura::WindowTracker> tracker,
+      std::vector<arc::mojom::TextSelectionActionPtr> actions);
+
+  // views::TouchSelectionMenuRunnerViews.
+  void OpenMenu(ui::TouchSelectionMenuClient* client,
+                const gfx::Rect& anchor_rect,
+                const gfx::Size& handle_image_size,
+                aura::Window* context) override;
+
+  base::WeakPtrFactory<TouchSelectionMenuRunnerChromeOS> weak_ptr_factory_{
+      this};
+
+  DISALLOW_COPY_AND_ASSIGN(TouchSelectionMenuRunnerChromeOS);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_TOUCH_SELECTION_MENU_RUNNER_CHROMEOS_H_
diff --git a/chrome/browser/ui/views/webauthn/authenticator_ble_pin_entry_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_ble_pin_entry_sheet_view.cc
new file mode 100644
index 0000000..7181b81
--- /dev/null
+++ b/chrome/browser/ui/views/webauthn/authenticator_ble_pin_entry_sheet_view.cc
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/webauthn/authenticator_ble_pin_entry_sheet_view.h"
+
+#include <utility>
+
+AuthenticatorBlePinEntrySheetView::AuthenticatorBlePinEntrySheetView(
+    std::unique_ptr<AuthenticatorBlePinEntrySheetModel> model)
+    : AuthenticatorRequestSheetView(std::move(model)) {}
+
+AuthenticatorBlePinEntrySheetView::~AuthenticatorBlePinEntrySheetView() =
+    default;
+
+std::unique_ptr<views::View>
+AuthenticatorBlePinEntrySheetView::BuildStepSpecificContent() {
+  return std::make_unique<BlePinEntryView>(this);
+}
+
+void AuthenticatorBlePinEntrySheetView::OnPincodeChanged(
+    base::string16 pincode) {
+  static_cast<AuthenticatorBlePinEntrySheetModel*>(model())->SetPinCode(
+      std::move(pincode));
+}
diff --git a/chrome/browser/ui/views/webauthn/authenticator_ble_pin_entry_sheet_view.h b/chrome/browser/ui/views/webauthn/authenticator_ble_pin_entry_sheet_view.h
new file mode 100644
index 0000000..a6acd70
--- /dev/null
+++ b/chrome/browser/ui/views/webauthn/authenticator_ble_pin_entry_sheet_view.h
@@ -0,0 +1,32 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_BLE_PIN_ENTRY_SHEET_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_BLE_PIN_ENTRY_SHEET_VIEW_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h"
+#include "chrome/browser/ui/views/webauthn/ble_pin_entry_view.h"
+#include "chrome/browser/ui/webauthn/sheet_models.h"
+
+// Represents a sheet in the Web Authentication request dialog that allows the
+// user to input pin code used to connect to BLE security key.
+class AuthenticatorBlePinEntrySheetView : public AuthenticatorRequestSheetView,
+                                          public BlePinEntryView::Delegate {
+ public:
+  explicit AuthenticatorBlePinEntrySheetView(
+      std::unique_ptr<AuthenticatorBlePinEntrySheetModel> model);
+  ~AuthenticatorBlePinEntrySheetView() override;
+
+ private:
+  // AuthenticatorRequestSheetView:
+  std::unique_ptr<views::View> BuildStepSpecificContent() override;
+
+  // BlePinEntryView::Delegate:
+  void OnPincodeChanged(base::string16 pincode) override;
+
+  DISALLOW_COPY_AND_ASSIGN(AuthenticatorBlePinEntrySheetView);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_BLE_PIN_ENTRY_SHEET_VIEW_H_
diff --git a/chrome/browser/ui/views/webauthn/ble_pin_entry_view.cc b/chrome/browser/ui/views/webauthn/ble_pin_entry_view.cc
new file mode 100644
index 0000000..fe87f583
--- /dev/null
+++ b/chrome/browser/ui/views/webauthn/ble_pin_entry_view.cc
@@ -0,0 +1,85 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/webauthn/ble_pin_entry_view.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/color_palette.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/layout_provider.h"
+#include "ui/views/style/typography.h"
+
+namespace {
+
+constexpr int kExpectedPincodeCharLength = 6;
+constexpr int kPreferredTextfieldCharLength = 20;
+constexpr int kTextfieldBottomBorderThickness = 2;
+
+}  // namespace
+
+BlePinEntryView::BlePinEntryView(Delegate* delegate) : delegate_(delegate) {
+  auto layout = std::make_unique<views::BoxLayout>(
+      views::BoxLayout::kVertical, gfx::Insets(),
+      ChromeLayoutProvider::Get()->GetDistanceMetric(
+          views::DISTANCE_CONTROL_VERTICAL_TEXT_PADDING));
+  layout->set_cross_axis_alignment(
+      views::BoxLayout::CROSS_AXIS_ALIGNMENT_START);
+  SetLayoutManager(std::move(layout));
+
+  auto textfield_label = std::make_unique<views::Label>(
+      l10n_util::GetStringUTF16(IDS_WEBAUTHN_BLE_PIN_ENTRY_PIN_LABEL),
+      views::style::CONTEXT_LABEL, views::style::STYLE_PRIMARY);
+  textfield_label->SetMultiLine(true);
+  textfield_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  textfield_label->SetEnabledColor(gfx::kGoogleBlue500);
+  auto* textfield_label_ptr = textfield_label.release();
+  AddChildView(textfield_label_ptr);
+
+  pin_text_field_ = new views::Textfield();
+  pin_text_field_->SetBackgroundColor(gfx::kGoogleGrey100);
+  pin_text_field_->SetMinimumWidthInChars(kExpectedPincodeCharLength);
+  pin_text_field_->SetDefaultWidthInChars(kPreferredTextfieldCharLength);
+  pin_text_field_->SetBorder(views::CreateSolidSidedBorder(
+      0, 0, kTextfieldBottomBorderThickness, 0, gfx::kGoogleBlue500));
+  pin_text_field_->set_controller(this);
+  pin_text_field_->SetAssociatedLabel(textfield_label_ptr);
+  AddChildView(pin_text_field_);
+}
+
+BlePinEntryView::~BlePinEntryView() = default;
+
+void BlePinEntryView::RequestFocus() {
+  pin_text_field_->RequestFocus();
+}
+
+void BlePinEntryView::ContentsChanged(views::Textfield* sender,
+                                      const base::string16& new_contents) {
+  DCHECK_EQ(pin_text_field_, sender);
+
+  int64_t received_pin;
+  if (new_contents.size() > kExpectedPincodeCharLength ||
+      (!new_contents.empty() &&
+       !base::StringToInt64(new_contents, &received_pin))) {
+    sender->SetInvalid(true);
+  } else {
+    sender->SetInvalid(false);
+    delegate_->OnPincodeChanged(new_contents);
+  }
+}
+
+bool BlePinEntryView::HandleKeyEvent(views::Textfield* sender,
+                                     const ui::KeyEvent& key_event) {
+  // As WebAuthN UI views do not intercept any key events, the key event must
+  // be further processed.
+  return false;
+}
diff --git a/chrome/browser/ui/views/webauthn/ble_pin_entry_view.h b/chrome/browser/ui/views/webauthn/ble_pin_entry_view.h
new file mode 100644
index 0000000..88bddcee
--- /dev/null
+++ b/chrome/browser/ui/views/webauthn/ble_pin_entry_view.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_WEBAUTHN_BLE_PIN_ENTRY_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_WEBAUTHN_BLE_PIN_ENTRY_VIEW_H_
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "ui/views/controls/textfield/textfield_controller.h"
+#include "ui/views/view.h"
+
+namespace views {
+class Textfield;
+}  // namespace views
+
+// View that shows a label and a textfield to which user will provide pincode to
+// pair with Bluetooth authenticator.
+class BlePinEntryView : public views::View, public views::TextfieldController {
+ public:
+  // Interface that the client should implement to learn BLE pincode user
+  // provided via the textfield input.
+  class Delegate {
+   public:
+    virtual void OnPincodeChanged(base::string16 pincode) = 0;
+  };
+
+  explicit BlePinEntryView(Delegate* delegate);
+  ~BlePinEntryView() override;
+
+ private:
+  // views::View:
+  void RequestFocus() override;
+
+  // views::TextFieldController:
+  void ContentsChanged(views::Textfield* sender,
+                       const base::string16& new_contents) override;
+  bool HandleKeyEvent(views::Textfield* sender,
+                      const ui::KeyEvent& key_event) override;
+
+  Delegate* const delegate_;
+  views::Textfield* pin_text_field_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlePinEntryView);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_WEBAUTHN_BLE_PIN_ENTRY_VIEW_H_
diff --git a/chrome/browser/ui/views/webauthn/sheet_view_factory.cc b/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
index 9ab5a90..607e257d 100644
--- a/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
+++ b/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/webauthn/sheet_view_factory.h"
 
 #include "base/logging.h"
+#include "chrome/browser/ui/views/webauthn/authenticator_ble_pin_entry_sheet_view.h"
 #include "chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h"
 #include "chrome/browser/ui/views/webauthn/authenticator_transport_selector_sheet_view.h"
 #include "chrome/browser/ui/webauthn/sheet_models.h"
@@ -97,7 +98,7 @@
               dialog_model));
       break;
     case Step::kBlePinEntry:
-      sheet_view = std::make_unique<AuthenticatorRequestSheetView>(
+      sheet_view = std::make_unique<AuthenticatorBlePinEntrySheetView>(
           std::make_unique<AuthenticatorBlePinEntrySheetModel>(dialog_model));
       break;
     case Step::kBleVerifying:
diff --git a/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc b/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
index 4bca480..17a9bd0 100644
--- a/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
+++ b/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
@@ -69,11 +69,12 @@
       model->SetCurrentStep(
           AuthenticatorRequestDialogModel::Step::kBleDeviceSelection);
     } else if (name == "ble_pin_entry") {
-      test_authenticator_ = std::make_unique<AuthenticatorReference>(
-          "authenticator" /* authenticator_id */,
-          base::string16() /* authenticator_display_name */,
-          AuthenticatorTransport::kInternal, false /* is_in_pairing_mode */);
-      model->SetSelectedAuthenticatorForTesting(test_authenticator_.get());
+      model->SetSelectedAuthenticatorForTesting(
+          std::make_unique<AuthenticatorReference>(
+              "test_authenticator_id" /* authenticator_id */,
+              base::string16() /* authenticator_display_name */,
+              AuthenticatorTransport::kInternal,
+              false /* is_in_pairing_mode */));
       model->SetCurrentStep(
           AuthenticatorRequestDialogModel::Step::kBlePinEntry);
     } else if (name == "ble_verifying") {
@@ -94,8 +95,6 @@
   }
 
  private:
-  std::unique_ptr<AuthenticatorReference> test_authenticator_;
-
   DISALLOW_COPY_AND_ASSIGN(AuthenticatorDialogTest);
 };
 
diff --git a/chrome/browser/ui/webauthn/sheet_models.cc b/chrome/browser/ui/webauthn/sheet_models.cc
index 39f1509..b465b5189 100644
--- a/chrome/browser/ui/webauthn/sheet_models.cc
+++ b/chrome/browser/ui/webauthn/sheet_models.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/ui/webauthn/sheet_models.h"
 
 #include <memory>
-#include <vector>
+#include <utility>
 
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
@@ -480,15 +480,19 @@
 
 // AuthenticatorBlePinEntrySheetModel -----------------------------------------
 
+void AuthenticatorBlePinEntrySheetModel::SetPinCode(base::string16 pin_code) {
+  pin_code_ = std::move(pin_code);
+}
+
 gfx::ImageSkia* AuthenticatorBlePinEntrySheetModel::GetStepIllustration()
     const {
   return GetImage(IDR_WEBAUTHN_ILLUSTRATION_BLE_PIN);
 }
 
 base::string16 AuthenticatorBlePinEntrySheetModel::GetStepTitle() const {
-  base::string16 device_display_name;
+  const auto& authenticator_id = dialog_model()->selected_authenticator_id();
   const auto* const ble_authenticator =
-      dialog_model()->selected_authenticator();
+      dialog_model()->GetAuthenticator(authenticator_id);
   DCHECK(ble_authenticator);
   return l10n_util::GetStringFUTF16(
       IDS_WEBAUTHN_BLE_PIN_ENTRY_TITLE,
@@ -512,6 +516,10 @@
   return l10n_util::GetStringUTF16(IDS_WEBAUTHN_BLE_PIN_ENTRY_NEXT);
 }
 
+void AuthenticatorBlePinEntrySheetModel::OnAccept() {
+  dialog_model()->FinishPairingWithPin(pin_code_);
+}
+
 // AuthenticatorBleVerifyingSheetModel ----------------------------------------
 
 bool AuthenticatorBleVerifyingSheetModel::IsActivityIndicatorVisible() const {
diff --git a/chrome/browser/ui/webauthn/sheet_models.h b/chrome/browser/ui/webauthn/sheet_models.h
index 6ffe9c1..6df38e4 100644
--- a/chrome/browser/ui/webauthn/sheet_models.h
+++ b/chrome/browser/ui/webauthn/sheet_models.h
@@ -5,7 +5,10 @@
 #ifndef CHROME_BROWSER_UI_WEBAUTHN_SHEET_MODELS_H_
 #define CHROME_BROWSER_UI_WEBAUTHN_SHEET_MODELS_H_
 
+#include <memory>
+
 #include "base/macros.h"
+#include "base/strings/string16.h"
 #include "chrome/browser/ui/webauthn/authenticator_request_sheet_model.h"
 #include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
 
@@ -261,6 +264,8 @@
  public:
   using AuthenticatorSheetModelBase::AuthenticatorSheetModelBase;
 
+  void SetPinCode(base::string16 pin_code);
+
  private:
   // AuthenticatorSheetModelBase:
   gfx::ImageSkia* GetStepIllustration() const override;
@@ -269,6 +274,9 @@
   bool IsAcceptButtonVisible() const override;
   bool IsAcceptButtonEnabled() const override;
   base::string16 GetAcceptButtonLabel() const override;
+  void OnAccept() override;
+
+  base::string16 pin_code_;
 };
 
 class AuthenticatorBleVerifyingSheetModel : public AuthenticatorSheetModelBase {
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chrome/browser/ui/webui/settings/md_settings_ui.cc
index cb9b7f53..cf4ed21 100644
--- a/chrome/browser/ui/webui/settings/md_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -46,10 +46,12 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/unified_consent/feature.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/common/content_features.h"
+#include "content/public/common/web_preferences.h"
 #include "printing/buildflags/buildflags.h"
 
 #if defined(OS_WIN)
@@ -366,6 +368,11 @@
                           base::FeatureList::IsEnabled(
                               password_manager::features::kPasswordImport));
 
+  html_source->AddBoolean("enableScrollAnimator", web_ui->GetWebContents()
+                                                      ->GetRenderViewHost()
+                                                      ->GetWebkitPreferences()
+                                                      .enable_scroll_animator);
+
   AddSettingsPageUIHandler(
       base::WrapUnique(AboutHandler::Create(html_source, profile)));
   AddSettingsPageUIHandler(
diff --git a/chrome/browser/web_applications/bookmark_apps/external_web_apps.cc b/chrome/browser/web_applications/bookmark_apps/external_web_apps.cc
index ecd3f7d..b0690c5 100644
--- a/chrome/browser/web_applications/bookmark_apps/external_web_apps.cc
+++ b/chrome/browser/web_applications/bookmark_apps/external_web_apps.cc
@@ -93,7 +93,6 @@
 
   for (base::FilePath file = json_files.Next(); !file.empty();
        file = json_files.Next()) {
-    LOG(ERROR) << "Processing " << file;
     if (!file.MatchesExtension(extension)) {
       continue;
     }
@@ -116,9 +115,9 @@
 
     std::string feature_name;
     if (dict_value->GetString(kFeatureName, &feature_name)) {
-      LOG(ERROR) << file.value() << " checking feature " << feature_name;
+      VLOG(1) << file.value() << " checking feature " << feature_name;
       if (!IsFeatureEnabled(feature_name)) {
-        LOG(ERROR) << file.value() << " feature not enabled";
+        VLOG(1) << file.value() << " feature not enabled";
         continue;
       }
     }
@@ -134,8 +133,6 @@
       continue;
     }
 
-    LOG(ERROR) << "Launch URL is " << app_url.spec();
-
     bool create_shortcuts = false;
     if (dict_value->HasKey(kCreateShortcuts) &&
         !dict_value->GetBoolean(kCreateShortcuts, &create_shortcuts)) {
@@ -158,9 +155,13 @@
       continue;
     }
 
-    app_infos.emplace_back(std::move(app_url), launch_container,
-                           web_app::InstallSource::kExternalDefault,
-                           create_shortcuts);
+    app_infos.emplace_back(
+        std::move(app_url), launch_container,
+        web_app::InstallSource::kExternalDefault, create_shortcuts,
+        web_app::PendingAppManager::AppInfo::
+            kDefaultOverridePreviousUserUninstall,
+        web_app::PendingAppManager::AppInfo::kDefaultBypassServiceWorkerCheck,
+        true /* require_manifest */);
   }
 
   return app_infos;
@@ -173,7 +174,6 @@
   // chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS is only defined for OS_LINUX,
   // which includes OS_CHROMEOS.
 
-  LOG(ERROR) << "Determining directory";
   if (chromeos::ProfileHelper::IsPrimaryProfile(profile)) {
     // For manual testing, you can change s/STANDALONE/USER/, as writing to
     // "$HOME/.config/chromium/test-user/.config/chromium/External
@@ -204,7 +204,6 @@
                             ScanForExternalWebAppsCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   base::FilePath dir = DetermineScanDir(profile);
-  LOG(ERROR) << "Scanning " << dir;
   if (dir.empty()) {
     std::move(callback).Run(std::vector<web_app::PendingAppManager::AppInfo>());
     return;
diff --git a/chrome/browser/web_applications/bookmark_apps/external_web_apps_unittest.cc b/chrome/browser/web_applications/bookmark_apps/external_web_apps_unittest.cc
index ac8bf43a..f944562 100644
--- a/chrome/browser/web_applications/bookmark_apps/external_web_apps_unittest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/external_web_apps_unittest.cc
@@ -52,12 +52,20 @@
           GURL("https://www.chromestatus.com/features"),
           web_app::LaunchContainer::kTab,
           web_app::InstallSource::kExternalDefault,
-          /* create_shortcuts */ true),
+          /* create_shortcuts */ true,
+          web_app::PendingAppManager::AppInfo::
+              kDefaultOverridePreviousUserUninstall,
+          web_app::PendingAppManager::AppInfo::kDefaultBypassServiceWorkerCheck,
+          /* require_manifest */ true),
       web_app::PendingAppManager::AppInfo(
           GURL("https://events.google.com/io2016/?utm_source=web_app_manifest"),
           web_app::LaunchContainer::kWindow,
           web_app::InstallSource::kExternalDefault,
-          /* create_shortcuts */ false),
+          /* create_shortcuts */ false,
+          web_app::PendingAppManager::AppInfo::
+              kDefaultOverridePreviousUserUninstall,
+          web_app::PendingAppManager::AppInfo::kDefaultBypassServiceWorkerCheck,
+          /* require_manifest */ true),
   };
   for (const auto& app_info : test_app_infos) {
     EXPECT_TRUE(base::ContainsValue(app_infos, app_info));
diff --git a/chrome/browser/web_applications/components/pending_app_manager.cc b/chrome/browser/web_applications/components/pending_app_manager.cc
index debac0f..b4ef37a 100644
--- a/chrome/browser/web_applications/components/pending_app_manager.cc
+++ b/chrome/browser/web_applications/components/pending_app_manager.cc
@@ -17,19 +17,22 @@
 const bool PendingAppManager::AppInfo::kDefaultOverridePreviousUserUninstall =
     false;
 const bool PendingAppManager::AppInfo::kDefaultBypassServiceWorkerCheck = false;
+const bool PendingAppManager::AppInfo::kDefaultRequireManifest = false;
 
 PendingAppManager::AppInfo::AppInfo(GURL url,
                                     LaunchContainer launch_container,
                                     InstallSource install_source,
                                     bool create_shortcuts,
                                     bool override_previous_user_uninstall,
-                                    bool bypass_service_worker_check)
+                                    bool bypass_service_worker_check,
+                                    bool require_manifest)
     : url(std::move(url)),
       launch_container(launch_container),
       install_source(install_source),
       create_shortcuts(create_shortcuts),
       override_previous_user_uninstall(override_previous_user_uninstall),
-      bypass_service_worker_check(bypass_service_worker_check) {}
+      bypass_service_worker_check(bypass_service_worker_check),
+      require_manifest(require_manifest) {}
 
 PendingAppManager::AppInfo::AppInfo(PendingAppManager::AppInfo&& other) =
     default;
@@ -48,12 +51,12 @@
 bool PendingAppManager::AppInfo::operator==(
     const PendingAppManager::AppInfo& other) const {
   return std::tie(url, launch_container, install_source, create_shortcuts,
-                  override_previous_user_uninstall,
-                  bypass_service_worker_check) ==
+                  override_previous_user_uninstall, bypass_service_worker_check,
+                  require_manifest) ==
          std::tie(other.url, other.launch_container, other.install_source,
                   other.create_shortcuts,
                   other.override_previous_user_uninstall,
-                  other.bypass_service_worker_check);
+                  other.bypass_service_worker_check, other.require_manifest);
 }
 
 PendingAppManager::PendingAppManager() = default;
@@ -93,7 +96,8 @@
              << "\n override_previous_user_uninstall: "
              << app_info.override_previous_user_uninstall
              << "\n bypass_service_worker_check: "
-             << app_info.bypass_service_worker_check;
+             << app_info.bypass_service_worker_check
+             << "\n require_manifest: " << app_info.require_manifest;
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/components/pending_app_manager.h b/chrome/browser/web_applications/components/pending_app_manager.h
index cc666a3..885e221 100644
--- a/chrome/browser/web_applications/components/pending_app_manager.h
+++ b/chrome/browser/web_applications/components/pending_app_manager.h
@@ -40,15 +40,16 @@
     static const bool kDefaultCreateShortcuts;
     static const bool kDefaultOverridePreviousUserUninstall;
     static const bool kDefaultBypassServiceWorkerCheck;
+    static const bool kDefaultRequireManifest;
 
-    AppInfo(
-        GURL url,
-        LaunchContainer launch_container,
-        InstallSource install_source,
-        bool create_shortcuts = kDefaultCreateShortcuts,
-        bool override_previous_user_uninstall =
-            kDefaultOverridePreviousUserUninstall,
-        bool bypass_service_worker_check = kDefaultBypassServiceWorkerCheck);
+    AppInfo(GURL url,
+            LaunchContainer launch_container,
+            InstallSource install_source,
+            bool create_shortcuts = kDefaultCreateShortcuts,
+            bool override_previous_user_uninstall =
+                kDefaultOverridePreviousUserUninstall,
+            bool bypass_service_worker_check = kDefaultBypassServiceWorkerCheck,
+            bool require_manifest = kDefaultRequireManifest);
     AppInfo(AppInfo&& other);
     ~AppInfo();
 
@@ -67,6 +68,10 @@
     // programmatically.
     const bool bypass_service_worker_check;
 
+    // This should be used for installing all default apps so that good metadata
+    // is ensured.
+    const bool require_manifest;
+
    private:
     DISALLOW_COPY_AND_ASSIGN(AppInfo);
   };
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_installation_task.cc b/chrome/browser/web_applications/extensions/bookmark_app_installation_task.cc
index 8f54899..78ae46e7 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_installation_task.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_installation_task.cc
@@ -140,6 +140,9 @@
   if (app_info_.bypass_service_worker_check)
     helper_->set_bypass_service_worker_check();
 
+  if (app_info_.require_manifest)
+    helper_->set_require_manifest();
+
   helper_->Create(base::Bind(&BookmarkAppInstallationTask::OnInstalled,
                              weak_ptr_factory_.GetWeakPtr(),
                              base::Passed(&result_callback)));
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_browsertest.cc b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_browsertest.cc
index 2c5d8a7a..3bb0a191 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_browsertest.cc
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_browsertest.cc
@@ -28,7 +28,9 @@
 
 class PendingBookmarkAppManagerBrowserTest : public InProcessBrowserTest {
  protected:
-  void InstallApp(const GURL& url, bool bypass_service_worker_check = false) {
+  void InstallApp(const GURL& url,
+                  bool bypass_service_worker_check = false,
+                  bool require_manifest = false) {
     base::RunLoop run_loop;
     web_app::WebAppProvider::Get(browser()->profile())
         ->pending_app_manager()
@@ -39,7 +41,7 @@
                                                     // shortcuts in tests.
                      web_app::PendingAppManager::AppInfo::
                          kDefaultOverridePreviousUserUninstall,
-                     bypass_service_worker_check),
+                     bypass_service_worker_check, require_manifest),
                  base::BindLambdaForTesting(
                      [this, &run_loop](const GURL& provided_url,
                                        web_app::InstallResultCode code) {
@@ -144,4 +146,21 @@
       app, GURL("chrome://settings")));
 }
 
+// Test that adding a web app without a manifest while using the
+// |require_manifest| flag fails.
+IN_PROC_BROWSER_TEST_F(PendingBookmarkAppManagerBrowserTest,
+                       RequireManifestFailsIfNoManifest) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url(
+      embedded_test_server()->GetURL("/banners/no_manifest_test_page.html"));
+  InstallApp(url, false /* bypass_service_worker_check */,
+             true /* require_manifest */);
+  EXPECT_EQ(web_app::InstallResultCode::kFailedUnknownReason,
+            result_code_.value());
+  base::Optional<std::string> id =
+      web_app::ExtensionIdsMap(browser()->profile()->GetPrefs())
+          .LookupExtensionId(url);
+  ASSERT_FALSE(id.has_value());
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc
index dd2e1a20..1a00b92 100644
--- a/chrome/browser/web_applications/web_app.cc
+++ b/chrome/browser/web_applications/web_app.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <ostream>
+
 #include "chrome/browser/web_applications/web_app.h"
 
 namespace web_app {
@@ -22,4 +24,11 @@
   launch_url_ = launch_url;
 }
 
+std::ostream& operator<<(std::ostream& out, const WebApp& app) {
+  return out << "app_id: " << app.app_id() << std::endl
+             << "  name: " << app.name() << std::endl
+             << "  launch_url: " << app.launch_url() << std::endl
+             << "  description: " << app.description();
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app.h b/chrome/browser/web_applications/web_app.h
index cc592a2b..4431df5 100644
--- a/chrome/browser/web_applications/web_app.h
+++ b/chrome/browser/web_applications/web_app.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_H_
 #define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_H_
 
+#include <iosfwd>
 #include <string>
 
 #include "base/macros.h"
@@ -37,6 +38,9 @@
   DISALLOW_COPY_AND_ASSIGN(WebApp);
 };
 
+// For logging and debug purposes.
+std::ostream& operator<<(std::ostream& out, const WebApp& app);
+
 }  // namespace web_app
 
 #endif  // CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_H_
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.cc b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
index cbd42def..2438bc11 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 
 namespace {
@@ -68,7 +69,8 @@
 
 }  // namespace
 
-AuthenticatorRequestDialogModel::AuthenticatorRequestDialogModel() {}
+AuthenticatorRequestDialogModel::AuthenticatorRequestDialogModel()
+    : weak_factory_(this) {}
 
 AuthenticatorRequestDialogModel::~AuthenticatorRequestDialogModel() {
   for (auto& observer : observers_)
@@ -183,13 +185,49 @@
 }
 
 void AuthenticatorRequestDialogModel::InitiatePairingDevice(
-    const std::string& device_address) {
+    const std::string& authenticator_id) {
   DCHECK_EQ(current_step(), Step::kBleDeviceSelection);
+  CHECK(GetAuthenticator(authenticator_id));
+  selected_authenticator_id_ = authenticator_id;
+  SetCurrentStep(Step::kBlePinEntry);
 }
 
 void AuthenticatorRequestDialogModel::FinishPairingWithPin(
     const base::string16& pin) {
   DCHECK_EQ(current_step(), Step::kBlePinEntry);
+  const auto* selected_authenticator =
+      GetAuthenticator(selected_authenticator_id_);
+  if (!selected_authenticator) {
+    // TODO(hongjunchoi): Implement an error screen for error encountered when
+    // pairing.
+    SetCurrentStep(Step::kBleDeviceSelection);
+    return;
+  }
+
+  DCHECK_EQ(device::FidoTransportProtocol::kBluetoothLowEnergy,
+            selected_authenticator->transport());
+  ble_pairing_callback_.Run(
+      selected_authenticator_id_, base::UTF16ToUTF8(pin),
+      base::BindOnce(&AuthenticatorRequestDialogModel::OnPairingSuccess,
+                     weak_factory_.GetWeakPtr(), selected_authenticator_id_),
+      base::BindOnce(&AuthenticatorRequestDialogModel::OnPairingFailure,
+                     weak_factory_.GetWeakPtr()));
+  SetCurrentStep(Step::kBleVerifying);
+}
+
+void AuthenticatorRequestDialogModel::OnPairingSuccess(
+    base::StringPiece authenticator_id) {
+  DCHECK_EQ(current_step(), Step::kBleVerifying);
+  auto* authenticator = GetAuthenticator(authenticator_id);
+  if (authenticator)
+    return;
+
+  DispatchRequestAsync(authenticator, base::TimeDelta());
+}
+
+void AuthenticatorRequestDialogModel::OnPairingFailure() {
+  DCHECK_EQ(current_step(), Step::kBleVerifying);
+  SetCurrentStep(Step::kBleDeviceSelection);
 }
 
 void AuthenticatorRequestDialogModel::TryUsbDevice() {
@@ -289,6 +327,11 @@
   request_callback_ = request_callback;
 }
 
+void AuthenticatorRequestDialogModel::SetBlePairingCallback(
+    BlePairingCallback ble_pairing_callback) {
+  ble_pairing_callback_ = ble_pairing_callback;
+}
+
 void AuthenticatorRequestDialogModel::SetBluetoothAdapterPowerOnCallback(
     base::RepeatingClosure bluetooth_adapter_power_on_callback) {
   bluetooth_adapter_power_on_callback_ = bluetooth_adapter_power_on_callback;
@@ -352,6 +395,21 @@
 }
 
 void AuthenticatorRequestDialogModel::SetSelectedAuthenticatorForTesting(
-    AuthenticatorReference* authenticator) {
-  selected_authenticator_ = authenticator;
+    std::unique_ptr<AuthenticatorReference> test_authenticator) {
+  selected_authenticator_id_ = test_authenticator->authenticator_id();
+  saved_authenticators_.emplace_back(std::move(test_authenticator));
+}
+
+AuthenticatorReference* AuthenticatorRequestDialogModel::GetAuthenticator(
+    base::StringPiece authenticator_id) const {
+  auto authenticator = std::find_if(
+      saved_authenticators_.begin(), saved_authenticators_.end(),
+      [authenticator_id](const auto& authenticator) {
+        return authenticator->authenticator_id() == authenticator_id;
+      });
+
+  if (authenticator == saved_authenticators_.end())
+    return nullptr;
+
+  return authenticator->get();
 }
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.h b/chrome/browser/webauthn/authenticator_request_dialog_model.h
index e6e3bf3..46e4b60 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.h
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/optional.h"
 #include "base/strings/string16.h"
@@ -28,6 +29,7 @@
 class AuthenticatorRequestDialogModel {
  public:
   using RequestCallback = device::FidoRequestHandlerBase::RequestCallback;
+  using BlePairingCallback = device::FidoRequestHandlerBase::BlePairingCallback;
   using TransportAvailabilityInfo =
       device::FidoRequestHandlerBase::TransportAvailabilityInfo;
 
@@ -125,9 +127,9 @@
     return transport_availability()->is_ble_powered;
   }
 
-  const AuthenticatorReference* selected_authenticator() const {
+  const std::string& selected_authenticator_id() const {
     DCHECK_EQ(Step::kBlePinEntry, current_step());
-    return selected_authenticator_;
+    return selected_authenticator_id_;
   }
 
   // Starts the UX flow, by either showing the welcome screen, the transport
@@ -184,7 +186,7 @@
   // Initiates pairing of the device that the user has chosen.
   //
   // Valid action when at step: kBleDeviceSelection.
-  void InitiatePairingDevice(const std::string& device_address);
+  void InitiatePairingDevice(const std::string& authenticator_id);
 
   // Finishes pairing of the previously chosen device with the |pin| code
   // entered.
@@ -192,6 +194,16 @@
   // Valid action when at step: kBlePinEntry.
   void FinishPairingWithPin(const base::string16& pin);
 
+  // Dispatches WebAuthN request to successfully paired Bluetooth authenticator.
+  //
+  // Valid action when at step: kBleVerifying.
+  void OnPairingSuccess(base::StringPiece authenticator_id);
+
+  // Returns to Bluetooth device selection modal.
+  //
+  // Valid action when at step: kBleVerifying.
+  void OnPairingFailure();
+
   // Tries if a USB device is present -- the user claims they plugged it in.
   //
   // Valid action when at step: kUsbInsert.
@@ -241,6 +253,8 @@
 
   void SetRequestCallback(RequestCallback request_callback);
 
+  void SetBlePairingCallback(BlePairingCallback ble_pairing_callback);
+
   void SetBluetoothAdapterPowerOnCallback(
       base::RepeatingClosure bluetooth_adapter_power_on_callback);
 
@@ -253,7 +267,10 @@
       base::StringPiece authenticator_id);
 
   void SetSelectedAuthenticatorForTesting(
-      AuthenticatorReference* authenticator);
+      std::unique_ptr<AuthenticatorReference> authenticator);
+
+  AuthenticatorReference* GetAuthenticator(
+      base::StringPiece authenticator_id) const;
 
   std::vector<std::unique_ptr<AuthenticatorReference>>& saved_authenticators() {
     return saved_authenticators_;
@@ -287,12 +304,15 @@
   // dispatched lazily after the user interacts with the UI element.
   std::vector<std::unique_ptr<AuthenticatorReference>> saved_authenticators_;
 
-  // Represents the Bluetooth authenticator that the user is trying to connect
-  // to or conduct WebAuthN request to via the WebAuthN UI.
-  AuthenticatorReference* selected_authenticator_ = nullptr;
+  // Represents the id of the Bluetooth authenticator that the user is trying to
+  // connect to or conduct WebAuthN request to via the WebAuthN UI.
+  std::string selected_authenticator_id_;
   RequestCallback request_callback_;
+  BlePairingCallback ble_pairing_callback_;
   base::RepeatingClosure bluetooth_adapter_power_on_callback_;
 
+  base::WeakPtrFactory<AuthenticatorRequestDialogModel> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(AuthenticatorRequestDialogModel);
 };
 
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index 82151065..71d9365 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -153,6 +153,7 @@
   transient_dialog_model_holder_->SetRequestCallback(request_callback);
   transient_dialog_model_holder_->SetBluetoothAdapterPowerOnCallback(
       bluetooth_adapter_power_on_callback);
+  transient_dialog_model_holder_->SetBlePairingCallback(ble_pairing_callback);
 
   weak_dialog_model_ = transient_dialog_model_holder_.get();
   weak_dialog_model_->AddObserver(this);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 99440db..f99687d 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -261,6 +261,12 @@
       "base/browser_process_platform_part_test_api_chromeos.h",
       "base/default_ash_event_generator_delegate.cc",
       "base/default_ash_event_generator_delegate.h",
+
+      # Only used for Chromevox tests, so only included on Chrome OS.
+      "base/extension_js_browser_test.cc",
+      "base/extension_js_browser_test.h",
+      "base/extension_load_waiter_one_shot.cc",
+      "base/extension_load_waiter_one_shot.h",
     ]
     public_deps += [
       "//ash",
@@ -1850,6 +1856,7 @@
         "//ash:test_support_with_content",
         "//ash/public/interfaces:test_interfaces",
         "//chrome/browser/chromeos:arc_test_support",
+        "//chrome/browser/resources/chromeos/chromevox:browser_tests",
         "//chrome/services/file_util/public/cpp:browser_tests",
         "//chromeos/components/drivefs:test_support",
         "//components/arc:arc_test_support",
diff --git a/chromecast/media/cma/backend/video/BUILD.gn b/chromecast/media/cma/backend/video/BUILD.gn
index 762bcb8..2a04a24e 100644
--- a/chromecast/media/cma/backend/video/BUILD.gn
+++ b/chromecast/media/cma/backend/video/BUILD.gn
@@ -37,8 +37,6 @@
     "cast_media_shlib_common.cc",
   ]
   deps = [
-    "//chromecast/media/cma/backend:for_mixer_audio",
-    "//chromecast/public",
     "//chromecast/public/media",
   ]
 }
@@ -72,4 +70,8 @@
     deps += [ "//chromecast/media/cma/backend/alsa:cma_backend_support" ]
     libs = [ "videodecoderformixer" ]
   }
+
+  if (is_fuchsia) {
+    deps += [ "//chromecast/media/cma/backend:null_video" ]
+  }
 }
diff --git a/chromecast/media/cma/backend/video/cast_media_shlib_common.cc b/chromecast/media/cma/backend/video/cast_media_shlib_common.cc
index 000cdcc..392e7f0 100644
--- a/chromecast/media/cma/backend/video/cast_media_shlib_common.cc
+++ b/chromecast/media/cma/backend/video/cast_media_shlib_common.cc
@@ -2,9 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromecast/public/cast_media_shlib.h"
-
-#include "chromecast/media/cma/backend/stream_mixer.h"
 #include "chromecast/public/media/media_capabilities_shlib.h"
 
 namespace chromecast {
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index 1b2f4fc..a4c8206 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -544,10 +544,6 @@
 // If true, files in Android internal storage will be hidden in Files app.
 const char kHideAndroidFilesInFilesApp[] = "hide-android-files-in-files-app";
 
-// If true, Files app navigation is NOT displayed with "My Files". Items are NOT
-// re-ordered and "Downloads" is NOT displayed inside section "My Files".
-const char kFilesAppDisableMyFilesNavigation[] = "disable-my-files-navigation";
-
 // If true, the developer tool overlay will be shown for the login/lock screen.
 // This makes it easier to test layout logic.
 const char kShowLoginDevOverlay[] = "show-login-dev-overlay";
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index f809825..88ae82e 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -152,7 +152,6 @@
 CHROMEOS_EXPORT extern const char kRlzPingDelay[];
 CHROMEOS_EXPORT extern const char kShelfHoverPreviews[];
 CHROMEOS_EXPORT extern const char kShowAndroidFilesInFilesApp[];
-CHROMEOS_EXPORT extern const char kFilesAppDisableMyFilesNavigation[];
 CHROMEOS_EXPORT extern const char kShowLoginDevOverlay[];
 CHROMEOS_EXPORT extern const char kShowPlayInDemoMode[];
 CHROMEOS_EXPORT extern const char kStubCrosSettings[];
diff --git a/components/arc/common/intent_helper.mojom b/components/arc/common/intent_helper.mojom
index a63c3ca..65f4139 100644
--- a/components/arc/common/intent_helper.mojom
+++ b/components/arc/common/intent_helper.mojom
@@ -6,6 +6,7 @@
 
 module arc.mojom;
 
+import "components/arc/common/bitmap.mojom";
 import "components/arc/common/intent_common.mojom";
 import "components/arc/common/scale_factor.mojom";
 
@@ -154,6 +155,12 @@
 
   // The intent to launch when the action is clicked.
   IntentInfo action_intent;
+
+  // Whether the action was generated by the Android TextClassifier.
+  [MinVersion=23] bool text_classifier_action;
+
+  // The bitmap icon of the component that can handle the action intent.
+  [MinVersion=23] ArcBitmap? bitmap_icon;
 };
 
 // Handles intents from ARC in Chrome.
diff --git a/components/autofill/core/browser/payments/payments_client.cc b/components/autofill/core/browser/payments/payments_client.cc
index 5e0cd216d..97d5c12 100644
--- a/components/autofill/core/browser/payments/payments_client.cc
+++ b/components/autofill/core/browser/payments/payments_client.cc
@@ -905,9 +905,8 @@
 
 void PaymentsClient::SetOAuth2TokenAndStartRequest() {
   DCHECK(resource_request_);
-  resource_request_->headers.AddHeaderFromString(
-      net::HttpRequestHeaders::kAuthorization + std::string(": Bearer ") +
-      access_token_);
+  resource_request_->headers.SetHeader(net::HttpRequestHeaders::kAuthorization,
+                                       std::string("Bearer ") + access_token_);
   StartRequest();
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
index 73e8b8ec..39fb431e 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
@@ -271,6 +271,11 @@
   return config_values_->FindConfiguredDataReductionProxy(proxy_server);
 }
 
+net::ProxyList DataReductionProxyConfig::GetAllConfiguredProxies() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return config_values_->GetAllConfiguredProxies();
+}
+
 bool DataReductionProxyConfig::IsBypassedByDataReductionProxyLocalRules(
     const net::URLRequest& request,
     const net::ProxyConfig& data_reduction_proxy_config) const {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
index 03ceffa..fd00946 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
@@ -115,6 +115,10 @@
   base::Optional<DataReductionProxyTypeInfo> FindConfiguredDataReductionProxy(
       const net::ProxyServer& proxy_server) const;
 
+  // Gets a list of all the configured proxies. These are the same proxies that
+  // will be used if FindConfiguredDataReductionProxy() is called.
+  net::ProxyList GetAllConfiguredProxies() const;
+
   // Returns true if this request would be bypassed by the Data Reduction Proxy
   // based on applying the |data_reduction_proxy_config| param rules to the
   // request URL.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
index 0a90b1f1..0349b0ad1 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
@@ -361,7 +361,15 @@
       FROM_HERE,
       base::BindOnce(&DataReductionProxyService::SetProxyRequestHeadersOnUI,
                      service_, std::move(headers)));
-  OnProxyConfigUpdated();
+  UpdateCustomProxyConfig();
+}
+
+void DataReductionProxyIOData::OnProxyConfigUpdated() {
+  ui_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&DataReductionProxyService::SetConfiguredProxiesOnUI,
+                     service_, config_->GetAllConfiguredProxies()));
+  UpdateCustomProxyConfig();
 }
 
 network::mojom::CustomProxyConfigPtr
@@ -397,7 +405,7 @@
   return config;
 }
 
-void DataReductionProxyIOData::OnProxyConfigUpdated() {
+void DataReductionProxyIOData::UpdateCustomProxyConfig() {
   if (!proxy_config_client_)
     return;
 
@@ -409,7 +417,7 @@
     net::EffectiveConnectionType type) {
   DCHECK(io_task_runner_->BelongsToCurrentThread());
   effective_connection_type_ = type;
-  OnProxyConfigUpdated();
+  UpdateCustomProxyConfig();
 }
 
 void DataReductionProxyIOData::OnRTTOrThroughputEstimatesComputed(
@@ -427,7 +435,7 @@
 void DataReductionProxyIOData::SetCustomProxyConfigClient(
     network::mojom::CustomProxyConfigClientPtrInfo config_client_info) {
   proxy_config_client_.Bind(std::move(config_client_info));
-  OnProxyConfigUpdated();
+  UpdateCustomProxyConfig();
 }
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
index 2f2b22d..142b2ead 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
@@ -135,8 +135,6 @@
   // Forwards proxy authentication headers to the UI thread.
   void UpdateProxyRequestHeaders(const net::HttpRequestHeaders& headers);
 
-  void OnProxyConfigUpdated();
-
   // Notifies |this| that there there is a change in the effective connection
   // type.
   void OnEffectiveConnectionTypeChanged(net::EffectiveConnectionType type);
@@ -246,6 +244,13 @@
   network::mojom::CustomProxyConfigPtr CreateCustomProxyConfig(
       const std::vector<DataReductionProxyServer>& proxies_for_http) const;
 
+  // Called when the list of proxies changes.
+  void OnProxyConfigUpdated();
+
+  // Should be called whenever there is a possible change to the custom proxy
+  // config.
+  void UpdateCustomProxyConfig();
+
   // The type of Data Reduction Proxy client.
   const Client client_;
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc
index 43b35cb..63c049c 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.cc
@@ -56,6 +56,20 @@
   return base::nullopt;
 }
 
+net::ProxyList DataReductionProxyMutableConfigValues::GetAllConfiguredProxies()
+    const {
+  net::ProxyList proxies;
+  for (const auto& proxy : proxies_for_http())
+    proxies.AddProxyServer(proxy.proxy_server());
+
+  for (const auto& recent_proxies : recently_configured_proxy_lists_) {
+    for (const auto& proxy : recent_proxies)
+      proxies.AddProxyServer(proxy.proxy_server());
+  }
+
+  return proxies;
+}
+
 void DataReductionProxyMutableConfigValues::UpdateValues(
     const std::vector<DataReductionProxyServer>& new_proxies_for_http) {
   DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h
index ec52878f..b24ff9de 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values.h
@@ -41,6 +41,7 @@
       const override;
   base::Optional<DataReductionProxyTypeInfo> FindConfiguredDataReductionProxy(
       const net::ProxyServer& proxy_server) const override;
+  net::ProxyList GetAllConfiguredProxies() const override;
 
  private:
   std::vector<DataReductionProxyServer> proxies_for_http_;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values_unittest.cc
index 61b6ef8..3f57498 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_mutable_config_values_unittest.cc
@@ -248,6 +248,34 @@
   }
 }
 
+TEST_F(DataReductionProxyMutableConfigValuesTest, GetAllConfiguredProxies) {
+  Init();
+  net::ProxyList expected_proxies;
+  EXPECT_TRUE(mutable_config_values()->GetAllConfiguredProxies().Equals(
+      expected_proxies));
+
+  net::ProxyServer proxy_server1 =
+      net::ProxyServer::FromPacString("PROXY proxy1.net");
+  mutable_config_values()->UpdateValues(
+      {DataReductionProxyServer(proxy_server1, ProxyServer::CORE)});
+  expected_proxies.SetSingleProxyServer(proxy_server1);
+
+  EXPECT_TRUE(mutable_config_values()->GetAllConfiguredProxies().Equals(
+      expected_proxies));
+
+  net::ProxyServer proxy_server2 =
+      net::ProxyServer::FromPacString("PROXY proxy2.net");
+  mutable_config_values()->UpdateValues(
+      {DataReductionProxyServer(proxy_server2, ProxyServer::CORE)});
+
+  // First proxy server should also still be in proxy list.
+  expected_proxies.Clear();
+  expected_proxies.AddProxyServer(proxy_server2);
+  expected_proxies.AddProxyServer(proxy_server1);
+  EXPECT_TRUE(mutable_config_values()->GetAllConfiguredProxies().Equals(
+      expected_proxies));
+}
+
 }  // namespace
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
index 60528a8c..bfc609cb 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
@@ -328,12 +328,12 @@
     mock_socket_factory_.reset(new net::MockClientSocketFactory());
 
     DataReductionProxyTestContext::Builder builder;
-    builder = builder.WithClient(kClient)
-                  .WithMockClientSocketFactory(mock_socket_factory_.get())
-                  .WithURLRequestContext(context_.get());
+    builder.WithClient(kClient)
+        .WithMockClientSocketFactory(mock_socket_factory_.get())
+        .WithURLRequestContext(context_.get());
 
     if (proxy_config != BYPASS_PROXY) {
-      builder = builder.WithProxiesForHttp({DataReductionProxyServer(
+      builder.WithProxiesForHttp({DataReductionProxyServer(
           proxy_server, ProxyServer::UNSPECIFIED_TYPE)});
     }
 
@@ -1804,7 +1804,7 @@
   Init(USE_SECURE_PROXY, true /* enable_brotli_globally */);
 
   net::HttpRequestHeaders request_headers;
-  request_headers.AddHeaderFromString("Accept-Encoding: gzip, deflate, br");
+  request_headers.SetHeader("Accept-Encoding", "gzip, deflate, br");
 
   std::string response_headers =
       "HTTP/1.1 200 OK\r\n"
@@ -1828,7 +1828,7 @@
   Init(USE_SECURE_PROXY, true /* enable_brotli_globally */);
 
   net::HttpRequestHeaders request_headers;
-  request_headers.AddHeaderFromString("Accept-Encoding:");
+  request_headers.SetHeader("Accept-Encoding", "");
 
   std::string response_headers =
       "HTTP/1.1 200 OK\r\n"
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
index 0d85b640..9f8f8eb 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
@@ -40,6 +40,7 @@
     std::unique_ptr<DataStore> store,
     std::unique_ptr<DataReductionProxyPingbackClient> pingback_client,
     network::NetworkQualityTracker* network_quality_tracker,
+    network::NetworkConnectionTracker* network_connection_tracker,
     data_use_measurement::DataUseMeasurement* data_use_measurement,
     const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
     const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
@@ -55,11 +56,13 @@
       db_task_runner_(db_task_runner),
       initialized_(false),
       network_quality_tracker_(network_quality_tracker),
+      network_connection_tracker_(network_connection_tracker),
       data_use_measurement_(data_use_measurement),
       effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
       weak_factory_(this) {
   DCHECK(settings);
   DCHECK(network_quality_tracker_);
+  DCHECK(network_connection_tracker_);
   db_task_runner_->PostTask(FROM_HERE,
                             base::BindOnce(&DBDataOwner::InitializeOnDBThread,
                                            db_data_owner_->GetWeakPtr()));
@@ -71,12 +74,20 @@
   network_quality_tracker_->AddRTTAndThroughputEstimatesObserver(this);
   if (base::FeatureList::IsEnabled(network::features::kNetworkService))
     data_use_measurement_->AddServicesDataUseObserver(this);
+
+  // TODO(rajendrant): Combine uses of NetworkConnectionTracker within DRP.
+  network_connection_tracker_->AddNetworkConnectionObserver(this);
+  network_connection_tracker_->GetConnectionType(
+      &connection_type_,
+      base::BindOnce(&DataReductionProxyService::OnConnectionChanged,
+                     GetWeakPtr()));
 }
 
 DataReductionProxyService::~DataReductionProxyService() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   network_quality_tracker_->RemoveEffectiveConnectionTypeObserver(this);
   network_quality_tracker_->RemoveRTTAndThroughputEstimatesObserver(this);
+  network_connection_tracker_->RemoveNetworkConnectionObserver(this);
   compression_stats_.reset();
   db_task_runner_->DeleteSoon(FROM_HERE, db_data_owner_.release());
   if (base::FeatureList::IsEnabled(network::features::kNetworkService))
@@ -138,6 +149,12 @@
           io_data_, config_value));
 }
 
+void DataReductionProxyService::OnConnectionChanged(
+    network::mojom::ConnectionType type) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  connection_type_ = type;
+}
+
 void DataReductionProxyService::OnEffectiveConnectionTypeChanged(
     net::EffectiveConnectionType type) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -246,6 +263,12 @@
   return effective_connection_type_;
 }
 
+network::mojom::ConnectionType DataReductionProxyService::GetConnectionType()
+    const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return connection_type_;
+}
+
 base::Optional<base::TimeDelta> DataReductionProxyService::GetHttpRttEstimate()
     const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -258,6 +281,12 @@
   settings_->SetProxyRequestHeaders(headers);
 }
 
+void DataReductionProxyService::SetConfiguredProxiesOnUI(
+    const net::ProxyList& proxies) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  settings_->SetConfiguredProxies(proxies);
+}
+
 void DataReductionProxyService::SetIgnoreLongTermBlackListRules(
     bool ignore_long_term_black_list_rules) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
index c379811..138008e 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
@@ -22,6 +22,7 @@
 #include "components/data_use_measurement/core/data_use_measurement.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
 #include "net/nqe/effective_connection_type.h"
+#include "services/network/public/cpp/network_connection_tracker.h"
 #include "services/network/public/cpp/network_quality_tracker.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
@@ -51,7 +52,8 @@
 class DataReductionProxyService
     : public data_use_measurement::DataUseMeasurement::ServicesDataUseObserver,
       public network::NetworkQualityTracker::EffectiveConnectionTypeObserver,
-      public network::NetworkQualityTracker::RTTAndThroughputEstimatesObserver {
+      public network::NetworkQualityTracker::RTTAndThroughputEstimatesObserver,
+      public network::NetworkConnectionTracker::NetworkConnectionObserver {
  public:
   // The caller must ensure that |settings|, |prefs|, |request_context|, and
   // |io_task_runner| remain alive for the lifetime of the
@@ -67,6 +69,7 @@
       std::unique_ptr<DataStore> store,
       std::unique_ptr<DataReductionProxyPingbackClient> pingback_client,
       network::NetworkQualityTracker* network_quality_tracker,
+      network::NetworkConnectionTracker* network_connection_tracker,
       data_use_measurement::DataUseMeasurement* data_use_measurement,
       const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
       const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
@@ -142,9 +145,14 @@
   net::EffectiveConnectionType GetEffectiveConnectionType() const;
   base::Optional<base::TimeDelta> GetHttpRttEstimate() const;
 
+  network::mojom::ConnectionType GetConnectionType() const;
+
   // Sends the given |headers| to |DataReductionProxySettings|.
   void SetProxyRequestHeadersOnUI(const net::HttpRequestHeaders& headers);
 
+  // Sends the given |proxies| to |DataReductionProxySettings|.
+  void SetConfiguredProxiesOnUI(const net::ProxyList& proxies);
+
   // Sets a config client that can be used to update Data Reduction Proxy
   // settings when the network service is enabled.
   void SetCustomProxyConfigClient(
@@ -187,6 +195,9 @@
 
   void OnServicesDataUse(int64_t recv_bytes, int64_t sent_bytes) override;
 
+  // NetworkConnectionTracker::NetworkConnectionObserver
+  void OnConnectionChanged(network::mojom::ConnectionType type) override;
+
   net::URLRequestContextGetter* url_request_context_getter_;
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 
@@ -220,6 +231,7 @@
   // Must be accessed on UI thread. Guaranteed to be non-null during the
   // lifetime of |this|.
   network::NetworkQualityTracker* network_quality_tracker_;
+  network::NetworkConnectionTracker* network_connection_tracker_;
 
   // Must be accessed on UI thread. Guaranteed to be non-null during the
   // lifetime of |this|.
@@ -229,6 +241,9 @@
   net::EffectiveConnectionType effective_connection_type_;
   base::Optional<base::TimeDelta> http_rtt_;
 
+  network::mojom::ConnectionType connection_type_ =
+      network::mojom::ConnectionType::CONNECTION_UNKNOWN;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   base::WeakPtrFactory<DataReductionProxyService> weak_factory_;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
index e53985e7..7724904 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
@@ -298,6 +298,24 @@
     observer.OnProxyRequestHeadersChanged(headers);
 }
 
+void DataReductionProxySettings::SetConfiguredProxies(
+    const net::ProxyList& proxies) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  configured_proxies_ = proxies;
+}
+
+bool DataReductionProxySettings::IsConfiguredDataReductionProxy(
+    const net::ProxyServer& proxy_server) const {
+  if (proxy_server.is_direct() || !proxy_server.is_valid())
+    return false;
+
+  for (const auto& drp_proxy : configured_proxies_.GetAll()) {
+    if (drp_proxy.host_port_pair().Equals(proxy_server.host_port_pair()))
+      return true;
+  }
+  return false;
+}
+
 void DataReductionProxySettings::AddDataReductionProxySettingsObserver(
     DataReductionProxySettingsObserver* observer) {
   DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
index 4846eab..16d8252 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
@@ -156,6 +156,8 @@
   // Sets the headers to use for requests to the compression server.
   void SetProxyRequestHeaders(const net::HttpRequestHeaders& headers);
 
+  void SetConfiguredProxies(const net::ProxyList& proxies);
+
   // Returns headers to use for requests to the compression server.
   const net::HttpRequestHeaders& GetProxyRequestHeaders() const;
 
@@ -205,6 +207,10 @@
   virtual void RecordStartupState(
       data_reduction_proxy::ProxyStartupState state) const;
 
+  // Checks whether |proxy_server| is a valid configured proxy.
+  bool IsConfiguredDataReductionProxy(
+      const net::ProxyServer& proxy_server) const;
+
  private:
   friend class DataReductionProxySettingsTestBase;
   friend class DataReductionProxySettingsTest;
@@ -317,6 +323,8 @@
   // The headers to use for requests to the proxy server.
   net::HttpRequestHeaders proxy_request_headers_;
 
+  net::ProxyList configured_proxies_;
+
   network::mojom::CustomProxyConfigClientPtrInfo proxy_config_client_;
 
   base::ThreadChecker thread_checker_;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
index 4e21ffc..01f88ff 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
@@ -237,18 +237,20 @@
     net::URLRequestContextGetter* request_context,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
-    : DataReductionProxyService(settings,
-                                prefs,
-                                request_context,
-                                std::move(url_loader_factory),
-                                std::make_unique<TestDataStore>(),
-                                nullptr,
-                                test_network_quality_tracker,
-                                nullptr,
-                                task_runner,
-                                task_runner,
-                                task_runner,
-                                base::TimeDelta()) {}
+    : DataReductionProxyService(
+          settings,
+          prefs,
+          request_context,
+          std::move(url_loader_factory),
+          std::make_unique<TestDataStore>(),
+          nullptr,
+          test_network_quality_tracker,
+          network::TestNetworkConnectionTracker::GetInstance(),
+          nullptr,
+          task_runner,
+          task_runner,
+          task_runner,
+          base::TimeDelta()) {}
 
 MockDataReductionProxyService::~MockDataReductionProxyService() {
 }
@@ -420,6 +422,13 @@
   return *this;
 }
 
+DataReductionProxyTestContext::Builder&
+DataReductionProxyTestContext::Builder::WithSettings(
+    std::unique_ptr<DataReductionProxySettings> settings) {
+  settings_ = std::move(settings);
+  return *this;
+}
+
 std::unique_ptr<DataReductionProxyTestContext>
 DataReductionProxyTestContext::Builder::Build() {
   // Check for invalid builder combinations.
@@ -500,10 +509,10 @@
         client_, "1.2.3.4", config.get()));
   }
 
-  std::unique_ptr<DataReductionProxySettings> settings(
-      new DataReductionProxySettings());
+  if (!settings_)
+    settings_ = std::make_unique<DataReductionProxySettings>();
   if (skip_settings_initialization_) {
-    settings->set_data_reduction_proxy_enabled_pref_name_for_test(
+    settings_->set_data_reduction_proxy_enabled_pref_name_for_test(
         kDataReductionProxyEnabled);
     test_context_flags |= SKIP_SETTINGS_INITIALIZATION;
   }
@@ -544,7 +553,7 @@
       new DataReductionProxyTestContext(
           task_runner, std::move(pref_service), request_context_getter,
           url_loader_factory, mock_socket_factory_, std::move(io_data),
-          std::move(settings), std::move(config_storer), raw_params,
+          std::move(settings_), std::move(config_storer), raw_params,
           test_context_flags));
 
   if (!skip_settings_initialization_)
@@ -655,8 +664,9 @@
   return std::make_unique<DataReductionProxyService>(
       settings, simple_pref_service_.get(), request_context_getter_.get(),
       test_shared_url_loader_factory_, base::WrapUnique(new TestDataStore()),
-      nullptr, test_network_quality_tracker_.get(), nullptr, task_runner_,
-      task_runner_, task_runner_, base::TimeDelta());
+      nullptr, test_network_quality_tracker_.get(),
+      network::TestNetworkConnectionTracker::GetInstance(), nullptr,
+      task_runner_, task_runner_, task_runner_, base::TimeDelta());
 }
 
 void DataReductionProxyTestContext::AttachToURLRequestContext(
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
index 4332e78..43dff8ea 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
@@ -359,6 +359,9 @@
     Builder& WithProxiesForHttp(
         const std::vector<DataReductionProxyServer>& proxy_servers);
 
+    // Specifies a settings object to use.
+    Builder& WithSettings(std::unique_ptr<DataReductionProxySettings> settings);
+
     // Creates a |DataReductionProxyTestContext|. Owned by the caller.
     std::unique_ptr<DataReductionProxyTestContext> Build();
 
@@ -375,6 +378,7 @@
     bool use_test_config_client_;
     bool skip_settings_initialization_;
     std::vector<DataReductionProxyServer> proxy_servers_;
+    std::unique_ptr<DataReductionProxySettings> settings_;
   };
 
   virtual ~DataReductionProxyTestContext();
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h
index 023d8c8..25e5190 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_config_values.h
@@ -9,6 +9,7 @@
 
 #include "base/optional.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_type_info.h"
+#include "net/proxy_resolution/proxy_list.h"
 
 namespace net {
 class ProxyServer;
@@ -41,6 +42,9 @@
   virtual base::Optional<DataReductionProxyTypeInfo>
   FindConfiguredDataReductionProxy(
       const net::ProxyServer& proxy_server) const = 0;
+
+  // Gets all current and recently configured Data Reduction Proxy servers.
+  virtual net::ProxyList GetAllConfiguredProxies() const = 0;
 };
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
index 101018c..700715f 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
@@ -494,6 +494,9 @@
   return params::FindConfiguredProxyInVector(proxies_for_http(), proxy_server);
 }
 
-
+net::ProxyList DataReductionProxyParams::GetAllConfiguredProxies() const {
+  NOTREACHED();
+  return net::ProxyList();
+}
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
index ba9bc5f4..43bec956 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
@@ -195,6 +195,7 @@
   // if any exist.
   base::Optional<DataReductionProxyTypeInfo> FindConfiguredDataReductionProxy(
       const net::ProxyServer& proxy_server) const override;
+  net::ProxyList GetAllConfiguredProxies() const override;
 
  private:
   std::vector<DataReductionProxyServer> proxies_for_http_;
diff --git a/components/image_fetcher/core/cache/cached_image_fetcher_metrics_reporter.cc b/components/image_fetcher/core/cache/cached_image_fetcher_metrics_reporter.cc
index ed5a86d..3524c62 100644
--- a/components/image_fetcher/core/cache/cached_image_fetcher_metrics_reporter.cc
+++ b/components/image_fetcher/core/cache/cached_image_fetcher_metrics_reporter.cc
@@ -15,23 +15,34 @@
 }
 
 // static
-void CachedImageFetcherMetricsReporter::ReportLoadTime(LoadTimeType type,
-                                                       base::Time start_time) {
+void CachedImageFetcherMetricsReporter::ReportImageLoadFromCacheTime(
+    base::Time start_time) {
   base::TimeDelta time_delta = base::Time::Now() - start_time;
-  switch (type) {
-    case LoadTimeType::kLoadFromCache:
-      UMA_HISTOGRAM_TIMES("CachedImageFetcher.ImageLoadFromCacheTime",
-                          time_delta);
-      break;
-    case LoadTimeType::kLoadFromNetwork:
-      UMA_HISTOGRAM_TIMES("CachedImageFetcher.ImageLoadFromNetworkTime",
-                          time_delta);
-      break;
-    case LoadTimeType::kLoadFromNetworkAfterCacheHit:
-      UMA_HISTOGRAM_TIMES(
-          "CachedImageFetcher.ImageLoadFromNetworkAfterCacheHit", time_delta);
-      break;
-  }
+  UMA_HISTOGRAM_TIMES("CachedImageFetcher.ImageLoadFromCacheTime", time_delta);
 }
 
-}  // namespace image_fetcher
\ No newline at end of file
+// static
+void CachedImageFetcherMetricsReporter::ReportImageLoadFromNetworkTime(
+    base::Time start_time) {
+  base::TimeDelta time_delta = base::Time::Now() - start_time;
+  UMA_HISTOGRAM_TIMES("CachedImageFetcher.ImageLoadFromNetworkTime",
+                      time_delta);
+}
+
+// static
+void CachedImageFetcherMetricsReporter::ReportImageLoadFromNetworkAfterCacheHit(
+    base::Time start_time) {
+  base::TimeDelta time_delta = base::Time::Now() - start_time;
+  UMA_HISTOGRAM_TIMES("CachedImageFetcher.ImageLoadFromNetworkAfterCacheHit",
+                      time_delta);
+}
+
+// static
+void CachedImageFetcherMetricsReporter::ReportTimeSinceLastCacheLRUEviction(
+    base::Time start_time) {
+  base::TimeDelta time_delta = base::Time::Now() - start_time;
+  UMA_HISTOGRAM_TIMES("CachedImageFetcher.TimeSinceLastCacheLRUEviction",
+                      time_delta);
+}
+
+}  // namespace image_fetcher
diff --git a/components/image_fetcher/core/cache/cached_image_fetcher_metrics_reporter.h b/components/image_fetcher/core/cache/cached_image_fetcher_metrics_reporter.h
index e6f87b6..0a7c0c86 100644
--- a/components/image_fetcher/core/cache/cached_image_fetcher_metrics_reporter.h
+++ b/components/image_fetcher/core/cache/cached_image_fetcher_metrics_reporter.h
@@ -18,15 +18,10 @@
   kCacheMiss = 2,
   kCacheDecodingError = 3,
   kTranscodingError = 4,
-  kFailure = 5,
-  kMaxValue = kFailure,
-};
-
-// Tracks the various forms of timing events.
-enum class LoadTimeType {
-  kLoadFromCache = 0,
-  kLoadFromNetwork = 1,
-  kLoadFromNetworkAfterCacheHit = 2
+  kTotalFailure = 5,
+  kCacheStartupEvictionStarted = 6,
+  kCacheStartupEvictionFinished = 7,
+  kMaxValue = kCacheStartupEvictionFinished,
 };
 
 class CachedImageFetcherMetricsReporter {
@@ -35,7 +30,10 @@
   static void ReportEvent(CachedImageFetcherEvent event);
 
   // Report timing for various Cache events related to CachedImageFetcher.
-  static void ReportLoadTime(LoadTimeType type, base::Time start_time);
+  static void ReportImageLoadFromCacheTime(base::Time start_time);
+  static void ReportImageLoadFromNetworkTime(base::Time start_time);
+  static void ReportImageLoadFromNetworkAfterCacheHit(base::Time start_time);
+  static void ReportTimeSinceLastCacheLRUEviction(base::Time start_time);
 };
 
 }  // namespace image_fetcher
diff --git a/components/image_fetcher/core/cache/image_cache.cc b/components/image_fetcher/core/cache/image_cache.cc
index f594837..4cb2e25 100644
--- a/components/image_fetcher/core/cache/image_cache.cc
+++ b/components/image_fetcher/core/cache/image_cache.cc
@@ -15,6 +15,7 @@
 #include "base/time/clock.h"
 #include "base/time/time.h"
 #include "components/base32/base32.h"
+#include "components/image_fetcher/core/cache/cached_image_fetcher_metrics_reporter.h"
 #include "components/image_fetcher/core/cache/image_data_store.h"
 #include "components/image_fetcher/core/cache/image_metadata_store.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -22,8 +23,11 @@
 
 namespace {
 
-constexpr char kPrefLastEvictionKey[] =
-    "cached_image_fetcher_last_eviction_time";
+constexpr char kPrefLastStartupEviction[] =
+    "cached_image_fetcher_last_startup_eviction_time";
+
+constexpr char kPrefLastLRUEviction[] =
+    "cached_image_fetcher_last_lru_eviction_time";
 
 // TODO(wylieb): Control these parameters server-side.
 constexpr int kCacheMaxSize = 64 * 1024 * 1024;         // 64mb.
@@ -45,7 +49,8 @@
 
 // static
 void ImageCache::RegisterProfilePrefs(PrefRegistrySimple* registry) {
-  registry->RegisterTimePref(kPrefLastEvictionKey, base::Time());
+  registry->RegisterTimePref(kPrefLastStartupEviction, base::Time());
+  registry->RegisterTimePref(kPrefLastLRUEviction, base::Time());
 }
 
 ImageCache::ImageCache(std::unique_ptr<ImageDataStore> data_store,
@@ -130,8 +135,9 @@
 
   // TODO(wylieb): Consider delaying eviction as new requests come in via
   // separate weak pointers.
-  // TODO(wylieb): Log UMA data about starting GC eviction here, then again
-  // when it's finished.
+  CachedImageFetcherMetricsReporter::ReportEvent(
+      CachedImageFetcherEvent::kCacheStartupEvictionStarted);
+
   // Once all the queued requests are taken care of, run eviction.
   base::PostTaskWithTraitsAndReply(
       FROM_HERE, {base::TaskPriority::BEST_EFFORT},
@@ -170,7 +176,8 @@
 }
 
 void ImageCache::RunEvictionOnStartup() {
-  base::Time last_eviction_time = pref_service_->GetTime(kPrefLastEvictionKey);
+  base::Time last_eviction_time =
+      pref_service_->GetTime(kPrefLastStartupEviction);
   // If we've already garbage collected in the past interval, bail out.
   if (last_eviction_time >
       clock_->Now() -
@@ -178,6 +185,9 @@
     return;
   }
 
+  // Update the time we did startup eviction so it can used for reporting.
+  pref_service_->SetTime(kPrefLastStartupEviction, clock_->Now());
+
   RunEviction(kCacheMaxSize, base::BindOnce(&ImageCache::RunReconciliation,
                                             weak_ptr_factory_.GetWeakPtr()));
 }
@@ -188,15 +198,25 @@
     return;
   }
 
+  // Report the time since the last LRU eviction. This allows us to gauge if the
+  // cache is properly sized.
+  base::Time last_eviction_time = pref_service_->GetTime(kPrefLastLRUEviction);
+  // Only report for non-null times.
+  if (last_eviction_time != base::Time()) {
+    CachedImageFetcherMetricsReporter::ReportTimeSinceLastCacheLRUEviction(
+        last_eviction_time);
+  }
+
+  // Update the time we did LRU eviction so it can used for reporting.
+  pref_service_->SetTime(kPrefLastLRUEviction, clock_->Now());
+
   RunEviction(kCacheResizeWhenFull, base::OnceClosure());
 }
 
 void ImageCache::RunEviction(size_t bytes_left,
                              base::OnceClosure on_completion) {
-  base::Time eviction_time = clock_->Now();
-  pref_service_->SetTime(kPrefLastEvictionKey, eviction_time);
   metadata_store_->EvictImageMetadata(
-      eviction_time - base::TimeDelta::FromDays(kCacheItemsTimeToLiveDays),
+      clock_->Now() - base::TimeDelta::FromDays(kCacheItemsTimeToLiveDays),
       bytes_left,
       base::BindOnce(&ImageCache::OnKeysEvicted, weak_ptr_factory_.GetWeakPtr(),
                      std::move(on_completion)));
@@ -244,6 +264,9 @@
   for (const std::string& key : diff) {
     data_store_->DeleteImage(key);
   }
+
+  CachedImageFetcherMetricsReporter::ReportEvent(
+      CachedImageFetcherEvent::kCacheStartupEvictionFinished);
 }
 
 }  // namespace image_fetcher
diff --git a/components/image_fetcher/core/cache/image_cache_unittest.cc b/components/image_fetcher/core/cache/image_cache_unittest.cc
index 15ee7c4..46f0ddc 100644
--- a/components/image_fetcher/core/cache/image_cache_unittest.cc
+++ b/components/image_fetcher/core/cache/image_cache_unittest.cc
@@ -9,9 +9,11 @@
 
 #include "base/files/scoped_temp_dir.h"
 #include "base/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/test/simple_test_clock.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "components/image_fetcher/core/cache/cached_image_fetcher_metrics_reporter.h"
 #include "components/image_fetcher/core/cache/image_data_store_disk.h"
 #include "components/image_fetcher/core/cache/image_metadata_store_leveldb.h"
 #include "components/image_fetcher/core/cache/proto/cached_image_metadata.pb.h"
@@ -27,6 +29,10 @@
 
 namespace {
 
+constexpr char kPrefLastStartupEviction[] =
+    "cached_image_fetcher_last_startup_eviction_time";
+constexpr char kCachedImageFetcherEventHistogramName[] =
+    "CachedImageFetcher.Events";
 constexpr char kImageUrl[] = "http://gstatic.img.com/foo.jpg";
 constexpr char kImageUrlHashed[] = "3H7UODDH3WKDWK6FQ3IZT3LQMVBPYJ4M";
 constexpr char kImageData[] = "data";
@@ -83,8 +89,9 @@
     image_cache()->RunEvictionOnStartup();
 
     if (success) {
-      db_->LoadCallback(true);
-      db_->UpdateCallback(true);
+      db()->LoadCallback(true);
+      db()->UpdateCallback(true);
+      db()->LoadKeysCallback(true);
     }
 
     RunUntilIdle();
@@ -137,6 +144,7 @@
   ImageDataStoreDisk* data_store() { return data_store_; }
   ImageMetadataStoreLevelDB* metadata_store() { return metadata_store_; }
   FakeDB<CachedImageMetadataProto>* db() { return db_; }
+  base::HistogramTester& histogram_tester() { return histogram_tester_; }
 
   MOCK_METHOD1(DataCallback, void(std::string));
 
@@ -152,6 +160,7 @@
   std::map<std::string, CachedImageMetadataProto> db_store_;
 
   base::test::ScopedTaskEnvironment scoped_task_environment_;
+  base::HistogramTester histogram_tester_;
 
   DISALLOW_COPY_AND_ASSIGN(ImageCacheTest);
 };
@@ -261,12 +270,20 @@
 
   clock()->SetNow(clock()->Now() + base::TimeDelta::FromDays(7));
   RunEvictionOnStartup(/* success */ true);
+  ASSERT_EQ(clock()->Now(), prefs()->GetTime(kPrefLastStartupEviction));
 
   EXPECT_CALL(*this, DataCallback(std::string()));
   image_cache()->LoadImage(
       false, kImageUrl,
       base::BindOnce(&ImageCacheTest::DataCallback, base::Unretained(this)));
   RunUntilIdle();
+
+  histogram_tester().ExpectBucketCount(
+      kCachedImageFetcherEventHistogramName,
+      CachedImageFetcherEvent::kCacheStartupEvictionStarted, 1);
+  histogram_tester().ExpectBucketCount(
+      kCachedImageFetcherEventHistogramName,
+      CachedImageFetcherEvent::kCacheStartupEvictionFinished, 1);
 }
 
 TEST_F(ImageCacheTest, EvictionTooSoon) {
@@ -285,7 +302,8 @@
 TEST_F(ImageCacheTest, EvictionWhenEvictionAlreadyPerformed) {
   PrepareImageCache();
 
-  prefs()->SetTime("cached_image_fetcher_last_eviction_time", clock()->Now());
+  prefs()->SetTime("cached_image_fetcher_last_startup_eviction_time",
+                   clock()->Now());
   clock()->SetNow(clock()->Now() + base::TimeDelta::FromHours(23));
   RunEvictionOnStartup(/* success */ false);
 
diff --git a/components/image_fetcher/core/cached_image_fetcher.cc b/components/image_fetcher/core/cached_image_fetcher.cc
index 8d0924a..007d6dfa 100644
--- a/components/image_fetcher/core/cached_image_fetcher.cc
+++ b/components/image_fetcher/core/cached_image_fetcher.cc
@@ -159,8 +159,7 @@
   } else {
     ImageCallbackIfPresent(std::move(image_callback), id, image,
                            RequestMetadata());
-    CachedImageFetcherMetricsReporter::ReportLoadTime(
-        LoadTimeType::kLoadFromCache, start_time);
+    CachedImageFetcherMetricsReporter::ReportImageLoadFromCacheTime(start_time);
   }
 }
 
@@ -201,14 +200,17 @@
   // Report failure if the image is empty.
   if (image.IsEmpty()) {
     CachedImageFetcherMetricsReporter::ReportEvent(
-        CachedImageFetcherEvent::kFailure);
+        CachedImageFetcherEvent::kTotalFailure);
   }
 
   // Report to different histograms depending upon if there was a cache hit.
-  CachedImageFetcherMetricsReporter::ReportLoadTime(
-      cache_hit ? LoadTimeType::kLoadFromNetworkAfterCacheHit
-                : LoadTimeType::kLoadFromNetwork,
-      start_time);
+  if (cache_hit) {
+    CachedImageFetcherMetricsReporter::ReportImageLoadFromNetworkAfterCacheHit(
+        start_time);
+  } else {
+    CachedImageFetcherMetricsReporter::ReportImageLoadFromNetworkTime(
+        start_time);
+  }
 }
 
 void CachedImageFetcher::DecodeDataForCaching(
diff --git a/components/ntp_tiles/custom_links_manager.h b/components/ntp_tiles/custom_links_manager.h
index 226791d0..df527c1f 100644
--- a/components/ntp_tiles/custom_links_manager.h
+++ b/components/ntp_tiles/custom_links_manager.h
@@ -65,13 +65,10 @@
   // no longer be considered Most Visited. Returns false and does nothing if
   // custom links is not initialized, either URL is invalid, |url| does not
   // exist in the list, |new_url| already exists in the list, or both parameters
-  // are empty. |is_user_action| is true if this was executed by the user (i.e.
-  // by editing a custom link). Only user actions will update the previous state
-  // that is restored when CustomLinksManager::UndoAction is called.
+  // are empty.
   virtual bool UpdateLink(const GURL& url,
                           const GURL& new_url,
-                          const base::string16& new_title,
-                          bool is_user_action) = 0;
+                          const base::string16& new_title) = 0;
   // Deletes the link with the specified |url|. Returns false and does nothing
   // if custom links is not initialized, |url| is invalid, or |url| does not
   // exist in the list.
diff --git a/components/ntp_tiles/custom_links_manager_impl.cc b/components/ntp_tiles/custom_links_manager_impl.cc
index fd83c95..e59b77ec 100644
--- a/components/ntp_tiles/custom_links_manager_impl.cc
+++ b/components/ntp_tiles/custom_links_manager_impl.cc
@@ -94,8 +94,7 @@
 
 bool CustomLinksManagerImpl::UpdateLink(const GURL& url,
                                         const GURL& new_url,
-                                        const base::string16& new_title,
-                                        bool is_user_action) {
+                                        const base::string16& new_title) {
   if (!IsInitialized() || !url.is_valid() ||
       (new_url.is_empty() && new_title.empty())) {
     return false;
@@ -113,10 +112,7 @@
     return false;
 
   // At this point, we will be modifying at least one of the values.
-  if (is_user_action) {
-    // Save the previous state since this was a user update.
-    previous_links_ = current_links_;
-  }
+  previous_links_ = current_links_;
 
   if (!new_url.is_empty())
     it->url = new_url;
diff --git a/components/ntp_tiles/custom_links_manager_impl.h b/components/ntp_tiles/custom_links_manager_impl.h
index d350223..cc5902b 100644
--- a/components/ntp_tiles/custom_links_manager_impl.h
+++ b/components/ntp_tiles/custom_links_manager_impl.h
@@ -48,8 +48,7 @@
   bool AddLink(const GURL& url, const base::string16& title) override;
   bool UpdateLink(const GURL& url,
                   const GURL& new_url,
-                  const base::string16& new_title,
-                  bool is_user_action) override;
+                  const base::string16& new_title) override;
   bool DeleteLink(const GURL& url) override;
   bool UndoAction() override;
 
diff --git a/components/ntp_tiles/custom_links_manager_impl_unittest.cc b/components/ntp_tiles/custom_links_manager_impl_unittest.cc
index 9839d6e1..bf54bfc 100644
--- a/components/ntp_tiles/custom_links_manager_impl_unittest.cc
+++ b/components/ntp_tiles/custom_links_manager_impl_unittest.cc
@@ -219,20 +219,18 @@
 
   // Update the link's URL.
   EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(kTestUrl),
-                                        base::string16(),
-                                        /*is_user_action=*/true));
+                                        base::string16()));
   EXPECT_EQ(links_after_update_url, custom_links_->GetLinks());
 
   // Update the link's title.
   EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestUrl), GURL(),
-                                        base::UTF8ToUTF16(kTestTitle),
-                                        /*is_user_action=*/true));
+                                        base::UTF8ToUTF16(kTestTitle)));
   EXPECT_EQ(links_after_update_title, custom_links_->GetLinks());
 
   // Update the link's URL and title.
-  EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestUrl), GURL(kTestCase1[0].url),
-                                        base::UTF8ToUTF16(kTestCase1[0].title),
-                                        /*is_user_action=*/true));
+  EXPECT_TRUE(
+      custom_links_->UpdateLink(GURL(kTestUrl), GURL(kTestCase1[0].url),
+                                base::UTF8ToUTF16(kTestCase1[0].title)));
   EXPECT_EQ(links_after_update_both, custom_links_->GetLinks());
 }
 
@@ -248,24 +246,20 @@
   // Try to update a link that does not exist. This should fail and not modify
   // the list.
   EXPECT_FALSE(custom_links_->UpdateLink(GURL(kTestUrl), GURL(),
-                                         base::UTF8ToUTF16(kTestTitle),
-                                         /*is_user_action=*/true));
+                                         base::UTF8ToUTF16(kTestTitle)));
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
 
   // Try to pass empty params. This should fail and not modify the list.
   EXPECT_FALSE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(),
-                                         base::string16(),
-                                         /*is_user_action=*/true));
+                                         base::string16()));
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
 
   // Try to pass an invalid URL. This should fail and not modify the list.
   EXPECT_FALSE(custom_links_->UpdateLink(kInvalidUrl, GURL(),
-                                         base::UTF8ToUTF16(kTestTitle),
-                                         /*is_user_action=*/true));
+                                         base::UTF8ToUTF16(kTestTitle)));
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
   EXPECT_FALSE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), kInvalidUrl,
-                                         base::string16(),
-                                         /*is_user_action=*/true));
+                                         base::string16()));
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
 }
 
@@ -280,8 +274,7 @@
   // Try to update a link with a URL that exists in the list. This should fail
   // and not modify the list.
   EXPECT_FALSE(custom_links_->UpdateLink(
-      GURL(kTestCase2[0].url), GURL(kTestCase2[1].url), base::string16(),
-      /*is_user_action=*/true));
+      GURL(kTestCase2[0].url), GURL(kTestCase2[1].url), base::string16()));
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
 }
 
@@ -357,8 +350,7 @@
 
   // Update the link's URL.
   EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(kTestUrl),
-                                        base::string16(),
-                                        /*is_user_action=*/true));
+                                        base::string16()));
   EXPECT_EQ(links_after_update_url, custom_links_->GetLinks());
 
   // Undo update link.
@@ -367,8 +359,7 @@
 
   // Update the link's title.
   EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(),
-                                        base::UTF8ToUTF16(kTestTitle),
-                                        /*is_user_action=*/true));
+                                        base::UTF8ToUTF16(kTestTitle)));
   EXPECT_EQ(links_after_update_title, custom_links_->GetLinks());
 
   // Undo update link.
@@ -422,48 +413,6 @@
   EXPECT_EQ(expected_links, custom_links_->GetLinks());
 }
 
-TEST_F(CustomLinksManagerImplTest, ShouldNotUndoActionIfInternal) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
-  std::vector<Link> links_after_update_twice;
-  links_after_update_twice.emplace_back(
-      Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false});
-  std::vector<Link> links_after_add_and_update(initial_links);
-  links_after_add_and_update.emplace_back(Link{
-      GURL(kTestCase2[1].url), base::UTF8ToUTF16(kTestCase2[1].title), false});
-  links_after_add_and_update[0].url = GURL(kTestUrl);
-  links_after_add_and_update[0].is_most_visited = false;
-
-  // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
-  ASSERT_EQ(initial_links, custom_links_->GetLinks());
-
-  // Update twice. Specify that the second update was internal.
-  EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(),
-                                        base::UTF8ToUTF16(kTestTitle),
-                                        /*is_user_action=*/true));
-  EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(kTestUrl),
-                                        base::string16(),
-                                        /*is_user_action=*/false));
-  EXPECT_EQ(links_after_update_twice, custom_links_->GetLinks());
-
-  // Undo should revert to the state before the first action.
-  EXPECT_TRUE(custom_links_->UndoAction());
-  EXPECT_EQ(initial_links, custom_links_->GetLinks());
-
-  // Add then update. Specify that the update was internal.
-  EXPECT_TRUE(custom_links_->AddLink(GURL(kTestCase2[1].url),
-                                     base::UTF8ToUTF16(kTestCase2[1].title)));
-  EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(kTestUrl),
-                                        base::string16(),
-                                        /*is_user_action=*/false));
-  EXPECT_EQ(links_after_add_and_update, custom_links_->GetLinks());
-
-  // Undo should revert to the state before the first action.
-  EXPECT_TRUE(custom_links_->UndoAction());
-  EXPECT_EQ(initial_links, custom_links_->GetLinks());
-}
-
 TEST_F(CustomLinksManagerImplTest, ShouldDeleteMostVisitedOnHistoryDeletion) {
   NTPTilesVector initial_tiles = FillTestTiles(kTestCase2);
   std::vector<Link> initial_links = FillTestLinks(kTestCase2);
diff --git a/components/ntp_tiles/most_visited_sites.cc b/components/ntp_tiles/most_visited_sites.cc
index 77ccd0d2..1e1057bb 100644
--- a/components/ntp_tiles/most_visited_sites.cc
+++ b/components/ntp_tiles/most_visited_sites.cc
@@ -299,19 +299,16 @@
 
 bool MostVisitedSites::UpdateCustomLink(const GURL& url,
                                         const GURL& new_url,
-                                        const base::string16& new_title,
-                                        bool is_user_action) {
+                                        const base::string16& new_title) {
   if (!custom_links_ || !custom_links_enabled_)
     return false;
 
   // Initialize custom links if they have not been initialized yet.
   InitializeCustomLinks();
 
-  bool success =
-      custom_links_->UpdateLink(url, new_url, new_title, is_user_action);
+  bool success = custom_links_->UpdateLink(url, new_url, new_title);
   if (success) {
-    // Only update the action count if this was executed by the user.
-    if (is_user_action && custom_links_action_count_ != -1)
+    if (custom_links_action_count_ != -1)
       custom_links_action_count_++;
     BuildCurrentTiles();
   }
diff --git a/components/ntp_tiles/most_visited_sites.h b/components/ntp_tiles/most_visited_sites.h
index ab24325..edb62215 100644
--- a/components/ntp_tiles/most_visited_sites.h
+++ b/components/ntp_tiles/most_visited_sites.h
@@ -180,12 +180,10 @@
   // Updates the URL and/or title of the custom link specified by |url|. If
   // |url| does not exist or |new_url| already exists in the custom link list,
   // returns false and does nothing. Will initialize custom links if they have
-  // not been initialized yet. |is_user_action| is true if this was executed by
-  // the user (i.e. editing a custom link). Custom links must be enabled.
+  // not been initialized yet. Custom links must be enabled.
   bool UpdateCustomLink(const GURL& url,
                         const GURL& new_url,
-                        const base::string16& new_title,
-                        bool is_user_action);
+                        const base::string16& new_title);
   // Deletes the custom link with the specified |url|. If |url| does not exist
   // in the custom link list, returns false and does nothing. Will initialize
   // custom links if they have not been initialized yet. Custom links must be
diff --git a/components/ntp_tiles/most_visited_sites_unittest.cc b/components/ntp_tiles/most_visited_sites_unittest.cc
index 03429a3..0e927e32 100644
--- a/components/ntp_tiles/most_visited_sites_unittest.cc
+++ b/components/ntp_tiles/most_visited_sites_unittest.cc
@@ -272,11 +272,10 @@
   MOCK_CONST_METHOD0(IsInitialized, bool());
   MOCK_CONST_METHOD0(GetLinks, const std::vector<CustomLinksManager::Link>&());
   MOCK_METHOD2(AddLink, bool(const GURL& url, const base::string16& title));
-  MOCK_METHOD4(UpdateLink,
+  MOCK_METHOD3(UpdateLink,
                bool(const GURL& url,
                     const GURL& new_url,
-                    const base::string16& new_title,
-                    bool is_user_action));
+                    const base::string16& new_title));
   MOCK_METHOD1(DeleteLink, bool(const GURL& url));
   MOCK_METHOD0(UndoAction, bool());
   MOCK_METHOD1(RegisterCallbackForOnChanged,
@@ -1505,8 +1504,7 @@
 
   // Initialize custom links and complete a custom link action.
   EXPECT_CALL(*mock_custom_links_, Initialize(_)).WillOnce(Return(true));
-  EXPECT_CALL(*mock_custom_links_, UpdateLink(_, _, _, _))
-      .WillOnce(Return(true));
+  EXPECT_CALL(*mock_custom_links_, UpdateLink(_, _, _)).WillOnce(Return(true));
   EXPECT_CALL(*mock_custom_links_, IsInitialized())
       .WillRepeatedly(Return(true));
   EXPECT_CALL(*mock_custom_links_, GetLinks())
@@ -1514,8 +1512,7 @@
   EXPECT_CALL(mock_observer_, OnURLsAvailable(_))
       .WillOnce(SaveArg<0>(&sections));
   most_visited_sites_->UpdateCustomLink(GURL("test.com"), GURL("test.com"),
-                                        base::UTF8ToUTF16("test"),
-                                        /*is_user_action=*/true);
+                                        base::UTF8ToUTF16("test"));
   base::RunLoop().RunUntilIdle();
   ASSERT_THAT(
       sections.at(SectionType::PERSONALIZED),
diff --git a/components/pdf/browser/pdf_web_contents_helper.cc b/components/pdf/browser/pdf_web_contents_helper.cc
index 00e9174..b18c900 100644
--- a/components/pdf/browser/pdf_web_contents_helper.cc
+++ b/components/pdf/browser/pdf_web_contents_helper.cc
@@ -188,6 +188,14 @@
   NOTIMPLEMENTED();
 }
 
+bool PDFWebContentsHelper::ShouldShowQuickMenu() {
+  return false;
+}
+
+base::string16 PDFWebContentsHelper::GetSelectedText() {
+  return base::string16();
+}
+
 void PDFWebContentsHelper::InitTouchSelectionClientManager() {
   content::RenderWidgetHostView* view =
       web_contents()->GetRenderWidgetHostView();
diff --git a/components/pdf/browser/pdf_web_contents_helper.h b/components/pdf/browser/pdf_web_contents_helper.h
index 98cef8f..a52045c 100644
--- a/components/pdf/browser/pdf_web_contents_helper.h
+++ b/components/pdf/browser/pdf_web_contents_helper.h
@@ -58,6 +58,8 @@
   bool IsCommandIdEnabled(int command_id) const override;
   void ExecuteCommand(int command_id, int event_flags) override;
   void RunContextMenu() override;
+  bool ShouldShowQuickMenu() override;
+  base::string16 GetSelectedText() override;
 
   // ui::TouchSelectionControllerClientManager::Observer:
   void OnManagerWillDestroy(
diff --git a/components/security_state/content/content_utils.cc b/components/security_state/content/content_utils.cc
index 8c78cb950..4a669ec6 100644
--- a/components/security_state/content/content_utils.cc
+++ b/components/security_state/content/content_utils.cc
@@ -225,9 +225,10 @@
       net::SSLConnectionStatusToCipherSuite(security_info.connection_status);
   net::SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, &is_aead,
                                &is_tls13, cipher_suite);
-  base::string16 protocol_name = base::ASCIIToUTF16(protocol);
-  const base::string16 cipher_name =
-      (mac == nullptr) ? base::ASCIIToUTF16(cipher)
+  const base::string16 protocol_name = base::ASCIIToUTF16(protocol);
+  const base::string16 cipher_name = base::ASCIIToUTF16(cipher);
+  const base::string16 cipher_full_name =
+      (mac == nullptr) ? cipher_name
                        : l10n_util::GetStringFUTF16(IDS_CIPHER_WITH_MAC,
                                                     base::ASCIIToUTF16(cipher),
                                                     base::ASCIIToUTF16(mac));
@@ -246,44 +247,43 @@
     key_exchange_name = base::ASCIIToUTF16(key_exchange);
   }
 
-  if (security_info.obsolete_ssl_status == net::OBSOLETE_SSL_NONE) {
-    security_style_explanations->secure_explanations.push_back(
-        content::SecurityStyleExplanation(
-            l10n_util::GetStringUTF8(IDS_SSL_CONNECTION_TITLE),
-            l10n_util::GetStringFUTF8(IDS_STRONG_SSL_SUMMARY, protocol_name),
-            l10n_util::GetStringFUTF8(IDS_STRONG_SSL_DESCRIPTION, protocol_name,
-                                      key_exchange_name, cipher_name)));
+  int status = security_info.obsolete_ssl_status;
+  if (status == net::OBSOLETE_SSL_NONE) {
+    security_style_explanations->secure_explanations.emplace_back(
+        l10n_util::GetStringUTF8(IDS_SSL_CONNECTION_TITLE),
+        l10n_util::GetStringUTF8(IDS_SECURE_SSL_SUMMARY),
+        l10n_util::GetStringFUTF8(IDS_SSL_DESCRIPTION, protocol_name,
+                                  key_exchange_name, cipher_full_name));
     return;
   }
 
-  std::vector<base::string16> description_replacements;
-  int status = security_info.obsolete_ssl_status;
-  int str_id;
+  std::vector<std::string> recommendations;
+  if (status & net::OBSOLETE_SSL_MASK_PROTOCOL) {
+    recommendations.push_back(
+        l10n_util::GetStringFUTF8(IDS_SSL_RECOMMEND_PROTOCOL, protocol_name));
+  }
+  if (status & net::OBSOLETE_SSL_MASK_KEY_EXCHANGE) {
+    recommendations.push_back(
+        l10n_util::GetStringUTF8(IDS_SSL_RECOMMEND_KEY_EXCHANGE));
+  }
+  if (status & net::OBSOLETE_SSL_MASK_CIPHER) {
+    // The problems with obsolete encryption come from the cipher portion rather
+    // than the MAC, so use the shorter |cipher_name| rather than
+    // |cipher_full_name|.
+    recommendations.push_back(
+        l10n_util::GetStringFUTF8(IDS_SSL_RECOMMEND_CIPHER, cipher_name));
+  }
+  if (status & net::OBSOLETE_SSL_MASK_SIGNATURE) {
+    recommendations.push_back(
+        l10n_util::GetStringUTF8(IDS_SSL_RECOMMEND_SIGNATURE));
+  }
 
-  str_id = (status & net::OBSOLETE_SSL_MASK_PROTOCOL)
-               ? IDS_SSL_AN_OBSOLETE_PROTOCOL
-               : IDS_SSL_A_STRONG_PROTOCOL;
-  description_replacements.push_back(protocol_name);
-  description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
-
-  str_id = (status & net::OBSOLETE_SSL_MASK_KEY_EXCHANGE)
-               ? IDS_SSL_AN_OBSOLETE_KEY_EXCHANGE
-               : IDS_SSL_A_STRONG_KEY_EXCHANGE;
-  description_replacements.push_back(key_exchange_name);
-  description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
-
-  str_id = (status & net::OBSOLETE_SSL_MASK_CIPHER) ? IDS_SSL_AN_OBSOLETE_CIPHER
-                                                    : IDS_SSL_A_STRONG_CIPHER;
-  description_replacements.push_back(cipher_name);
-  description_replacements.push_back(l10n_util::GetStringUTF16(str_id));
-
-  security_style_explanations->info_explanations.push_back(
-      content::SecurityStyleExplanation(
-          l10n_util::GetStringUTF8(IDS_SSL_CONNECTION_TITLE),
-          l10n_util::GetStringUTF8(IDS_OBSOLETE_SSL_SUMMARY),
-          base::UTF16ToUTF8(
-              l10n_util::GetStringFUTF16(IDS_OBSOLETE_SSL_DESCRIPTION,
-                                         description_replacements, nullptr))));
+  security_style_explanations->info_explanations.emplace_back(
+      l10n_util::GetStringUTF8(IDS_SSL_CONNECTION_TITLE),
+      l10n_util::GetStringUTF8(IDS_OBSOLETE_SSL_SUMMARY),
+      l10n_util::GetStringFUTF8(IDS_SSL_DESCRIPTION, protocol_name,
+                                key_exchange_name, cipher_full_name),
+      std::move(recommendations));
 }
 
 void ExplainContentSecurity(
@@ -414,6 +414,7 @@
   state->cert_status = ssl.cert_status;
   state->connection_status = ssl.connection_status;
   state->key_exchange_group = ssl.key_exchange_group;
+  state->peer_signature_algorithm = ssl.peer_signature_algorithm;
   state->security_bits = ssl.security_bits;
   state->pkp_bypassed = ssl.pkp_bypassed;
   state->displayed_mixed_content =
diff --git a/components/security_state/content/content_utils_unittest.cc b/components/security_state/content/content_utils_unittest.cc
index 18d8a859..40b1d4f 100644
--- a/components/security_state/content/content_utils_unittest.cc
+++ b/components/security_state/content/content_utils_unittest.cc
@@ -203,26 +203,19 @@
                                      &security_info.connection_status);
   security_info.key_exchange_group = 29;  // X25519
 
-  const char* protocol;
-  net::SSLVersionToString(&protocol, net::SSL_CONNECTION_VERSION_TLS1_2);
-
   std::string connection_title =
       l10n_util::GetStringUTF8(IDS_SSL_CONNECTION_TITLE);
 
-  std::string tls_1_2_connection_string = l10n_util::GetStringFUTF8(
-      IDS_STRONG_SSL_SUMMARY, base::ASCIIToUTF16(protocol));
-
   {
     content::SecurityStyleExplanations explanations;
     GetSecurityStyle(security_info, &explanations);
     content::SecurityStyleExplanation explanation;
     ASSERT_TRUE(FindSecurityStyleExplanation(
         explanations.secure_explanations, connection_title,
-        tls_1_2_connection_string, &explanation));
+        l10n_util::GetStringUTF8(IDS_SECURE_SSL_SUMMARY), &explanation));
     EXPECT_EQ(
         "The connection to this site is encrypted and authenticated using TLS "
-        "1.2 (a strong protocol), ECDHE_RSA with X25519 (a strong key "
-        "exchange), and CHACHA20_POLY1305 (a strong cipher).",
+        "1.2, ECDHE_RSA with X25519, and CHACHA20_POLY1305.",
         explanation.description);
   }
 
@@ -235,11 +228,10 @@
     content::SecurityStyleExplanation explanation;
     ASSERT_TRUE(FindSecurityStyleExplanation(
         explanations.secure_explanations, connection_title,
-        tls_1_2_connection_string, &explanation));
+        l10n_util::GetStringUTF8(IDS_SECURE_SSL_SUMMARY), &explanation));
     EXPECT_EQ(
         "The connection to this site is encrypted and authenticated using TLS "
-        "1.2 (a strong protocol), ECDHE_RSA (a strong key exchange), and "
-        "CHACHA20_POLY1305 (a strong cipher).",
+        "1.2, ECDHE_RSA, and CHACHA20_POLY1305.",
         explanation.description);
   }
 
@@ -254,23 +246,46 @@
     GetSecurityStyle(security_info, &explanations);
     content::SecurityStyleExplanation explanation;
 
-    net::SSLVersionToString(&protocol, net::SSL_CONNECTION_VERSION_TLS1_3);
-    std::string tls_1_3_connection_string = l10n_util::GetStringFUTF8(
-        IDS_STRONG_SSL_SUMMARY, base::ASCIIToUTF16(protocol));
-
     ASSERT_TRUE(FindSecurityStyleExplanation(
         explanations.secure_explanations, connection_title,
-        tls_1_3_connection_string, &explanation));
+        l10n_util::GetStringUTF8(IDS_SECURE_SSL_SUMMARY), &explanation));
     EXPECT_EQ(
         "The connection to this site is encrypted and authenticated using TLS "
-        "1.3 (a strong protocol), X25519 (a strong key exchange), and "
-        "AES_128_GCM (a strong cipher).",
+        "1.3, X25519, and AES_128_GCM.",
         explanation.description);
   }
 }
 
+void UpdateObsoleteSSLStatus(security_state::SecurityInfo* info) {
+  info->obsolete_ssl_status = net::ObsoleteSSLStatus(
+      info->connection_status, info->peer_signature_algorithm);
+}
+
+bool IsProtocolRecommendation(const std::string& recommendation,
+                              const std::string& bad_protocol) {
+  return recommendation.find(bad_protocol) != std::string::npos &&
+         recommendation.find("TLS 1.2") != std::string::npos;
+}
+
+bool IsKeyExchangeRecommendation(const std::string& recommendation) {
+  return recommendation.find("RSA") != std::string::npos &&
+         recommendation.find("ECDHE") != std::string::npos;
+}
+
+bool IsCipherRecommendation(const std::string& recommendation,
+                            const std::string& bad_cipher) {
+  return recommendation.find(bad_cipher) != std::string::npos &&
+         recommendation.find("GCM") != std::string::npos;
+}
+
+bool IsSignatureRecommendation(const std::string& recommendation) {
+  return recommendation.find("SHA-1") != std::string::npos &&
+         recommendation.find("SHA-2") != std::string::npos;
+}
+
 // Test that obsolete connection explanations are formatted as expected.
 TEST(SecurityStateContentUtilsTest, ObsoleteConnectionExplanation) {
+  // Obsolete cipher.
   security_state::SecurityInfo security_info;
   security_info.cert_status = net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
   security_info.scheme_is_cryptographic = true;
@@ -280,9 +295,7 @@
   net::SSLConnectionStatusSetVersion(net::SSL_CONNECTION_VERSION_TLS1_2,
                                      &security_info.connection_status);
   security_info.key_exchange_group = 29;  // X25519
-  security_info.obsolete_ssl_status =
-      net::ObsoleteSSLMask::OBSOLETE_SSL_MASK_CIPHER;
-
+  UpdateObsoleteSSLStatus(&security_info);
   {
     content::SecurityStyleExplanations explanations;
     GetSecurityStyle(security_info, &explanations);
@@ -292,10 +305,125 @@
         l10n_util::GetStringUTF8(IDS_SSL_CONNECTION_TITLE),
         l10n_util::GetStringUTF8(IDS_OBSOLETE_SSL_SUMMARY), &explanation));
     EXPECT_EQ(
-        "The connection to this site uses TLS 1.2 (a strong protocol), "
-        "ECDHE_RSA with X25519 (a strong key exchange), and AES_128_CBC with "
-        "HMAC-SHA1 (an obsolete cipher).",
+        "The connection to this site is encrypted and authenticated using TLS "
+        "1.2, ECDHE_RSA with X25519, and AES_128_CBC with HMAC-SHA1.",
         explanation.description);
+
+    ASSERT_EQ(1u, explanation.recommendations.size());
+    EXPECT_TRUE(
+        IsCipherRecommendation(explanation.recommendations[0], "AES_128_CBC"))
+        << explanation.recommendations[0];
+  }
+
+  // Obsolete cipher and signature.
+  security_info.peer_signature_algorithm = 0x0201;  // rsa_pkcs1_sha1
+  UpdateObsoleteSSLStatus(&security_info);
+  {
+    content::SecurityStyleExplanations explanations;
+    GetSecurityStyle(security_info, &explanations);
+    content::SecurityStyleExplanation explanation;
+    ASSERT_TRUE(FindSecurityStyleExplanation(
+        explanations.info_explanations,
+        l10n_util::GetStringUTF8(IDS_SSL_CONNECTION_TITLE),
+        l10n_util::GetStringUTF8(IDS_OBSOLETE_SSL_SUMMARY), &explanation));
+
+    ASSERT_EQ(2u, explanation.recommendations.size());
+    EXPECT_TRUE(
+        IsCipherRecommendation(explanation.recommendations[0], "AES_128_CBC"))
+        << explanation.recommendations[0];
+    EXPECT_TRUE(IsSignatureRecommendation(explanation.recommendations[1]))
+        << explanation.recommendations[1];
+  }
+
+  // Obsolete protocol version and cipher.
+  security_info.peer_signature_algorithm = 0;  // TLS 1.0 doesn't negotiate a
+                                               // signature algorithm.
+  net::SSLConnectionStatusSetVersion(net::SSL_CONNECTION_VERSION_TLS1,
+                                     &security_info.connection_status);
+  UpdateObsoleteSSLStatus(&security_info);
+  {
+    content::SecurityStyleExplanations explanations;
+    GetSecurityStyle(security_info, &explanations);
+    content::SecurityStyleExplanation explanation;
+    ASSERT_TRUE(FindSecurityStyleExplanation(
+        explanations.info_explanations,
+        l10n_util::GetStringUTF8(IDS_SSL_CONNECTION_TITLE),
+        l10n_util::GetStringUTF8(IDS_OBSOLETE_SSL_SUMMARY), &explanation));
+
+    ASSERT_EQ(2u, explanation.recommendations.size());
+    EXPECT_TRUE(
+        IsProtocolRecommendation(explanation.recommendations[0], "TLS 1.0"))
+        << explanation.recommendations[0];
+    EXPECT_TRUE(
+        IsCipherRecommendation(explanation.recommendations[1], "AES_128_CBC"))
+        << explanation.recommendations[1];
+  }
+
+  // Obsolete protocol version, cipher, and key exchange.
+  net::SSLConnectionStatusSetCipherSuite(
+      0x000a /* TLS_RSA_WITH_3DES_EDE_CBC_SHA */,
+      &security_info.connection_status);
+  UpdateObsoleteSSLStatus(&security_info);
+  {
+    content::SecurityStyleExplanations explanations;
+    GetSecurityStyle(security_info, &explanations);
+    content::SecurityStyleExplanation explanation;
+    ASSERT_TRUE(FindSecurityStyleExplanation(
+        explanations.info_explanations,
+        l10n_util::GetStringUTF8(IDS_SSL_CONNECTION_TITLE),
+        l10n_util::GetStringUTF8(IDS_OBSOLETE_SSL_SUMMARY), &explanation));
+
+    ASSERT_EQ(3u, explanation.recommendations.size());
+    EXPECT_TRUE(
+        IsProtocolRecommendation(explanation.recommendations[0], "TLS 1.0"))
+        << explanation.recommendations[0];
+    EXPECT_TRUE(IsKeyExchangeRecommendation(explanation.recommendations[1]))
+        << explanation.recommendations[1];
+    EXPECT_TRUE(
+        IsCipherRecommendation(explanation.recommendations[2], "3DES_EDE_CBC"))
+        << explanation.recommendations[2];
+  }
+
+  // Obsolete key exchange.
+  net::SSLConnectionStatusSetCipherSuite(
+      0x009c /* TLS_RSA_WITH_AES_128_GCM_SHA256 */,
+      &security_info.connection_status);
+  net::SSLConnectionStatusSetVersion(net::SSL_CONNECTION_VERSION_TLS1_2,
+                                     &security_info.connection_status);
+  security_info.peer_signature_algorithm = 0x0804;  // rsa_pss_rsae_sha256
+  UpdateObsoleteSSLStatus(&security_info);
+  {
+    content::SecurityStyleExplanations explanations;
+    GetSecurityStyle(security_info, &explanations);
+    content::SecurityStyleExplanation explanation;
+    ASSERT_TRUE(FindSecurityStyleExplanation(
+        explanations.info_explanations,
+        l10n_util::GetStringUTF8(IDS_SSL_CONNECTION_TITLE),
+        l10n_util::GetStringUTF8(IDS_OBSOLETE_SSL_SUMMARY), &explanation));
+
+    ASSERT_EQ(1u, explanation.recommendations.size());
+    EXPECT_TRUE(IsKeyExchangeRecommendation(explanation.recommendations[0]))
+        << explanation.recommendations[0];
+  }
+
+  // Obsolete signature.
+  net::SSLConnectionStatusSetCipherSuite(
+      0xc02f /* TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 */,
+      &security_info.connection_status);
+  security_info.peer_signature_algorithm = 0x0201;  // rsa_pkcs1_sha1
+  UpdateObsoleteSSLStatus(&security_info);
+  {
+    content::SecurityStyleExplanations explanations;
+    GetSecurityStyle(security_info, &explanations);
+    content::SecurityStyleExplanation explanation;
+    ASSERT_TRUE(FindSecurityStyleExplanation(
+        explanations.info_explanations,
+        l10n_util::GetStringUTF8(IDS_SSL_CONNECTION_TITLE),
+        l10n_util::GetStringUTF8(IDS_OBSOLETE_SSL_SUMMARY), &explanation));
+
+    ASSERT_EQ(1u, explanation.recommendations.size());
+    EXPECT_TRUE(IsSignatureRecommendation(explanation.recommendations[0]))
+        << explanation.recommendations[0];
   }
 }
 
diff --git a/components/security_state/core/security_state.cc b/components/security_state/core/security_state.cc
index fc2ca57..8901289c 100644
--- a/components/security_state/core/security_state.cc
+++ b/components/security_state/core/security_state.cc
@@ -258,11 +258,14 @@
   security_info->security_bits = visible_security_state.security_bits;
   security_info->connection_status = visible_security_state.connection_status;
   security_info->key_exchange_group = visible_security_state.key_exchange_group;
+  security_info->peer_signature_algorithm =
+      visible_security_state.peer_signature_algorithm;
   security_info->cert_status = visible_security_state.cert_status;
   security_info->scheme_is_cryptographic =
       visible_security_state.url.SchemeIsCryptographic();
   security_info->obsolete_ssl_status =
-      net::ObsoleteSSLStatus(security_info->connection_status);
+      net::ObsoleteSSLStatus(security_info->connection_status,
+                             security_info->peer_signature_algorithm);
   security_info->pkp_bypassed = visible_security_state.pkp_bypassed;
 
   security_info->malicious_content_status =
@@ -298,6 +301,7 @@
       security_bits(-1),
       connection_status(0),
       key_exchange_group(0),
+      peer_signature_algorithm(0),
       obsolete_ssl_status(net::OBSOLETE_SSL_NONE),
       pkp_bypassed(false),
       contained_mixed_form(false),
@@ -323,6 +327,7 @@
       cert_status(0),
       connection_status(0),
       key_exchange_group(0),
+      peer_signature_algorithm(0),
       security_bits(-1),
       displayed_mixed_content(false),
       contained_mixed_form(false),
diff --git a/components/security_state/core/security_state.h b/components/security_state/core/security_state.h
index 7e17299..7c8ec4d 100644
--- a/components/security_state/core/security_state.h
+++ b/components/security_state/core/security_state.h
@@ -132,6 +132,9 @@
   // The ID of the (EC)DH group used by the key exchange. The value is zero if
   // unknown (older cache entries may not store the value) or not applicable.
   uint16_t key_exchange_group;
+  // The signature algorithm used by the peer in the TLS handshake, or zero if
+  // unknown (older cache entries may not store the value) or not applicable.
+  uint16_t peer_signature_algorithm;
   // A mask that indicates which of the protocol version,
   // key exchange, or cipher for the connection is considered
   // obsolete. See net::ObsoleteSSLMask for specific mask values.
@@ -159,7 +162,6 @@
 struct VisibleSecurityState {
   VisibleSecurityState();
   ~VisibleSecurityState();
-  bool operator==(const VisibleSecurityState& other) const;
   GURL url;
 
   MaliciousContentStatus malicious_content_status;
@@ -175,6 +177,9 @@
   // The ID of the (EC)DH group used by the key exchange. The value is zero if
   // unknown (older cache entries may not store the value) or not applicable.
   uint16_t key_exchange_group;
+  // The signature algorithm used by the peer in the TLS handshake, or zero if
+  // unknown (older cache entries may not store the value) or not applicable.
+  uint16_t peer_signature_algorithm;
   int security_bits;
   // True if the page displayed passive mixed content.
   bool displayed_mixed_content;
diff --git a/components/security_state_strings.grdp b/components/security_state_strings.grdp
index 74ed1a8e..8aa8d5e 100644
--- a/components/security_state_strings.grdp
+++ b/components/security_state_strings.grdp
@@ -67,8 +67,8 @@
   <message name="IDS_SSL_CONNECTION_TITLE" desc="Title for the SSL connection explanation." translateable="false">
     Connection
   </message>
-  <message name="IDS_STRONG_SSL_SUMMARY" desc="Summary phrase for a site that uses a modern, secure TLS protocol and cipher." translateable="false">
-    secure (strong <ph name="PROTOCOL_VERSION">$1<ex>TLS 1.2</ex></ph>)
+  <message name="IDS_SECURE_SSL_SUMMARY" desc="Summary phrase for a site that uses a modern, secure TLS protocol and cipher." translateable="false">
+    secure connection settings
   </message>
   <message name="IDS_PRIVATE_KEY_PINNING_BYPASSED" desc="Summary phrase for a security state where Private Key Pinning is ignored because the certificate chains to a locally-trusted root." translateable="false">
     Public-Key-Pinning bypassed
@@ -76,40 +76,33 @@
   <message name="IDS_PRIVATE_KEY_PINNING_BYPASSED_DESCRIPTION" desc="Description of a security state where Private Key Pinning is ignored because the certificate chains to a locally-trusted root." translateable="false">
     Public-Key-Pinning was bypassed by a local root certificate.
   </message>
-  <message name="IDS_STRONG_SSL_DESCRIPTION" desc="Description of a site that uses a modern, secure TLS protocol and cipher." translateable="false">
-    The connection to this site is encrypted and authenticated using <ph name="PROTOCOL_VERSION">$1<ex>TLS 1.2</ex></ph> (a strong protocol), <ph name="KEY_EXCHANGE">$2<ex>ECDHE_RSA</ex></ph> (a strong key exchange), and <ph name="CIPHER_SUTE">$3<ex>AES_128_GCM</ex></ph> (a strong cipher).
+  <message name="IDS_SSL_DESCRIPTION" desc="Description of a site's TLS settings." translateable="false">
+    The connection to this site is encrypted and authenticated using <ph name="PROTOCOL_VERSION">$1<ex>TLS 1.2</ex></ph>, <ph name="KEY_EXCHANGE">$2<ex>ECDHE_RSA</ex></ph>, and <ph name="CIPHER_SUTE">$3<ex>AES_128_GCM</ex></ph>.
   </message>
   <message name="IDS_OBSOLETE_SSL_SUMMARY" desc="Summary phrase for a site that uses an outdated SSL settings (protocol, key exchange, or cipher)." translateable="false">
     obsolete connection settings
   </message>
-  <message name="IDS_OBSOLETE_SSL_DESCRIPTION" desc="Description of a site that uses an outdated TLS protocol or cipher." translateable="false">
-    The connection to this site uses <ph name="PROTOCOL">$1<ex>TLS 1.0</ex></ph> (<ph name="A_PROTOCOL">$2<ex>an obsolete protocol</ex></ph>), <ph name="KEY_EXCHANGE">$3<ex>ECDHE_RSA</ex></ph> (<ph name="A_KEY_EXCHANGE">$4<ex>an obsolete key exchange</ex></ph>), and <ph name="CIPHER">$5<ex>AES_256_CBC with HMAC-SHA1</ex></ph> (<ph name="A_CIPHER">$6<ex>an obsolete cipher</ex></ph>).
-  </message>
   <message name="IDS_CIPHER_WITH_MAC" desc="Description of an SSL cipher that contains a separate (bulk) cipher and MAC." translateable="false">
     <ph name="CIPHER">$1<ex>AES_256_CBC</ex></ph> with <ph name="MAC">$2<ex>HMAC-SHA1</ex></ph>
   </message>
-  <message name="IDS_SSL_AN_OBSOLETE_PROTOCOL" desc="A phrase to describe an SSL/TLS protocol (usually an older version like TLS 1.0) that is old and insecure" translateable="false">
-    an obsolete protocol
-  </message>
-  <message name="IDS_SSL_A_STRONG_PROTOCOL" desc="A phrase to describe an SSL/TLS protocol (e.g. TLS 1.2 or QUIC) that is modern and secure." translateable="false">
-    a strong protocol
-  </message>
-  <message name="IDS_SSL_AN_OBSOLETE_KEY_EXCHANGE" desc="A phrase to describe an SSL/TLS key exchange (e.g. ECDHE) that is old and insecure." translateable="false">
-    an obsolete key exchange
-  </message>
-  <message name="IDS_SSL_A_STRONG_KEY_EXCHANGE" desc="A phrase to describe an SSL/TLS key exchange (e.g. RSA) that is modern and secure." translateable="false">
-    a strong key exchange
-  </message>
-  <message name="IDS_SSL_AN_OBSOLETE_CIPHER" desc="A phrase to describe an SSL/TLS cipher (e.g. AES_256_CBC) that is old and insecure." translateable="false">
-    an obsolete cipher
-  </message>
-  <message name="IDS_SSL_A_STRONG_CIPHER" desc="A phrase to describe an SSL/TLS cipher (e.g. AES_128_GCM or ChaCha20 with Poly1305) that is modern and secure." translateable="false">
-    a strong cipher
-  </message>
   <message name="IDS_SSL_KEY_EXCHANGE_WITH_GROUP" desc="A phrase to describe an SSL/TLS ECDHE-based key exchange with a group." translateable="false">
     <ph name="A_KEY_EXCHANGE">$1<ex>ECDHE_RSA</ex></ph> with <ph name="A_GROUP">$2<ex>X25519</ex></ph>
   </message>
 
+  <!-- Recommendations for sites using obsolete TLS settings. -->
+  <message name="IDS_SSL_RECOMMEND_PROTOCOL" desc="A recommendation to the site owner to use a modern TLS protocol" translateable="false">
+    <ph name="PROTOCOL">$1<ex>TLS 1.0</ex></ph> is obsolete. Enable TLS 1.2 or later.
+  </message>
+  <message name="IDS_SSL_RECOMMEND_KEY_EXCHANGE" desc="A recommendation to the site owner to use a modern TLS key exchange" translateable="false">
+    RSA key exchange is obsolete. Enable an ECDHE-based cipher suite.
+  </message>
+  <message name="IDS_SSL_RECOMMEND_CIPHER" desc="A recommendation to the site owner to use a modern TLS cipher" translateable="false">
+    <ph name="CIPHER">$1<ex>3DES_EDE_CBC</ex></ph> is obsolete. Enable an AES-GCM-based cipher suite.
+  </message>
+  <message name="IDS_SSL_RECOMMEND_SIGNATURE" desc="A recommendation to the site owner to use a modern TLS server signature" translateable="false">
+    The server signature uses SHA-1, which is obsolete. Enable a SHA-2 signature algorithm instead. (Note this is different from the signature in the certificate.)
+  </message>
+
   <!-- Mixed Content -->
   <message name="IDS_RESOURCE_SECURITY_TITLE" desc="Title for the resources security explanation" translateable="false">
     Resources
diff --git a/components/translate/core/browser/translate_url_fetcher.cc b/components/translate/core/browser/translate_url_fetcher.cc
index 9dae291..254e0ab 100644
--- a/components/translate/core/browser/translate_url_fetcher.cc
+++ b/components/translate/core/browser/translate_url_fetcher.cc
@@ -98,7 +98,7 @@
   resource_request->load_flags =
       net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
   if (!extra_request_header_.empty())
-    resource_request->headers.AddHeadersFromString(extra_request_header_);
+    resource_request->headers.AddHeaderFromString(extra_request_header_);
 
   simple_loader_ =
       variations::CreateSimpleURLLoaderWithVariationsHeadersUnknownSignedIn(
diff --git a/components/viz/service/display/overlay_unittest.cc b/components/viz/service/display/overlay_unittest.cc
index 2dd9c48..688349bc 100644
--- a/components/viz/service/display/overlay_unittest.cc
+++ b/components/viz/service/display/overlay_unittest.cc
@@ -568,6 +568,16 @@
   std::vector<gfx::Rect> content_bounds_;
 };
 
+OverlayCandidateList BackbufferOverlayList(const RenderPass* root_render_pass) {
+  OverlayCandidateList list;
+  OverlayCandidate output_surface_plane;
+  output_surface_plane.display_rect = gfx::RectF(root_render_pass->output_rect);
+  output_surface_plane.use_output_surface_for_resource = true;
+  output_surface_plane.overlay_handled = true;
+  list.push_back(output_surface_plane);
+  return list;
+}
+
 using FullscreenOverlayTest = OverlayTest<FullscreenOverlayValidator>;
 using SingleOverlayOnTopTest = OverlayTest<SingleOnTopOverlayValidator>;
 using UnderlayTest = OverlayTest<UnderlayOverlayValidator>;
@@ -1529,6 +1539,25 @@
   EXPECT_EQ(0U, candidate_list.size());
 }
 
+TEST_F(SingleOverlayOnTopTest, AllowVideoNormalTransform) {
+  std::unique_ptr<RenderPass> pass = CreateRenderPass();
+  CreateFullscreenCandidateVideoQuad(
+      resource_provider_.get(), child_resource_provider_.get(),
+      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(),
+      kNormalTransform);
+
+  OverlayCandidateList candidate_list;
+  OverlayProcessor::FilterOperationsMap render_pass_filters;
+  OverlayProcessor::FilterOperationsMap render_pass_background_filters;
+  RenderPassList pass_list;
+  pass_list.push_back(std::move(pass));
+  overlay_processor_->ProcessForOverlays(
+      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+      render_pass_filters, render_pass_background_filters, &candidate_list,
+      nullptr, nullptr, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(1U, candidate_list.size());
+}
+
 TEST_F(SingleOverlayOnTopTest, RejectVideoSwapTransform) {
   std::unique_ptr<RenderPass> pass = CreateRenderPass();
   CreateFullscreenCandidateVideoQuad(
@@ -1548,6 +1577,33 @@
   EXPECT_EQ(0U, candidate_list.size());
 }
 
+TEST_F(SingleOverlayOnTopTest,
+       AllowVideoNormalTransformWithOutputSurfaceOverlay) {
+  std::unique_ptr<RenderPass> pass = CreateRenderPass();
+  CreateFullscreenCandidateQuad(
+      resource_provider_.get(), child_resource_provider_.get(),
+      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+
+  gfx::Rect video_rect = gfx::ScaleToEnclosingRect(pass->output_rect, .5);
+  CreateCandidateVideoQuadAt(
+      resource_provider_.get(), child_resource_provider_.get(),
+      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get(),
+      video_rect, kNormalTransform);
+
+  OverlayCandidateList candidate_list(BackbufferOverlayList(pass.get()));
+  OverlayProcessor::FilterOperationsMap render_pass_filters;
+  OverlayProcessor::FilterOperationsMap render_pass_background_filters;
+  RenderPassList pass_list;
+  pass_list.push_back(std::move(pass));
+  overlay_processor_->ProcessForOverlays(
+      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+      render_pass_filters, render_pass_background_filters, &candidate_list,
+      nullptr, nullptr, &damage_rect_, &content_bounds_);
+  ASSERT_EQ(2U, candidate_list.size());
+  EXPECT_EQ(candidate_list[0].uv_rect, gfx::RectF(.5, .5));
+  EXPECT_EQ(candidate_list[1].uv_rect, gfx::RectF(.1, .2, .9, .8));
+}
+
 TEST_F(UnderlayTest, AllowVideoXMirrorTransform) {
   std::unique_ptr<RenderPass> pass = CreateRenderPass();
   CreateFullscreenCandidateVideoQuad(
@@ -2231,16 +2287,6 @@
   EXPECT_EQ(output_rect, content_bounds_[0]);
 }
 
-OverlayCandidateList BackbufferOverlayList(const RenderPass* root_render_pass) {
-  OverlayCandidateList list;
-  OverlayCandidate output_surface_plane;
-  output_surface_plane.display_rect = gfx::RectF(root_render_pass->output_rect);
-  output_surface_plane.use_output_surface_for_resource = true;
-  output_surface_plane.overlay_handled = true;
-  list.push_back(output_surface_plane);
-  return list;
-}
-
 TEST_F(CALayerOverlayTest, AllowNonAxisAlignedTransform) {
   std::unique_ptr<RenderPass> pass = CreateRenderPass();
   CreateFullscreenCandidateQuad(
diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc
index 30a9ccc..d6320ee 100644
--- a/content/app/content_main_runner_impl.cc
+++ b/content/app/content_main_runner_impl.cc
@@ -281,10 +281,12 @@
   // zygote are both disabled. It initializes the sandboxed process socket.
   SandboxHostLinux::GetInstance()->Init();
 
-  if (parsed_command_line.HasSwitch(switches::kNoZygote) &&
-      !parsed_command_line.HasSwitch(service_manager::switches::kNoSandbox)) {
-    LOG(ERROR) << "--no-sandbox should be used together with --no--zygote";
-    exit(EXIT_FAILURE);
+  if (parsed_command_line.HasSwitch(switches::kNoZygote)) {
+    if (!parsed_command_line.HasSwitch(service_manager::switches::kNoSandbox)) {
+      LOG(ERROR) << "--no-sandbox should be used together with --no--zygote";
+      exit(EXIT_FAILURE);
+    }
+    return;
   }
 
   // Tickle the zygote host so it forks now.
diff --git a/content/browser/devtools/devtools_network_interceptor.cc b/content/browser/devtools/devtools_network_interceptor.cc
index 7554d6f..500932d3 100644
--- a/content/browser/devtools/devtools_network_interceptor.cc
+++ b/content/browser/devtools/devtools_network_interceptor.cc
@@ -9,6 +9,17 @@
 
 namespace content {
 
+DevToolsNetworkInterceptor::AuthChallengeResponse::AuthChallengeResponse(
+    ResponseType response_type)
+    : response_type(response_type) {
+  DCHECK_NE(kProvideCredentials, response_type);
+}
+
+DevToolsNetworkInterceptor::AuthChallengeResponse::AuthChallengeResponse(
+    const base::string16& username,
+    const base::string16& password)
+    : response_type(kProvideCredentials), credentials(username, password) {}
+
 InterceptedRequestInfo::InterceptedRequestInfo()
     : is_navigation(false), response_error_code(net::OK) {}
 
@@ -33,9 +44,8 @@
     protocol::Maybe<std::string> modified_url,
     protocol::Maybe<std::string> modified_method,
     protocol::Maybe<std::string> modified_post_data,
-    protocol::Maybe<protocol::Network::Headers> modified_headers,
-    protocol::Maybe<protocol::Network::AuthChallengeResponse>
-        auth_challenge_response)
+    std::unique_ptr<HeadersVector> modified_headers,
+    std::unique_ptr<AuthChallengeResponse> auth_challenge_response)
     : error_reason(std::move(error_reason)),
       raw_response(std::move(raw_response)),
       modified_url(std::move(modified_url)),
diff --git a/content/browser/devtools/devtools_network_interceptor.h b/content/browser/devtools/devtools_network_interceptor.h
index 684ec854..331e326 100644
--- a/content/browser/devtools/devtools_network_interceptor.h
+++ b/content/browser/devtools/devtools_network_interceptor.h
@@ -12,6 +12,7 @@
 #include "content/browser/devtools/protocol/network.h"
 #include "content/public/common/resource_type.h"
 #include "mojo/public/cpp/system/data_pipe.h"
+#include "net/base/auth.h"
 #include "net/base/net_errors.h"
 
 namespace net {
@@ -52,16 +53,35 @@
                               mojo::ScopedDataPipeConsumerHandle,
                               const std::string& mime_type)>;
 
+  struct AuthChallengeResponse {
+    enum ResponseType {
+      kDefault,
+      kCancelAuth,
+      kProvideCredentials,
+    };
+
+    explicit AuthChallengeResponse(ResponseType response_type);
+    AuthChallengeResponse(const base::string16& username,
+                          const base::string16& password);
+
+    const ResponseType response_type;
+    const net::AuthCredentials credentials;
+
+    DISALLOW_COPY_AND_ASSIGN(AuthChallengeResponse);
+  };
+
   struct Modifications {
+    using HeadersVector = std::vector<std::pair<std::string, std::string>>;
+
     Modifications();
-    Modifications(base::Optional<net::Error> error_reason,
-                  base::Optional<std::string> raw_response,
-                  protocol::Maybe<std::string> modified_url,
-                  protocol::Maybe<std::string> modified_method,
-                  protocol::Maybe<std::string> modified_post_data,
-                  protocol::Maybe<protocol::Network::Headers> modified_headers,
-                  protocol::Maybe<protocol::Network::AuthChallengeResponse>
-                      auth_challenge_response);
+    Modifications(
+        base::Optional<net::Error> error_reason,
+        base::Optional<std::string> raw_response,
+        protocol::Maybe<std::string> modified_url,
+        protocol::Maybe<std::string> modified_method,
+        protocol::Maybe<std::string> modified_post_data,
+        std::unique_ptr<HeadersVector> modified_headers,
+        std::unique_ptr<AuthChallengeResponse> auth_challenge_response);
     ~Modifications();
 
     // If none of the following are set then the request will be allowed to
@@ -73,11 +93,9 @@
     protocol::Maybe<std::string> modified_url;
     protocol::Maybe<std::string> modified_method;
     protocol::Maybe<std::string> modified_post_data;
-    protocol::Maybe<protocol::Network::Headers> modified_headers;
-
+    std::unique_ptr<HeadersVector> modified_headers;
     // AuthChallengeResponse is mutually exclusive with the above.
-    protocol::Maybe<protocol::Network::AuthChallengeResponse>
-        auth_challenge_response;
+    std::unique_ptr<AuthChallengeResponse> auth_challenge_response;
   };
 
   enum InterceptionStage {
diff --git a/content/browser/devtools/devtools_url_interceptor_request_job.cc b/content/browser/devtools/devtools_url_interceptor_request_job.cc
index 6c66d79..cfc0913a 100644
--- a/content/browser/devtools/devtools_url_interceptor_request_job.cc
+++ b/content/browser/devtools/devtools_url_interceptor_request_job.cc
@@ -917,23 +917,12 @@
           std::make_unique<DevToolsNetworkInterceptor::Modifications>(
               base::nullopt, base::nullopt, protocol::Maybe<std::string>(),
               protocol::Maybe<std::string>(), protocol::Maybe<std::string>(),
-              protocol::Maybe<protocol::Network::Headers>(),
-              protocol::Maybe<protocol::Network::AuthChallengeResponse>()));
+              nullptr, nullptr));
       return;
-    case WaitingForUserResponse::WAITING_FOR_AUTH_ACK: {
-      std::unique_ptr<protocol::Network::AuthChallengeResponse> auth_response =
-          protocol::Network::AuthChallengeResponse::Create()
-              .SetResponse(protocol::Network::AuthChallengeResponse::
-                               ResponseEnum::Default)
-              .Build();
-      ProcessAuthResponse(
-          std::make_unique<DevToolsNetworkInterceptor::Modifications>(
-              base::nullopt, base::nullopt, protocol::Maybe<std::string>(),
-              protocol::Maybe<std::string>(), protocol::Maybe<std::string>(),
-              protocol::Maybe<protocol::Network::Headers>(),
-              std::move(auth_response)));
+    case WaitingForUserResponse::WAITING_FOR_AUTH_ACK:
+      ProcessAuthResponse(DevToolsNetworkInterceptor::AuthChallengeResponse(
+          DevToolsNetworkInterceptor::AuthChallengeResponse::kDefault));
       return;
-    }
 
     default:
       NOTREACHED();
@@ -958,7 +947,7 @@
     case WaitingForUserResponse::WAITING_FOR_RESPONSE_ACK:
     // Fallthough.
     case WaitingForUserResponse::WAITING_FOR_REQUEST_ACK:
-      if (modifications->auth_challenge_response.isJust()) {
+      if (modifications->auth_challenge_response) {
         base::PostTaskWithTraits(
             FROM_HERE, {BrowserThread::UI},
             base::BindOnce(&ContinueInterceptedRequestCallback::sendFailure,
@@ -975,7 +964,7 @@
       break;
 
     case WaitingForUserResponse::WAITING_FOR_AUTH_ACK:
-      if (!modifications->auth_challenge_response.isJust()) {
+      if (!modifications->auth_challenge_response) {
         base::PostTaskWithTraits(
             FROM_HERE, {BrowserThread::UI},
             base::BindOnce(&ContinueInterceptedRequestCallback::sendFailure,
@@ -984,19 +973,11 @@
                                "authChallengeResponse required.")));
         break;
       }
-      if (ProcessAuthResponse(std::move(modifications))) {
-        base::PostTaskWithTraits(
-            FROM_HERE, {BrowserThread::UI},
-            base::BindOnce(&ContinueInterceptedRequestCallback::sendSuccess,
-                           std::move(callback)));
-      } else {
-        base::PostTaskWithTraits(
-            FROM_HERE, {BrowserThread::UI},
-            base::BindOnce(&ContinueInterceptedRequestCallback::sendFailure,
-                           std::move(callback),
-                           protocol::Response::InvalidParams(
-                               "Unrecognized authChallengeResponse.")));
-      }
+      ProcessAuthResponse(*modifications->auth_challenge_response);
+      base::PostTaskWithTraits(
+          FROM_HERE, {BrowserThread::UI},
+          base::BindOnce(&ContinueInterceptedRequestCallback::sendSuccess,
+                         std::move(callback)));
       break;
 
     default:
@@ -1163,22 +1144,17 @@
               std::make_unique<net::UploadOwnedBytesElementReader>(&data), 0);
     }
 
-    if (modifications->modified_headers.isJust()) {
+    if (modifications->modified_headers) {
       request_details_.extra_request_headers.Clear();
-      std::unique_ptr<protocol::DictionaryValue> headers =
-          modifications->modified_headers.fromJust()->toValue();
-      for (size_t i = 0; i < headers->size(); i++) {
-        protocol::DictionaryValue::Entry entry = headers->at(i);
-        std::string value;
-        if (!entry.second->asString(&value))
-          continue;
+      for (const auto& entry : *modifications->modified_headers) {
         if (base::EqualsCaseInsensitiveASCII(
                 entry.first, net::HttpRequestHeaders::kReferer)) {
-          request_details_.referrer = value;
+          request_details_.referrer = entry.second;
           request_details_.referrer_policy =
               net::URLRequest::NEVER_CLEAR_REFERRER;
         } else {
-          request_details_.extra_request_headers.SetHeader(entry.first, value);
+          request_details_.extra_request_headers.SetHeader(entry.first,
+                                                           entry.second);
         }
       }
     }
@@ -1196,40 +1172,26 @@
   }
 }
 
-bool DevToolsURLInterceptorRequestJob::ProcessAuthResponse(
-    std::unique_ptr<DevToolsNetworkInterceptor::Modifications> modifications) {
+void DevToolsURLInterceptorRequestJob::ProcessAuthResponse(
+    const DevToolsNetworkInterceptor::AuthChallengeResponse& response) {
   waiting_for_user_response_ = WaitingForUserResponse::NOT_WAITING;
 
-  protocol::Network::AuthChallengeResponse* auth_challenge_response =
-      modifications->auth_challenge_response.fromJust();
-
-  if (auth_challenge_response->GetResponse() ==
-      protocol::Network::AuthChallengeResponse::ResponseEnum::Default) {
-    // The user wants the default behavior, we must proxy the auth request to
-    // the original URLRequest::Delegate.  We can't do that directly but by
-    // implementing NeedsAuth and calling NotifyHeadersComplete we trigger it.
-    // To close the loop we also need to implement GetAuthChallengeInfo, SetAuth
-    // and CancelAuth.
-    NotifyHeadersComplete();
-    return true;
+  switch (response.response_type) {
+    case DevToolsNetworkInterceptor::AuthChallengeResponse::kDefault:
+      // The user wants the default behavior, we must proxy the auth request to
+      // the original URLRequest::Delegate.  We can't do that directly but by
+      // implementing NeedsAuth and calling NotifyHeadersComplete we trigger it.
+      // To close the loop we also need to implement GetAuthChallengeInfo,
+      // SetAuth and CancelAuth.
+      NotifyHeadersComplete();
+      break;
+    case DevToolsNetworkInterceptor::AuthChallengeResponse::kCancelAuth:
+      CancelAuth();
+      break;
+    case DevToolsNetworkInterceptor::AuthChallengeResponse::kProvideCredentials:
+      SetAuth(response.credentials);
+      break;
   }
-
-  if (auth_challenge_response->GetResponse() ==
-      protocol::Network::AuthChallengeResponse::ResponseEnum::CancelAuth) {
-    CancelAuth();
-    return true;
-  }
-
-  if (auth_challenge_response->GetResponse() ==
-      protocol::Network::AuthChallengeResponse::ResponseEnum::
-          ProvideCredentials) {
-    SetAuth(net::AuthCredentials(
-        base::UTF8ToUTF16(auth_challenge_response->GetUsername("")),
-        base::UTF8ToUTF16(auth_challenge_response->GetPassword(""))));
-    return true;
-  }
-
-  return false;
 }
 
 void DevToolsURLInterceptorRequestJob::SetRequestHeadersCallback(
diff --git a/content/browser/devtools/devtools_url_interceptor_request_job.h b/content/browser/devtools/devtools_url_interceptor_request_job.h
index 0dc53c2..5e52884c 100644
--- a/content/browser/devtools/devtools_url_interceptor_request_job.h
+++ b/content/browser/devtools/devtools_url_interceptor_request_job.h
@@ -135,8 +135,8 @@
   void ProcessInterceptionResponse(
       std::unique_ptr<DevToolsNetworkInterceptor::Modifications> modification);
 
-  bool ProcessAuthResponse(
-      std::unique_ptr<DevToolsNetworkInterceptor::Modifications> modification);
+  void ProcessAuthResponse(
+      const DevToolsNetworkInterceptor::AuthChallengeResponse& response);
 
   enum class WaitingForUserResponse {
     NOT_WAITING,
diff --git a/content/browser/devtools/devtools_url_loader_interceptor.cc b/content/browser/devtools/devtools_url_loader_interceptor.cc
index 35e4e3b..c524694 100644
--- a/content/browser/devtools/devtools_url_loader_interceptor.cc
+++ b/content/browser/devtools/devtools_url_loader_interceptor.cc
@@ -43,7 +43,6 @@
 using Modifications = DevToolsNetworkInterceptor::Modifications;
 using InterceptionStage = DevToolsNetworkInterceptor::InterceptionStage;
 using protocol::Response;
-using protocol::Network::AuthChallengeResponse;
 using GlobalRequestId = std::tuple<int32_t, int32_t, int32_t>;
 
 struct CreateLoaderParameters {
@@ -214,7 +213,9 @@
   }
 
   Response InnerContinueRequest(std::unique_ptr<Modifications> modifications);
-  Response ProcessAuthResponse(AuthChallengeResponse* auth_challenge_response);
+  void ProcessAuthResponse(
+      const DevToolsNetworkInterceptor::AuthChallengeResponse&
+          auth_challenge_response);
   Response ProcessResponseOverride(std::string response);
   void ProcessRedirectByClient(const GURL& redirect_url);
   void ProcessSetCookies(const net::HttpResponseHeaders& response_headers,
@@ -802,12 +803,12 @@
   waiting_for_resolution_ = false;
 
   if (state_ == State::kAuthRequired) {
-    if (!modifications->auth_challenge_response.isJust())
+    if (!modifications->auth_challenge_response)
       return Response::InvalidParams("authChallengeResponse required.");
-    return ProcessAuthResponse(
-        modifications->auth_challenge_response.fromJust());
+    ProcessAuthResponse(*modifications->auth_challenge_response);
+    return Response::OK();
   }
-  if (modifications->auth_challenge_response.isJust())
+  if (modifications->auth_challenge_response)
     return Response::InvalidParams("authChallengeResponse not expected.");
 
   if (modifications->error_reason) {
@@ -910,45 +911,33 @@
         post_data.data(), post_data.size());
   }
 
-  if (modifications->modified_headers.isJust()) {
+  if (modifications->modified_headers) {
     request->headers.Clear();
-    std::unique_ptr<protocol::DictionaryValue> headers =
-        modifications->modified_headers.fromJust()->toValue();
-    for (size_t i = 0; i < headers->size(); i++) {
-      protocol::DictionaryValue::Entry entry = headers->at(i);
-      std::string value;
-      if (!entry.second->asString(&value))
-        continue;
+    for (const auto& entry : *modifications->modified_headers) {
       if (base::EqualsCaseInsensitiveASCII(entry.first,
                                            net::HttpRequestHeaders::kReferer)) {
-        request->referrer = GURL(value);
+        request->referrer = GURL(entry.second);
         request->referrer_policy = net::URLRequest::NEVER_CLEAR_REFERRER;
       } else {
-        request->headers.SetHeader(entry.first, value);
+        request->headers.SetHeader(entry.first, entry.second);
       }
     }
   }
 }
 
-Response InterceptionJob::ProcessAuthResponse(
-    AuthChallengeResponse* auth_challenge_response) {
-  std::string response = auth_challenge_response->GetResponse();
-  state_ = State::kRequestSent;
-  if (response == AuthChallengeResponse::ResponseEnum::Default) {
-    std::move(pending_auth_callback_).Run(true, base::nullopt);
-  } else if (response == AuthChallengeResponse::ResponseEnum::CancelAuth) {
-    std::move(pending_auth_callback_).Run(false, base::nullopt);
-  } else if (response ==
-             AuthChallengeResponse::ResponseEnum::ProvideCredentials) {
-    net::AuthCredentials credentials(
-        base::UTF8ToUTF16(auth_challenge_response->GetUsername("")),
-        base::UTF8ToUTF16(auth_challenge_response->GetPassword("")));
-    std::move(pending_auth_callback_).Run(false, std::move(credentials));
-  } else {
-    return Response::InvalidParams("Unrecognized authChallengeResponse.");
+void InterceptionJob::ProcessAuthResponse(
+    const DevToolsNetworkInterceptor::AuthChallengeResponse& response) {
+  switch (response.response_type) {
+    case DevToolsNetworkInterceptor::AuthChallengeResponse::kDefault:
+      std::move(pending_auth_callback_).Run(true, base::nullopt);
+      break;
+    case DevToolsNetworkInterceptor::AuthChallengeResponse::kCancelAuth:
+      std::move(pending_auth_callback_).Run(false, base::nullopt);
+      break;
+    case DevToolsNetworkInterceptor::AuthChallengeResponse::kProvideCredentials:
+      std::move(pending_auth_callback_).Run(false, response.credentials);
+      break;
   }
-
-  return Response::OK();
 }
 
 Response InterceptionJob::ProcessResponseOverride(std::string response) {
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 722e29a1..f7bbc60 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -1858,7 +1858,7 @@
     Maybe<std::string> url,
     Maybe<std::string> method,
     Maybe<std::string> post_data,
-    Maybe<protocol::Network::Headers> headers,
+    Maybe<protocol::Network::Headers> opt_headers,
     Maybe<protocol::Network::AuthChallengeResponse> auth_challenge_response,
     std::unique_ptr<ContinueInterceptedRequestCallback> callback) {
   base::Optional<std::string> raw_response;
@@ -1881,11 +1881,54 @@
     }
   }
 
+  std::unique_ptr<DevToolsNetworkInterceptor::Modifications::HeadersVector>
+      override_headers;
+  if (opt_headers.isJust()) {
+    std::unique_ptr<protocol::DictionaryValue> headers =
+        opt_headers.fromJust()->toValue();
+    override_headers = std::make_unique<
+        DevToolsNetworkInterceptor::Modifications::HeadersVector>();
+    for (size_t i = 0; i < headers->size(); ++i) {
+      const protocol::DictionaryValue::Entry& entry = headers->at(i);
+      std::string value;
+      if (!entry.second->asString(&value)) {
+        callback->sendFailure(Response::InvalidParams("Invalid header value"));
+        return;
+      }
+      override_headers->emplace_back(entry.first, value);
+    }
+  }
+  using AuthChallengeResponse =
+      DevToolsNetworkInterceptor::AuthChallengeResponse;
+  std::unique_ptr<AuthChallengeResponse> override_auth;
+  if (auth_challenge_response.isJust()) {
+    std::string type = auth_challenge_response.fromJust()->GetResponse();
+    if (type == Network::AuthChallengeResponse::ResponseEnum::Default) {
+      override_auth = std::make_unique<AuthChallengeResponse>(
+          AuthChallengeResponse::kDefault);
+    } else if (type ==
+               Network::AuthChallengeResponse::ResponseEnum::CancelAuth) {
+      override_auth = std::make_unique<AuthChallengeResponse>(
+          AuthChallengeResponse::kCancelAuth);
+    } else if (type == Network::AuthChallengeResponse::ResponseEnum::
+                           ProvideCredentials) {
+      override_auth = std::make_unique<AuthChallengeResponse>(
+          base::UTF8ToUTF16(
+              auth_challenge_response.fromJust()->GetUsername("")),
+          base::UTF8ToUTF16(
+              auth_challenge_response.fromJust()->GetPassword("")));
+    } else {
+      callback->sendFailure(
+          Response::InvalidParams("Unrecognized authChallengeResponse."));
+      return;
+    }
+  }
+
   auto modifications =
       std::make_unique<DevToolsNetworkInterceptor::Modifications>(
           std::move(error), std::move(raw_response), std::move(url),
-          std::move(method), std::move(post_data), std::move(headers),
-          std::move(auth_challenge_response));
+          std::move(method), std::move(post_data), std::move(override_headers),
+          std::move(override_auth));
 
   if (url_loader_interceptor_) {
     url_loader_interceptor_->ContinueInterceptedRequest(
diff --git a/content/browser/devtools/protocol/security_handler.cc b/content/browser/devtools/protocol/security_handler.cc
index 29e074d..e27a7db 100644
--- a/content/browser/devtools/protocol/security_handler.cc
+++ b/content/browser/devtools/protocol/security_handler.cc
@@ -86,6 +86,12 @@
       }
     }
 
+    std::unique_ptr<protocol::Array<String>> recommendations =
+        protocol::Array<String>::create();
+    for (const auto& recommendation : it.recommendations) {
+      recommendations->addItem(recommendation);
+    }
+
     explanations->addItem(
         Security::SecurityStateExplanation::Create()
             .SetSecurityState(security_style)
@@ -95,6 +101,7 @@
             .SetCertificate(std::move(certificate))
             .SetMixedContentType(MixedContentTypeToProtocolMixedContentType(
                 it.mixed_content_type))
+            .SetRecommendations(std::move(recommendations))
             .Build());
   }
 }
diff --git a/content/browser/frame_host/navigation_controller_delegate.h b/content/browser/frame_host/navigation_controller_delegate.h
index 64c22ec..8a54d98 100644
--- a/content/browser/frame_host/navigation_controller_delegate.h
+++ b/content/browser/frame_host/navigation_controller_delegate.h
@@ -70,14 +70,6 @@
   virtual void DetachInterstitialPage(bool has_focus) = 0;
 
   virtual void UpdateOverridingUserAgent() = 0;
-
-  // Gives the delegate a chance to adjust the previews state during navigation.
-  // When called, previews_state will be pointing to a valid set of previews, or
-  // an enum value disabling previews.  The call will change the value of
-  // previews_state in place, and must change it to either a value disabling
-  // previews, or a subset of the previews passed in.
-  virtual void AdjustPreviewsStateForNavigation(
-      PreviewsState* previews_state) = 0;
 };
 
 }  // namespace content
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 760bd03..28f7159 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -2748,10 +2748,6 @@
                          ->last_navigation_previews_state();
   }
 
-  // Give the delegate an opportunity to adjust the previews state.
-  if (delegate_)
-    delegate_->AdjustPreviewsStateForNavigation(&previews_state);
-
   // This will be used to set the Navigation Timing API navigationStart
   // parameter for browser navigations in new tabs (intents, tabs opened through
   // "Open link in new tab"). If the navigation must wait on the current
@@ -2848,13 +2844,6 @@
                          ->last_navigation_previews_state();
   }
 
-  // Give the delegate an opportunity to adjust the previews state.
-  // TODO(ryansturm): move this into ContentBrowserClient
-  // DetermineEnabledPreviews, so redirects can be evaluated.
-  // https://crbug.com/892253.
-  if (delegate_)
-    delegate_->AdjustPreviewsStateForNavigation(&previews_state);
-
   // This will be used to set the Navigation Timing API navigationStart
   // parameter for browser navigations in new tabs (intents, tabs opened through
   // "Open link in new tab"). If the navigation must wait on the current
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc
index f8a8685..56a5851 100644
--- a/content/browser/frame_host/navigation_handle_impl.cc
+++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -234,6 +234,7 @@
       is_download_(false),
       is_stream_(false),
       is_signed_exchange_inner_response_(false),
+      was_cached_(false),
       started_from_context_menu_(started_from_context_menu),
       is_same_process_(true),
       weak_factory_(this) {
@@ -552,17 +553,20 @@
 NavigationThrottle::ThrottleCheckResult
 NavigationHandleImpl::CallWillProcessResponseForTesting(
     RenderFrameHost* render_frame_host,
-    const std::string& raw_response_headers) {
+    const std::string& raw_response_headers,
+    bool was_cached,
+    const net::ProxyServer& proxy_server) {
   scoped_refptr<net::HttpResponseHeaders> headers =
       new net::HttpResponseHeaders(raw_response_headers);
   NavigationThrottle::ThrottleCheckResult result = NavigationThrottle::DEFER;
+  set_proxy_server(proxy_server);
   WillProcessResponse(static_cast<RenderFrameHostImpl*>(render_frame_host),
                       headers, net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN,
                       net::HostPortPair(), net::SSLInfo(), GlobalRequestID(),
                       /* should_replace_current_entry=*/false,
                       /* is_download=*/false,
                       /* is_stream=*/false,
-                      /* is_signed_exchange_inner_response=*/false,
+                      /* is_signed_exchange_inner_response=*/false, was_cached,
                       base::Bind(&UpdateThrottleCheckResult, &result));
 
   // Reset the callback to ensure it will not be called later.
@@ -656,6 +660,14 @@
   return is_signed_exchange_inner_response_;
 }
 
+bool NavigationHandleImpl::WasResponseCached() {
+  return was_cached_;
+}
+
+const net::ProxyServer& NavigationHandleImpl::GetProxyServer() {
+  return proxy_server_;
+}
+
 void NavigationHandleImpl::InitServiceWorkerHandle(
     ServiceWorkerContextWrapper* service_worker_context) {
   service_worker_handle_.reset(
@@ -818,6 +830,7 @@
     bool is_download,
     bool is_stream,
     bool is_signed_exchange_inner_response,
+    bool was_cached,
     const ThrottleChecksFinishedCallback& callback) {
   TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationHandle", this,
                                "WillProcessResponse");
@@ -831,6 +844,7 @@
   is_download_ = is_download;
   is_stream_ = is_stream;
   is_signed_exchange_inner_response_ = is_signed_exchange_inner_response;
+  was_cached_ = was_cached;
   state_ = WILL_PROCESS_RESPONSE;
   ssl_info_ = ssl_info;
   socket_address_ = socket_address;
diff --git a/content/browser/frame_host/navigation_handle_impl.h b/content/browser/frame_host/navigation_handle_impl.h
index 56cfa2c..84cc852 100644
--- a/content/browser/frame_host/navigation_handle_impl.h
+++ b/content/browser/frame_host/navigation_handle_impl.h
@@ -149,7 +149,9 @@
       base::Optional<net::SSLInfo> ssl_info) override;
   NavigationThrottle::ThrottleCheckResult CallWillProcessResponseForTesting(
       RenderFrameHost* render_frame_host,
-      const std::string& raw_response_header) override;
+      const std::string& raw_response_header,
+      bool was_cached,
+      const net::ProxyServer& proxy_server) override;
   void CallDidCommitNavigationForTesting(const GURL& url) override;
   void CallResumeForTesting() override;
   bool IsDeferredForTesting() override;
@@ -163,6 +165,8 @@
   bool IsDownload() override;
   bool IsFormSubmission() override;
   bool IsSignedExchangeInnerResponse() override;
+  bool WasResponseCached() override;
+  const net::ProxyServer& GetProxyServer() override;
 
   const std::string& origin_policy() const { return origin_policy_; }
   void set_origin_policy(const std::string& origin_policy) {
@@ -297,6 +301,7 @@
       bool is_download,
       bool is_stream,
       bool is_signed_exchange_inner_response,
+      bool was_cached,
       const ThrottleChecksFinishedCallback& callback);
 
   // Returns the FrameTreeNode this navigation is happening in.
@@ -364,6 +369,10 @@
     source_location_ = source_location;
   }
 
+  void set_proxy_server(const net::ProxyServer& proxy_server) {
+    proxy_server_ = proxy_server;
+  }
+
   // Sets ID of the RenderProcessHost we expect the navigation to commit in.
   // This is used to inform the RenderProcessHost to expect a navigation to the
   // url we're navigating to.
@@ -597,6 +606,12 @@
   // True if the target is an inner response of a signed exchange.
   bool is_signed_exchange_inner_response_;
 
+  // Whether the response was cached.
+  bool was_cached_;
+
+  // Which proxy server was used for this navigation, if any.
+  net::ProxyServer proxy_server_;
+
   // False by default unless the navigation started within a context menu.
   bool started_from_context_menu_;
 
diff --git a/content/browser/frame_host/navigation_handle_impl_unittest.cc b/content/browser/frame_host/navigation_handle_impl_unittest.cc
index 62d5ec1..4d913f3 100644
--- a/content/browser/frame_host/navigation_handle_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_handle_impl_unittest.cc
@@ -167,7 +167,7 @@
     test_handle_->WillProcessResponse(
         main_test_rfh(), scoped_refptr<net::HttpResponseHeaders>(),
         net::HttpResponseInfo::CONNECTION_INFO_QUIC_35, net::HostPortPair(),
-        net::SSLInfo(), GlobalRequestID(), false, false, false, false,
+        net::SSLInfo(), GlobalRequestID(), false, false, false, false, false,
         base::Bind(&NavigationHandleImplTest::UpdateThrottleCheckResult,
                    base::Unretained(this)));
   }
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 11b32d3..445dc876 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -1013,6 +1013,9 @@
   if (navigation_data)
     navigation_handle_->set_navigation_data(std::move(navigation_data));
 
+  // This must be set before DetermineCommittedPreviews is called.
+  navigation_handle_->set_proxy_server(response->head.proxy_server);
+
   // Update the previews state of the request.
   common_params_.previews_state =
       GetContentClient()->browser()->DetermineCommittedPreviews(
@@ -1099,6 +1102,7 @@
       response->head.connection_info, response->head.socket_address, ssl_info_,
       request_id, common_params_.should_replace_current_entry, is_download,
       is_stream, response->head.is_signed_exchange_inner_response,
+      response->head.was_fetched_via_cache,
       base::Bind(&NavigationRequest::OnWillProcessResponseChecksComplete,
                  base::Unretained(this)));
 }
diff --git a/content/browser/frame_host/origin_policy_throttle_unittest.cc b/content/browser/frame_host/origin_policy_throttle_unittest.cc
index 859a318d..2f61ac6 100644
--- a/content/browser/frame_host/origin_policy_throttle_unittest.cc
+++ b/content/browser/frame_host/origin_policy_throttle_unittest.cc
@@ -135,7 +135,8 @@
             nav_handle_
                 ->CallWillProcessResponseForTesting(
                     main_rfh(),
-                    net::HttpUtil::AssembleRawHeaders(headers, strlen(headers)))
+                    net::HttpUtil::AssembleRawHeaders(headers, strlen(headers)),
+                    false, net::ProxyServer::Direct())
                 .action());
   EXPECT_EQ(NavigationHandleImpl::DEFERRING_RESPONSE,
             nav_handle_->state_for_testing());
diff --git a/content/browser/loader/resource_loader.cc b/content/browser/loader/resource_loader.cc
index ff351ba..b162850b 100644
--- a/content/browser/loader/resource_loader.cc
+++ b/content/browser/loader/resource_loader.cc
@@ -59,6 +59,7 @@
   net::SSLInfo out;
   out.connection_status = in.connection_status;
   out.key_exchange_group = in.key_exchange_group;
+  out.peer_signature_algorithm = in.peer_signature_algorithm;
   out.signed_certificate_timestamps = in.signed_certificate_timestamps;
   out.cert = in.cert;
   return out;
@@ -133,7 +134,8 @@
     DCHECK(!request->ssl_info().cert_status);
     DCHECK_EQ(request->ssl_info().security_bits, -1);
     DCHECK_EQ(request->ssl_info().key_exchange_group, 0);
-    DCHECK(!request->ssl_info().connection_status);
+    DCHECK_EQ(request->ssl_info().peer_signature_algorithm, 0);
+    DCHECK_EQ(request->ssl_info().connection_status, 0);
   }
 }
 
diff --git a/content/browser/renderer_host/input/touch_action_filter.h b/content/browser/renderer_host/input/touch_action_filter.h
index 8969967..5796ae035 100644
--- a/content/browser/renderer_host/input/touch_action_filter.h
+++ b/content/browser/renderer_host/input/touch_action_filter.h
@@ -48,12 +48,6 @@
   // action.
   void ReportAndResetTouchAction();
 
-  // Must be called at least once between when the last gesture events for the
-  // previous touch sequence have passed through the touch action filter and the
-  // time the touch start for the next touch sequence has reached the
-  // renderer. It may be called multiple times during this interval.
-  void ResetTouchAction();
-
   // Called when a set-white-listed-touch-action message is received from the
   // renderer for a touch start event that is currently in flight.
   void OnSetWhiteListedTouchAction(cc::TouchAction white_listed_touch_action);
@@ -78,11 +72,13 @@
   friend class InputRouterImplTest;
   friend class MockRenderWidgetHost;
   friend class TouchActionFilterTest;
+  friend class TouchActionFilterPinchTest;
   friend class SitePerProcessBrowserTouchActionTest;
 
   bool ShouldSuppressManipulation(const blink::WebGestureEvent&);
   bool FilterManipulationEventAndResetState();
   void ReportTouchAction();
+  void ResetTouchAction();
   void SetTouchAction(cc::TouchAction touch_action);
 
   // Whether scroll and pinch gestures should be discarded due to touch-action.
diff --git a/content/browser/renderer_host/input/touch_action_filter_unittest.cc b/content/browser/renderer_host/input/touch_action_filter_unittest.cc
index caef545..c7abd1c 100644
--- a/content/browser/renderer_host/input/touch_action_filter_unittest.cc
+++ b/content/browser/renderer_host/input/touch_action_filter_unittest.cc
@@ -29,6 +29,7 @@
   base::Optional<cc::TouchAction> ScrollingTouchAction() const {
     return filter_.scrolling_touch_action_;
   }
+  void ResetTouchAction() { filter_.ResetTouchAction(); }
   void PanTest(cc::TouchAction action,
                float scroll_x,
                float scroll_y,
@@ -43,7 +44,7 @@
 
     {
       // Scrolls with no direction hint are permitted in the |action| direction.
-      filter_.ResetTouchAction();
+      ResetTouchAction();
       filter_.OnSetTouchAction(action);
       filter_.IncreaseActiveTouches();
 
@@ -70,7 +71,7 @@
 
     {
       // Scrolls biased towards the touch-action axis are permitted.
-      filter_.ResetTouchAction();
+      ResetTouchAction();
       filter_.OnSetTouchAction(action);
       filter_.IncreaseActiveTouches();
       WebGestureEvent scroll_begin =
@@ -109,7 +110,7 @@
     {
       // Scrolls biased towards the perpendicular of the touch-action axis are
       // suppressed entirely.
-      filter_.ResetTouchAction();
+      ResetTouchAction();
       filter_.OnSetTouchAction(action);
       filter_.IncreaseActiveTouches();
       WebGestureEvent scroll_begin =
@@ -144,7 +145,7 @@
 
     {
       // Scrolls towards the touch-action direction are permitted.
-      filter_.ResetTouchAction();
+      ResetTouchAction();
       filter_.OnSetTouchAction(action);
       filter_.IncreaseActiveTouches();
       WebGestureEvent scroll_begin =
@@ -168,7 +169,7 @@
     {
       // Scrolls towards the exact opposite of the touch-action direction are
       // suppressed entirely.
-      filter_.ResetTouchAction();
+      ResetTouchAction();
       filter_.OnSetTouchAction(action);
       filter_.IncreaseActiveTouches();
       WebGestureEvent scroll_begin =
@@ -192,7 +193,7 @@
     {
       // Scrolls towards the diagonal opposite of the touch-action direction are
       // suppressed entirely.
-      filter_.ResetTouchAction();
+      ResetTouchAction();
       filter_.OnSetTouchAction(action);
       filter_.IncreaseActiveTouches();
       WebGestureEvent scroll_begin =
@@ -230,7 +231,7 @@
       WebInputEvent::kGestureScrollEnd, kSourceDevice);
 
   // cc::kTouchActionAuto doesn't cause any filtering.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionAuto);
   filter_.IncreaseActiveTouches();
   EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
@@ -246,7 +247,7 @@
   filter_.DecreaseActiveTouches();
 
   // cc::kTouchActionNone filters out all scroll events, but no other events.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionNone);
   filter_.IncreaseActiveTouches();
   EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
@@ -264,7 +265,7 @@
   filter_.DecreaseActiveTouches();
 
   // When a new touch sequence begins, the state is reset.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionAuto);
   filter_.IncreaseActiveTouches();
   EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
@@ -278,7 +279,7 @@
   filter_.DecreaseActiveTouches();
 
   // Setting touch action doesn't impact any in-progress gestures.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionAuto);
   filter_.IncreaseActiveTouches();
   EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
@@ -293,7 +294,7 @@
   filter_.DecreaseActiveTouches();
 
   // And the state is still cleared for the next gesture.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionAuto);
   filter_.IncreaseActiveTouches();
   EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
@@ -305,7 +306,7 @@
   filter_.DecreaseActiveTouches();
 
   // Changing the touch action during a gesture has no effect.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionNone);
   filter_.IncreaseActiveTouches();
   EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
@@ -392,7 +393,7 @@
 
   {
     // Scrolls hinted in the X axis are permitted and unmodified.
-    filter_.ResetTouchAction();
+    ResetTouchAction();
     filter_.OnSetTouchAction(cc::kTouchActionPan);
     filter_.IncreaseActiveTouches();
     WebGestureEvent scroll_begin =
@@ -417,7 +418,7 @@
 
   {
     // Scrolls hinted in the Y axis are permitted and unmodified.
-    filter_.ResetTouchAction();
+    ResetTouchAction();
     filter_.OnSetTouchAction(cc::kTouchActionPan);
     filter_.IncreaseActiveTouches();
     WebGestureEvent scroll_begin =
@@ -442,7 +443,7 @@
 
   {
     // A two-finger gesture is not allowed.
-    filter_.ResetTouchAction();
+    ResetTouchAction();
     filter_.OnSetTouchAction(cc::kTouchActionPan);
     filter_.IncreaseActiveTouches();
     WebGestureEvent scroll_begin =
@@ -493,7 +494,7 @@
       WebInputEvent::kGestureScrollEnd, kSourceDevice);
 
   // For multiple points, the intersection is what matters.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionNone);
   filter_.OnSetTouchAction(cc::kTouchActionAuto);
   filter_.IncreaseActiveTouches();
@@ -512,7 +513,7 @@
   filter_.DecreaseActiveTouches();
 
   // Intersection of PAN_X and PAN_Y is NONE.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionPanX);
   filter_.OnSetTouchAction(cc::kTouchActionPanY);
   filter_.OnSetTouchAction(cc::kTouchActionPan);
@@ -778,7 +779,7 @@
       WebInputEvent::kGestureDoubleTap, kSourceDevice);
 
   // Double tap is allowed with touch action auto.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionAuto);
   EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
             FilterGestureEventResult::kFilterGestureEventAllowed);
@@ -786,7 +787,7 @@
             FilterGestureEventResult::kFilterGestureEventAllowed);
   EXPECT_EQ(unconfirmed_tap.GetType(), WebInputEvent::kGestureTapUnconfirmed);
   // The tap cancel will come as part of the next touch sequence.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   // Changing the touch action for the second tap doesn't effect the behaviour
   // of the event.
   filter_.OnSetTouchAction(cc::kTouchActionNone);
@@ -809,7 +810,7 @@
       WebInputEvent::kGestureDoubleTap, kSourceDevice);
 
   // Double tap is disabled with any touch action other than auto.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionManipulation);
   filter_.IncreaseActiveTouches();
   EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
@@ -819,7 +820,7 @@
   EXPECT_EQ(WebInputEvent::kGestureTap, unconfirmed_tap.GetType());
   // Changing the touch action for the second tap doesn't effect the behaviour
   // of the event. The tap cancel will come as part of the next touch sequence.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionAuto);
   EXPECT_EQ(filter_.FilterGestureEvent(&tap_cancel),
             FilterGestureEventResult::kFilterGestureEventFiltered);
@@ -841,7 +842,7 @@
       WebInputEvent::kGestureTap, kSourceDevice);
 
   // Single tap is allowed with touch action auto.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionAuto);
   EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
             FilterGestureEventResult::kFilterGestureEventAllowed);
@@ -861,7 +862,7 @@
       WebInputEvent::kGestureTap, kSourceDevice);
 
   // With touch action other than auto, tap unconfirmed is turned into tap.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionNone);
   filter_.IncreaseActiveTouches();
   EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
@@ -884,7 +885,7 @@
   WebGestureEvent scroll_end = SyntheticWebGestureEventBuilder::Build(
       WebInputEvent::kGestureScrollEnd, kSourceDevice);
 
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionNone);
   filter_.IncreaseActiveTouches();
   EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
@@ -895,7 +896,7 @@
             FilterGestureEventResult::kFilterGestureEventFiltered);
   filter_.DecreaseActiveTouches();
 
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionNone);
   filter_.IncreaseActiveTouches();
   EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
@@ -904,7 +905,7 @@
             FilterGestureEventResult::kFilterGestureEventAllowed);
   filter_.DecreaseActiveTouches();
 
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionAuto);
   filter_.IncreaseActiveTouches();
   EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
@@ -943,7 +944,7 @@
 
   // Even though the allowed action is auto after the reset, the remaining
   // scroll and pinch events should be suppressed.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionAuto);
   EXPECT_EQ(filter_.FilterGestureEvent(&pinch_update),
             FilterGestureEventResult::kFilterGestureEventFiltered);
@@ -964,7 +965,7 @@
             FilterGestureEventResult::kFilterGestureEventAllowed);
 
   // Resetting from auto to auto mid-stream should have no effect.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionAuto);
   EXPECT_EQ(filter_.FilterGestureEvent(&pinch_update),
             FilterGestureEventResult::kFilterGestureEventAllowed);
@@ -1039,7 +1040,7 @@
             FilterGestureEventResult::kFilterGestureEventAllowed);
   // Gesture tap indicates that there is no scroll in progress, so this should
   // reset the |allowed_touch_action_|.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   EXPECT_FALSE(filter_.allowed_touch_action().has_value());
 }
 
@@ -1198,7 +1199,7 @@
 
 TEST_F(TouchActionFilterTest, ResetBeforeHasHandlerSet) {
   // This should not crash, and should set touch action to auto.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   WebGestureEvent tap_down = SyntheticWebGestureEventBuilder::Build(
       WebInputEvent::kGestureTapDown, kSourceDevice);
   EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
@@ -1254,7 +1255,7 @@
           2, 3, blink::kWebGestureDeviceTouchpad);
 
   // cc::kTouchActionNone filters out only touchscreen scroll events.
-  filter_.ResetTouchAction();
+  ResetTouchAction();
   filter_.OnSetTouchAction(cc::kTouchActionNone);
   EXPECT_EQ(filter_.FilterGestureEvent(&scroll_begin),
             FilterGestureEventResult::kFilterGestureEventAllowed);
diff --git a/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc b/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc
index 1683ec3..fda92ec6 100644
--- a/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc
+++ b/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc
@@ -282,12 +282,8 @@
   else
     quick_menu_timer_.Stop();
 
-  bool should_show_menu = quick_menu_requested_ && !touch_down_ &&
-                          !scroll_in_progress_ && !handle_drag_in_progress_ &&
-                          IsQuickMenuAvailable();
-
   // Start timer to show quick menu if necessary.
-  if (should_show_menu) {
+  if (ShouldShowQuickMenu()) {
     if (show_quick_menu_immediately_for_test_)
       ShowQuickMenu();
     else
@@ -492,4 +488,17 @@
   rwhva_->selection_controller()->HideAndDisallowShowingAutomatically();
 }
 
+bool TouchSelectionControllerClientAura::ShouldShowQuickMenu() {
+  return quick_menu_requested_ && !touch_down_ && !scroll_in_progress_ &&
+         !handle_drag_in_progress_ && IsQuickMenuAvailable();
+}
+
+base::string16 TouchSelectionControllerClientAura::GetSelectedText() {
+  gfx::Range selection_range;
+  rwhva_->GetSelectionRange(&selection_range);
+  base::string16 selection_text;
+  rwhva_->GetTextFromRange(selection_range, &selection_text);
+  return selection_text;
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/input/touch_selection_controller_client_aura.h b/content/browser/renderer_host/input/touch_selection_controller_client_aura.h
index 3464fdb7..48464cd 100644
--- a/content/browser/renderer_host/input/touch_selection_controller_client_aura.h
+++ b/content/browser/renderer_host/input/touch_selection_controller_client_aura.h
@@ -71,6 +71,7 @@
  private:
   friend class TestTouchSelectionControllerClientAura;
   class EnvEventObserver;
+  class EnvPreTargetHandler;
 
   bool IsQuickMenuAvailable() const;
   void ShowQuickMenu();
@@ -92,13 +93,15 @@
   bool IsCommandIdEnabled(int command_id) const override;
   void ExecuteCommand(int command_id, int event_flags) override;
   void RunContextMenu() override;
+  bool ShouldShowQuickMenu() override;
+  base::string16 GetSelectedText() override;
 
   // Not owned, non-null for the lifetime of this object.
   RenderWidgetHostViewAura* rwhva_;
 
   class InternalClient : public TouchSelectionControllerClient {
    public:
-    InternalClient(RenderWidgetHostViewAura* rwhva) : rwhva_(rwhva) {}
+    explicit InternalClient(RenderWidgetHostViewAura* rwhva) : rwhva_(rwhva) {}
     ~InternalClient() final {}
 
     bool SupportsAnimation() const final;
diff --git a/content/browser/renderer_host/input/touch_selection_controller_client_child_frame.cc b/content/browser/renderer_host/input/touch_selection_controller_client_child_frame.cc
index 6f26cc4..5020238 100644
--- a/content/browser/renderer_host/input/touch_selection_controller_client_child_frame.cc
+++ b/content/browser/renderer_host/input/touch_selection_controller_client_child_frame.cc
@@ -4,6 +4,7 @@
 
 #include "content/browser/renderer_host/input/touch_selection_controller_client_child_frame.h"
 
+#include "base/logging.h"
 #include "content/browser/renderer_host/render_widget_host_delegate.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
@@ -199,4 +200,14 @@
       ->HideAndDisallowShowingAutomatically();
 }
 
+bool TouchSelectionControllerClientChildFrame::ShouldShowQuickMenu() {
+  NOTREACHED();
+  return false;
+}
+
+base::string16 TouchSelectionControllerClientChildFrame::GetSelectedText() {
+  NOTREACHED();
+  return base::string16();
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/input/touch_selection_controller_client_child_frame.h b/content/browser/renderer_host/input/touch_selection_controller_client_child_frame.h
index 7ce87b14..58aa08b 100644
--- a/content/browser/renderer_host/input/touch_selection_controller_client_child_frame.h
+++ b/content/browser/renderer_host/input/touch_selection_controller_client_child_frame.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_SELECTION_CONTROLLER_CLIENT_CHILD_FRAME_H_
 #define CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_SELECTION_CONTROLLER_CLIENT_CHILD_FRAME_H_
 
+#include <memory>
+
 #include "base/macros.h"
 #include "components/viz/common/quads/selection.h"
 #include "content/common/content_export.h"
@@ -55,6 +57,8 @@
   bool IsCommandIdEnabled(int command_id) const override;
   void ExecuteCommand(int command_id, int event_flags) override;
   void RunContextMenu() override;
+  bool ShouldShowQuickMenu() override;
+  base::string16 GetSelectedText() override;
 
   gfx::Point ConvertFromRoot(const gfx::PointF& point) const;
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 9b4cad6..68b0df9 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3687,12 +3687,6 @@
   return interstitial_page_ != nullptr;
 }
 
-void WebContentsImpl::AdjustPreviewsStateForNavigation(
-    PreviewsState* previews_state) {
-  if (delegate_)
-    delegate_->AdjustPreviewsStateForNavigation(this, previews_state);
-}
-
 InterstitialPageImpl* WebContentsImpl::GetInterstitialPage() const {
   return interstitial_page_;
 }
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index e508720..e3eb4c5 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -401,7 +401,6 @@
   void RestoreFocus() override;
   void FocusThroughTabTraversal(bool reverse) override;
   bool ShowingInterstitialPage() const override;
-  void AdjustPreviewsStateForNavigation(PreviewsState* previews_state) override;
   InterstitialPageImpl* GetInterstitialPage() const override;
   bool IsSavable() override;
   void OnSavePage() override;
diff --git a/content/common/common_param_traits_unittest.cc b/content/common/common_param_traits_unittest.cc
index cb95c23f..3f63cee 100644
--- a/content/common/common_param_traits_unittest.cc
+++ b/content/common/common_param_traits_unittest.cc
@@ -168,6 +168,7 @@
   in.cert_status = net::CERT_STATUS_COMMON_NAME_INVALID;
   in.security_bits = 0x100;
   in.key_exchange_group = 1024;
+  in.peer_signature_algorithm = 0x0804;
   in.connection_status = 0x300039;  // TLS_DHE_RSA_WITH_AES_256_CBC_SHA
   in.is_issued_by_known_root = true;
   in.pkp_bypassed = true;
@@ -211,6 +212,7 @@
       in.unverified_cert->EqualsIncludingChain(out.unverified_cert.get()));
   ASSERT_EQ(in.security_bits, out.security_bits);
   ASSERT_EQ(in.key_exchange_group, out.key_exchange_group);
+  ASSERT_EQ(in.peer_signature_algorithm, out.peer_signature_algorithm);
   ASSERT_EQ(in.connection_status, out.connection_status);
   ASSERT_EQ(in.is_issued_by_known_root, out.is_issued_by_known_root);
   ASSERT_EQ(in.pkp_bypassed, out.pkp_bypassed);
diff --git a/content/public/browser/navigation_handle.h b/content/public/browser/navigation_handle.h
index bd5c217b..5bbfec4 100644
--- a/content/public/browser/navigation_handle.h
+++ b/content/public/browser/navigation_handle.h
@@ -273,6 +273,12 @@
   // Returns true if the target is an inner response of a signed exchange.
   virtual bool IsSignedExchangeInnerResponse() = 0;
 
+  // Returns true if the navigation response was cached.
+  virtual bool WasResponseCached() = 0;
+
+  // Returns the proxy server used for this navigation, if any.
+  virtual const net::ProxyServer& GetProxyServer() = 0;
+
   // Testing methods ----------------------------------------------------------
   //
   // The following methods should be used exclusively for writing unit tests.
@@ -315,9 +321,10 @@
 
   // Simulates the reception of the network response.
   virtual NavigationThrottle::ThrottleCheckResult
-  CallWillProcessResponseForTesting(
-      RenderFrameHost* render_frame_host,
-      const std::string& raw_response_headers) = 0;
+  CallWillProcessResponseForTesting(RenderFrameHost* render_frame_host,
+                                    const std::string& raw_response_headers,
+                                    bool was_cached,
+                                    const net::ProxyServer& proxy_server) = 0;
 
   // Simulates the navigation being committed.
   virtual void CallDidCommitNavigationForTesting(const GURL& url) = 0;
diff --git a/content/public/browser/security_style_explanation.cc b/content/public/browser/security_style_explanation.cc
index 0309bb6..7ae1e74 100644
--- a/content/public/browser/security_style_explanation.cc
+++ b/content/public/browser/security_style_explanation.cc
@@ -4,42 +4,61 @@
 
 #include "content/public/browser/security_style_explanation.h"
 
+#include <utility>
+
 namespace content {
 
 SecurityStyleExplanation::SecurityStyleExplanation() {}
 
-SecurityStyleExplanation::SecurityStyleExplanation(
-    const std::string& summary,
-    const std::string& description)
-    : SecurityStyleExplanation(std::string(), summary, description) {}
+SecurityStyleExplanation::SecurityStyleExplanation(std::string summary,
+                                                   std::string description)
+    : SecurityStyleExplanation(std::string(),
+                               std::move(summary),
+                               std::move(description)) {}
+
+SecurityStyleExplanation::SecurityStyleExplanation(std::string title,
+                                                   std::string summary,
+                                                   std::string description)
+    : SecurityStyleExplanation(std::move(title),
+                               std::move(summary),
+                               std::move(description),
+                               {}) {}
 
 SecurityStyleExplanation::SecurityStyleExplanation(
-    const std::string& title,
-    const std::string& summary,
-    const std::string& description)
-    : title(title),
-      summary(summary),
-      description(description),
-      mixed_content_type(blink::WebMixedContentContextType::kNotMixedContent) {}
-
-SecurityStyleExplanation::SecurityStyleExplanation(
-    const std::string& title,
-    const std::string& summary,
-    const std::string& description,
+    std::string title,
+    std::string summary,
+    std::string description,
     scoped_refptr<net::X509Certificate> certificate,
     blink::WebMixedContentContextType mixed_content_type)
-    : title(title),
-      summary(summary),
-      description(description),
-      certificate(certificate),
+    : title(std::move(title)),
+      summary(std::move(summary)),
+      description(std::move(description)),
+      certificate(std::move(certificate)),
       mixed_content_type(mixed_content_type) {}
 
 SecurityStyleExplanation::SecurityStyleExplanation(
+    std::string title,
+    std::string summary,
+    std::string description,
+    std::vector<std::string> recommendations)
+    : title(std::move(title)),
+      summary(std::move(summary)),
+      description(std::move(description)),
+      mixed_content_type(blink::WebMixedContentContextType::kNotMixedContent),
+      recommendations(std::move(recommendations)) {}
+
+SecurityStyleExplanation::SecurityStyleExplanation(
     const SecurityStyleExplanation& other) = default;
 
+SecurityStyleExplanation::SecurityStyleExplanation(
+    SecurityStyleExplanation&& other) = default;
+
 SecurityStyleExplanation& SecurityStyleExplanation::operator=(
     const SecurityStyleExplanation& other) = default;
 
+SecurityStyleExplanation& SecurityStyleExplanation::operator=(
+    SecurityStyleExplanation&& other) = default;
+
 SecurityStyleExplanation::~SecurityStyleExplanation() {}
 
 }  // namespace content
diff --git a/content/public/browser/security_style_explanation.h b/content/public/browser/security_style_explanation.h
index 022c813..fa008f21 100644
--- a/content/public/browser/security_style_explanation.h
+++ b/content/public/browser/security_style_explanation.h
@@ -6,6 +6,7 @@
 #define CONTENT_PUBLIC_BROWSER_SECURITY_STYLE_EXPLANATION_H_
 
 #include <string>
+#include <vector>
 
 #include "content/common/content_export.h"
 #include "net/cert/x509_certificate.h"
@@ -20,19 +21,24 @@
 // contains errors (net::CERT_DATE_INVALID)".
 struct CONTENT_EXPORT SecurityStyleExplanation {
   SecurityStyleExplanation();
-  SecurityStyleExplanation(const std::string& summary,
-                           const std::string& description);
-  SecurityStyleExplanation(const std::string& title,
-                           const std::string& summary,
-                           const std::string& description);
+  SecurityStyleExplanation(std::string summary, std::string description);
+  SecurityStyleExplanation(std::string title,
+                           std::string summary,
+                           std::string description);
   SecurityStyleExplanation(
-      const std::string& title,
-      const std::string& summary,
-      const std::string& description,
+      std::string title,
+      std::string summary,
+      std::string description,
       scoped_refptr<net::X509Certificate> certificate,
       blink::WebMixedContentContextType mixed_content_type);
+  SecurityStyleExplanation(std::string title,
+                           std::string summary,
+                           std::string description,
+                           std::vector<std::string> recommendations);
   SecurityStyleExplanation(const SecurityStyleExplanation& other);
+  SecurityStyleExplanation(SecurityStyleExplanation&& other);
   SecurityStyleExplanation& operator=(const SecurityStyleExplanation& other);
+  SecurityStyleExplanation& operator=(SecurityStyleExplanation&& other);
   ~SecurityStyleExplanation();
 
   std::string title;
@@ -46,6 +52,9 @@
   // explanation does not relate to mixed content. UI surfaces can use this to
   // customize the display of mixed content explanations.
   blink::WebMixedContentContextType mixed_content_type;
+  // |recommendations| contains a list of recommendations for the server to
+  // follow.
+  std::vector<std::string> recommendations;
 };
 
 }  // namespace content
diff --git a/content/public/browser/ssl_status.cc b/content/public/browser/ssl_status.cc
index a3a5838..1848735 100644
--- a/content/public/browser/ssl_status.cc
+++ b/content/public/browser/ssl_status.cc
@@ -14,6 +14,7 @@
       cert_status(0),
       security_bits(-1),
       key_exchange_group(0),
+      peer_signature_algorithm(0),
       connection_status(0),
       content_status(NORMAL_CONTENT),
       pkp_bypassed(false),
@@ -27,6 +28,7 @@
       public_key_hashes(ssl_info.public_key_hashes),
       security_bits(ssl_info.security_bits),
       key_exchange_group(ssl_info.key_exchange_group),
+      peer_signature_algorithm(ssl_info.peer_signature_algorithm),
       connection_status(ssl_info.connection_status),
       content_status(NORMAL_CONTENT),
       pkp_bypassed(ssl_info.pkp_bypassed),
@@ -39,6 +41,7 @@
       public_key_hashes(other.public_key_hashes),
       security_bits(other.security_bits),
       key_exchange_group(other.key_exchange_group),
+      peer_signature_algorithm(other.peer_signature_algorithm),
       connection_status(other.connection_status),
       content_status(other.content_status),
       pkp_bypassed(other.pkp_bypassed),
@@ -52,6 +55,7 @@
   public_key_hashes = other.public_key_hashes;
   security_bits = other.security_bits;
   key_exchange_group = other.key_exchange_group;
+  peer_signature_algorithm = other.peer_signature_algorithm;
   connection_status = other.connection_status;
   content_status = other.content_status;
   pkp_bypassed = other.pkp_bypassed;
diff --git a/content/public/browser/ssl_status.h b/content/public/browser/ssl_status.h
index d3dc5b8e..3f01504 100644
--- a/content/public/browser/ssl_status.h
+++ b/content/public/browser/ssl_status.h
@@ -76,6 +76,7 @@
   net::HashValueVector public_key_hashes;
   int security_bits;
   uint16_t key_exchange_group;
+  uint16_t peer_signature_algorithm;
   int connection_status;
   // A combination of the ContentStatusFlags above. Flags are cleared when a
   // navigation commits.
diff --git a/content/renderer/mus/renderer_window_tree_client.cc b/content/renderer/mus/renderer_window_tree_client.cc
index 73ea626..32a371b 100644
--- a/content/renderer/mus/renderer_window_tree_client.cc
+++ b/content/renderer/mus/renderer_window_tree_client.cc
@@ -377,4 +377,8 @@
 void RendererWindowTreeClient::GetScreenProviderObserver(
     ws::mojom::ScreenProviderObserverAssociatedRequest observer) {}
 
+void RendererWindowTreeClient::OnOcclusionStateChanged(
+    ws::Id window_id,
+    ws::mojom::OcclusionState occlusion_state) {}
+
 }  // namespace content
diff --git a/content/renderer/mus/renderer_window_tree_client.h b/content/renderer/mus/renderer_window_tree_client.h
index cafba8f..c42037d 100644
--- a/content/renderer/mus/renderer_window_tree_client.h
+++ b/content/renderer/mus/renderer_window_tree_client.h
@@ -200,6 +200,9 @@
   void RequestClose(ws::Id window_id) override;
   void GetScreenProviderObserver(
       ws::mojom::ScreenProviderObserverAssociatedRequest observer) override;
+  void OnOcclusionStateChanged(
+      ws::Id window_id,
+      ws::mojom::OcclusionState occlusion_state) override;
 
   const int routing_id_;
   ws::Id root_window_id_ = 0u;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 6e81d193..6ead595 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1106,7 +1106,10 @@
 
   if (is_linux) {
     sources += [ "../zygote/zygote_browsertest.cc" ]
-    deps += [ "//ui/gfx:test_support" ]
+    deps += [
+      "//services/service_manager/zygote",
+      "//ui/gfx:test_support",
+    ]
   }
 
   if (is_android) {
diff --git a/content/test/gpu/gpu_tests/gpu_process_expectations.py b/content/test/gpu/gpu_tests/gpu_process_expectations.py
index be3cb00b..4540c4a5 100644
--- a/content/test/gpu/gpu_tests/gpu_process_expectations.py
+++ b/content/test/gpu/gpu_tests/gpu_process_expectations.py
@@ -12,6 +12,3 @@
 
     # Seems to have become flaky on Windows recently.
     self.Flaky('GpuProcess_one_extra_workaround', ['win'], bug=700522)
-
-    self.Fail('GpuProcess_feature_status_under_swiftshader',
-        ['mac'], bug=897914)
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index 266e35b..aef69d4 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -694,8 +694,6 @@
     #     ['highsierra', 'amd'], bug=870856)
     self.Fail('conformance2/textures/misc/tex-base-level-bug.html',
         ['highsierra', 'amd'], bug=870856)
-    self.Fail('deqp/functional/gles3/shaderoperator/common_functions_*.html',
-        ['mac', 'amd'], bug=820225)
     self.Fail('deqp/functional/gles3/transformfeedback/' +
         'array_interleaved_lines.html',
         ['sierra', 'amd'], bug=483282)
@@ -797,8 +795,6 @@
         ['mac', 'no_passthrough', 'intel'], bug=679692)
     self.Fail('deqp/functional/gles3/fbomultisample*',
         ['mac', 'intel'], bug=641209)
-    self.Fail('deqp/functional/gles3/shaderoperator/common_functions_*.html',
-        ['mac', 'intel'], bug=820225)
     self.Fail('deqp/functional/gles3/texturefiltering/2d_combinations_01.html',
         ['mac', 'intel'], bug=606074)
     self.Fail('deqp/functional/gles3/texturefiltering/' +
diff --git a/content/zygote/zygote_browsertest.cc b/content/zygote/zygote_browsertest.cc
index 48ffdd2..8c1a139 100644
--- a/content/zygote/zygote_browsertest.cc
+++ b/content/zygote/zygote_browsertest.cc
@@ -14,6 +14,10 @@
 #include "content/shell/browser/shell.h"
 #include "services/service_manager/embedder/switches.h"
 #include "services/service_manager/sandbox/switches.h"
+#include "services/service_manager/zygote/common/zygote_buildflags.h"
+#if BUILDFLAG(USE_ZYGOTE_HANDLE)
+#include "services/service_manager/zygote/host/zygote_host_impl_linux.h"
+#endif
 
 namespace content {
 
@@ -67,4 +71,13 @@
 }
 #endif
 
+#if BUILDFLAG(USE_ZYGOTE_HANDLE)
+IN_PROC_BROWSER_TEST_F(LinuxZygoteDisabledBrowserTest,
+                       NoZygoteWhenZygoteDisabled) {
+  NavigateToURL(shell(), GURL("data:text/html,start page"));
+
+  EXPECT_FALSE(service_manager::ZygoteHostImpl::GetInstance()->HasZygote());
+}
+#endif
+
 }  // namespace content
diff --git a/docs/accessibility/chromevox.md b/docs/accessibility/chromevox.md
index e72c2f33..c45edd5 100644
--- a/docs/accessibility/chromevox.md
+++ b/docs/accessibility/chromevox.md
@@ -16,7 +16,7 @@
 Code location: ```chrome/browser/resources/chromeos/chromevox```
 
 Ninja target: it's built as part of "chrome", but you can build and run
-chromevox_tests to test it (Chrome OS target only - you must have target_os =
+browser_tests to test it (Chrome OS target only - you must have target_os =
 "chromeos" in your GN args first).
 
 ## Developing On Linux
@@ -64,13 +64,15 @@
 
 ### Running tests
 
-Build the chromevox_tests target. To run
-lots of tests in parallel, run it like this:
+Build the browser_tests target. To run lots of tests in parallel, run it like
+this:
 
-```out/Release/chromevox_tests --test-launcher-jobs=20```
+```
+out/Release/browser_tests --test-launcher-jobs=20 --gtest_filter=ChromeVox*
+```
 
-Use a test filter if you only want to run some of the tests from a
-particular test suite - for example, most of the ChromeVox Next tests
-have "E2E" in them (for "end-to-end"), so to only run those:
+Use a narrower test filter if you only want to run some of the tests. For
+example, most of the ChromeVox Next tests have "E2E" in them (for "end-to-end"),
+so to only run those:
 
-```out/Release/chromevox_tests --test-launcher-jobs=20 --gtest_filter="*E2E*"```
+```out/Release/browser_tests --test-launcher-jobs=20 --gtest_filter="*E2E*"```
diff --git a/docs/accessibility/tests.md b/docs/accessibility/tests.md
index fb1793e1..85439633 100644
--- a/docs/accessibility/tests.md
+++ b/docs/accessibility/tests.md
@@ -99,12 +99,13 @@
 
 ## ChromeVox tests
 
-You must build with ```target_os = "chromeos"``` in your GN args.
+ChromeVox tests are part of the browser_tests suite. You must build with
+```target_os = "chromeos"``` in your GN args.
 
 To run all tests:
 ```
-autoninja -C out/release chromevox_tests
-out/release/chromevox_tests --test-launcher-jobs=10
+autoninja -C out/release browser_tests
+out/release/browser_tests --test-launcher-jobs=20 --gtest_filter=ChromeVox*
 ```
 
 ### Select-To-Speak tests
diff --git a/extensions/common/image_util.cc b/extensions/common/image_util.cc
index 4c44162..9cd7bd69 100644
--- a/extensions/common/image_util.cc
+++ b/extensions/common/image_util.cc
@@ -15,7 +15,9 @@
 #include "base/strings/stringprintf.h"
 #include "third_party/re2/src/re2/re2.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/utils/SkParse.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/color_utils.h"
@@ -194,9 +196,51 @@
   SkBitmap icon;
   if (!LoadPngFromFile(path, &icon)) {
     return false;
-  } else {
-    return image_util::IsIconSufficientlyVisible(icon);
   }
+  return IsIconSufficientlyVisible(icon);
+}
+
+bool IsRenderedIconSufficientlyVisible(const SkBitmap& icon,
+                                       SkColor background_color) {
+  // If any of a pixel's RGB values is greater than this number, the pixel is
+  // considered visible.
+  constexpr unsigned int kThreshold = 15;
+  // The minimum "percent" of pixels that must be visible for the icon to be
+  // considered OK.
+  constexpr double kMinPercentVisiblePixels = 0.05;
+  const int total_pixels = icon.height() * icon.width();
+
+  // Draw the icon onto a canvas, then draw the background color onto the
+  // resulting bitmap, using SkBlendMode::kDifference. Then, check the RGB
+  // values against the threshold. Any pixel with a value greater than the
+  // threshold is considered visible.
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(icon.width(), icon.height());
+  bitmap.eraseColor(background_color);
+  SkCanvas offscreen(bitmap);
+  offscreen.drawImage(SkImage::MakeFromBitmap(icon), 0, 0);
+  offscreen.drawColor(background_color, SkBlendMode::kDifference);
+  int visible_pixels = 0;
+  for (int x = 0; x < icon.width(); ++x) {
+    for (int y = 0; y < icon.height(); ++y) {
+      SkColor pixel = bitmap.getColor(x, y);
+      if (SkColorGetR(pixel) > kThreshold || SkColorGetB(pixel) > kThreshold ||
+          SkColorGetG(pixel) > kThreshold) {
+        ++visible_pixels;
+      }
+    }
+  }
+  return static_cast<double>(visible_pixels) / total_pixels >=
+         kMinPercentVisiblePixels;
+}
+
+bool IsRenderedIconAtPathSufficientlyVisible(const base::FilePath& path,
+                                             SkColor background_color) {
+  SkBitmap icon;
+  if (!LoadPngFromFile(path, &icon)) {
+    return false;
+  }
+  return IsRenderedIconSufficientlyVisible(icon, background_color);
 }
 
 bool LoadPngFromFile(const base::FilePath& path, SkBitmap* dst) {
diff --git a/extensions/common/image_util.h b/extensions/common/image_util.h
index 727977e..b90a3e18e 100644
--- a/extensions/common/image_util.h
+++ b/extensions/common/image_util.h
@@ -44,6 +44,17 @@
 // context.
 bool IsIconAtPathSufficientlyVisible(const base::FilePath& path);
 
+// Renders the icon bitmap onto another bitmap, combining it with the specified
+// background color, then determines whether the rendered icon is sufficiently
+// visible against the background.
+bool IsRenderedIconSufficientlyVisible(const SkBitmap& bitmap,
+                                       SkColor background_color);
+
+// Returns whether an icon image is considered to be visible in its display
+// context, according to the previous function.
+bool IsRenderedIconAtPathSufficientlyVisible(const base::FilePath& path,
+                                             SkColor background_color);
+
 // Load a PNG image from a file into the destination bitmap.
 bool LoadPngFromFile(const base::FilePath& path, SkBitmap* dst);
 
diff --git a/extensions/common/image_util_unittest.cc b/extensions/common/image_util_unittest.cc
index 1acd375..c2bc598 100644
--- a/extensions/common/image_util_unittest.cc
+++ b/extensions/common/image_util_unittest.cc
@@ -190,6 +190,8 @@
     SkBitmap transparent_icon;
     ASSERT_TRUE(image_util::LoadPngFromFile(icon_path, &transparent_icon));
     EXPECT_FALSE(image_util::IsIconSufficientlyVisible(transparent_icon));
+    EXPECT_FALSE(image_util::IsRenderedIconSufficientlyVisible(transparent_icon,
+                                                               SK_ColorWHITE));
   }
   {
     // Test with an icon that has one opaque pixel.
@@ -197,6 +199,8 @@
     SkBitmap visible_icon;
     ASSERT_TRUE(image_util::LoadPngFromFile(icon_path, &visible_icon));
     EXPECT_FALSE(image_util::IsIconSufficientlyVisible(visible_icon));
+    EXPECT_FALSE(image_util::IsRenderedIconSufficientlyVisible(visible_icon,
+                                                               SK_ColorWHITE));
   }
   {
     // Test with an icon that has one transparent pixel.
@@ -204,6 +208,8 @@
     SkBitmap visible_icon;
     ASSERT_TRUE(image_util::LoadPngFromFile(icon_path, &visible_icon));
     EXPECT_TRUE(image_util::IsIconSufficientlyVisible(visible_icon));
+    EXPECT_TRUE(image_util::IsRenderedIconSufficientlyVisible(visible_icon,
+                                                              SK_ColorWHITE));
   }
   {
     // Test with an icon that is completely opaque.
@@ -211,6 +217,8 @@
     SkBitmap visible_icon;
     ASSERT_TRUE(image_util::LoadPngFromFile(icon_path, &visible_icon));
     EXPECT_TRUE(image_util::IsIconSufficientlyVisible(visible_icon));
+    EXPECT_TRUE(image_util::IsRenderedIconSufficientlyVisible(visible_icon,
+                                                              SK_ColorWHITE));
   }
   {
     // Test with an icon that is rectangular.
@@ -218,6 +226,30 @@
     SkBitmap visible_icon;
     ASSERT_TRUE(image_util::LoadPngFromFile(icon_path, &visible_icon));
     EXPECT_TRUE(image_util::IsIconSufficientlyVisible(visible_icon));
+    EXPECT_TRUE(image_util::IsRenderedIconSufficientlyVisible(visible_icon,
+                                                              SK_ColorWHITE));
+  }
+  {
+    // Test with a solid color icon that is completely opaque. Use the icon's
+    // color as the background color in the call to analyze its visibility.
+    // It should be invisible in this case.
+    icon_path = test_dir.AppendASCII("grey_21x21.png");
+    SkBitmap solid_icon;
+    ASSERT_TRUE(image_util::LoadPngFromFile(icon_path, &solid_icon));
+    const SkColor pixel_color = solid_icon.getColor(0, 0);
+    EXPECT_FALSE(
+        image_util::IsRenderedIconSufficientlyVisible(solid_icon, pixel_color));
+  }
+  {
+    // Test with a two-color icon that is completely opaque. Use one of the
+    // icon's colors as the background color in the call to analyze its
+    // visibility. It should be visible in this case.
+    icon_path = test_dir.AppendASCII("two_color_21x21.png");
+    SkBitmap two_color_icon;
+    ASSERT_TRUE(image_util::LoadPngFromFile(icon_path, &two_color_icon));
+    const SkColor pixel_color = two_color_icon.getColor(0, 0);
+    EXPECT_TRUE(image_util::IsRenderedIconSufficientlyVisible(two_color_icon,
+                                                              pixel_color));
   }
 }
 
diff --git a/extensions/test/data/grey_21x21.png b/extensions/test/data/grey_21x21.png
new file mode 100644
index 0000000..a52ad3e2
--- /dev/null
+++ b/extensions/test/data/grey_21x21.png
Binary files differ
diff --git a/extensions/test/data/two_color_21x21.png b/extensions/test/data/two_color_21x21.png
new file mode 100644
index 0000000..ddb94aa
--- /dev/null
+++ b/extensions/test/data/two_color_21x21.png
Binary files differ
diff --git a/gpu/command_buffer/client/client_transfer_cache.cc b/gpu/command_buffer/client/client_transfer_cache.cc
index 077963e..5c773d6 100644
--- a/gpu/command_buffer/client/client_transfer_cache.cc
+++ b/gpu/command_buffer/client/client_transfer_cache.cc
@@ -37,25 +37,13 @@
 }
 
 void ClientTransferCache::UnmapAndCreateEntry(uint32_t type, uint32_t id) {
-  DCHECK(mapped_ptr_ || transfer_buffer_ptr_);
   EntryKey key(type, id);
 
   base::AutoLock hold(lock_);
-  ClientDiscardableHandle::Id discardable_handle_id =
-      discardable_manager_.CreateHandle(client_->command_buffer());
-  if (discardable_handle_id.is_null())
+  auto handle = CreateDiscardableHandle(key);
+  if (!handle.IsValid())
     return;
 
-  // We must have a valid handle here, since the id was generated above and
-  // should be in locked state.
-  ClientDiscardableHandle handle =
-      discardable_manager_.GetHandle(discardable_handle_id);
-
-  // Store the mapping from the given namespace/discardable_handle_id to the
-  // transfer cache discardable_handle_id.
-  DCHECK(FindDiscardableHandleId(key).is_null());
-  discardable_handle_id_map_.emplace(key, discardable_handle_id);
-
   if (mapped_ptr_) {
     DCHECK(!transfer_buffer_ptr_);
     client_->IssueCreateTransferCacheEntry(
@@ -72,6 +60,45 @@
   }
 }
 
+void ClientTransferCache::AddTransferCacheEntry(uint32_t type,
+                                                uint32_t id,
+                                                uint32_t shm_id,
+                                                uint32_t shm_offset,
+                                                size_t size) {
+  DCHECK(!mapped_ptr_);
+  EntryKey key(type, id);
+
+  base::AutoLock hold(lock_);
+  auto handle = CreateDiscardableHandle(key);
+  if (!handle.IsValid())
+    return;
+
+  client_->IssueCreateTransferCacheEntry(type, id, handle.shm_id(),
+                                         handle.byte_offset(), shm_id,
+                                         shm_offset, size);
+}
+
+ClientDiscardableHandle ClientTransferCache::CreateDiscardableHandle(
+    const EntryKey& key) {
+  lock_.AssertAcquired();
+
+  ClientDiscardableHandle::Id discardable_handle_id =
+      discardable_manager_.CreateHandle(client_->command_buffer());
+  if (discardable_handle_id.is_null())
+    return ClientDiscardableHandle();
+
+  // We must have a valid handle here, since the id was generated above and
+  // should be in locked state.
+  ClientDiscardableHandle handle =
+      discardable_manager_.GetHandle(discardable_handle_id);
+
+  // Store the mapping from the given namespace/discardable_handle_id to the
+  // transfer cache discardable_handle_id.
+  DCHECK(FindDiscardableHandleId(key).is_null());
+  discardable_handle_id_map_.emplace(key, discardable_handle_id);
+  return handle;
+}
+
 bool ClientTransferCache::LockEntry(uint32_t type, uint32_t id) {
   EntryKey key(type, id);
 
diff --git a/gpu/command_buffer/client/client_transfer_cache.h b/gpu/command_buffer/client/client_transfer_cache.h
index 98280f2..9fbb014 100644
--- a/gpu/command_buffer/client/client_transfer_cache.h
+++ b/gpu/command_buffer/client/client_transfer_cache.h
@@ -60,6 +60,13 @@
   explicit ClientTransferCache(Client* client);
   ~ClientTransferCache();
 
+  // Adds a transfer cache entry with previously written memory.
+  void AddTransferCacheEntry(uint32_t type,
+                             uint32_t id,
+                             uint32_t shm_id,
+                             uint32_t shm_offset,
+                             size_t size);
+
   // Map(of either type) must always be followed by an Unmap.
   void* MapEntry(MappedMemoryManager* mapped_memory, size_t size);
   void* MapTransferBufferEntry(TransferBufferInterface* transfer_buffer,
@@ -72,6 +79,7 @@
  private:
   using EntryKey = std::pair<uint32_t, uint32_t>;
   ClientDiscardableHandle::Id FindDiscardableHandleId(const EntryKey& key);
+  ClientDiscardableHandle CreateDiscardableHandle(const EntryKey& key);
 
   Client* const client_;  // not owned --- client_ outlives this
 
diff --git a/gpu/command_buffer/client/raster_implementation.cc b/gpu/command_buffer/client/raster_implementation.cc
index 8fa92c9..2bc6e2e3 100644
--- a/gpu/command_buffer/client/raster_implementation.cc
+++ b/gpu/command_buffer/client/raster_implementation.cc
@@ -88,90 +88,100 @@
 
 const size_t kMaxTransferCacheEntrySizeForTransferBuffer = 1024;
 
+}  // namespace
+
 // Helper to copy data to the GPU service over the transfer cache.
-class TransferCacheSerializeHelperImpl
+class RasterImplementation::TransferCacheSerializeHelperImpl
     : public cc::TransferCacheSerializeHelper {
  public:
-  explicit TransferCacheSerializeHelperImpl(ContextSupport* support)
-      : support_(support) {}
+  explicit TransferCacheSerializeHelperImpl(RasterImplementation* ri)
+      : ri_(ri) {}
   ~TransferCacheSerializeHelperImpl() final = default;
 
-  void SubmitDeferredEntries() final {
-    for (auto& entry : deferred_entries_) {
-      void* data = support_->MapTransferCacheEntry(entry.size);
-      memcpy(data, entry.data.get(), entry.size);
-      support_->UnmapAndCreateTransferCacheEntry(entry.type, entry.id);
-    }
-    deferred_entries_.clear();
+  size_t take_end_offset_of_last_inlined_entry() {
+    auto offset = end_offset_of_last_inlined_entry_;
+    end_offset_of_last_inlined_entry_ = 0u;
+    return offset;
   }
 
  private:
   bool LockEntryInternal(const EntryKey& key) final {
-    return support_->ThreadsafeLockTransferCacheEntry(
+    return ri_->ThreadsafeLockTransferCacheEntry(
         static_cast<uint32_t>(key.first), key.second);
   }
 
-  void CreateEntryInternal(const cc::ClientTransferCacheEntry& entry) final {
+  size_t CreateEntryInternal(const cc::ClientTransferCacheEntry& entry,
+                             char* memory) final {
     size_t size = entry.SerializedSize();
-    // As an optimization, don't defer entries that are too large, as we will
-    // always choose mapped memory for them in MapTransferCacheEntry below.
-    // If we defer but then use mapped memory, it's an unneeded copy.
-    if (size <= kMaxTransferCacheEntrySizeForTransferBuffer) {
-      DeferEntry(entry);
-      return;
+    // Cap the entries inlined to a specific size.
+    if (size <= ri_->max_inlined_entry_size_ && ri_->raster_mapped_buffer_) {
+      size_t written = InlineEntry(entry, memory);
+      if (written > 0u)
+        return written;
     }
 
-    void* data = support_->MapTransferCacheEntry(size);
+    void* data = ri_->MapTransferCacheEntry(size);
     if (!data)
-      return;
+      return 0u;
 
     bool succeeded = entry.Serialize(
         base::make_span(reinterpret_cast<uint8_t*>(data), size));
     DCHECK(succeeded);
-    support_->UnmapAndCreateTransferCacheEntry(entry.UnsafeType(), entry.Id());
+    ri_->UnmapAndCreateTransferCacheEntry(entry.UnsafeType(), entry.Id());
+    return 0u;
   }
 
   void FlushEntriesInternal(std::set<EntryKey> entries) final {
-    DCHECK(deferred_entries_.empty());
-
     std::vector<std::pair<uint32_t, uint32_t>> transformed;
     transformed.reserve(entries.size());
     for (const auto& e : entries)
       transformed.emplace_back(static_cast<uint32_t>(e.first), e.second);
-    support_->UnlockTransferCacheEntries(transformed);
+    ri_->UnlockTransferCacheEntries(transformed);
   }
 
-  void DeferEntry(const cc::ClientTransferCacheEntry& entry) {
-    // Because lifetime of the transfer cache entry is transient, we need
-    // to serialize on the heap, instead of holding onto the entry.
-    size_t size = entry.SerializedSize();
-    std::unique_ptr<uint8_t[]> mem(new uint8_t[size]);
-    bool succeeded = entry.Serialize(base::make_span(mem.get(), size));
+  // Writes the entry into |memory| if there is enough space. Returns the number
+  // of bytes written on success or 0u on failure due to insufficient size.
+  size_t InlineEntry(const cc::ClientTransferCacheEntry& entry, char* memory) {
+    DCHECK(memory);
+    DCHECK(SkIsAlign4(reinterpret_cast<uintptr_t>(memory)));
+
+    // The memory passed from the PaintOpWriter for inlining the transfer cache
+    // entry must be from the transfer buffer mapped during RasterCHROMIUM.
+    const auto& buffer = ri_->raster_mapped_buffer_;
+    DCHECK(buffer->BelongsToBuffer(memory));
+
+    size_t memory_offset = memory - static_cast<char*>(buffer->address());
+    size_t bytes_to_write = entry.SerializedSize();
+    size_t bytes_remaining = buffer->size() - memory_offset;
+    DCHECK_GT(bytes_to_write, 0u);
+
+    if (bytes_to_write > bytes_remaining)
+      return 0u;
+
+    bool succeeded = entry.Serialize(
+        base::make_span(reinterpret_cast<uint8_t*>(memory), bytes_remaining));
     DCHECK(succeeded);
-    deferred_entries_.push_back(
-        {size, entry.UnsafeType(), entry.Id(), std::move(mem)});
+    ri_->transfer_cache_.AddTransferCacheEntry(
+        entry.UnsafeType(), entry.Id(), buffer->shm_id(),
+        buffer->offset() + memory_offset, bytes_to_write);
+
+    end_offset_of_last_inlined_entry_ = memory_offset + bytes_to_write;
+    return bytes_to_write;
   }
 
-  ContextSupport* const support_;
-
-  struct DeferredEntry {
-    size_t size;
-    uint32_t type;
-    uint32_t id;
-    std::unique_ptr<uint8_t[]> data;
-  };
-  std::vector<DeferredEntry> deferred_entries_;
+  RasterImplementation* const ri_;
+  size_t end_offset_of_last_inlined_entry_ = 0u;
 
   DISALLOW_COPY_AND_ASSIGN(TransferCacheSerializeHelperImpl);
 };
 
 // Helper to copy PaintOps to the GPU service over the transfer buffer.
-class PaintOpSerializer {
+class RasterImplementation::PaintOpSerializer {
  public:
   PaintOpSerializer(size_t initial_size,
                     RasterImplementation* ri,
                     cc::DecodeStashingImageProvider* stashing_image_provider,
-                    cc::TransferCacheSerializeHelper* transfer_cache_helper,
+                    TransferCacheSerializeHelperImpl* transfer_cache_helper,
                     ClientFontManager* font_manager)
       : ri_(ri),
         buffer_(static_cast<char*>(ri_->MapRasterCHROMIUM(initial_size))),
@@ -214,16 +224,16 @@
     // Serialize fonts before sending raster commands.
     font_manager_->Serialize();
 
-    // Shrink the transfer buffer down to what was written during raster.
-    ri_->FinalizeRasterCHROMIUM(written_bytes_);
-
-    // Create and submit any transfer cache entries that were waiting on raster
-    // to finish to be written into the transfer buffer.
-    transfer_cache_helper_->SubmitDeferredEntries();
+    // Check the address of the last inlined entry to figured out whether
+    // transfer cache entries were written past the last successfully serialized
+    // op.
+    size_t total_written_size = std::max(
+        written_bytes_,
+        transfer_cache_helper_->take_end_offset_of_last_inlined_entry());
 
     // Send the raster command itself now that the commands for its
     // dependencies have been sent.
-    ri_->UnmapRasterCHROMIUM(written_bytes_);
+    ri_->UnmapRasterCHROMIUM(written_bytes_, total_written_size);
 
     // Now that we've issued the RasterCHROMIUM referencing the stashed
     // images, Reset the |stashing_image_provider_|, causing us to issue
@@ -243,7 +253,7 @@
   RasterImplementation* const ri_;
   char* buffer_;
   cc::DecodeStashingImageProvider* const stashing_image_provider_;
-  cc::TransferCacheSerializeHelper* const transfer_cache_helper_;
+  TransferCacheSerializeHelperImpl* const transfer_cache_helper_;
   ClientFontManager* font_manager_;
 
   size_t written_bytes_ = 0;
@@ -252,8 +262,6 @@
   DISALLOW_COPY_AND_ASSIGN(PaintOpSerializer);
 };
 
-}  // namespace
-
 RasterImplementation::SingleThreadChecker::SingleThreadChecker(
     RasterImplementation* raster_implementation)
     : raster_implementation_(raster_implementation) {
@@ -282,6 +290,7 @@
       aggressively_free_resources_(false),
       font_manager_(this, helper->command_buffer()),
       lost_(false),
+      max_inlined_entry_size_(kMaxTransferCacheEntrySizeForTransferBuffer),
       transfer_cache_(this) {
   DCHECK(helper);
   DCHECK(transfer_buffer);
@@ -384,6 +393,11 @@
   } else {
     ShallowFlushCHROMIUM();
   }
+
+  if (aggressively_free_resources_) {
+    temp_raster_offsets_.clear();
+    temp_raster_offsets_.shrink_to_fit();
+  }
 }
 
 void RasterImplementation::Swap(
@@ -455,15 +469,9 @@
 }
 
 void* RasterImplementation::MapTransferCacheEntry(size_t serialized_size) {
-  // Putting small entries in the transfer buffer is an optimization.  To avoid
-  // the case of one client exhausting the entire transfer buffer with transfer
-  // cache entries, only use this path if this fits without waiting and fall
-  // back to mapped memory otherwise.  Additionally, if we are inside raster,
-  // then we must use mapped memory because rasterizing requests
-  // GetTransferBufferFreeSize as the initial size and shrink later during
-  // FinalizeRasterCHROMIUM.
-  if (inside_raster_ ||
-      serialized_size > kMaxTransferCacheEntrySizeForTransferBuffer ||
+  // Prefer to use transfer buffer when possible, since transfer buffer
+  // allocations are much cheaper.
+  if (raster_mapped_buffer_ ||
       transfer_buffer_->GetFreeSize() < serialized_size) {
     return transfer_cache_.MapEntry(mapped_memory_.get(), serialized_size);
   }
@@ -1070,7 +1078,6 @@
     return nullptr;
   }
 
-  inside_raster_ = true;
   return raster_mapped_buffer_->address();
 }
 
@@ -1104,12 +1111,11 @@
   return font_mapped_buffer_->address();
 }
 
-void RasterImplementation::FinalizeRasterCHROMIUM(GLsizeiptr written_size) {
-  if (written_size < 0) {
+void RasterImplementation::UnmapRasterCHROMIUM(GLsizeiptr raster_written_size,
+                                               GLsizeiptr total_written_size) {
+  if (total_written_size < 0) {
     SetGLError(GL_INVALID_VALUE, "glUnmapRasterCHROMIUM",
                "negative written_size");
-    raster_mapped_buffer_->Discard();
-    raster_mapped_buffer_ = base::nullopt;
     return;
   }
   if (!raster_mapped_buffer_) {
@@ -1117,21 +1123,12 @@
     return;
   }
   DCHECK(raster_mapped_buffer_->valid());
-  if (written_size == 0) {
+  if (total_written_size == 0) {
     raster_mapped_buffer_->Discard();
     raster_mapped_buffer_ = base::nullopt;
     return;
   }
-  raster_mapped_buffer_->Shrink(written_size);
-  inside_raster_ = false;
-}
-
-void RasterImplementation::UnmapRasterCHROMIUM(GLsizeiptr written_size) {
-  if (!raster_mapped_buffer_)
-    return;
-  DCHECK_EQ(static_cast<uint32_t>(raster_mapped_buffer_->size()),
-            static_cast<uint32_t>(written_size))
-      << "Call FinalizeRasterCHROMIUM first";
+  raster_mapped_buffer_->Shrink(total_written_size);
 
   GLuint font_shm_id = 0u;
   GLuint font_shm_offset = 0u;
@@ -1141,9 +1138,13 @@
     font_shm_offset = font_mapped_buffer_->offset();
     font_shm_size = font_mapped_buffer_->size();
   }
-  helper_->RasterCHROMIUM(raster_mapped_buffer_->shm_id(),
-                          raster_mapped_buffer_->offset(), written_size,
-                          font_shm_id, font_shm_offset, font_shm_size);
+
+  if (raster_written_size != 0u) {
+    helper_->RasterCHROMIUM(
+        raster_mapped_buffer_->shm_id(), raster_mapped_buffer_->offset(),
+        raster_written_size, font_shm_id, font_shm_offset, font_shm_size);
+  }
+
   raster_mapped_buffer_ = base::nullopt;
   font_mapped_buffer_ = base::nullopt;
   CheckGLError();
@@ -1225,14 +1226,11 @@
           cc::TransferCacheEntryType::kColorSpace,
           raster_color_space.color_space_id)) {
     transfer_cache_serialize_helper.CreateEntry(
-        cc::ClientColorSpaceTransferCacheEntry(raster_color_space));
+        cc::ClientColorSpaceTransferCacheEntry(raster_color_space), nullptr);
   }
   transfer_cache_serialize_helper.AssertLocked(
       cc::TransferCacheEntryType::kColorSpace,
       raster_color_space.color_space_id);
-  // Creating this entry could have been deferred, so submit it so that
-  // BeginRasterCHROMIUM can depend on it.
-  transfer_cache_serialize_helper.SubmitDeferredEntries();
 
   helper_->BeginRasterCHROMIUMImmediate(
       sk_color, msaa_sample_count, can_use_lcd_text, color_type,
@@ -1259,12 +1257,10 @@
 
   gfx::Rect query_rect =
       gfx::ScaleToEnclosingRect(playback_rect, 1.f / post_scale);
-  std::vector<size_t> offsets = list->rtree_.Search(query_rect);
-  if (offsets.empty())
+  list->rtree_.Search(query_rect, &temp_raster_offsets_);
+  if (temp_raster_offsets_.empty())
     return;
 
-  DCHECK(!inside_raster_);
-
   // TODO(enne): Tune these numbers
   // TODO(enne): Convert these types here and in transfer buffer to be size_t.
   static constexpr unsigned int kMinAlloc = 16 * 1024;
@@ -1300,12 +1296,10 @@
       capabilities().context_supports_distance_field_text,
       capabilities().max_texture_size,
       capabilities().glyph_cache_max_texture_bytes);
-  DCHECK(inside_raster_);
-  serializer.Serialize(&list->paint_op_buffer_, &offsets, preamble);
+  serializer.Serialize(&list->paint_op_buffer_, &temp_raster_offsets_,
+                       preamble);
   // TODO(piman): raise error if !serializer.valid()?
   op_serializer.SendSerializedData();
-
-  DCHECK(!inside_raster_);
 }
 
 void RasterImplementation::EndRasterCHROMIUM() {
@@ -1373,6 +1367,16 @@
   helper_->ResetActiveURLCHROMIUM();
 }
 
+std::unique_ptr<cc::TransferCacheSerializeHelper>
+RasterImplementation::CreateTransferCacheHelperForTesting() {
+  return std::make_unique<TransferCacheSerializeHelperImpl>(this);
+}
+
+void RasterImplementation::SetRasterMappedBufferForTesting(
+    ScopedTransferBufferPtr buffer) {
+  raster_mapped_buffer_.emplace(std::move(buffer));
+}
+
 RasterImplementation::RasterProperties::RasterProperties(
     SkColor background_color,
     bool can_use_lcd_text,
diff --git a/gpu/command_buffer/client/raster_implementation.h b/gpu/command_buffer/client/raster_implementation.h
index 3e48362..5ede987 100644
--- a/gpu/command_buffer/client/raster_implementation.h
+++ b/gpu/command_buffer/client/raster_implementation.h
@@ -31,6 +31,10 @@
 #include "gpu/raster_export.h"
 #include "third_party/skia/include/core/SkColor.h"
 
+namespace cc {
+class TransferCacheSerializeHelper;
+}  // namespace cc
+
 namespace gpu {
 
 class GpuControl;
@@ -163,14 +167,20 @@
                                  GLenum pname,
                                  GLuint64* params);
 
-  void* MapRasterCHROMIUM(GLsizeiptr size);
-  void FinalizeRasterCHROMIUM(GLsizeiptr written_size);
-  void UnmapRasterCHROMIUM(GLsizeiptr written_size);
-
   // ClientFontManager::Client implementation.
   void* MapFontBuffer(size_t size) override;
 
+  void set_max_inlined_entry_size_for_testing(size_t max_size) {
+    max_inlined_entry_size_ = max_size;
+  }
+
+  std::unique_ptr<cc::TransferCacheSerializeHelper>
+  CreateTransferCacheHelperForTesting();
+  void SetRasterMappedBufferForTesting(ScopedTransferBufferPtr buffer);
+
  private:
+  class TransferCacheSerializeHelperImpl;
+  class PaintOpSerializer;
   friend class RasterImplementationTest;
 
   using IdNamespaces = raster::id_namespaces::IdNamespaces;
@@ -211,6 +221,14 @@
                              GLenum value,
                              const char* label);
 
+  void* MapRasterCHROMIUM(GLsizeiptr size);
+
+  // |raster_written_size| is the size of buffer used by raster commands.
+  // |total_written_size| is the total size of the buffer written to, including
+  // any transfer cache entries inlined into the buffer.
+  void UnmapRasterCHROMIUM(GLsizeiptr raster_written_size,
+                           GLsizeiptr total_written_size);
+
   // Returns the last error and clears it. Useful for debugging.
   const std::string& GetLastError() { return last_error_; }
 
@@ -296,7 +314,10 @@
   mutable base::Lock lost_lock_;
   bool lost_;
 
-  bool inside_raster_ = false;
+  // To avoid repeated allocations when searching the rtrees, hold onto this
+  // vector between RasterCHROMIUM calls.  It is not valid outside of that
+  // function.
+  std::vector<size_t> temp_raster_offsets_;
 
   struct RasterProperties {
     RasterProperties(SkColor background_color,
@@ -309,6 +330,7 @@
   };
   base::Optional<RasterProperties> raster_properties_;
 
+  size_t max_inlined_entry_size_;
   ClientTransferCache transfer_cache_;
 
   // Tracing helpers.
diff --git a/gpu/command_buffer/client/raster_implementation_unittest.cc b/gpu/command_buffer/client/raster_implementation_unittest.cc
index 60db959..d68234d 100644
--- a/gpu/command_buffer/client/raster_implementation_unittest.cc
+++ b/gpu/command_buffer/client/raster_implementation_unittest.cc
@@ -15,6 +15,8 @@
 #include <memory>
 
 #include "base/compiler_specific.h"
+#include "cc/paint/raw_memory_transfer_cache_entry.h"
+#include "cc/paint/transfer_cache_serialize_helper.h"
 #include "gpu/command_buffer/client/client_test_helper.h"
 #include "gpu/command_buffer/client/mock_transfer_buffer.h"
 #include "gpu/command_buffer/client/query_tracker.h"
@@ -203,7 +205,7 @@
     return gl_->MapRasterCHROMIUM(size);
   }
   void UnmapRasterCHROMIUM(GLsizeiptr written_size) {
-    gl_->UnmapRasterCHROMIUM(written_size);
+    gl_->UnmapRasterCHROMIUM(written_size, written_size);
   }
 
   struct ContextInitOptions {
@@ -904,6 +906,29 @@
   EXPECT_FALSE(Initialize(init_options));
 }
 
+TEST_F(RasterImplementationTest, TransferCacheSerialization) {
+  gl_->set_max_inlined_entry_size_for_testing(768u);
+  size_t buffer_size = transfer_buffer_->MaxTransferBufferSize();
+  ScopedTransferBufferPtr buffer(buffer_size, helper_, transfer_buffer_);
+  ASSERT_EQ(buffer.size(), buffer_size);
+
+  char* buffer_start = reinterpret_cast<char*>(buffer.address());
+  memset(buffer_start, 0, buffer_size);
+  gl_->SetRasterMappedBufferForTesting(std::move(buffer));
+  auto transfer_cache = gl_->CreateTransferCacheHelperForTesting();
+
+  std::vector<uint8_t> data(buffer_size - 16u);
+  char* memory = buffer_start + 8u;
+  cc::ClientRawMemoryTransferCacheEntry inlined_entry(data);
+  EXPECT_EQ(transfer_cache->CreateEntry(inlined_entry, memory), data.size());
+  EXPECT_EQ(memcmp(data.data(), memory, data.size()), 0);
+
+  data.resize(buffer_size + 16u);
+  memory = buffer_start + 8u;
+  cc::ClientRawMemoryTransferCacheEntry non_inlined_entry(data);
+  EXPECT_EQ(transfer_cache->CreateEntry(non_inlined_entry, memory), 0u);
+}
+
 #include "base/macros.h"
 #include "gpu/command_buffer/client/raster_implementation_unittest_autogen.h"
 
diff --git a/gpu/command_buffer/client/transfer_buffer.cc b/gpu/command_buffer/client/transfer_buffer.cc
index 9aa00a32..a577ed5 100644
--- a/gpu/command_buffer/client/transfer_buffer.cc
+++ b/gpu/command_buffer/client/transfer_buffer.cc
@@ -251,6 +251,16 @@
   return HaveBuffer() ? max_buffer_size_ - result_size_ : 0;
 }
 
+ScopedTransferBufferPtr::ScopedTransferBufferPtr(
+    ScopedTransferBufferPtr&& other)
+    : buffer_(other.buffer_),
+      size_(other.size_),
+      helper_(other.helper_),
+      transfer_buffer_(other.transfer_buffer_) {
+  other.buffer_ = nullptr;
+  other.size_ = 0u;
+}
+
 void ScopedTransferBufferPtr::Release() {
   if (buffer_) {
     transfer_buffer_->FreePendingToken(buffer_, helper_->InsertToken());
@@ -284,4 +294,12 @@
   size_ = new_size;
 }
 
+bool ScopedTransferBufferPtr::BelongsToBuffer(char* memory) const {
+  if (!buffer_)
+    return false;
+  char* start = reinterpret_cast<char*>(buffer_);
+  char* end = start + size_;
+  return memory >= start && memory <= end;
+}
+
 }  // namespace gpu
diff --git a/gpu/command_buffer/client/transfer_buffer.h b/gpu/command_buffer/client/transfer_buffer.h
index cd18fbd7..866c7dc 100644
--- a/gpu/command_buffer/client/transfer_buffer.h
+++ b/gpu/command_buffer/client/transfer_buffer.h
@@ -185,6 +185,8 @@
     Release();
   }
 
+  ScopedTransferBufferPtr(ScopedTransferBufferPtr&& other);
+
   bool valid() const { return buffer_ != nullptr; }
 
   unsigned int size() const {
@@ -203,6 +205,9 @@
     return buffer_;
   }
 
+  // Returns true if |memory| lies inside this buffer.
+  bool BelongsToBuffer(char* memory) const;
+
   void Release();
 
   void Discard();
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 92a2b366..5fd0089 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -1862,7 +1862,7 @@
     builders {
       name: "linux-code-coverage-tester"
       mixins: "code-coverage"
-      dimensions: "cores:32"
+      dimensions: "cores:8"
     }
 
     builders {
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 5a8e20d..c5d35b78 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -110,6 +110,7 @@
   triggers: "Chromium Linux Goma Staging"
   triggers: "Chromium Mac 10.13"
   triggers: "Chromium Mac Goma GCE Staging"
+  triggers: "Chromium Mac Goma RBE Staging (clobber)"
   triggers: "Chromium Mac Goma Staging"
   triggers: "Chromium Windows Analyze"
   triggers: "ChromiumOS ASAN Release"
@@ -4151,6 +4152,16 @@
 }
 
 job {
+  id: "Chromium Mac Goma RBE Staging (clobber)"
+  acl_sets: "default"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "Chromium Mac Goma RBE Staging (clobber)"
+  }
+}
+
+job {
   id: "Chromium Mac Goma Staging"
   acl_sets: "default"
   buildbucket: {
diff --git a/ios/web_view/internal/autofill/cwv_credit_card_verifier.mm b/ios/web_view/internal/autofill/cwv_credit_card_verifier.mm
index c9ec2de6..0fbf604 100644
--- a/ios/web_view/internal/autofill/cwv_credit_card_verifier.mm
+++ b/ios/web_view/internal/autofill/cwv_credit_card_verifier.mm
@@ -91,6 +91,8 @@
   __weak id<CWVCreditCardVerifierDataSource> _dataSource;
   // Delegate to receive callbacks.
   __weak id<CWVCreditCardVerifierDelegate> _delegate;
+  // The callback to invoke for returning risk data.
+  base::OnceCallback<void(const std::string&)> _riskDataCallback;
 }
 
 @synthesize creditCard = _creditCard;
@@ -166,6 +168,16 @@
                  (nullable __weak id<CWVCreditCardVerifierDelegate>)delegate {
   _dataSource = dataSource;
   _delegate = delegate;
+
+  // It is possible for |_riskDataCallback| to be null when a failed
+  // verification attempt is retried.
+  if (!_riskDataCallback.is_null()) {
+    [_dataSource creditCardVerifier:self
+        getRiskDataWithCompletionHandler:^(NSString* _Nonnull riskData) {
+          std::move(_riskDataCallback).Run(base::SysNSStringToUTF8(riskData));
+        }];
+  }
+
   _unmaskingController->OnUnmaskResponse(
       base::SysNSStringToUTF16(CVC), base::SysNSStringToUTF16(expirationMonth),
       base::SysNSStringToUTF16(expirationYear), storeLocally);
@@ -212,12 +224,7 @@
 }
 
 - (void)loadRiskData:(base::OnceCallback<void(const std::string&)>)callback {
-  __block base::OnceCallback<void(const std::string&)> blockCallback =
-      std::move(callback);
-  [_dataSource creditCardVerifier:self
-      getRiskDataWithCompletionHandler:^(NSString* _Nonnull riskData) {
-        std::move(blockCallback).Run(base::SysNSStringToUTF8(riskData));
-      }];
+  _riskDataCallback = std::move(callback);
 }
 
 @end
diff --git a/ios/web_view/internal/autofill/cwv_credit_card_verifier_unittest.mm b/ios/web_view/internal/autofill/cwv_credit_card_verifier_unittest.mm
index a27b2e2..708b158 100644
--- a/ios/web_view/internal/autofill/cwv_credit_card_verifier_unittest.mm
+++ b/ios/web_view/internal/autofill/cwv_credit_card_verifier_unittest.mm
@@ -9,6 +9,7 @@
 
 #include "base/base_paths.h"
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/path_service.h"
@@ -179,6 +180,7 @@
       OCMProtocolMock(@protocol(CWVCreditCardVerifierDataSource));
   NSString* cvc = @"123";
   BOOL store_locally = YES;
+  [credit_card_verifier_ loadRiskData:std::move(base::DoNothing())];
   [credit_card_verifier_
         verifyWithCVC:cvc
       expirationMonth:@""  // Expiration dates are ignored here because
@@ -199,6 +201,7 @@
   id unused_data_source =
       OCMProtocolMock(@protocol(CWVCreditCardVerifierDataSource));
   id delegate = OCMProtocolMock(@protocol(CWVCreditCardVerifierDelegate));
+  [credit_card_verifier_ loadRiskData:std::move(base::DoNothing())];
   [credit_card_verifier_ verifyWithCVC:@"123"
                        expirationMonth:@""
                         expirationYear:@""
@@ -225,13 +228,6 @@
 // Tests CWVCreditCardVerifier properly invokes its data source.
 TEST_F(CWVCreditCardVerifierTest, DataSourceCallbacks) {
   id data_source = OCMProtocolMock(@protocol(CWVCreditCardVerifierDataSource));
-  [credit_card_verifier_ verifyWithCVC:@"123"
-                       expirationMonth:@""
-                        expirationYear:@""
-                          storeLocally:NO
-                            dataSource:data_source
-                              delegate:nil];
-
   [[data_source expect]
                     creditCardVerifier:credit_card_verifier_
       getRiskDataWithCompletionHandler:[OCMArg checkWithBlock:^BOOL(id arg) {
@@ -239,6 +235,7 @@
         completionHandler(@"dummy-risk-data");
         return YES;
       }]];
+
   __block bool callback_called = false;
   base::OnceCallback<void(const std::string&)> callback = base::BindOnce(
       [](bool* callback_called, const std::string& risk_data) -> void {
@@ -247,6 +244,12 @@
       },
       &callback_called);
   [credit_card_verifier_ loadRiskData:std::move(callback)];
+  [credit_card_verifier_ verifyWithCVC:@"123"
+                       expirationMonth:@""
+                        expirationYear:@""
+                          storeLocally:NO
+                            dataSource:data_source
+                              delegate:nil];
 
   EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForActionTimeout, ^bool {
     base::RunLoop().RunUntilIdle();
diff --git a/ios/web_view/public/cwv_credit_card_verifier.h b/ios/web_view/public/cwv_credit_card_verifier.h
index d3f47d45..b2631b8 100644
--- a/ios/web_view/public/cwv_credit_card_verifier.h
+++ b/ios/web_view/public/cwv_credit_card_verifier.h
@@ -85,8 +85,9 @@
 // |storeLocally| Whether or not to save |creditCard| locally. If YES, user will
 // not be asked again to verify this card. Ignored if |canSaveLocally| is NO.
 // |dataSource| will be asked to return risk data needed for verification.
-// |delegate| will be passed the verification result. Must wait for |delegate|
-// methods before attempting to verify again.
+// |delegate| will be passed the verification result.
+// If |delegate| is passed an error object indicating retry is not allowed,
+// additional verifications will be ignored.
 - (void)verifyWithCVC:(NSString*)CVC
       expirationMonth:(nullable NSString*)expirationMonth
        expirationYear:(nullable NSString*)expirationYear
diff --git a/media/base/test_data_util.cc b/media/base/test_data_util.cc
index 171776c..f41c68f 100644
--- a/media/base/test_data_util.cc
+++ b/media/base/test_data_util.cc
@@ -18,21 +18,27 @@
 
 namespace {
 
-// Mime types for test files. Sorted in alphabetical order.
+// Mime types for test files. Sorted in the ASCII code order of the variable
+// names.
 const char kAacAdtsAudioOnly[] = "audio/aac";
+const char kMp2AudioSBR[] = "video/mp2t; codecs=\"avc1.4D4041,mp4a.40.5\"";
 const char kMp2tAudioVideo[] = "video/mp2t; codecs=\"mp4a.40.2, avc1.42E01E\"";
+const char kMp3AudioOnly[] = "audio/mpeg";
+// MP4
 const char kMp4AacAudioOnly[] = "audio/mp4; codecs=\"mp4a.40.2\"";
-const char kMp4AudioOnly[] = "audio/mp4; codecs=\"mp4a.40.2\"'";
 const char kMp4Av110bitVideoOnly[] = "video/mp4; codecs=\"av01.0.04M.10\"";
 const char kMp4Av1VideoOnly[] = "video/mp4; codecs=\"av01.0.04M.08\"";
 const char kMp4Avc1VideoOnly[] = "video/mp4; codecs=\"avc1.64001E\"";
+const char kMp4AacAudioAvc1Video[] =
+    "video/mp4; codecs=\"mp4a.40.2, avc1.64001E\"";
+const char kMp4Avc3VideoOnly[] = "video/mp4; codecs=\"avc3.64001f\"";
 const char kMp4FlacAudioOnly[] = "audio/mp4; codecs=\"flac\"";
-const char kMp4VideoOnly[] = "video/mp4; codecs=\"avc1.4D4041\"'";
+const char kMp4OpusAudioOnly[] = "audio/mp4; codecs=\"opus\"";
 const char kMp4Vp9Profile2VideoOnly[] =
     "video/mp4; codecs=\"vp09.02.10.10.01.02.02.02.00\"";
 const char kMp4Vp9VideoOnly[] =
     "video/mp4; codecs=\"vp09.00.10.08.01.02.02.02.00\"";
-const char kWebMAudioVideo[] = "video/webm; codecs=\"vorbis, vp8\"";
+// WebM
 const char kWebMAv110bitVideoOnly[] = "video/webm; codecs=\"av01.0.04M.10\"";
 const char kWebMAv1VideoOnly[] = "video/webm; codecs=\"av01.0.04M.08\"";
 const char kWebMOpusAudioOnly[] = "audio/webm; codecs=\"opus\"";
@@ -48,15 +54,33 @@
 // media/test/data.
 using FileToMimeTypeMap = base::flat_map<std::string, std::string>;
 
-// Wrapped to avoid static initializer startup cost. The list is sorted in
-// alphabetical order.
+// Wrapped to avoid static initializer startup cost. The list is sorted in the
+// the ASCII code order of file names.
+// Note: Some files are old and the codec string in the mime type may not be
+// accurate.
+// Warning: When adding new files, make sure the codec string is accurate. For
+// example kMp4Avc1VideoOnly is for H264 high profile. If you add a file that
+// uses main profile, a new mime type should be added.
 const FileToMimeTypeMap& GetFileToMimeTypeMap() {
   static const base::NoDestructor<FileToMimeTypeMap> kFileToMimeTypeMap({
+      {"bear-1280x720-a_frag-cenc-key_rotation.mp4", kMp4AacAudioOnly},
+      {"bear-1280x720-a_frag-cenc.mp4", kMp4AacAudioOnly},
+      {"bear-1280x720-a_frag-cenc_clear-all.mp4", kMp4AacAudioOnly},
+      {"bear-1280x720-aac_he.ts", kMp2AudioSBR},
+      {"bear-1280x720-v_frag-avc3.mp4", kMp4Avc3VideoOnly},
+      {"bear-1280x720-v_frag-cenc-key_rotation.mp4", kMp4Avc1VideoOnly},
+      {"bear-1280x720-v_frag-cenc.mp4", kMp4Avc1VideoOnly},
+      {"bear-1280x720-v_frag-cenc_clear-all.mp4", kMp4Avc1VideoOnly},
       {"bear-1280x720.ts", kMp2tAudioVideo},
+      {"bear-320x240-16x9-aspect-av_enc-av.webm", kWebMVorbisAudioVp8Video},
+      {"bear-320x240-16x9-aspect.webm", kWebMVorbisAudioVp8Video},
       {"bear-320x240-audio-only.webm", kWebMVorbisAudioOnly},
       {"bear-320x240-av_enc-a.webm", kWebMVorbisAudioVp8Video},
       {"bear-320x240-av_enc-av.webm", kWebMVorbisAudioVp8Video},
+      {"bear-320x240-av_enc-av_clear-1s.webm", kWebMVorbisAudioVp8Video},
+      {"bear-320x240-av_enc-av_clear-all.webm", kWebMVorbisAudioVp8Video},
       {"bear-320x240-av_enc-v.webm", kWebMVorbisAudioVp8Video},
+      {"bear-320x240-live.webm", kWebMVorbisAudioVp8Video},
       {"bear-320x240-opus-a_enc-a.webm", kWebMOpusAudioOnly},
       {"bear-320x240-opus-av_enc-av.webm", kWebMOpusAudioVp9Video},
       {"bear-320x240-opus-av_enc-v.webm", kWebMOpusAudioVp9Video},
@@ -68,27 +92,56 @@
       {"bear-320x240-v-vp9_subsample_enc-v.webm", kWebMVp9VideoOnly},
       {"bear-320x240-v_enc-v.webm", kWebMVp8VideoOnly},
       {"bear-320x240-v_frag-vp9-cenc.mp4", kMp4Vp9VideoOnly},
+      {"bear-320x240-v_frag-vp9.mp4", kMp4Vp9VideoOnly},
       {"bear-320x240-video-only.webm", kWebMVp8VideoOnly},
-      {"bear-320x240.webm", kWebMAudioVideo},
+      {"bear-320x240.webm", kWebMVorbisAudioVp8Video},
+      {"bear-320x240_corrupted_after_init_segment.webm",
+       kWebMVorbisAudioVp8Video},
       {"bear-640x360-a_frag-cbcs.mp4", kMp4AacAudioOnly},
       {"bear-640x360-a_frag-cenc.mp4", kMp4AacAudioOnly},
-      {"bear-640x360-a_frag.mp4", kMp4AudioOnly},
+      {"bear-640x360-a_frag.mp4", kMp4AacAudioOnly},
+      {"bear-640x360-av_frag.mp4", kMp4AacAudioAvc1Video},
       {"bear-640x360-v_frag-cbc1.mp4", kMp4Avc1VideoOnly},
       {"bear-640x360-v_frag-cbcs.mp4", kMp4Avc1VideoOnly},
+      {"bear-640x360-v_frag-cenc-key_rotation.mp4", kMp4Avc1VideoOnly},
       {"bear-640x360-v_frag-cenc-mdat.mp4", kMp4Avc1VideoOnly},
+      {"bear-640x360-v_frag-cenc-senc-no-saiz-saio.mp4", kMp4Avc1VideoOnly},
+      {"bear-640x360-v_frag-cenc-senc.mp4", kMp4Avc1VideoOnly},
       {"bear-640x360-v_frag-cenc.mp4", kMp4Avc1VideoOnly},
       {"bear-640x360-v_frag-cens.mp4", kMp4Avc1VideoOnly},
-      {"bear-640x360-v_frag.mp4", kMp4VideoOnly},
+      {"bear-640x360-v_frag.mp4", kMp4Avc1VideoOnly},
       {"bear-a_enc-a.webm", kWebMVorbisAudioOnly},
+      {"bear-audio-implicit-he-aac-v1.aac", kAacAdtsAudioOnly},
+      {"bear-audio-implicit-he-aac-v2.aac", kAacAdtsAudioOnly},
+      {"bear-audio-lc-aac.aac", kAacAdtsAudioOnly},
+      {"bear-audio-main-aac.aac", kAacAdtsAudioOnly},
+      {"bear-audio-mp4a.69.ts", "video/mp2t; codecs=\"mp4a.69\""},
+      {"bear-audio-mp4a.6B.ts", "video/mp2t; codecs=\"mp4a.6B\""},
       {"bear-av1-320x180-10bit-cenc.mp4", kMp4Av110bitVideoOnly},
       {"bear-av1-320x180-10bit-cenc.webm", kWebMAv110bitVideoOnly},
+      {"bear-av1-320x180-10bit.mp4", kMp4Av110bitVideoOnly},
+      {"bear-av1-320x180-10bit.webm", kWebMAv110bitVideoOnly},
+      {"bear-av1-480x360.webm", kWebMAv1VideoOnly},
       {"bear-av1-cenc.mp4", kMp4Av1VideoOnly},
       {"bear-av1-cenc.webm", kWebMAv1VideoOnly},
+      {"bear-av1.mp4", kMp4Av1VideoOnly},
+      {"bear-av1.webm", kWebMAv1VideoOnly},
       {"bear-flac-cenc.mp4", kMp4FlacAudioOnly},
       {"bear-flac_frag.mp4", kMp4FlacAudioOnly},
+      {"bear-opus.mp4", kMp4OpusAudioOnly},
       {"bear-opus.webm", kWebMOpusAudioOnly},
+      {"bear-vp8a.webm", kWebMVp8VideoOnly},
+      {"bear-vp9-blockgroup.webm", kWebMVp9VideoOnly},
+      {"bear-vp9.webm", kWebMVp9VideoOnly},
       {"frame_size_change-av_enc-v.webm", kWebMVorbisAudioVp8Video},
+      {"icy_sfx.mp3", kMp3AudioOnly},
+      {"opus-trimming-test.mp4", kMp4OpusAudioOnly},
+      {"opus-trimming-test.webm", kWebMOpusAudioOnly},
+      {"sfx-flac_frag.mp4", kMp4FlacAudioOnly},
+      {"sfx-opus-441.webm", kWebMOpusAudioOnly},
+      {"sfx-opus_frag.mp4", kMp4OpusAudioOnly},
       {"sfx.adts", kAacAdtsAudioOnly},
+      {"sfx.mp3", kMp3AudioOnly},
   });
 
   return *kFileToMimeTypeMap;
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
index 9f091e6..90c6245 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
@@ -226,10 +226,13 @@
     return false;
   }
 
-  if (config.input_format != PIXEL_FORMAT_I420) {
-    VLOGF(1) << "Unsupported input format: "
-             << VideoPixelFormatToString(config.input_format);
-    return false;
+  switch (config.input_format) {
+    case PIXEL_FORMAT_I420:
+    case PIXEL_FORMAT_NV12:
+      break;
+    default:
+      VLOGF(1) << "Unsupported input format: " << config.input_format;
+      return false;
   }
 
   const SupportedProfiles& profiles = GetSupportedProfiles();
@@ -349,6 +352,7 @@
 void VaapiVideoEncodeAccelerator::UploadFrame(scoped_refptr<VideoFrame> frame,
                                               VASurfaceID va_surface_id) {
   DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
+  DVLOGF(4) << "frame is uploading: " << va_surface_id;
   if (!vaapi_wrapper_->UploadVideoFrameToSurface(frame, va_surface_id))
     NOTIFY_ERROR(kPlatformFailureError, "Failed to upload frame");
 }
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index e575c03..dcd60fb 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -1159,13 +1159,32 @@
   int ret = 0;
   {
     base::AutoUnlock auto_unlock(*va_lock_);
-    ret = libyuv::I420ToNV12(
-        frame->data(VideoFrame::kYPlane), frame->stride(VideoFrame::kYPlane),
-        frame->data(VideoFrame::kUPlane), frame->stride(VideoFrame::kUPlane),
-        frame->data(VideoFrame::kVPlane), frame->stride(VideoFrame::kVPlane),
-        image_ptr + image.offsets[0], image.pitches[0],
-        image_ptr + image.offsets[1], image.pitches[1], image.width,
-        image.height);
+    switch (frame->format()) {
+      case PIXEL_FORMAT_I420:
+        ret = libyuv::I420ToNV12(frame->data(VideoFrame::kYPlane),
+                                 frame->stride(VideoFrame::kYPlane),
+                                 frame->data(VideoFrame::kUPlane),
+                                 frame->stride(VideoFrame::kUPlane),
+                                 frame->data(VideoFrame::kVPlane),
+                                 frame->stride(VideoFrame::kVPlane),
+                                 image_ptr + image.offsets[0], image.pitches[0],
+                                 image_ptr + image.offsets[1], image.pitches[1],
+                                 image.width, image.height);
+        break;
+      case PIXEL_FORMAT_NV12:
+        libyuv::CopyPlane(frame->data(VideoFrame::kYPlane),
+                          frame->stride(VideoFrame::kYPlane),
+                          image_ptr + image.offsets[0], image.pitches[0],
+                          image.width, image.height);
+        libyuv::CopyPlane(frame->data(VideoFrame::kUVPlane),
+                          frame->stride(VideoFrame::kUVPlane),
+                          image_ptr + image.offsets[1], image.pitches[1],
+                          image.width, image.height / 2);
+        break;
+      default:
+        LOG(ERROR) << "Unsupported pixel format: " << frame->format();
+        return false;
+    }
   }
   return ret == 0;
 }
diff --git a/media/remoting/integration_test.cc b/media/remoting/integration_test.cc
index 90335a3..45d80f9 100644
--- a/media/remoting/integration_test.cc
+++ b/media/remoting/integration_test.cc
@@ -14,7 +14,6 @@
 
 namespace {
 
-constexpr char kWebM[] = "video/webm; codecs=\"vp8,vorbis\"";
 constexpr int kAppendTimeSec = 1;
 
 class TestRendererFactory final : public PipelineTestRendererFactory {
@@ -65,7 +64,7 @@
 }
 
 TEST_F(MediaRemotingIntegrationTest, BasicPlayback_MediaSource) {
-  MockMediaSource source("bear-320x240.webm", kWebM, 219229);
+  MockMediaSource source("bear-320x240.webm", 219229);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.EndOfStream();
 
@@ -76,8 +75,7 @@
 }
 
 TEST_F(MediaRemotingIntegrationTest, MediaSource_ConfigChange_WebM) {
-  MockMediaSource source("bear-320x240-16x9-aspect.webm", kWebM,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-320x240-16x9-aspect.webm", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
 
   EXPECT_CALL(*this, OnVideoNaturalSizeChange(gfx::Size(640, 360))).Times(1);
diff --git a/media/test/data/README.md b/media/test/data/README.md
index 45b30eb3..8f8f9a36 100644
--- a/media/test/data/README.md
+++ b/media/test/data/README.md
@@ -34,6 +34,9 @@
 #### bear-vp8-webvtt.webm
 WebM VP8 video with WebVTT subtitle track.
 
+#### bear-1280x720.mp4
+AAC audio and H264 high profile video.
+
 #### bear-1280x720-avt_subt_frag.mp4
 Fragmented bear_1280x720.mp4 with text track containing srt from
 bear-vp8-webvtt.webm as a 'subt' handler type.
@@ -379,6 +382,9 @@
 A fragmented MP4 version of the audio track of bear-640x360.mp4 encrypted (ISO
 CENC) using key ID [1] and key [2].
 
+**Note**: bear-640x360.mp4 file does not exist any more. Files encrypted from
+it has AAC audio and H264 high profile video (if applicable).
+
 #### bear-640x360-a_frag-cenc-key_rotation.mp4
 A fragmented MP4 version of the audio track of bear-640x360.mp4 encrypted (ISO
 CENC) using key ID [1] and key [2] with key rotation [3].
diff --git a/media/test/mock_media_source.cc b/media/test/mock_media_source.cc
index 9dc79ab..d68cd0a 100644
--- a/media/test/mock_media_source.cc
+++ b/media/test/mock_media_source.cc
@@ -68,6 +68,14 @@
   CHECK_LE(initial_append_size_, file_data_->data_size());
 }
 
+MockMediaSource::MockMediaSource(const std::string& filename,
+                                 size_t initial_append_size,
+                                 bool initial_sequence_mode)
+    : MockMediaSource(filename,
+                      GetMimeTypeForFile(filename),
+                      initial_append_size,
+                      initial_sequence_mode) {}
+
 MockMediaSource::MockMediaSource(scoped_refptr<DecoderBuffer> data,
                                  const std::string& mimetype,
                                  size_t initial_append_size,
diff --git a/media/test/mock_media_source.h b/media/test/mock_media_source.h
index 3ff7361c..1474cf19 100644
--- a/media/test/mock_media_source.h
+++ b/media/test/mock_media_source.h
@@ -26,6 +26,11 @@
                   const std::string& mimetype,
                   size_t initial_append_size,
                   bool initial_sequence_mode = false);
+  // Same as the constructor above, but use GetMimeTypeForFile() to get the mime
+  // type.
+  MockMediaSource(const std::string& filename,
+                  size_t initial_append_size,
+                  bool initial_sequence_mode = false);
   MockMediaSource(scoped_refptr<DecoderBuffer> data,
                   const std::string& mimetype,
                   size_t initial_append_size,
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index aa9f8ae..b777480 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -103,34 +103,10 @@
 
 namespace media {
 
-const char kWebM[] = "video/webm; codecs=\"vp8,vorbis\"";
-const char kWebMVP9[] = "video/webm; codecs=\"vp9\"";
-const char kAudioOnlyWebM[] = "video/webm; codecs=\"vorbis\"";
-const char kOpusAudioOnlyWebM[] = "video/webm; codecs=\"opus\"";
-const char kVideoOnlyWebM[] = "video/webm; codecs=\"vp8\"";
-const char kMP4VideoVP9[] =
-    "video/mp4; codecs=\"vp09.00.10.08.01.02.02.02.00\"";
-const char kMP4AudioFlac[] = "audio/mp4; codecs=\"flac\"";
-const char kMP4AudioOpus[] = "audio/mp4; codecs=\"opus\"";
-const char kMP3[] = "audio/mpeg";
 #if BUILDFLAG(ENABLE_AV1_DECODER)
-const char kMP4AV1[] = "video/mp4; codecs=\"av01.0.04M.08\"";
-const char kWebMAV1[] = "video/webm; codecs=\"av01.0.04M.08\"";
-const char kMP4AV110bit[] = "video/mp4; codecs=\"av01.0.04M.10\"";
-const char kWebMAV110bit[] = "video/webm; codecs=\"av01.0.04M.10\"";
 const int kAV110bitMp4FileDurationMs = 2735;
 const int kAV1640WebMFileDurationMs = 2736;
 #endif  // BUILDFLAG(ENABLE_AV1_DECODER)
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
-const char kADTS[] = "audio/aac";
-const char kMP4[] = "video/mp4; codecs=\"avc1.4D4041,mp4a.40.2\"";
-const char kMP4VideoAVC3[] = "video/mp4; codecs=\"avc3.64001f\"";
-const char kMP4VideoHEVC1[] = "video/mp4; codecs=\"hvc1.1.6.L93.B0\"";
-const char kMP4VideoHEVC2[] = "video/mp4; codecs=\"hev1.1.6.L93.B0\"";
-const char kMP4Video[] = "video/mp4; codecs=\"avc1.4D4041\"";
-const char kMP4Audio[] = "audio/mp4; codecs=\"mp4a.40.2\"";
-const char kMP2AudioSBR[] = "video/mp2t; codecs=\"avc1.4D4041,mp4a.40.5\"";
-#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
 // Constants for the Media Source config change tests.
 const int kAppendTimeSec = 1;
@@ -473,13 +449,12 @@
   // seek happens while there is a pending read on the ChunkDemuxer
   // and no data is available.
   bool TestSeekDuringRead(const std::string& filename,
-                          const std::string& mimetype,
                           int initial_append_size,
                           base::TimeDelta start_seek_time,
                           base::TimeDelta seek_time,
                           int seek_file_position,
                           int seek_append_size) {
-    MockMediaSource source(filename, mimetype, initial_append_size);
+    MockMediaSource source(filename, initial_append_size);
 
     if (StartPipelineWithMediaSource(&source, kNoClockless, nullptr) !=
         PIPELINE_OK) {
@@ -527,7 +502,6 @@
 
 struct MSEPlaybackTestData {
   const std::string filename;
-  const std::string mimetype;
   const size_t append_bytes;
   const uint32_t duration_ms;
 };
@@ -552,7 +526,7 @@
   void PlayToEnd() {
     MSEPlaybackTestData data = GetParam();
 
-    MockMediaSource source(data.filename, data.mimetype, data.append_bytes);
+    MockMediaSource source(data.filename, data.append_bytes);
     ASSERT_EQ(PIPELINE_OK,
               StartPipelineWithMediaSource(&source, kNormal, nullptr));
     source.EndOfStream();
@@ -616,10 +590,10 @@
                         testing::ValuesIn(kADTSTests));
 
 const MSEPlaybackTestData kMediaSourceADTSTests[] = {
-    {"bear-audio-main-aac.aac", kADTS, kAppendWholeFile, 2773},
-    {"bear-audio-lc-aac.aac", kADTS, kAppendWholeFile, 2794},
-    {"bear-audio-implicit-he-aac-v1.aac", kADTS, kAppendWholeFile, 2858},
-    {"bear-audio-implicit-he-aac-v2.aac", kADTS, kAppendWholeFile, 2901},
+    {"bear-audio-main-aac.aac", kAppendWholeFile, 2773},
+    {"bear-audio-lc-aac.aac", kAppendWholeFile, 2794},
+    {"bear-audio-implicit-he-aac-v1.aac", kAppendWholeFile, 2858},
+    {"bear-audio-implicit-he-aac-v2.aac", kAppendWholeFile, 2901},
 };
 
 // TODO(chcunningham): Migrate other basic MSE playback tests to TEST_P.
@@ -679,8 +653,7 @@
     // TODO(wolenetz): Switch back to 'segments' mode once we have some
     // incubation of a way to flexibly allow playback through unbuffered
     // regions. Known test media requiring sequence mode: MP3-in-MP2T
-    MockMediaSource source(file_one.filename, file_one.mimetype,
-                           file_one.append_bytes, true);
+    MockMediaSource source(file_one.filename, file_one.append_bytes, true);
     ASSERT_EQ(PIPELINE_OK,
               StartPipelineWithMediaSource(&source, kNormal, nullptr));
     source.EndOfStream();
@@ -697,7 +670,7 @@
     // Change type and append |file_two| with start time abutting end of
     // the previous buffered range.
     source.UnmarkEndOfStream();
-    source.ChangeType(file_two.mimetype);
+    source.ChangeType(GetMimeTypeForFile(file_two.filename));
     scoped_refptr<DecoderBuffer> file_two_contents =
         ReadTestDataFile(file_two.filename);
     source.AppendAtTime(file_one_end_time, file_two_contents->data(),
@@ -736,57 +709,55 @@
 
 const MSEPlaybackTestData kMediaSourceAudioFiles[] = {
     // MP3
-    {"sfx.mp3", kMP3, kAppendWholeFile, 313},
+    {"sfx.mp3", kAppendWholeFile, 313},
 
     // Opus in WebM
-    {"sfx-opus-441.webm", kOpusAudioOnlyWebM, kAppendWholeFile, 301},
+    {"sfx-opus-441.webm", kAppendWholeFile, 301},
 
     // Vorbis in WebM
-    {"bear-320x240-audio-only.webm", kAudioOnlyWebM, kAppendWholeFile, 2768},
+    {"bear-320x240-audio-only.webm", kAppendWholeFile, 2768},
 
     // FLAC in MP4
-    {"sfx-flac_frag.mp4", kMP4AudioFlac, kAppendWholeFile, 288},
+    {"sfx-flac_frag.mp4", kAppendWholeFile, 288},
 
     // Opus in MP4
-    {"sfx-opus_frag.mp4", kMP4AudioOpus, kAppendWholeFile, 301},
+    {"sfx-opus_frag.mp4", kAppendWholeFile, 301},
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
     // AAC in ADTS
-    {"bear-audio-main-aac.aac", kADTS, kAppendWholeFile, 2773},
+    {"bear-audio-main-aac.aac", kAppendWholeFile, 2773},
 
     // AAC in MP4
-    {"bear-640x360-a_frag.mp4", kMP4Audio, kAppendWholeFile, 2803},
+    {"bear-640x360-a_frag.mp4", kAppendWholeFile, 2803},
 
 #if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
     // MP3 in MP2T
-    {"bear-audio-mp4a.6B.ts", "video/mp2t; codecs=\"mp4a.6B\"",
-     kAppendWholeFile, 1097},
+    {"bear-audio-mp4a.6B.ts", kAppendWholeFile, 1097},
 #endif  // BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 };
 
 const MSEPlaybackTestData kMediaSourceVideoFiles[] = {
     // VP9 in WebM
-    {"bear-vp9.webm", kWebMVP9, kAppendWholeFile, kVP9WebMFileDurationMs},
+    {"bear-vp9.webm", kAppendWholeFile, kVP9WebMFileDurationMs},
 
     // VP9 in MP4
-    {"bear-320x240-v_frag-vp9.mp4", kMP4VideoVP9, kAppendWholeFile, 2736},
+    {"bear-320x240-v_frag-vp9.mp4", kAppendWholeFile, 2736},
 
     // VP8 in WebM
-    {"bear-vp8a.webm", kVideoOnlyWebM, kAppendWholeFile,
-     kVP8AWebMFileDurationMs},
+    {"bear-vp8a.webm", kAppendWholeFile, kVP8AWebMFileDurationMs},
 
 #if BUILDFLAG(ENABLE_AV1_DECODER)
     // AV1 in MP4
-    {"bear-av1.mp4", kMP4AV1, kAppendWholeFile, kVP9WebMFileDurationMs},
+    {"bear-av1.mp4", kAppendWholeFile, kVP9WebMFileDurationMs},
 
     // AV1 in WebM
-    {"bear-av1.webm", kWebMAV1, kAppendWholeFile, kVP9WebMFileDurationMs},
+    {"bear-av1.webm", kAppendWholeFile, kVP9WebMFileDurationMs},
 #endif  // BUILDFLAG(ENABLE_AV1_DECODER)
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
     // H264 AVC3 in MP4
-    {"bear-1280x720-v_frag-avc3.mp4", kMP4VideoAVC3, kAppendWholeFile,
+    {"bear-1280x720-v_frag-avc3.mp4", kAppendWholeFile,
      k1280IsoAVC3FileDurationMs},
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 };
@@ -1192,8 +1163,7 @@
 }
 
 TEST_P(MSEPipelineIntegrationTest, BasicPlaybackOpusWebmTrimmingHashed) {
-  MockMediaSource source("opus-trimming-test.webm", kOpusAudioOnlyWebM,
-                         kAppendWholeFile);
+  MockMediaSource source("opus-trimming-test.webm", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithMediaSource(&source, kHashed, nullptr));
   source.EndOfStream();
@@ -1222,8 +1192,7 @@
 }
 
 TEST_P(MSEPipelineIntegrationTest, BasicPlaybackOpusMp4TrimmingHashed) {
-  MockMediaSource source("opus-trimming-test.mp4", kMP4AudioOpus,
-                         kAppendWholeFile);
+  MockMediaSource source("opus-trimming-test.mp4", kAppendWholeFile);
 
   // TODO(dalecurtis): The test clip currently does not have the edit list
   // entries required to achieve correctness here, so we're manually specifying
@@ -1333,8 +1302,7 @@
 }
 
 TEST_P(MSEPipelineIntegrationTest, BasicPlaybackOpusPrerollExceedsCodecDelay) {
-  MockMediaSource source("bear-opus.webm", kOpusAudioOnlyWebM,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-opus.webm", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithMediaSource(&source, kHashed, nullptr));
   source.EndOfStream();
@@ -1363,7 +1331,7 @@
 
 TEST_P(MSEPipelineIntegrationTest,
        BasicPlaybackOpusMp4PrerollExceedsCodecDelay) {
-  MockMediaSource source("bear-opus.mp4", kMP4AudioOpus, kAppendWholeFile);
+  MockMediaSource source("bear-opus.mp4", kAppendWholeFile);
 
   // TODO(dalecurtis): The test clip currently does not have the edit list
   // entries required to achieve correctness here, so we're manually specifying
@@ -1457,7 +1425,7 @@
 }
 
 TEST_P(MSEPipelineIntegrationTest, BasicPlayback) {
-  MockMediaSource source("bear-320x240.webm", kWebM, 219229);
+  MockMediaSource source("bear-320x240.webm", 219229);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.EndOfStream();
 
@@ -1480,7 +1448,7 @@
   // stream should let the test complete with error indicating failure to open
   // demuxer. Here we append only the first 10 bytes of a test WebM, definitely
   // less than the ~4400 bytes needed to parse its full initialization segment.
-  MockMediaSource source("bear-320x240.webm", kWebM, 10);
+  MockMediaSource source("bear-320x240.webm", 10);
   source.set_do_eos_after_next_append(true);
   EXPECT_EQ(
       DEMUXER_ERROR_COULD_NOT_OPEN,
@@ -1492,14 +1460,14 @@
   // immediately append a corrupted media segment to trigger parse error while
   // pipeline is still completing renderer setup.
   MockMediaSource source("bear-320x240_corrupted_after_init_segment.webm",
-                         kWebM, 4380);
+                         4380);
   source.set_expect_append_success(false);
   EXPECT_EQ(CHUNK_DEMUXER_ERROR_APPEND_FAILED,
             StartPipelineWithMediaSource(&source));
 }
 
 TEST_P(MSEPipelineIntegrationTest, BasicPlayback_Live) {
-  MockMediaSource source("bear-320x240-live.webm", kWebM, 219221);
+  MockMediaSource source("bear-320x240-live.webm", 219221);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.EndOfStream();
 
@@ -1522,7 +1490,7 @@
   base::test::ScopedFeatureList scoped_feature_list_;
   scoped_feature_list_.InitAndEnableFeature(kAv1Decoder);
 
-  MockMediaSource source("bear-av1.webm", kWebMAV1, 18898);
+  MockMediaSource source("bear-av1.webm", 18898);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.EndOfStream();
 
@@ -1542,7 +1510,7 @@
   base::test::ScopedFeatureList scoped_feature_list_;
   scoped_feature_list_.InitAndEnableFeature(kAv1Decoder);
 
-  MockMediaSource source("bear-av1-320x180-10bit.webm", kWebMAV110bit, 19076);
+  MockMediaSource source("bear-av1-320x180-10bit.webm", 19076);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.EndOfStream();
 
@@ -1562,7 +1530,7 @@
 #endif
 
 TEST_P(MSEPipelineIntegrationTest, BasicPlayback_VP9_WebM) {
-  MockMediaSource source("bear-vp9.webm", kWebMVP9, 67504);
+  MockMediaSource source("bear-vp9.webm", 67504);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.EndOfStream();
 
@@ -1579,7 +1547,7 @@
 }
 
 TEST_P(MSEPipelineIntegrationTest, BasicPlayback_VP9_BlockGroup_WebM) {
-  MockMediaSource source("bear-vp9-blockgroup.webm", kWebMVP9, 67871);
+  MockMediaSource source("bear-vp9-blockgroup.webm", 67871);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.EndOfStream();
 
@@ -1596,7 +1564,7 @@
 }
 
 TEST_P(MSEPipelineIntegrationTest, BasicPlayback_VP8A_WebM) {
-  MockMediaSource source("bear-vp8a.webm", kVideoOnlyWebM, kAppendWholeFile);
+  MockMediaSource source("bear-vp8a.webm", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.EndOfStream();
 
@@ -1614,8 +1582,7 @@
 
 #if BUILDFLAG(ENABLE_AV1_DECODER)
 TEST_P(MSEPipelineIntegrationTest, ConfigChange_AV1_WebM) {
-  MockMediaSource source("bear-av1-480x360.webm", kWebMAV1,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-av1-480x360.webm", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
 
   const gfx::Size kNewSize(640, 480);
@@ -1643,8 +1610,7 @@
 #endif  // BUILDFLAG(ENABLE_AV1_DECODER)
 
 TEST_P(MSEPipelineIntegrationTest, ConfigChange_WebM) {
-  MockMediaSource source("bear-320x240-16x9-aspect.webm", kWebM,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-320x240-16x9-aspect.webm", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
 
   const gfx::Size kNewSize(640, 360);
@@ -1671,8 +1637,7 @@
 }
 
 TEST_P(MSEPipelineIntegrationTest, AudioConfigChange_WebM) {
-  MockMediaSource source("bear-320x240-audio-only.webm", kAudioOnlyWebM,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-320x240-audio-only.webm", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
 
   const int kNewSampleRate = 48000;
@@ -1706,8 +1671,7 @@
 }
 
 TEST_P(MSEPipelineIntegrationTest, Remove_Updates_BufferedRanges) {
-  const char* input_filename = "bear-320x240.webm";
-  MockMediaSource source(input_filename, kWebM, kAppendWholeFile);
+  MockMediaSource source("bear-320x240.webm", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
 
   auto buffered_ranges = pipeline_->GetBufferedTimeRanges();
@@ -1736,7 +1700,7 @@
 // will no longer start at 0.
 TEST_P(MSEPipelineIntegrationTest, FillUp_Buffer) {
   const char* input_filename = "bear-320x240.webm";
-  MockMediaSource source(input_filename, kWebM, kAppendWholeFile);
+  MockMediaSource source(input_filename, kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.SetMemoryLimits(1048576);
 
@@ -1765,7 +1729,7 @@
 
 TEST_P(MSEPipelineIntegrationTest, GCWithDisabledVideoStream) {
   const char* input_filename = "bear-320x240.webm";
-  MockMediaSource source(input_filename, kWebM, kAppendWholeFile);
+  MockMediaSource source(input_filename, kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   scoped_refptr<DecoderBuffer> file = ReadTestDataFile(input_filename);
   // The input file contains audio + video data. Assuming video data size is
@@ -1795,7 +1759,7 @@
 
 MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
                  MAYBE_EME(ConfigChange_Encrypted_WebM)) {
-  MockMediaSource source("bear-320x240-16x9-aspect-av_enc-av.webm", kWebM,
+  MockMediaSource source("bear-320x240-16x9-aspect-av_enc-av.webm",
                          kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
@@ -1827,8 +1791,7 @@
 
 MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
                  MAYBE_EME(ConfigChange_ClearThenEncrypted_WebM)) {
-  MockMediaSource source("bear-320x240-16x9-aspect.webm", kWebM,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-320x240-16x9-aspect.webm", kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithEncryptedMedia(&source, &encrypted_media));
@@ -1861,7 +1824,7 @@
 // supported by the Renderer.
 MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
                  MAYBE_EME(ConfigChange_EncryptedThenClear_WebM)) {
-  MockMediaSource source("bear-320x240-16x9-aspect-av_enc-av.webm", kWebM,
+  MockMediaSource source("bear-320x240-16x9-aspect-av_enc-av.webm",
                          kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
@@ -1913,7 +1876,7 @@
 TEST_P(MSEPipelineIntegrationTest, BasicPlayback_AV1_MP4) {
   base::test::ScopedFeatureList scoped_feature_list_;
   scoped_feature_list_.InitAndEnableFeature(kAv1Decoder);
-  MockMediaSource source("bear-av1.mp4", kMP4AV1, 24355);
+  MockMediaSource source("bear-av1.mp4", 24355);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.EndOfStream();
 
@@ -1932,7 +1895,7 @@
 TEST_P(MSEPipelineIntegrationTest, BasicPlayback_AV1_10bit_MP4) {
   base::test::ScopedFeatureList scoped_feature_list_;
   scoped_feature_list_.InitAndEnableFeature(kAv1Decoder);
-  MockMediaSource source("bear-av1-320x180-10bit.mp4", kMP4AV110bit, 19658);
+  MockMediaSource source("bear-av1-320x180-10bit.mp4", 19658);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.EndOfStream();
 
@@ -1951,7 +1914,7 @@
 #endif
 
 TEST_P(MSEPipelineIntegrationTest, FlacInMp4_Hashed) {
-  MockMediaSource source("sfx-flac_frag.mp4", kMP4AudioFlac, kAppendWholeFile);
+  MockMediaSource source("sfx-flac_frag.mp4", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithMediaSource(&source, kHashed, nullptr));
   source.EndOfStream();
@@ -2063,7 +2026,7 @@
                                         "-0.22,0.80,1.19,0.73,-0.31,-1.12,")));
 
 TEST_P(MSEPipelineIntegrationTest, MP3) {
-  MockMediaSource source("sfx.mp3", kMP3, kAppendWholeFile);
+  MockMediaSource source("sfx.mp3", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithMediaSource(&source, kHashed, nullptr));
   source.EndOfStream();
@@ -2081,7 +2044,7 @@
 }
 
 TEST_P(MSEPipelineIntegrationTest, MP3_TimestampOffset) {
-  MockMediaSource source("sfx.mp3", kMP3, kAppendWholeFile);
+  MockMediaSource source("sfx.mp3", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   EXPECT_EQ(313, source.last_timestamp_offset().InMilliseconds());
 
@@ -2108,7 +2071,7 @@
 }
 
 TEST_P(MSEPipelineIntegrationTest, MP3_Icecast) {
-  MockMediaSource source("icy_sfx.mp3", kMP3, kAppendWholeFile);
+  MockMediaSource source("icy_sfx.mp3", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.EndOfStream();
 
@@ -2120,7 +2083,7 @@
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
 
 TEST_P(MSEPipelineIntegrationTest, ADTS) {
-  MockMediaSource source("sfx.adts", kADTS, kAppendWholeFile);
+  MockMediaSource source("sfx.adts", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithMediaSource(&source, kHashed, nullptr));
   source.EndOfStream();
@@ -2138,7 +2101,7 @@
 }
 
 TEST_P(MSEPipelineIntegrationTest, ADTS_TimestampOffset) {
-  MockMediaSource source("sfx.adts", kADTS, kAppendWholeFile);
+  MockMediaSource source("sfx.adts", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithMediaSource(&source, kHashed, nullptr));
   EXPECT_EQ(325, source.last_timestamp_offset().InMilliseconds());
@@ -2225,7 +2188,7 @@
 };
 
 TEST_P(MSEPipelineIntegrationTest, ConfigChange_MP4) {
-  MockMediaSource source("bear-640x360-av_frag.mp4", kMP4, kAppendWholeFile);
+  MockMediaSource source("bear-640x360-av_frag.mp4", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
 
   const gfx::Size kNewSize(1280, 720);
@@ -2258,8 +2221,7 @@
 
 MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
                  MAYBE_EME(ConfigChange_Encrypted_MP4_CENC_VideoOnly)) {
-  MockMediaSource source("bear-640x360-v_frag-cenc-mdat.mp4", kMP4Video,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-640x360-v_frag-cenc-mdat.mp4", kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithEncryptedMedia(&source, &encrypted_media));
@@ -2293,7 +2255,7 @@
 MAYBE_EME_TEST_P(
     MSEPipelineIntegrationTest,
     MAYBE_EME(ConfigChange_Encrypted_MP4_CENC_KeyRotation_VideoOnly)) {
-  MockMediaSource source("bear-640x360-v_frag-cenc-key_rotation.mp4", kMP4Video,
+  MockMediaSource source("bear-640x360-v_frag-cenc-key_rotation.mp4",
                          kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new RotatingKeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
@@ -2325,8 +2287,7 @@
 // TODO(ddorwin): Figure out why this CHECKs in AppendAtTime().
 TEST_P(MSEPipelineIntegrationTest,
        DISABLED_ConfigChange_ClearThenEncrypted_MP4_CENC) {
-  MockMediaSource source("bear-640x360-av_frag.mp4", kMP4Video,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-640x360-av_frag.mp4", kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithEncryptedMedia(&source, &encrypted_media));
@@ -2358,8 +2319,7 @@
 // Config changes from encrypted to clear are not currently supported.
 MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
                  MAYBE_EME(ConfigChange_EncryptedThenClear_MP4_CENC)) {
-  MockMediaSource source("bear-640x360-v_frag-cenc-mdat.mp4", kMP4Video,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-640x360-v_frag-cenc-mdat.mp4", kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithEncryptedMedia(&source, &encrypted_media));
@@ -2410,7 +2370,7 @@
 
 MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
                  MAYBE_EME(EncryptedPlayback_WebM)) {
-  MockMediaSource source("bear-320x240-av_enc-av.webm", kWebM, 219816);
+  MockMediaSource source("bear-320x240-av_enc-av.webm", 219816);
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithEncryptedMedia(&source, &encrypted_media));
@@ -2427,7 +2387,7 @@
 
 MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
                  MAYBE_EME(EncryptedPlayback_ClearStart_WebM)) {
-  MockMediaSource source("bear-320x240-av_enc-av_clear-1s.webm", kWebM,
+  MockMediaSource source("bear-320x240-av_enc-av_clear-1s.webm",
                          kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
@@ -2445,7 +2405,7 @@
 
 MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
                  MAYBE_EME(EncryptedPlayback_NoEncryptedFrames_WebM)) {
-  MockMediaSource source("bear-320x240-av_enc-av_clear-all.webm", kWebM,
+  MockMediaSource source("bear-320x240-av_enc-av_clear-all.webm",
                          kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new NoResponseApp());
   EXPECT_EQ(PIPELINE_OK,
@@ -2463,8 +2423,7 @@
 
 MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
                  MAYBE_EME(EncryptedPlayback_MP4_VP9_CENC_VideoOnly)) {
-  MockMediaSource source("bear-320x240-v_frag-vp9-cenc.mp4", kMP4VideoVP9,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-320x240-v_frag-vp9-cenc.mp4", kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithEncryptedMedia(&source, &encrypted_media));
@@ -2479,8 +2438,7 @@
 }
 
 TEST_P(MSEPipelineIntegrationTest, BasicPlayback_VideoOnly_MP4_VP9) {
-  MockMediaSource source("bear-320x240-v_frag-vp9.mp4", kMP4VideoVP9,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-320x240-v_frag-vp9.mp4", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.EndOfStream();
   ASSERT_EQ(PIPELINE_OK, pipeline_status_);
@@ -2495,8 +2453,7 @@
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
 MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
                  MAYBE_EME(EncryptedPlayback_MP4_CENC_VideoOnly)) {
-  MockMediaSource source("bear-1280x720-v_frag-cenc.mp4", kMP4Video,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-1280x720-v_frag-cenc.mp4", kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithEncryptedMedia(&source, &encrypted_media));
@@ -2513,8 +2470,7 @@
 
 MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
                  MAYBE_EME(EncryptedPlayback_MP4_CENC_AudioOnly)) {
-  MockMediaSource source("bear-1280x720-a_frag-cenc.mp4", kMP4Audio,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-1280x720-a_frag-cenc.mp4", kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithEncryptedMedia(&source, &encrypted_media));
@@ -2532,7 +2488,7 @@
 MAYBE_EME_TEST_P(
     MSEPipelineIntegrationTest,
     MAYBE_EME(EncryptedPlayback_NoEncryptedFrames_MP4_CENC_VideoOnly)) {
-  MockMediaSource source("bear-1280x720-v_frag-cenc_clear-all.mp4", kMP4Video,
+  MockMediaSource source("bear-1280x720-v_frag-cenc_clear-all.mp4",
                          kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new NoResponseApp());
   EXPECT_EQ(PIPELINE_OK,
@@ -2548,8 +2504,7 @@
 }
 
 TEST_P(MSEPipelineIntegrationTest, Mp2ts_AAC_HE_SBR_Audio) {
-  MockMediaSource source("bear-1280x720-aac_he.ts", kMP2AudioSBR,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-1280x720-aac_he.ts", kAppendWholeFile);
 #if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.EndOfStream();
@@ -2598,7 +2553,7 @@
 MAYBE_EME_TEST_P(
     MSEPipelineIntegrationTest,
     MAYBE_EME(EncryptedPlayback_NoEncryptedFrames_MP4_CENC_AudioOnly)) {
-  MockMediaSource source("bear-1280x720-a_frag-cenc_clear-all.mp4", kMP4Audio,
+  MockMediaSource source("bear-1280x720-a_frag-cenc_clear-all.mp4",
                          kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new NoResponseApp());
   EXPECT_EQ(PIPELINE_OK,
@@ -2617,8 +2572,7 @@
 // beginning of mdat box.
 MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
                  MAYBE_EME(EncryptedPlayback_MP4_CENC_MDAT_Video)) {
-  MockMediaSource source("bear-640x360-v_frag-cenc-mdat.mp4", kMP4Video,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-640x360-v_frag-cenc-mdat.mp4", kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithEncryptedMedia(&source, &encrypted_media));
@@ -2634,8 +2588,7 @@
 
 MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
                  MAYBE_EME(EncryptedPlayback_MP4_CENC_SENC_Video)) {
-  MockMediaSource source("bear-640x360-v_frag-cenc-senc.mp4", kMP4Video,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-640x360-v_frag-cenc-senc.mp4", kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithEncryptedMedia(&source, &encrypted_media));
@@ -2658,7 +2611,7 @@
     MSEPipelineIntegrationTest,
     MAYBE_EME(EncryptedPlayback_MP4_CENC_SENC_NO_SAIZ_SAIO_Video)) {
   MockMediaSource source("bear-640x360-v_frag-cenc-senc-no-saiz-saio.mp4",
-                         kMP4Video, kAppendWholeFile);
+                         kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithEncryptedMedia(&source, &encrypted_media));
@@ -2675,7 +2628,7 @@
 MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
                  MAYBE_EME(EncryptedPlayback_MP4_CENC_KeyRotation_Video)) {
   MockMediaSource source("bear-1280x720-v_frag-cenc-key_rotation.mp4",
-                         kMP4Video, kAppendWholeFile);
+                         kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new RotatingKeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithEncryptedMedia(&source, &encrypted_media));
@@ -2692,7 +2645,7 @@
 MAYBE_EME_TEST_P(MSEPipelineIntegrationTest,
                  MAYBE_EME(EncryptedPlayback_MP4_CENC_KeyRotation_Audio)) {
   MockMediaSource source("bear-1280x720-a_frag-cenc-key_rotation.mp4",
-                         kMP4Audio, kAppendWholeFile);
+                         kAppendWholeFile);
   FakeEncryptedMedia encrypted_media(new RotatingKeyProvidingApp());
   EXPECT_EQ(PIPELINE_OK,
             StartPipelineWithEncryptedMedia(&source, &encrypted_media));
@@ -2707,8 +2660,7 @@
 }
 
 TEST_P(MSEPipelineIntegrationTest, BasicPlayback_VideoOnly_MP4_AVC3) {
-  MockMediaSource source("bear-1280x720-v_frag-avc3.mp4", kMP4VideoAVC3,
-                         kAppendWholeFile);
+  MockMediaSource source("bear-1280x720-v_frag-avc3.mp4", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.EndOfStream();
 
@@ -2724,13 +2676,14 @@
   Stop();
 }
 
-TEST_P(MSEPipelineIntegrationTest, BasicPlayback_VideoOnly_MP4_HEVC1) {
+TEST_P(MSEPipelineIntegrationTest, BasicPlayback_VideoOnly_MP4_HEVC) {
   // HEVC demuxing might be enabled even on platforms that don't support HEVC
   // decoding. For those cases we'll get DECODER_ERROR_NOT_SUPPORTED, which
   // indicates indicates that we did pass media mime type checks and attempted
   // to actually demux and decode the stream. On platforms that support both
   // demuxing and decoding we'll get PIPELINE_OK.
-  MockMediaSource source("bear-320x240-v_frag-hevc.mp4", kMP4VideoHEVC1,
+  const char kMp4HevcVideoOnly[] = "video/mp4; codecs=\"hvc1.1.6.L93.B0\"";
+  MockMediaSource source("bear-320x240-v_frag-hevc.mp4", kMp4HevcVideoOnly,
                          kAppendWholeFile);
 #if BUILDFLAG(ENABLE_HEVC_DEMUXING)
   PipelineStatus status = StartPipelineWithMediaSource(&source);
@@ -2742,13 +2695,10 @@
 #endif
 }
 
-TEST_P(MSEPipelineIntegrationTest, BasicPlayback_VideoOnly_MP4_HEVC2) {
-  // HEVC demuxing might be enabled even on platforms that don't support HEVC
-  // decoding. For those cases we'll get DECODER_ERROR_NOT_SUPPORTED, which
-  // indicates indicates that we did pass media mime type checks and attempted
-  // to actually demux and decode the stream. On platforms that support both
-  // demuxing and decoding we'll get PIPELINE_OK.
-  MockMediaSource source("bear-320x240-v_frag-hevc.mp4", kMP4VideoHEVC2,
+// Same test as above but using a different mime type.
+TEST_P(MSEPipelineIntegrationTest, BasicPlayback_VideoOnly_MP4_HEV1) {
+  const char kMp4Hev1VideoOnly[] = "video/mp4; codecs=\"hev1.1.6.L93.B0\"";
+  MockMediaSource source("bear-320x240-v_frag-hevc.mp4", kMp4Hev1VideoOnly,
                          kAppendWholeFile);
 #if BUILDFLAG(ENABLE_HEVC_DEMUXING)
   PipelineStatus status = StartPipelineWithMediaSource(&source);
@@ -2885,16 +2835,16 @@
 
 // Verify audio decoder & renderer can handle aborted demuxer reads.
 TEST_P(MSEPipelineIntegrationTest, ChunkDemuxerAbortRead_AudioOnly) {
-  ASSERT_TRUE(TestSeekDuringRead("bear-320x240-audio-only.webm", kAudioOnlyWebM,
-                                 16384, base::TimeDelta::FromMilliseconds(464),
+  ASSERT_TRUE(TestSeekDuringRead("bear-320x240-audio-only.webm", 16384,
+                                 base::TimeDelta::FromMilliseconds(464),
                                  base::TimeDelta::FromMilliseconds(617), 0x10CA,
                                  19730));
 }
 
 // Verify video decoder & renderer can handle aborted demuxer reads.
 TEST_P(MSEPipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) {
-  ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", kVideoOnlyWebM,
-                                 32768, base::TimeDelta::FromMilliseconds(167),
+  ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", 32768,
+                                 base::TimeDelta::FromMilliseconds(167),
                                  base::TimeDelta::FromMilliseconds(1668),
                                  0x1C896, 65536));
 }
@@ -3038,8 +2988,7 @@
 
 // Same as above but using MediaSource.
 TEST_P(MSEPipelineIntegrationTest, BasicPlayback_Opus441kHz) {
-  MockMediaSource source("sfx-opus-441.webm", kOpusAudioOnlyWebM,
-                         kAppendWholeFile);
+  MockMediaSource source("sfx-opus-441.webm", kAppendWholeFile);
   EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
   source.EndOfStream();
   Play();
diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc
index 1f389731..4f5d92e 100644
--- a/net/http/http_response_info.cc
+++ b/net/http/http_response_info.cc
@@ -5,6 +5,7 @@
 #include "net/http/http_response_info.h"
 
 #include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/pickle.h"
 #include "base/time/time.h"
 #include "net/base/auth.h"
@@ -107,6 +108,9 @@
   // This bit is set if stale_revalidate_time is stored.
   RESPONSE_INFO_HAS_STALENESS = 1 << 24,
 
+  // This bit is set if the response has a peer signature algorithm.
+  RESPONSE_INFO_HAS_PEER_SIGNATURE_ALGORITHM = 1 << 25,
+
   // TODO(darin): Add other bits to indicate alternate request methods.
   // For now, we don't support storing those.
 };
@@ -279,6 +283,18 @@
 
   ssl_info.pkp_bypassed = (flags & RESPONSE_INFO_PKP_BYPASSED) != 0;
 
+  // Read peer_signature_algorithm.
+  if (flags & RESPONSE_INFO_HAS_PEER_SIGNATURE_ALGORITHM) {
+    int peer_signature_algorithm;
+    if (!iter.ReadInt(&peer_signature_algorithm) ||
+        !base::IsValueInRangeForNumericType<uint16_t>(
+            peer_signature_algorithm)) {
+      return false;
+    }
+    ssl_info.peer_signature_algorithm =
+        base::checked_cast<uint16_t>(peer_signature_algorithm);
+  }
+
   return true;
 }
 
@@ -295,6 +311,8 @@
       flags |= RESPONSE_INFO_HAS_KEY_EXCHANGE_GROUP;
     if (ssl_info.connection_status != 0)
       flags |= RESPONSE_INFO_HAS_SSL_CONNECTION_STATUS;
+    if (ssl_info.peer_signature_algorithm != 0)
+      flags |= RESPONSE_INFO_HAS_PEER_SIGNATURE_ALGORITHM;
   }
   if (vary_data.is_valid())
     flags |= RESPONSE_INFO_HAS_VARY_DATA;
@@ -365,6 +383,9 @@
     pickle->WriteInt64(
         (stale_revalidate_timeout - base::Time()).InMicroseconds());
   }
+
+  if (ssl_info.is_valid() && ssl_info.peer_signature_algorithm != 0)
+    pickle->WriteInt(ssl_info.peer_signature_algorithm);
 }
 
 bool HttpResponseInfo::DidUseQuic() const {
diff --git a/net/http/http_response_info_unittest.cc b/net/http/http_response_info_unittest.cc
index f722c50f..af3eb0c 100644
--- a/net/http/http_response_info_unittest.cc
+++ b/net/http/http_response_info_unittest.cc
@@ -184,6 +184,17 @@
   EXPECT_EQ(0, restored_response_info.ssl_info.key_exchange_group);
 }
 
+// Test that peer_signature_algorithm is preserved.
+TEST_F(HttpResponseInfoTest, PeerSignatureAlgorithm) {
+  response_info_.ssl_info.cert =
+      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem");
+  response_info_.ssl_info.peer_signature_algorithm =
+      0x0804;  // rsa_pss_rsae_sha256
+  net::HttpResponseInfo restored_response_info;
+  PickleAndRestore(response_info_, &restored_response_info);
+  EXPECT_EQ(0x0804, restored_response_info.ssl_info.peer_signature_algorithm);
+}
+
 // Tests that cache entries loaded over SSLv3 (no longer supported) are dropped.
 TEST_F(HttpResponseInfoTest, FailsInitFromPickleWithSSLV3) {
   // A valid certificate is needed for ssl_info.is_valid() to be true.
diff --git a/net/http/transport_security_state.cc b/net/http/transport_security_state.cc
index bbf78b7a..f2b3b76 100644
--- a/net/http/transport_security_state.cc
+++ b/net/http/transport_security_state.cc
@@ -1301,14 +1301,6 @@
   enabled_sts_hosts_[hashed_host] = state;
 }
 
-void TransportSecurityState::AddOrUpdateEnabledPKPHosts(
-    const std::string& hashed_host,
-    const PKPState& state) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(state.HasPublicKeyPins());
-  enabled_pkp_hosts_[hashed_host] = state;
-}
-
 void TransportSecurityState::AddOrUpdateEnabledExpectCTHosts(
     const std::string& hashed_host,
     const ExpectCTState& state) {
diff --git a/net/http/transport_security_state.h b/net/http/transport_security_state.h
index e84a80a..5617c50 100644
--- a/net/http/transport_security_state.h
+++ b/net/http/transport_security_state.h
@@ -406,13 +406,6 @@
   void AddOrUpdateEnabledSTSHosts(const std::string& hashed_host,
                                   const STSState& state);
 
-  // Inserts |state| into |enabled_pkp_hosts_| under the key |hashed_host|.
-  // |hashed_host| is already in the internal representation.
-  // Note: This is only used for serializing/deserializing the
-  // TransportSecurityState.
-  void AddOrUpdateEnabledPKPHosts(const std::string& hashed_host,
-                                  const PKPState& state);
-
   // Inserts |state| into |enabled_expect_ct_hosts_| under the key
   // |hashed_host|. |hashed_host| is already in the internal representation.
   // Note: This is only used for serializing/deserializing the
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index 8dff5df..a08abc8d 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -1148,6 +1148,27 @@
       return false;
   }
 
+  // QUIC-Crypto always uses RSA-PSS or ECDSA with SHA-256.
+  //
+  // TODO(nharper): This will no longer be true in TLS 1.3. This logic, and
+  // likely the rest of this logic, will want some adjustments for QUIC with TLS
+  // 1.3.
+  size_t unused;
+  X509Certificate::PublicKeyType key_type;
+  X509Certificate::GetPublicKeyInfo(ssl_info->cert->cert_buffer(), &unused,
+                                    &key_type);
+  switch (key_type) {
+    case X509Certificate::kPublicKeyTypeRSA:
+      ssl_info->peer_signature_algorithm = SSL_SIGN_RSA_PSS_RSAE_SHA256;
+      break;
+    case X509Certificate::kPublicKeyTypeECDSA:
+      ssl_info->peer_signature_algorithm = SSL_SIGN_ECDSA_SECP256R1_SHA256;
+      break;
+    default:
+      NOTREACHED();
+      return false;
+  }
+
   ssl_info->public_key_hashes = cert_verify_result_->public_key_hashes;
   ssl_info->is_issued_by_known_root =
       cert_verify_result_->is_issued_by_known_root;
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index 7968ef78..e3347bd 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -665,6 +665,8 @@
   ssl_info->security_bits = SSL_CIPHER_get_bits(cipher, NULL);
   // Historically, the "group" was known as "curve".
   ssl_info->key_exchange_group = SSL_get_curve_id(ssl_.get());
+  ssl_info->peer_signature_algorithm =
+      SSL_get_peer_signature_algorithm(ssl_.get());
 
   SSLConnectionStatusSetCipherSuite(
       static_cast<uint16_t>(SSL_CIPHER_get_id(cipher)),
diff --git a/net/ssl/ssl_cipher_suite_names.cc b/net/ssl/ssl_cipher_suite_names.cc
index 9436602..1156ff17 100644
--- a/net/ssl/ssl_cipher_suite_names.cc
+++ b/net/ssl/ssl_cipher_suite_names.cc
@@ -43,6 +43,17 @@
   return obsolete_ssl;
 }
 
+int ObsoleteSSLStatusForSignature(uint16_t signature_algorithm) {
+  switch (signature_algorithm) {
+    case SSL_SIGN_ECDSA_SHA1:
+    case SSL_SIGN_RSA_PKCS1_MD5_SHA1:
+    case SSL_SIGN_RSA_PKCS1_SHA1:
+      return OBSOLETE_SSL_MASK_SIGNATURE;
+    default:
+      return OBSOLETE_SSL_NONE;
+  }
+}
+
 }  // namespace
 
 void SSLCipherSuiteToStrings(const char** key_exchange_str,
@@ -161,7 +172,7 @@
   return false;
 }
 
-int ObsoleteSSLStatus(int connection_status) {
+int ObsoleteSSLStatus(int connection_status, uint16_t signature_algorithm) {
   int obsolete_ssl = OBSOLETE_SSL_NONE;
 
   int ssl_version = SSLConnectionStatusToVersion(connection_status);
@@ -170,6 +181,8 @@
   uint16_t cipher_suite = SSLConnectionStatusToCipherSuite(connection_status);
   obsolete_ssl |= ObsoleteSSLStatusForCipherSuite(cipher_suite);
 
+  obsolete_ssl |= ObsoleteSSLStatusForSignature(signature_algorithm);
+
   return obsolete_ssl;
 }
 
diff --git a/net/ssl/ssl_cipher_suite_names.h b/net/ssl/ssl_cipher_suite_names.h
index 36a6d152..222974b8 100644
--- a/net/ssl/ssl_cipher_suite_names.h
+++ b/net/ssl/ssl_cipher_suite_names.h
@@ -57,12 +57,12 @@
   OBSOLETE_SSL_MASK_PROTOCOL = 1 << 0,
   OBSOLETE_SSL_MASK_KEY_EXCHANGE = 1 << 1,
   OBSOLETE_SSL_MASK_CIPHER = 1 << 2,
+  OBSOLETE_SSL_MASK_SIGNATURE = 1 << 3,
 };
 
-// Takes the given |connection_status| and returns a bitmask indicating which of
-// the protocol, key exchange, and cipher suite do not meet modern best-practice
-// security standards (when backwards compatibility can be ignored) - that is,
-// which ones are "obsolete".
+// Takes the given |connection_status| and |signature_algorithm| and returns a
+// bitmask indicating which settings do not meet modern best-practice security
+// standards - that is, which ones are "obsolete".
 //
 // Currently, this function uses the following criteria to determine what is
 // obsolete:
@@ -71,7 +71,9 @@
 // - Key exchange: Does not use ECDHE-based key exchanges authenticated by a
 //   certificate
 // - Cipher: not an AEAD cipher
-NET_EXPORT int ObsoleteSSLStatus(int connection_status);
+// - Signature algorithm: MD5 or SHA-1
+NET_EXPORT int ObsoleteSSLStatus(int connection_status,
+                                 uint16_t signature_algorithm);
 
 // Returns true if |cipher_suite| is suitable for use with HTTP/2. See
 // https://http2.github.io/http2-spec/#rfc.section.9.2.2.
diff --git a/net/ssl/ssl_cipher_suite_names_unittest.cc b/net/ssl/ssl_cipher_suite_names_unittest.cc
index 54f48a39d..733ca0f 100644
--- a/net/ssl/ssl_cipher_suite_names_unittest.cc
+++ b/net/ssl/ssl_cipher_suite_names_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/strings/stringprintf.h"
 #include "net/ssl/ssl_connection_status_flags.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/boringssl/src/include/openssl/ssl.h"
 
 namespace net {
 
@@ -28,6 +29,9 @@
 uint16_t kModernCipherModernKeyExchange =
     0xc02f; /* TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 */
 
+uint16_t kObsoleteSignature = SSL_SIGN_RSA_PKCS1_SHA1;
+uint16_t kModernSignature = SSL_SIGN_RSA_PSS_RSAE_SHA256;
+
 int MakeConnectionStatus(int version, uint16_t cipher_suite) {
   int connection_status = 0;
 
@@ -130,59 +134,114 @@
 
 TEST(CipherSuiteNamesTest, ObsoleteSSLStatusProtocol) {
   // Obsolete
+  // Note all of these combinations are impossible; TLS 1.2 is necessary for
+  // kModernCipherSuite.
   EXPECT_EQ(OBSOLETE_SSL_MASK_PROTOCOL,
             ObsoleteSSLStatus(MakeConnectionStatus(SSL_CONNECTION_VERSION_SSL2,
-                                                   kModernCipherSuite)));
+                                                   kModernCipherSuite),
+                              kModernSignature));
   EXPECT_EQ(OBSOLETE_SSL_MASK_PROTOCOL,
             ObsoleteSSLStatus(MakeConnectionStatus(SSL_CONNECTION_VERSION_SSL3,
-                                                   kModernCipherSuite)));
+                                                   kModernCipherSuite),
+                              kModernSignature));
   EXPECT_EQ(OBSOLETE_SSL_MASK_PROTOCOL,
             ObsoleteSSLStatus(MakeConnectionStatus(SSL_CONNECTION_VERSION_TLS1,
-                                                   kModernCipherSuite)));
-  EXPECT_EQ(OBSOLETE_SSL_MASK_PROTOCOL,
-            ObsoleteSSLStatus(MakeConnectionStatus(
-                SSL_CONNECTION_VERSION_TLS1_1, kModernCipherSuite)));
+                                                   kModernCipherSuite),
+                              kModernSignature));
+  EXPECT_EQ(
+      OBSOLETE_SSL_MASK_PROTOCOL,
+      ObsoleteSSLStatus(MakeConnectionStatus(SSL_CONNECTION_VERSION_TLS1_1,
+                                             kModernCipherSuite),
+                        kModernSignature));
 
   // Modern
-  EXPECT_EQ(OBSOLETE_SSL_NONE,
-            ObsoleteSSLStatus(MakeConnectionStatus(
-                SSL_CONNECTION_VERSION_TLS1_2, kModernCipherSuite)));
+  EXPECT_EQ(
+      OBSOLETE_SSL_NONE,
+      ObsoleteSSLStatus(MakeConnectionStatus(SSL_CONNECTION_VERSION_TLS1_2,
+                                             kModernCipherSuite),
+                        kModernSignature));
   EXPECT_EQ(OBSOLETE_SSL_NONE,
             ObsoleteSSLStatus(MakeConnectionStatus(SSL_CONNECTION_VERSION_QUIC,
-                                                   kModernCipherSuite)));
+                                                   kModernCipherSuite),
+                              kModernSignature));
 }
 
 TEST(CipherSuiteNamesTest, ObsoleteSSLStatusProtocolAndCipherSuite) {
   // Cartesian combos
   // As above, some of these combinations can't happen in practice.
   EXPECT_EQ(OBSOLETE_SSL_MASK_PROTOCOL | OBSOLETE_SSL_MASK_KEY_EXCHANGE |
+                OBSOLETE_SSL_MASK_CIPHER | OBSOLETE_SSL_MASK_SIGNATURE,
+            ObsoleteSSLStatus(
+                MakeConnectionStatus(kObsoleteVersion,
+                                     kObsoleteCipherObsoleteKeyExchange),
+                kObsoleteSignature));
+  EXPECT_EQ(OBSOLETE_SSL_MASK_PROTOCOL | OBSOLETE_SSL_MASK_KEY_EXCHANGE |
                 OBSOLETE_SSL_MASK_CIPHER,
-            ObsoleteSSLStatus(MakeConnectionStatus(
-                kObsoleteVersion, kObsoleteCipherObsoleteKeyExchange)));
-  EXPECT_EQ(OBSOLETE_SSL_MASK_PROTOCOL | OBSOLETE_SSL_MASK_KEY_EXCHANGE,
-            ObsoleteSSLStatus(MakeConnectionStatus(
-                kObsoleteVersion, kModernCipherObsoleteKeyExchange)));
-  EXPECT_EQ(OBSOLETE_SSL_MASK_PROTOCOL | OBSOLETE_SSL_MASK_CIPHER,
-            ObsoleteSSLStatus(MakeConnectionStatus(
-                kObsoleteVersion, kObsoleteCipherModernKeyExchange)));
-  EXPECT_EQ(OBSOLETE_SSL_MASK_PROTOCOL,
-            ObsoleteSSLStatus(MakeConnectionStatus(
-                kObsoleteVersion, kModernCipherModernKeyExchange)));
-  EXPECT_EQ(OBSOLETE_SSL_MASK_KEY_EXCHANGE | OBSOLETE_SSL_MASK_CIPHER,
-            ObsoleteSSLStatus(MakeConnectionStatus(
-                kModernVersion, kObsoleteCipherObsoleteKeyExchange)));
-  EXPECT_EQ(OBSOLETE_SSL_MASK_KEY_EXCHANGE,
-            ObsoleteSSLStatus(MakeConnectionStatus(
-                kModernVersion, kModernCipherObsoleteKeyExchange)));
-  EXPECT_EQ(OBSOLETE_SSL_MASK_CIPHER,
-            ObsoleteSSLStatus(MakeConnectionStatus(
-                kModernVersion, kObsoleteCipherModernKeyExchange)));
-  EXPECT_EQ(OBSOLETE_SSL_NONE,
-            ObsoleteSSLStatus(MakeConnectionStatus(
-                kModernVersion, kModernCipherModernKeyExchange)));
-  EXPECT_EQ(OBSOLETE_SSL_NONE, ObsoleteSSLStatus(MakeConnectionStatus(
-                                   SSL_CONNECTION_VERSION_TLS1_3,
-                                   0x1301 /* AES_128_GCM_SHA256 */)));
+            ObsoleteSSLStatus(
+                MakeConnectionStatus(kObsoleteVersion,
+                                     kObsoleteCipherObsoleteKeyExchange),
+                kModernSignature));
+  EXPECT_EQ(
+      OBSOLETE_SSL_MASK_PROTOCOL | OBSOLETE_SSL_MASK_KEY_EXCHANGE,
+      ObsoleteSSLStatus(MakeConnectionStatus(kObsoleteVersion,
+                                             kModernCipherObsoleteKeyExchange),
+                        kModernSignature));
+  EXPECT_EQ(
+      OBSOLETE_SSL_MASK_PROTOCOL | OBSOLETE_SSL_MASK_CIPHER,
+      ObsoleteSSLStatus(MakeConnectionStatus(kObsoleteVersion,
+                                             kObsoleteCipherModernKeyExchange),
+                        kModernSignature));
+  EXPECT_EQ(
+      OBSOLETE_SSL_MASK_PROTOCOL,
+      ObsoleteSSLStatus(MakeConnectionStatus(kObsoleteVersion,
+                                             kModernCipherModernKeyExchange),
+                        kModernSignature));
+  EXPECT_EQ(
+      OBSOLETE_SSL_MASK_KEY_EXCHANGE | OBSOLETE_SSL_MASK_CIPHER,
+      ObsoleteSSLStatus(MakeConnectionStatus(
+                            kModernVersion, kObsoleteCipherObsoleteKeyExchange),
+                        kModernSignature));
+  EXPECT_EQ(
+      OBSOLETE_SSL_MASK_KEY_EXCHANGE,
+      ObsoleteSSLStatus(MakeConnectionStatus(kModernVersion,
+                                             kModernCipherObsoleteKeyExchange),
+                        kModernSignature));
+  EXPECT_EQ(
+      OBSOLETE_SSL_MASK_CIPHER,
+      ObsoleteSSLStatus(MakeConnectionStatus(kModernVersion,
+                                             kObsoleteCipherModernKeyExchange),
+                        kModernSignature));
+  EXPECT_EQ(
+      OBSOLETE_SSL_NONE,
+      ObsoleteSSLStatus(
+          MakeConnectionStatus(kModernVersion, kModernCipherModernKeyExchange),
+          kModernSignature));
+  EXPECT_EQ(
+      OBSOLETE_SSL_NONE,
+      ObsoleteSSLStatus(MakeConnectionStatus(SSL_CONNECTION_VERSION_TLS1_3,
+                                             0x1301 /* AES_128_GCM_SHA256 */),
+                        kModernSignature));
+
+  // Don't flag the signature as obsolete if not present. It may be an old cache
+  // entry or a key exchange that doesn't involve a signature. (Though, in the
+  // latter case, we would always flag a bad key exchange.)
+  EXPECT_EQ(
+      OBSOLETE_SSL_NONE,
+      ObsoleteSSLStatus(
+          MakeConnectionStatus(kModernVersion, kModernCipherModernKeyExchange),
+          0));
+  EXPECT_EQ(
+      OBSOLETE_SSL_MASK_KEY_EXCHANGE,
+      ObsoleteSSLStatus(MakeConnectionStatus(kModernVersion,
+                                             kModernCipherObsoleteKeyExchange),
+                        0));
+
+  // Flag obsolete signatures.
+  EXPECT_EQ(
+      OBSOLETE_SSL_MASK_SIGNATURE,
+      ObsoleteSSLStatus(
+          MakeConnectionStatus(kModernVersion, kModernCipherModernKeyExchange),
+          kObsoleteSignature));
 }
 
 TEST(CipherSuiteNamesTest, HTTP2CipherSuites) {
diff --git a/net/ssl/ssl_info.h b/net/ssl/ssl_info.h
index 696e921..a1fdcf5a 100644
--- a/net/ssl/ssl_info.h
+++ b/net/ssl/ssl_info.h
@@ -80,6 +80,13 @@
   // (older cache entries may not store the value) or not applicable.
   uint16_t key_exchange_group = 0;
 
+  // The signature algorithm used by the peer in the TLS handshake, as defined
+  // by the TLS SignatureScheme registry
+  // (https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-signaturescheme).
+  // These correspond to |SSL_SIGN_*| constants in BoringSSL. The value is zero
+  // if unknown (older cache entries may not store the value) or not applicable.
+  uint16_t peer_signature_algorithm = 0;
+
   // Information about the SSL connection itself. See
   // ssl_connection_status_flags.h for values. The protocol version,
   // ciphersuite, and compression in use are encoded within.
diff --git a/services/device/bluetooth/bluetooth_system.cc b/services/device/bluetooth/bluetooth_system.cc
index 817bba25..15d5d2c8 100644
--- a/services/device/bluetooth/bluetooth_system.cc
+++ b/services/device/bluetooth/bluetooth_system.cc
@@ -5,6 +5,7 @@
 #include "services/device/bluetooth/bluetooth_system.h"
 
 #include <memory>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -80,6 +81,8 @@
 
   if (properties->powered.name() == property_name)
     UpdateStateAndNotifyIfNecessary();
+  else if (properties->discovering.name() == property_name)
+    client_ptr_->OnScanStateChanged(GetScanStateFromActiveAdapter());
 }
 
 void BluetoothSystem::GetState(GetStateCallback callback) {
@@ -134,11 +137,7 @@
       break;
   }
 
-  bool discovering = GetBluetoothAdapterClient()
-                         ->GetProperties(active_adapter_.value())
-                         ->discovering.value();
-  std::move(callback).Run(discovering ? ScanState::kScanning
-                                      : ScanState::kNotScanning);
+  std::move(callback).Run(GetScanStateFromActiveAdapter());
 }
 
 bluez::BluetoothAdapterClient* BluetoothSystem::GetBluetoothAdapterClient() {
@@ -162,6 +161,13 @@
     client_ptr_->OnStateChanged(state_);
 }
 
+BluetoothSystem::ScanState BluetoothSystem::GetScanStateFromActiveAdapter() {
+  bool discovering = GetBluetoothAdapterClient()
+                         ->GetProperties(active_adapter_.value())
+                         ->discovering.value();
+  return discovering ? ScanState::kScanning : ScanState::kNotScanning;
+}
+
 void BluetoothSystem::OnSetPoweredFinished(SetPoweredCallback callback,
                                            bool succeeded) {
   if (!succeeded) {
diff --git a/services/device/bluetooth/bluetooth_system.h b/services/device/bluetooth/bluetooth_system.h
index 7e25433..0ade738 100644
--- a/services/device/bluetooth/bluetooth_system.h
+++ b/services/device/bluetooth/bluetooth_system.h
@@ -5,6 +5,8 @@
 #ifndef SERVICES_DEVICE_BLUETOOTH_BLUETOOTH_SYSTEM_H_
 #define SERVICES_DEVICE_BLUETOOTH_BLUETOOTH_SYSTEM_H_
 
+#include <string>
+
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
@@ -43,6 +45,8 @@
 
   void UpdateStateAndNotifyIfNecessary();
 
+  ScanState GetScanStateFromActiveAdapter();
+
   void OnSetPoweredFinished(SetPoweredCallback callback, bool succeeded);
 
   mojom::BluetoothSystemClientPtr client_ptr_;
diff --git a/services/device/bluetooth/bluetooth_system_unittest.cc b/services/device/bluetooth/bluetooth_system_unittest.cc
index 1deb44c5..e27fa7af 100644
--- a/services/device/bluetooth/bluetooth_system_unittest.cc
+++ b/services/device/bluetooth/bluetooth_system_unittest.cc
@@ -4,7 +4,11 @@
 
 #include "services/device/bluetooth/bluetooth_system.h"
 
+#include <map>
+#include <memory>
+#include <string>
 #include <utility>
+#include <vector>
 
 #include "base/observer_list.h"
 #include "base/run_loop.h"
@@ -324,6 +328,10 @@
     on_state_changed_states_.push_back(state);
   }
 
+  void OnScanStateChanged(mojom::BluetoothSystem::ScanState state) override {
+    on_scan_state_changed_states_.push_back(state);
+  }
+
  protected:
   mojom::BluetoothSystemPtr CreateBluetoothSystem() {
     mojom::BluetoothSystemClientPtr client_ptr;
@@ -337,12 +345,17 @@
 
   void ResetResults() {
     on_state_changed_states_.clear();
+    on_scan_state_changed_states_.clear();
   }
 
   // Saves the states passed to OnStateChanged.
   using StateVector = std::vector<mojom::BluetoothSystem::State>;
   StateVector on_state_changed_states_;
 
+  // Saves the states passed to OnScanStateChanged.
+  using ScanStateVector = std::vector<mojom::BluetoothSystem::ScanState>;
+  ScanStateVector on_scan_state_changed_states_;
+
   mojom::BluetoothSystemFactoryPtr system_factory_;
 
   TestBluetoothAdapterClient* test_bluetooth_adapter_client_;
@@ -704,15 +717,16 @@
 }
 
 // Tests scan state is kNotScanning when there is no adapter.
-TEST_F(BluetoothSystemTest, GetScanState_NoAdapter) {
+TEST_F(BluetoothSystemTest, ScanState_NoAdapter) {
   auto system = CreateBluetoothSystem();
 
   EXPECT_EQ(mojom::BluetoothSystem::ScanState::kNotScanning,
             GetScanStateAndWait(system));
+  EXPECT_TRUE(on_scan_state_changed_states_.empty());
 }
 
 // Tests scan state is kNotScanning when the adapter is not scanning.
-TEST_F(BluetoothSystemTest, GetScanState_NotScanning) {
+TEST_F(BluetoothSystemTest, ScanState_NotScanning) {
   test_bluetooth_adapter_client_->SimulatePoweredOnAdapter(kFooObjectPathStr);
   // Added adapters are not scanning by default.
 
@@ -720,10 +734,11 @@
 
   EXPECT_EQ(mojom::BluetoothSystem::ScanState::kNotScanning,
             GetScanStateAndWait(system));
+  EXPECT_TRUE(on_scan_state_changed_states_.empty());
 }
 
 // Tests scan state is kScanning when the adapter is scanning.
-TEST_F(BluetoothSystemTest, GetScanState_Scanning) {
+TEST_F(BluetoothSystemTest, ScanState_Scanning) {
   test_bluetooth_adapter_client_->SimulatePoweredOnAdapter(kFooObjectPathStr);
   test_bluetooth_adapter_client_->SimulateAdapterDiscoveringStateChanged(
       kFooObjectPathStr, true);
@@ -732,17 +747,19 @@
 
   EXPECT_EQ(mojom::BluetoothSystem::ScanState::kScanning,
             GetScanStateAndWait(system));
+  EXPECT_TRUE(on_scan_state_changed_states_.empty());
 }
 
 // Tests scan state changes to kScanning when the adapter starts scanning and
 // then changes to kNotScanning when the adapter stops scanning.
-TEST_F(BluetoothSystemTest, GetScanState_ScanningThenNotScanning) {
+TEST_F(BluetoothSystemTest, ScanState_ScanningThenNotScanning) {
   test_bluetooth_adapter_client_->SimulatePoweredOnAdapter(kFooObjectPathStr);
 
   auto system = CreateBluetoothSystem();
 
   EXPECT_EQ(mojom::BluetoothSystem::ScanState::kNotScanning,
             GetScanStateAndWait(system));
+  EXPECT_TRUE(on_scan_state_changed_states_.empty());
 
   // Adapter starts scanning.
   test_bluetooth_adapter_client_->SimulateAdapterDiscoveringStateChanged(
@@ -750,6 +767,9 @@
 
   EXPECT_EQ(mojom::BluetoothSystem::ScanState::kScanning,
             GetScanStateAndWait(system));
+  EXPECT_EQ(ScanStateVector({mojom::BluetoothSystem::ScanState::kScanning}),
+            on_scan_state_changed_states_);
+  ResetResults();
 
   // Adapter stops scanning.
   test_bluetooth_adapter_client_->SimulateAdapterDiscoveringStateChanged(
@@ -757,11 +777,13 @@
 
   EXPECT_EQ(mojom::BluetoothSystem::ScanState::kNotScanning,
             GetScanStateAndWait(system));
+  EXPECT_EQ(ScanStateVector({mojom::BluetoothSystem::ScanState::kNotScanning}),
+            on_scan_state_changed_states_);
 }
 
 // Tests scan state is updated as expected when removing and re-adding the same
 // adapter.
-TEST_F(BluetoothSystemTest, GetScanState_AdapterRemoved) {
+TEST_F(BluetoothSystemTest, ScanState_AdapterRemoved) {
   test_bluetooth_adapter_client_->SimulatePoweredOnAdapter(kFooObjectPathStr);
   test_bluetooth_adapter_client_->SimulateAdapterDiscoveringStateChanged(
       kFooObjectPathStr, true);
@@ -777,12 +799,16 @@
 
   EXPECT_EQ(mojom::BluetoothSystem::ScanState::kNotScanning,
             GetScanStateAndWait(system));
+  EXPECT_EQ(ScanStateVector({mojom::BluetoothSystem::ScanState::kNotScanning}),
+            on_scan_state_changed_states_);
+  ResetResults();
 
   // Add the adapter again; it's not scanning by default.
   test_bluetooth_adapter_client_->SimulatePoweredOnAdapter(kFooObjectPathStr);
 
   EXPECT_EQ(mojom::BluetoothSystem::ScanState::kNotScanning,
             GetScanStateAndWait(system));
+  EXPECT_TRUE(on_scan_state_changed_states_.empty());
 
   // The adapter starts scanning again.
   test_bluetooth_adapter_client_->SimulateAdapterDiscoveringStateChanged(
@@ -790,11 +816,13 @@
 
   EXPECT_EQ(mojom::BluetoothSystem::ScanState::kScanning,
             GetScanStateAndWait(system));
+  EXPECT_EQ(ScanStateVector({mojom::BluetoothSystem::ScanState::kScanning}),
+            on_scan_state_changed_states_);
 }
 
 // Tests that scan state is updated as expected when replacing the adapter with
 // a different adapter.
-TEST_F(BluetoothSystemTest, GetScanState_AdapterReplaced) {
+TEST_F(BluetoothSystemTest, ScanState_AdapterReplaced) {
   // Start with a scanning adapter.
   test_bluetooth_adapter_client_->SimulatePoweredOnAdapter(kFooObjectPathStr);
   test_bluetooth_adapter_client_->SimulateAdapterDiscoveringStateChanged(
@@ -811,12 +839,16 @@
 
   EXPECT_EQ(mojom::BluetoothSystem::ScanState::kNotScanning,
             GetScanStateAndWait(system));
+  EXPECT_EQ(ScanStateVector({mojom::BluetoothSystem::ScanState::kNotScanning}),
+            on_scan_state_changed_states_);
+  ResetResults();
 
   // Add a different adapter. It's not scanning by default.
   test_bluetooth_adapter_client_->SimulatePoweredOnAdapter(kBarObjectPathStr);
 
   EXPECT_EQ(mojom::BluetoothSystem::ScanState::kNotScanning,
             GetScanStateAndWait(system));
+  EXPECT_TRUE(on_scan_state_changed_states_.empty());
 
   // The new adapter starts scanning.
   test_bluetooth_adapter_client_->SimulateAdapterDiscoveringStateChanged(
@@ -824,6 +856,8 @@
 
   EXPECT_EQ(mojom::BluetoothSystem::ScanState::kScanning,
             GetScanStateAndWait(system));
+  EXPECT_EQ(ScanStateVector({mojom::BluetoothSystem::ScanState::kScanning}),
+            on_scan_state_changed_states_);
 }
 
 }  // namespace device
diff --git a/services/device/public/mojom/bluetooth_system.mojom b/services/device/public/mojom/bluetooth_system.mojom
index 31e75fc..113de365 100644
--- a/services/device/public/mojom/bluetooth_system.mojom
+++ b/services/device/public/mojom/bluetooth_system.mojom
@@ -76,4 +76,5 @@
 // like Bluetooth State changes.
 interface BluetoothSystemClient {
   OnStateChanged(BluetoothSystem.State new_state);
+  OnScanStateChanged(BluetoothSystem.ScanState new_state);
 };
diff --git a/services/media_session/audio_focus_manager.cc b/services/media_session/audio_focus_manager.cc
index 244c4767..e57c0bd 100644
--- a/services/media_session/audio_focus_manager.cc
+++ b/services/media_session/audio_focus_manager.cc
@@ -357,9 +357,9 @@
   if (audio_focus_stack_.empty()) {
     active_media_controller_.ClearMediaSession();
   } else {
-    active_media_controller_.SetMediaSession(
-        audio_focus_stack_.back()->session(),
-        audio_focus_stack_.back()->info()->state);
+    StackRow* row = audio_focus_stack_.back().get();
+    active_media_controller_.SetMediaSession(row->session(),
+                                             row->info()->playback_state);
   }
 }
 
diff --git a/services/media_session/media_controller.cc b/services/media_session/media_controller.cc
index 39e739d4..1baf09d 100644
--- a/services/media_session/media_controller.cc
+++ b/services/media_session/media_controller.cc
@@ -29,30 +29,27 @@
 void MediaController::ToggleSuspendResume() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  switch (state_) {
-    case mojom::MediaSessionInfo::SessionState::kInactive:
-    case mojom::MediaSessionInfo::SessionState::kSuspended:
-      Resume();
-      break;
-    case mojom::MediaSessionInfo::SessionState::kDucking:
-    case mojom::MediaSessionInfo::SessionState::kActive:
+  switch (playback_state_) {
+    case mojom::MediaPlaybackState::kPlaying:
       Suspend();
       break;
+    case mojom::MediaPlaybackState::kPaused:
+      Resume();
+      break;
   }
 }
 
-void MediaController::SetMediaSession(
-    mojom::MediaSession* session,
-    mojom::MediaSessionInfo::SessionState state) {
+void MediaController::SetMediaSession(mojom::MediaSession* session,
+                                      mojom::MediaPlaybackState state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   session_ = session;
-  state_ = state;
+  playback_state_ = state;
 }
 
 void MediaController::ClearMediaSession() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   session_ = nullptr;
-  state_ = mojom::MediaSessionInfo::SessionState::kInactive;
+  playback_state_ = mojom::MediaPlaybackState::kPaused;
 }
 
 void MediaController::BindToInterface(mojom::MediaControllerRequest request) {
diff --git a/services/media_session/media_controller.h b/services/media_session/media_controller.h
index 51cedda0..b279ca5 100644
--- a/services/media_session/media_controller.h
+++ b/services/media_session/media_controller.h
@@ -27,8 +27,7 @@
   void Resume() override;
   void ToggleSuspendResume() override;
 
-  void SetMediaSession(mojom::MediaSession*,
-                       mojom::MediaSessionInfo::SessionState);
+  void SetMediaSession(mojom::MediaSession*, mojom::MediaPlaybackState);
   void ClearMediaSession();
 
   void BindToInterface(mojom::MediaControllerRequest);
@@ -38,9 +37,9 @@
   // Holds mojo bindings for mojom::MediaController.
   mojo::BindingSet<mojom::MediaController> bindings_;
 
-  // The current state of the |session_|.
-  mojom::MediaSessionInfo::SessionState state_ =
-      mojom::MediaSessionInfo::SessionState::kInactive;
+  // The current playback state of the |session_|.
+  mojom::MediaPlaybackState playback_state_ =
+      mojom::MediaPlaybackState::kPaused;
 
   // Raw pointer to the local proxy. This is used for sending control events to
   // the underlying MediaSession.
diff --git a/services/media_session/media_controller_unittest.cc b/services/media_session/media_controller_unittest.cc
index 42efcae..e80f159 100644
--- a/services/media_session/media_controller_unittest.cc
+++ b/services/media_session/media_controller_unittest.cc
@@ -64,13 +64,13 @@
   {
     test::MockMediaSessionMojoObserver observer(media_session);
     RequestAudioFocus(media_session);
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
   }
 
   {
     test::MockMediaSessionMojoObserver observer(media_session);
     controller()->Suspend();
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kSuspended);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
   }
 }
 
@@ -80,7 +80,7 @@
   {
     test::MockMediaSessionMojoObserver observer(media_session_1);
     RequestAudioFocus(media_session_1);
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
   }
 
   test::MockMediaSession media_session_2;
@@ -91,26 +91,26 @@
 
     RequestAudioFocus(media_session_2);
 
-    observer_1.WaitForState(mojom::MediaSessionInfo::SessionState::kSuspended);
-    observer_2.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+    observer_1.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
+    observer_2.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
   }
 
   {
     test::MockMediaSessionMojoObserver observer(media_session_2);
     controller()->Suspend();
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kSuspended);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
   }
 
   {
     test::MockMediaSessionMojoObserver observer(media_session_1);
     media_session_2.AbandonAudioFocusFromClient();
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
   }
 
   {
     test::MockMediaSessionMojoObserver observer(media_session_1);
     controller()->Suspend();
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kSuspended);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
   }
 }
 
@@ -124,7 +124,7 @@
   {
     test::MockMediaSessionMojoObserver observer(media_session);
     RequestAudioFocus(media_session);
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
   }
 
   media_session.AbandonAudioFocusFromClient();
@@ -134,7 +134,7 @@
   {
     test::MockMediaSessionMojoObserver observer(media_session);
     RequestAudioFocus(media_session);
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
   }
 }
 
@@ -144,35 +144,35 @@
   {
     test::MockMediaSessionMojoObserver observer(media_session);
     RequestAudioFocus(media_session);
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
   }
 
   {
     test::MockMediaSessionMojoObserver observer(media_session);
     controller()->Suspend();
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kSuspended);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
   }
 
   {
     test::MockMediaSessionMojoObserver observer(media_session);
     controller()->Resume();
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
   }
 }
 
-TEST_F(MediaControllerTest, ActiveController_ToggleSuspendResume_Active) {
+TEST_F(MediaControllerTest, ActiveController_ToggleSuspendResume_Playing) {
   test::MockMediaSession media_session;
 
   {
     test::MockMediaSessionMojoObserver observer(media_session);
     RequestAudioFocus(media_session);
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
   }
 
   {
     test::MockMediaSessionMojoObserver observer(media_session);
     controller()->ToggleSuspendResume();
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kSuspended);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
   }
 }
 
@@ -182,6 +182,11 @@
   {
     test::MockMediaSessionMojoObserver observer(media_session);
     RequestAudioFocus(media_session);
+    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+  }
+
+  {
+    test::MockMediaSessionMojoObserver observer(media_session);
     media_session.StartDucking();
     observer.WaitForState(mojom::MediaSessionInfo::SessionState::kDucking);
   }
@@ -189,13 +194,7 @@
   {
     test::MockMediaSessionMojoObserver observer(media_session);
     controller()->ToggleSuspendResume();
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kDucking);
-  }
-
-  {
-    test::MockMediaSessionMojoObserver observer(media_session);
-    media_session.StopDucking();
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kSuspended);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
   }
 }
 
@@ -212,29 +211,29 @@
   {
     test::MockMediaSessionMojoObserver observer(media_session);
     controller()->ToggleSuspendResume();
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
   }
 }
 
-TEST_F(MediaControllerTest, ActiveController_ToggleSuspendResume_Suspended) {
+TEST_F(MediaControllerTest, ActiveController_ToggleSuspendResume_Paused) {
   test::MockMediaSession media_session;
 
   {
     test::MockMediaSessionMojoObserver observer(media_session);
     RequestAudioFocus(media_session);
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
   }
 
   {
     test::MockMediaSessionMojoObserver observer(media_session);
     controller()->Suspend();
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kSuspended);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPaused);
   }
 
   {
     test::MockMediaSessionMojoObserver observer(media_session);
     controller()->ToggleSuspendResume();
-    observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
+    observer.WaitForPlaybackState(mojom::MediaPlaybackState::kPlaying);
   }
 }
 
diff --git a/services/media_session/mock_media_session.cc b/services/media_session/mock_media_session.cc
index 1986ff3..83136d3e 100644
--- a/services/media_session/mock_media_session.cc
+++ b/services/media_session/mock_media_session.cc
@@ -25,8 +25,10 @@
     mojom::MediaSessionInfoPtr session) {
   session_info_ = std::move(session);
 
-  if (wanted_state_ == session_info_->state)
+  if (wanted_state_ == session_info_->state ||
+      session_info_->playback_state == wanted_playback_state_) {
     run_loop_.Quit();
+  }
 }
 
 void MockMediaSessionMojoObserver::WaitForState(
@@ -38,6 +40,15 @@
   run_loop_.Run();
 }
 
+void MockMediaSessionMojoObserver::WaitForPlaybackState(
+    mojom::MediaPlaybackState wanted_state) {
+  if (session_info_ && session_info_->playback_state == wanted_state)
+    return;
+
+  wanted_playback_state_ = wanted_state;
+  run_loop_.Run();
+}
+
 MockMediaSession::MockMediaSession() = default;
 
 MockMediaSession::MockMediaSession(bool force_duck) : force_duck_(force_duck) {}
@@ -170,6 +181,11 @@
   info->state = state_;
   if (is_ducking_)
     info->state = mojom::MediaSessionInfo::SessionState::kDucking;
+
+  info->playback_state = mojom::MediaPlaybackState::kPaused;
+  if (state_ == mojom::MediaSessionInfo::SessionState::kActive)
+    info->playback_state = mojom::MediaPlaybackState::kPlaying;
+
   return info;
 }
 
diff --git a/services/media_session/mock_media_session.h b/services/media_session/mock_media_session.h
index 1c96f7c..80e13b4 100644
--- a/services/media_session/mock_media_session.h
+++ b/services/media_session/mock_media_session.h
@@ -30,10 +30,12 @@
   void MediaSessionInfoChanged(mojom::MediaSessionInfoPtr session) override;
 
   void WaitForState(mojom::MediaSessionInfo::SessionState wanted_state);
+  void WaitForPlaybackState(mojom::MediaPlaybackState wanted_state);
 
  private:
   mojom::MediaSessionInfoPtr session_info_;
   base::Optional<mojom::MediaSessionInfo::SessionState> wanted_state_;
+  base::Optional<mojom::MediaPlaybackState> wanted_playback_state_;
   base::RunLoop run_loop_;
 
   mojo::Binding<mojom::MediaSessionObserver> binding_;
diff --git a/services/media_session/public/mojom/media_session.mojom b/services/media_session/public/mojom/media_session.mojom
index 25901a2..4c60a20e 100644
--- a/services/media_session/public/mojom/media_session.mojom
+++ b/services/media_session/public/mojom/media_session.mojom
@@ -4,7 +4,13 @@
 
 module media_session.mojom;
 
-// Next MinVersion: 1
+// Next MinVersion: 2
+
+[Extensible]
+enum MediaPlaybackState {
+  kPaused,
+  kPlaying,
+};
 
 // Contains state information about a MediaSession.
 struct MediaSessionInfo {
@@ -23,11 +29,16 @@
     kInactive,
   };
 
-  // The current state of the MediaSession.
+  // The current audio focus state of the MediaSession.
   SessionState state;
 
   // If true then we will always duck this MediaSession instead of suspending.
   bool force_duck;
+
+  // The playback state tells the client whether the audio is playing. This is
+  // different from the audio focus state as it is common for a media session
+  // to hold audio focus sometimes even though it is not actually playing.
+  [MinVersion=1] MediaPlaybackState playback_state;
 };
 
 // Contains debugging information about a MediaSession. This will be displayed
diff --git a/services/network/public/cpp/net_ipc_param_traits.cc b/services/network/public/cpp/net_ipc_param_traits.cc
index 445809f0..1250cda 100644
--- a/services/network/public/cpp/net_ipc_param_traits.cc
+++ b/services/network/public/cpp/net_ipc_param_traits.cc
@@ -326,6 +326,7 @@
   WriteParam(m, p.cert_status);
   WriteParam(m, p.security_bits);
   WriteParam(m, p.key_exchange_group);
+  WriteParam(m, p.peer_signature_algorithm);
   WriteParam(m, p.connection_status);
   WriteParam(m, p.is_issued_by_known_root);
   WriteParam(m, p.pkp_bypassed);
@@ -353,6 +354,7 @@
          ReadParam(m, iter, &r->cert_status) &&
          ReadParam(m, iter, &r->security_bits) &&
          ReadParam(m, iter, &r->key_exchange_group) &&
+         ReadParam(m, iter, &r->peer_signature_algorithm) &&
          ReadParam(m, iter, &r->connection_status) &&
          ReadParam(m, iter, &r->is_issued_by_known_root) &&
          ReadParam(m, iter, &r->pkp_bypassed) &&
diff --git a/services/network/public/cpp/resource_response.cc b/services/network/public/cpp/resource_response.cc
index d3c8d8b..38fc6cb 100644
--- a/services/network/public/cpp/resource_response.cc
+++ b/services/network/public/cpp/resource_response.cc
@@ -39,6 +39,8 @@
   new_response->head.alpn_negotiated_protocol = head.alpn_negotiated_protocol;
   new_response->head.socket_address = head.socket_address;
   new_response->head.was_fetched_via_cache = head.was_fetched_via_cache;
+  new_response->head.was_fetched_via_proxy = head.was_fetched_via_proxy;
+  new_response->head.proxy_server = head.proxy_server;
   new_response->head.was_fetched_via_service_worker =
       head.was_fetched_via_service_worker;
   new_response->head.was_fallback_required_by_service_worker =
diff --git a/services/service_manager/sandbox/mac/cdm.sb b/services/service_manager/sandbox/mac/cdm.sb
index a7ba200..dbec5d1 100644
--- a/services/service_manager/sandbox/mac/cdm.sb
+++ b/services/service_manager/sandbox/mac/cdm.sb
@@ -7,9 +7,5 @@
 ; Allow preloading of the CDM using seatbelt extension.
 (allow file-read* (extension "com.apple.app-sandbox.read"))
 
-; Allow to read framework and CDM resources files for CDM host verification
-(define bundle-version-path "BUNDLE_VERSION_PATH")
-(allow file-read* (subpath (param bundle-version-path)))
-
 ; mach IPC
 (allow mach-lookup (global-name "com.apple.windowserver.active"))
diff --git a/services/service_manager/sandbox/mac/common.sb b/services/service_manager/sandbox/mac/common.sb
index 0e90c9ab..51f6a4b9 100644
--- a/services/service_manager/sandbox/mac/common.sb
+++ b/services/service_manager/sandbox/mac/common.sb
@@ -14,6 +14,7 @@
 (define (param-defined? str) (string? (param str)))
 
 ; Define constants for all of the parameter strings passed in.
+(define bundle-version-path "BUNDLE_VERSION_PATH")
 (define disable-sandbox-denial-logging "DISABLE_SANDBOX_DENIAL_LOGGING")
 (define enable-logging "ENABLE_LOGGING")
 (define homedir-as-literal "USER_HOMEDIR_AS_LITERAL")
diff --git a/services/service_manager/sandbox/mac/gpu.sb b/services/service_manager/sandbox/mac/gpu.sb
index 576976f0..619e630 100644
--- a/services/service_manager/sandbox/mac/gpu.sb
+++ b/services/service_manager/sandbox/mac/gpu.sb
@@ -28,4 +28,7 @@
   (allow file-read* (subpath "/System/Library/Extensions")))
 
 ; Needed for VideoToolbox usage - https://crbug.com/767037
-(allow mach-lookup (global-name "com.apple.coremedia.videodecoder"))
\ No newline at end of file
+(allow mach-lookup (global-name "com.apple.coremedia.videodecoder"))
+
+; Needed for GPU process to fallback to SwiftShader - https://crbug.com/897914
+(allow file-read-data file-read-metadata (subpath (param bundle-version-path)))
diff --git a/services/service_manager/sandbox/mac/sandbox_mac.mm b/services/service_manager/sandbox/mac/sandbox_mac.mm
index 36b90f6..eb01a0ab 100644
--- a/services/service_manager/sandbox/mac/sandbox_mac.mm
+++ b/services/service_manager/sandbox/mac/sandbox_mac.mm
@@ -242,9 +242,9 @@
   if (!compiler.InsertBooleanParam(kSandboxMacOS1013, macos_1013))
     return false;
 
-  if (sandbox_type == service_manager::SANDBOX_TYPE_CDM) {
-    base::FilePath bundle_path = SandboxMac::GetCanonicalPath(
-        base::mac::FrameworkBundlePath().DirName());
+  if (sandbox_type == service_manager::SANDBOX_TYPE_GPU) {
+    base::FilePath bundle_path =
+        SandboxMac::GetCanonicalPath(base::mac::FrameworkBundlePath());
     if (!compiler.InsertStringParam(kSandboxBundleVersionPath,
                                     bundle_path.value()))
       return false;
diff --git a/services/service_manager/zygote/host/zygote_host_impl_linux.h b/services/service_manager/zygote/host/zygote_host_impl_linux.h
index 7f4f822..64d6c45 100644
--- a/services/service_manager/zygote/host/zygote_host_impl_linux.h
+++ b/services/service_manager/zygote/host/zygote_host_impl_linux.h
@@ -46,6 +46,7 @@
 
   void AdjustRendererOOMScore(base::ProcessHandle process_handle,
                               int score) override;
+  bool HasZygote() { return !zygote_pids_.empty(); }
 
  private:
   friend struct base::DefaultSingletonTraits<ZygoteHostImpl>;
diff --git a/services/ws/public/mojom/window_tree.mojom b/services/ws/public/mojom/window_tree.mojom
index 809af683..23bae10 100644
--- a/services/ws/public/mojom/window_tree.mojom
+++ b/services/ws/public/mojom/window_tree.mojom
@@ -396,6 +396,21 @@
   // |new_id| need to be valid window ID created by the client. This operation
   // is not allowed for embedded clients.
   TransferGestureEventsTo(uint64 current_id, uint64 new_id, bool should_cancel);
+
+  // Called by the client to start occlusion tracking for a window.
+  TrackOcclusionState(uint64 window_id);
+
+  // Called by the client to pause occlusion states computation when there are
+  // massive window changes (such as a hierarchy change, or multiple window
+  // visibility change) to avoid unnecessary occlusion state updates. Server
+  // will re-compute the occlusion state after a balancing
+  // UnpauseWindowOcclusionTracking call.
+  PauseWindowOcclusionTracking();
+
+  // Called by the client the resume occlusion states computation. When
+  // all outstanding pause requests are cleared, server will re-compute
+  // occlusion states for the tracked windows and send back updates.
+  UnpauseWindowOcclusionTracking();
 };
 
 // Changes to windows are not sent to the connection that originated the
@@ -612,6 +627,10 @@
   // Requests the ScreenProviderObserver from the client. See
   // ScreenProviderObserver for details.
   GetScreenProviderObserver(associated ScreenProviderObserver& observer);
+
+  // Called when the occlusion state changes for a window. See also
+  // TrackOcclusionState on WindowTree.
+  OnOcclusionStateChanged(uint64 window_id, OcclusionState occlusion_state);
 };
 
 // Mus provides this interface as a way for clients to connect and obtain a
diff --git a/services/ws/public/mojom/window_tree_constants.mojom b/services/ws/public/mojom/window_tree_constants.mojom
index fe7df63..cd2f14c 100644
--- a/services/ws/public/mojom/window_tree_constants.mojom
+++ b/services/ws/public/mojom/window_tree_constants.mojom
@@ -105,6 +105,14 @@
   UNKNOWN,
 };
 
+enum OcclusionState {
+  // These come from aura::Window::OcclusionState. See it for details.
+  kUnknown,
+  kVisible,
+  kOccluded,
+  kHidden,
+};
+
 // A bitfield of what drag operations are possible.
 const uint32 kDropEffectNone = 0;
 const uint32 kDropEffectMove = 1;
diff --git a/services/ws/test_change_tracker.cc b/services/ws/test_change_tracker.cc
index 6a0b63d..5aff46b 100644
--- a/services/ws/test_change_tracker.cc
+++ b/services/ws/test_change_tracker.cc
@@ -30,6 +30,28 @@
   return direction == mojom::OrderDirection::ABOVE ? "above" : "below";
 }
 
+std::string OcclusionStateToString(
+    const base::Optional<mojom::OcclusionState>& occlusion_state) {
+  if (!occlusion_state.has_value()) {
+    NOTREACHED();
+    return "(null)";
+  }
+
+  switch (occlusion_state.value()) {
+    case mojom::OcclusionState::kUnknown:
+      return "UNKNOWN";
+    case mojom::OcclusionState::kVisible:
+      return "VISIBLE";
+    case mojom::OcclusionState::kOccluded:
+      return "OCCLUDED";
+    case mojom::OcclusionState::kHidden:
+      return "HIDDEN";
+  }
+
+  NOTREACHED();
+  return "UNKNOWN";
+}
+
 enum class ChangeDescriptionType { ONE, TWO };
 
 std::string ChangeToDescription(const Change& change,
@@ -186,6 +208,11 @@
           "OnPerformDragDropCompleted id=%d success=%s action=%d",
           change.change_id, change.bool_value ? "true" : "false",
           change.drag_drop_action);
+    case CHANGE_TYPE_ON_OCCLUSION_STATE_CHANGED:
+      return base::StringPrintf(
+          "OnOcclusionStateChanged window_id=%s, state=%s",
+          WindowIdToString(change.window_id).c_str(),
+          OcclusionStateToString(change.occlusion_state).c_str());
   }
   return std::string();
 }
@@ -579,6 +606,16 @@
   AddChange(change);
 }
 
+void TestChangeTracker::OnOcclusionStateChanged(
+    Id window_id,
+    mojom::OcclusionState occlusion_state) {
+  Change change;
+  change.type = CHANGE_TYPE_ON_OCCLUSION_STATE_CHANGED;
+  change.window_id = window_id;
+  change.occlusion_state = occlusion_state;
+  AddChange(change);
+}
+
 void TestChangeTracker::AddChange(const Change& change) {
   changes_.push_back(change);
   if (delegate_)
diff --git a/services/ws/test_change_tracker.h b/services/ws/test_change_tracker.h
index cece115..97edfb806 100644
--- a/services/ws/test_change_tracker.h
+++ b/services/ws/test_change_tracker.h
@@ -12,6 +12,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "services/ws/common/types.h"
 #include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/gfx/geometry/mojo/geometry.mojom.h"
@@ -55,6 +56,7 @@
   CHANGE_TYPE_DRAG_DROP_DONE,
   CHANGE_TYPE_TOPMOST_WINDOW_CHANGED,
   CHANGE_TYPE_ON_PERFORM_DRAG_DROP_COMPLETED,
+  CHANGE_TYPE_ON_OCCLUSION_STATE_CHANGED,
 };
 
 // TODO(sky): consider nuking and converting directly to WindowData.
@@ -109,6 +111,7 @@
   gfx::Point location1;
   base::flat_map<std::string, std::vector<uint8_t>> drag_data;
   uint32_t drag_drop_action = 0u;
+  base::Optional<mojom::OcclusionState> occlusion_state;
 };
 
 // The ChangeToDescription related functions convert a Change into a string.
@@ -227,6 +230,8 @@
                                   bool success,
                                   uint32_t action_taken);
   void RequestClose(Id window_id);
+  void OnOcclusionStateChanged(Id window_id,
+                               mojom::OcclusionState occlusion_state);
 
  private:
   void AddChange(const Change& change);
diff --git a/services/ws/test_window_tree_client.cc b/services/ws/test_window_tree_client.cc
index 3c4bd67..c1a58d03 100644
--- a/services/ws/test_window_tree_client.cc
+++ b/services/ws/test_window_tree_client.cc
@@ -285,4 +285,10 @@
   screen_provider_observer_binding_.Bind(std::move(observer));
 }
 
+void TestWindowTreeClient::OnOcclusionStateChanged(
+    Id window_id,
+    mojom::OcclusionState occlusion_state) {
+  tracker_.OnOcclusionStateChanged(window_id, occlusion_state);
+}
+
 }  // namespace ws
diff --git a/services/ws/test_window_tree_client.h b/services/ws/test_window_tree_client.h
index 993582d..61bc2ab 100644
--- a/services/ws/test_window_tree_client.h
+++ b/services/ws/test_window_tree_client.h
@@ -171,6 +171,8 @@
   void RequestClose(Id window_id) override;
   void GetScreenProviderObserver(
       mojom::ScreenProviderObserverAssociatedRequest observer) override;
+  void OnOcclusionStateChanged(Id window_id,
+                               mojom::OcclusionState occlusion_state) override;
 
  protected:
   TestChangeTracker tracker_;
diff --git a/services/ws/window_delegate_impl.cc b/services/ws/window_delegate_impl.cc
index 4940f568..b586f37 100644
--- a/services/ws/window_delegate_impl.cc
+++ b/services/ws/window_delegate_impl.cc
@@ -7,6 +7,7 @@
 #include "services/ws/embedding.h"
 #include "services/ws/server_window.h"
 #include "services/ws/window_properties.h"
+#include "services/ws/window_tree.h"
 #include "ui/aura/window.h"
 #include "ui/base/cursor/cursor.h"
 #include "ui/base/hit_test.h"
@@ -86,6 +87,13 @@
 
 void WindowDelegateImpl::OnWindowTargetVisibilityChanged(bool visible) {}
 
+void WindowDelegateImpl::OnWindowOcclusionChanged(
+    aura::Window::OcclusionState occlusion_state) {
+  ServerWindow* const server_window = ServerWindow::GetMayBeNull(window_);
+  if (server_window)
+    server_window->owning_window_tree()->SendOcclusionState(window_);
+}
+
 bool WindowDelegateImpl::HasHitTestMask() const {
   return false;
 }
diff --git a/services/ws/window_delegate_impl.h b/services/ws/window_delegate_impl.h
index fb60a5b..c694756 100644
--- a/services/ws/window_delegate_impl.h
+++ b/services/ws/window_delegate_impl.h
@@ -43,6 +43,8 @@
   void OnWindowDestroying(aura::Window* window) override;
   void OnWindowDestroyed(aura::Window* window) override;
   void OnWindowTargetVisibilityChanged(bool visible) override;
+  void OnWindowOcclusionChanged(
+      aura::Window::OcclusionState occlusion_state) override;
   bool HasHitTestMask() const override;
   void GetHitTestMask(gfx::Path* mask) const override;
 
diff --git a/services/ws/window_service.cc b/services/ws/window_service.cc
index 9e89689..b513724 100644
--- a/services/ws/window_service.cc
+++ b/services/ws/window_service.cc
@@ -26,6 +26,7 @@
 #include "services/ws/window_tree.h"
 #include "services/ws/window_tree_factory.h"
 #include "ui/aura/env.h"
+#include "ui/aura/window_occlusion_tracker.h"
 #include "ui/base/mojo/clipboard_host.h"
 #include "ui/wm/core/shadow_types.h"
 
@@ -61,6 +62,11 @@
       ::wm::kShadowElevationKey,
       mojom::WindowManager::kShadowElevation_Property,
       aura::PropertyConverter::CreateAcceptAnyValueCallback());
+
+  // Extends WindowOcclusionTracker to treat windows with remote client as
+  // has-content.
+  env_->GetWindowOcclusionTracker()->set_window_has_content_callback(
+      base::BindRepeating(&WindowService::HasRemoteClient));
 }
 
 WindowService::~WindowService() {
diff --git a/services/ws/window_tree.cc b/services/ws/window_tree.cc
index 32c037cb..6717015 100644
--- a/services/ws/window_tree.cc
+++ b/services/ws/window_tree.cc
@@ -731,6 +731,14 @@
   window_tree_client_->OnTopmostWindowChanged(topmost_ids);
 }
 
+void WindowTree::SendOcclusionState(aura::Window* window) {
+  DCHECK(IsWindowKnown(window));
+
+  window_tree_client_->OnOcclusionStateChanged(
+      TransportIdForWindow(window),
+      aura::WindowOcclusionStateToMojom(window->occlusion_state()));
+}
+
 bool WindowTree::NewWindowImpl(
     const ClientWindowId& client_window_id,
     const std::map<std::string, std::vector<uint8_t>>& properties) {
@@ -2148,4 +2156,33 @@
                     : ui::TransferTouchesBehavior::kDontCancel);
 }
 
+void WindowTree::TrackOcclusionState(Id transport_window_id) {
+  aura::Window* window = GetWindowByTransportId(transport_window_id);
+  if (!window) {
+    DVLOG(1) << "TrackOcclusionState failed (no window)";
+    return;
+  }
+  if (!IsClientCreatedWindow(window)) {
+    DVLOG(1) << "TrackOcclusionState failed (access denied)";
+    return;
+  }
+
+  window->TrackOcclusionState();
+}
+
+void WindowTree::PauseWindowOcclusionTracking() {
+  window_occlusion_tracking_pauses_.emplace_back(
+      std::make_unique<aura::WindowOcclusionTracker::ScopedPause>(
+          window_service_->env()));
+}
+
+void WindowTree::UnpauseWindowOcclusionTracking() {
+  if (window_occlusion_tracking_pauses_.empty()) {
+    DVLOG(1) << "Unbalanced UnpauseWindowOcclusionTracking call.";
+    return;
+  }
+
+  window_occlusion_tracking_pauses_.pop_back();
+}
+
 }  // namespace ws
diff --git a/services/ws/window_tree.h b/services/ws/window_tree.h
index 44f5d190..b7be085 100644
--- a/services/ws/window_tree.h
+++ b/services/ws/window_tree.h
@@ -21,6 +21,7 @@
 #include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/aura/client/capture_client_observer.h"
 #include "ui/aura/window_observer.h"
+#include "ui/aura/window_occlusion_tracker.h"
 
 namespace aura {
 class Window;
@@ -117,6 +118,9 @@
   // windows, as described for OnTopmostWindowChanged() in window_tree.mojom.
   void SendTopmostWindows(const std::vector<aura::Window*>& topmosts);
 
+  // Notifies the client that the window occlusion state has changed.
+  void SendOcclusionState(aura::Window* window);
+
   WindowService* window_service() { return window_service_; }
 
   // Returns the ClientWindowId for the window the client previously supplied
@@ -485,6 +489,9 @@
   void TransferGestureEventsTo(Id current_id,
                                Id new_id,
                                bool should_cancel) override;
+  void TrackOcclusionState(Id transport_window_id) override;
+  void PauseWindowOcclusionTracking() override;
+  void UnpauseWindowOcclusionTracking() override;
 
   WindowService* window_service_;
 
@@ -555,6 +562,14 @@
   std::vector<std::unique_ptr<WindowManagerInterface>>
       window_manager_interfaces_;
 
+  // Keeps track of outstanding occlusion tracking pauses. A ScopedPause object
+  // is added to the list when the client requests to pause and removed from the
+  // list  when the client no longer wishes to pause. Using a tracking vector so
+  // that outstanding pauses from the client are properly removed in case the
+  // client goes away.
+  std::vector<std::unique_ptr<aura::WindowOcclusionTracker::ScopedPause>>
+      window_occlusion_tracking_pauses_;
+
   base::WeakPtrFactory<WindowTree> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(WindowTree);
diff --git a/services/ws/window_tree_unittest.cc b/services/ws/window_tree_unittest.cc
index 39840c8..b4a0753 100644
--- a/services/ws/window_tree_unittest.cc
+++ b/services/ws/window_tree_unittest.cc
@@ -19,15 +19,18 @@
 #include "services/ws/public/mojom/window_manager.mojom.h"
 #include "services/ws/server_window.h"
 #include "services/ws/server_window_test_helper.h"
+#include "services/ws/window_delegate_impl.h"
 #include "services/ws/window_service.h"
 #include "services/ws/window_service_test_setup.h"
 #include "services/ws/window_tree_test_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/aura/env.h"
 #include "ui/aura/layout_manager.h"
 #include "ui/aura/test/aura_test_helper.h"
 #include "ui/aura/test/test_screen.h"
 #include "ui/aura/test/test_window_delegate.h"
+#include "ui/aura/test/window_occlusion_tracker_test_api.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tracker.h"
 #include "ui/events/mojo/event_constants.mojom.h"
@@ -2156,5 +2159,151 @@
   host_frame_sink_manager->InvalidateFrameSinkId(test_frame_sink_id);
 }
 
+TEST(WindowTreeTest, OcclusionStateChange) {
+  WindowServiceTestSetup setup;
+
+  // WindowDelegateImpl deletes itself when the window is deleted.
+  WindowDelegateImpl* delegate = new WindowDelegateImpl();
+  setup.delegate()->set_delegate_for_next_top_level(delegate);
+
+  // Create |top_level1| and tracks its occlusion state.
+  aura::Window* top_level1 =
+      setup.window_tree_test_helper()->NewTopLevelWindow();
+  delegate->set_window(top_level1);
+  ASSERT_TRUE(top_level1);
+  top_level1->SetBounds(gfx::Rect(0, 0, 10, 10));
+
+  top_level1->TrackOcclusionState();
+
+  // Gets HIDDEN state since |top_level1| is created hidden.
+  EXPECT_TRUE(ContainsChange(
+      *setup.changes(), "OnOcclusionStateChanged window_id=0,1, state=HIDDEN"));
+
+  // Gets VISIBLE state when |top_level1| is shown.
+  top_level1->Show();
+  EXPECT_TRUE(
+      ContainsChange(*setup.changes(),
+                     "OnOcclusionStateChanged window_id=0,1, state=VISIBLE"));
+
+  // Creates |top_level2| and make it occlude |top_level1|.
+  aura::Window* top_level2 =
+      setup.window_tree_test_helper()->NewTopLevelWindow();
+  ASSERT_TRUE(top_level2);
+  top_level2->SetBounds(gfx::Rect(0, 0, 15, 15));
+  top_level2->Show();
+
+  // Gets OCCLUDED state since |top_level2| covers |top_level1|.
+  EXPECT_TRUE(
+      ContainsChange(*setup.changes(),
+                     "OnOcclusionStateChanged window_id=0,1, state=OCCLUDED"));
+}
+
+TEST(WindowTreeTest, OcclusionTrackingPause) {
+  WindowServiceTestSetup setup;
+  aura::test::WindowOcclusionTrackerTestApi tracker_api(
+      setup.service()->env()->GetWindowOcclusionTracker());
+  ASSERT_FALSE(tracker_api.IsPaused());
+
+  // Simple case of one pause.
+  setup.window_tree_test_helper()
+      ->window_tree()
+      ->PauseWindowOcclusionTracking();
+  EXPECT_TRUE(tracker_api.IsPaused());
+  setup.window_tree_test_helper()
+      ->window_tree()
+      ->UnpauseWindowOcclusionTracking();
+  EXPECT_FALSE(tracker_api.IsPaused());
+
+  // Multiple pauses.
+  constexpr int kPauses = 3;
+  for (int i = 0; i < kPauses; ++i) {
+    setup.window_tree_test_helper()
+        ->window_tree()
+        ->PauseWindowOcclusionTracking();
+    EXPECT_TRUE(tracker_api.IsPaused());
+  }
+  for (int i = 0; i < kPauses - 1; ++i) {
+    setup.window_tree_test_helper()
+        ->window_tree()
+        ->UnpauseWindowOcclusionTracking();
+    EXPECT_TRUE(tracker_api.IsPaused());
+  }
+  setup.window_tree_test_helper()
+      ->window_tree()
+      ->UnpauseWindowOcclusionTracking();
+  EXPECT_FALSE(tracker_api.IsPaused());
+}
+
+TEST(WindowTreeTest, OcclusionTrackingPauseInterleaved) {
+  WindowServiceTestSetup setup;
+  aura::test::WindowOcclusionTrackerTestApi tracker_api(
+      setup.service()->env()->GetWindowOcclusionTracker());
+  ASSERT_FALSE(tracker_api.IsPaused());
+
+  // Creates a second WindowTree.
+  TestWindowTreeClient tree_client;
+  std::unique_ptr<WindowTree> tree2 =
+      setup.service()->CreateWindowTree(&tree_client);
+  tree2->InitFromFactory();
+  auto helper2 = std::make_unique<WindowTreeTestHelper>(tree2.get());
+
+  // Tree1 pauses.
+  setup.window_tree_test_helper()
+      ->window_tree()
+      ->PauseWindowOcclusionTracking();
+  EXPECT_TRUE(tracker_api.IsPaused());
+
+  // Tree2 pauses.
+  helper2->window_tree()->PauseWindowOcclusionTracking();
+  EXPECT_TRUE(tracker_api.IsPaused());
+
+  // Tree1 unpauses.
+  setup.window_tree_test_helper()
+      ->window_tree()
+      ->UnpauseWindowOcclusionTracking();
+  EXPECT_TRUE(tracker_api.IsPaused());
+
+  // Tree2 unpauses
+  helper2->window_tree()->UnpauseWindowOcclusionTracking();
+  EXPECT_FALSE(tracker_api.IsPaused());
+}
+
+TEST(WindowTreeTest, OcclusionTrackingPauseGoingAwayTree) {
+  WindowServiceTestSetup setup;
+  aura::test::WindowOcclusionTrackerTestApi tracker_api(
+      setup.service()->env()->GetWindowOcclusionTracker());
+  ASSERT_FALSE(tracker_api.IsPaused());
+
+  // Tree1 pauses.
+  setup.window_tree_test_helper()
+      ->window_tree()
+      ->PauseWindowOcclusionTracking();
+  EXPECT_TRUE(tracker_api.IsPaused());
+
+  // Creates a second WindowTree.
+  TestWindowTreeClient tree_client;
+  std::unique_ptr<WindowTree> tree2 =
+      setup.service()->CreateWindowTree(&tree_client);
+  tree2->InitFromFactory();
+  auto helper2 = std::make_unique<WindowTreeTestHelper>(tree2.get());
+
+  // Tree2 creates an outstanding pause.
+  helper2->window_tree()->PauseWindowOcclusionTracking();
+  EXPECT_TRUE(tracker_api.IsPaused());
+
+  // Tree2 goes away with the outstanding pause.
+  helper2.reset();
+  tree2.reset();
+
+  // Still paused because tree1 still holds a pause.
+  EXPECT_TRUE(tracker_api.IsPaused());
+
+  // Tree1 releases the pause and tracker is unpaused.
+  setup.window_tree_test_helper()
+      ->window_tree()
+      ->UnpauseWindowOcclusionTracking();
+  EXPECT_FALSE(tracker_api.IsPaused());
+}
+
 }  // namespace
 }  // namespace ws
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 0323d79..b8f6baf 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -515,7 +515,7 @@
       {
         "swarming": {
           "can_use_on_swarming_builders": true,
-          "shards": 20
+          "shards": 21
         },
         "test": "browser_tests"
       },
@@ -623,12 +623,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "chromevox_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "components_browsertests"
       },
       {
@@ -1341,12 +1335,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "chromevox_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "components_browsertests"
       },
       {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index bdcec7b..4016830 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -4246,7 +4246,7 @@
         ],
         "swarming": {
           "can_use_on_swarming_builders": true,
-          "shards": 30
+          "shards": 31
         },
         "test": "browser_tests"
       },
@@ -4383,15 +4383,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "chromevox_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "components_browsertests"
       },
       {
@@ -5344,7 +5335,7 @@
               "os": "Ubuntu-14.04"
             }
           ],
-          "shards": 20
+          "shards": 25
         },
         "test": "browser_tests"
       },
@@ -5557,22 +5548,6 @@
               "cpu": "x86-64",
               "os": "Ubuntu-14.04"
             }
-          ],
-          "shards": 6
-        },
-        "test": "chromevox_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
-            }
           ]
         },
         "test": "components_browsertests"
diff --git a/testing/buildbot/client.v8.fyi.json b/testing/buildbot/client.v8.fyi.json
index d3feddf..44973347 100644
--- a/testing/buildbot/client.v8.fyi.json
+++ b/testing/buildbot/client.v8.fyi.json
@@ -1082,8 +1082,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-23.21.13.8792",
-              "os": "Windows-2008ServerR2-SP1",
+              "gpu": "10de:1cb3-23.21.13.8816",
+              "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
           ],
@@ -1105,8 +1105,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-23.21.13.8792",
-              "os": "Windows-2008ServerR2-SP1",
+              "gpu": "10de:1cb3-23.21.13.8816",
+              "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
           ],
@@ -1128,8 +1128,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-23.21.13.8792",
-              "os": "Windows-2008ServerR2-SP1",
+              "gpu": "10de:1cb3-23.21.13.8816",
+              "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
           ],
@@ -1151,8 +1151,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-23.21.13.8792",
-              "os": "Windows-2008ServerR2-SP1",
+              "gpu": "10de:1cb3-23.21.13.8816",
+              "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
           ],
@@ -1181,8 +1181,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-23.21.13.8792",
-              "os": "Windows-2008ServerR2-SP1",
+              "gpu": "10de:1cb3-23.21.13.8816",
+              "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
           ],
@@ -1219,8 +1219,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-23.21.13.8792",
-              "os": "Windows-2008ServerR2-SP1",
+              "gpu": "10de:1cb3-23.21.13.8816",
+              "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
           ],
@@ -1243,8 +1243,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-23.21.13.8792",
-              "os": "Windows-2008ServerR2-SP1",
+              "gpu": "10de:1cb3-23.21.13.8816",
+              "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
           ],
@@ -1266,8 +1266,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-23.21.13.8792",
-              "os": "Windows-2008ServerR2-SP1",
+              "gpu": "10de:1cb3-23.21.13.8816",
+              "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
           ],
@@ -1291,8 +1291,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-23.21.13.8792",
-              "os": "Windows-2008ServerR2-SP1",
+              "gpu": "10de:1cb3-23.21.13.8816",
+              "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
           ],
@@ -1315,8 +1315,8 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "gpu": "10de:1cb3-23.21.13.8792",
-              "os": "Windows-2008ServerR2-SP1",
+              "gpu": "10de:1cb3-23.21.13.8816",
+              "os": "Windows-10",
               "pool": "Chrome-GPU"
             }
           ],
diff --git a/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter b/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter
index 07178d7..887b230 100644
--- a/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter
+++ b/testing/buildbot/filters/chromeos.mash.fyi.browser_tests.filter
@@ -33,6 +33,9 @@
 -AutomationApiTestWithDeviceScaleFactor.*
 -AutomationManagerAuraBrowserTest.*
 
+# ChromeVox not yet supported. https://crbug.com/594887
+-ChromeVox*
+
 # Touch gestures don't work in webcontents. https://crbug.com/866991.
 -TopControlsSlideControllerTest.TestScrollingMaximizedPageBeforeGoingToTabletMode
 -TopControlsSlideControllerTest.TestScrollingPage
diff --git a/testing/buildbot/filters/chromeos.single_process_mash.ash_unittests.filter b/testing/buildbot/filters/chromeos.single_process_mash.ash_unittests.filter
index b396ad03..46ddb992 100644
--- a/testing/buildbot/filters/chromeos.single_process_mash.ash_unittests.filter
+++ b/testing/buildbot/filters/chromeos.single_process_mash.ash_unittests.filter
@@ -11,6 +11,7 @@
 -SystemModalContainerLayoutManagerTest.SystemModalDialogGetPushedFromKeyboard
 -VirtualKeyboardControllerTest.ForceToShowKeyboardWithKeysetWhenKeyboardIsDisabled
 -VirtualKeyboardRootWindowControllerTest.ClickWithActiveModalDialog
+-WindowAnimationsTest.SlideOutAnimation
 -WorkspaceLayoutManagerSystemUiAreaTest.*
 
 # These tests crash. https://crbug.com/646565
diff --git a/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter b/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter
index ed234637..2481fff 100644
--- a/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter
+++ b/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter
@@ -27,6 +27,9 @@
 -AutomationApiTestWithDeviceScaleFactor.*
 -AutomationManagerAuraBrowserTest.*
 
+# ChromeVox not yet supported. https://crbug.com/594887
+-ChromeVox*
+
 # Touch gestures don't work in webcontents. https://crbug.com/866991.
 -TopControlsSlideControllerTest.DisplayRotation
 -TopControlsSlideControllerTest.TestClosingATab
diff --git a/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter b/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter
index fc870256..c9da5a0e 100644
--- a/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter
+++ b/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter
@@ -77,8 +77,5 @@
 -TabScrubberTest.Single
 -TabScrubberTest.Skipped
 
-# OobeInteractiveUITest: crbug.com/892263
--OobeInteractiveUITest.SimpleEndToEnd
-
 # This test is flaky. https://crbug.com/897879
 -ExtensionApiTest.DisplayModeWindowIsInFullscreen
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index ba851ae..1ee1e1f 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -369,10 +369,6 @@
     "label": "//chromeos/components:chromeos_components_unittests",
     "type": "console_test_launcher",
   },
-  "chromevox_tests": {
-    "label": "//chrome/browser/resources/chromeos/chromevox:chromevox_tests",
-    "type": "windowed_test_launcher",
-  },
   "chromium_builder_asan": {
     "label": "//:chromium_builder_asan",
     "type": "additional_compile_target",
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index c5cc3ee..4ab2b24 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -85,7 +85,7 @@
       # chromium.chromiumos
       'linux-chromeos-dbg': {
         'swarming': {
-          'shards': 20,
+          'shards': 21,
         },
       },
       # chromium.clang
@@ -182,14 +182,14 @@
         # These are very slow on the Chrome OS MSAN trybot for some reason.
         # crbug.com/865455
         'swarming': {
-          'shards': 20,
+          'shards': 25,
         },
       },
       'Linux Chromium OS ASan LSan Tests (1)': {
         # These are very slow on the ASAN trybot for some reason.
         # crbug.com/794372
         'swarming': {
-          'shards': 30,
+          'shards': 31,
         },
       },
       # client.v8.chromium
@@ -333,17 +333,6 @@
       },
     },
   },
-  'chromevox_tests': {
-    'modifications': {
-      'Linux ChromiumOS MSan Tests': {
-        # These are very slow on the Chrome OS MSAN trybot for some reason.
-        # crbug.com/865455
-        'swarming': {
-          'shards': 6,
-        },
-      },
-    },
-  },
   'components_unittests': {
     'modifications': {
       # chromium.memory
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 92a7b7b..913948f 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -1708,7 +1708,6 @@
       'aura_unittests': {},
       'chromeos_components_unittests': {},
       'chromeos_unittests': {},
-      'chromevox_tests': {},
       'exo_unittests': {},
       'gl_unittests_ozone': {},
       'keyboard_unittests': {},
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index b59fc75f..aa67dd7 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -3448,9 +3448,7 @@
         'os_type': 'win',
         'browser_config': 'release',
         'mixins': [
-          # TODO(kbr): cut this bot over to Win10, coordinating with
-          # V8 team.
-          'win7_nvidia_quadro_p400',
+          'win10_nvidia_quadro_p400_stable',
         ],
         'test_suites': {
           'gpu_telemetry_tests': 'gpu_v8_desktop_telemetry_tests',
diff --git a/third_party/WebKit/LayoutTests/LeakExpectations b/third_party/WebKit/LayoutTests/LeakExpectations
index 09430a9..16910c46 100644
--- a/third_party/WebKit/LayoutTests/LeakExpectations
+++ b/third_party/WebKit/LayoutTests/LeakExpectations
@@ -91,6 +91,9 @@
 crbug.com/878724 [ Linux ] editing/selection/modify_move/move_right_word_09_ltr_multi_line.html [ Pass Leak ]
 crbug.com/878727 [ Linux ] virtual/threaded/http/tests/devtools/tracing/timeline-misc/timeline-flame-chart-automatically-size-window.js [ Pass Leak ]
 
+# Sheriff 2018-10-25
+crbug.com/862029 [ Linux ] virtual/threaded/http/tests/devtools/tracing/timeline-misc/timeline-window-filter.js [ Pass Leak ]
+
 ###########################################################################
 # WARNING: Memory leaks must be fixed asap. Sheriff is expected to revert #
 # culprit CLs instead of suppressing the leaks. If you have any question, #
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index ddaf0f54..fd768170 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -4005,9 +4005,7 @@
 crbug.com/655458 external/wpt/workers/interfaces/SharedWorkerGlobalScope/onconnect.html [ Failure ]
 crbug.com/655458 external/wpt/workers/constructors/SharedWorker/setting-port-members.html [ Failure ]
 crbug.com/655458 external/wpt/workers/constructors/SharedWorker/connect-event.html [ Failure ]
-crbug.com/655458 external/wpt/workers/semantics/interface-objects/003.html [ Failure ]
 crbug.com/655458 external/wpt/workers/constructors/Worker/unresolvable-url.html [ Failure ]
-crbug.com/655458 external/wpt/workers/semantics/interface-objects/004.html [ Failure ]
 crbug.com/655458 external/wpt/workers/interfaces/WorkerUtils/importScripts/004.html [ Failure ]
 crbug.com/655458 external/wpt/workers/semantics/reporting-errors/002.html [ Failure ]
 crbug.com/655458 external/wpt/workers/semantics/run-a-worker/003.html [ Failure ]
@@ -5463,3 +5461,23 @@
 crbug.com/898394 [ Linux ] virtual/android/url-bar/bottom-and-top-fixed-sticks-to-top.html [ Failure ]
 crbug.com/898378 [ Mac10.13 ] virtual/scroll_customization/fast/scroll-behavior/smooth-scroll/keyboard-scroll.html [ Timeout ]
 crbug.com/898485 [ Mac ] paint/invalidation/table/cached-69296.html [ Failure Pass ]
+
+# Sheriff 2018-10-24
+crbug.com/889185 [ Win10 ] http/tests/images/feature-policy-image-compression.html [ Failure ]
+crbug.com/889185 [ Win10 ] http/tests/images/feature-policy-image-policies-with-border-radius.html [ Failure ]
+crbug.com/889185 [ Win10 ] http/tests/images/feature-policy-legacy-formats.html [ Failure ]
+crbug.com/889185 [ Win10 ] images/feature-policy-max-downscaling-image-edge-cases.html [ Failure ]
+crbug.com/889185 [ Win10 ] images/feature-policy-max-downscaling-image-forced-layout.html [ Failure ]
+crbug.com/889185 [ Win10 ] images/feature-policy-max-downscaling-image-responsive-image.html [ Failure ]
+crbug.com/889185 [ Win10 ] images/feature-policy-max-downscaling-image-styles.html [ Failure ]
+crbug.com/889185 [ Win10 ] images/feature-policy-max-downscaling-image.html [ Failure ]
+crbug.com/889185 [ Win10 ] virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases.html [ Failure ]
+crbug.com/889185 [ Win10 ] virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout.html [ Failure ]
+crbug.com/889185 [ Win10 ] virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image.html [ Failure ]
+crbug.com/889185 [ Win10 ] virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles.html [ Failure ]
+crbug.com/889185 [ Win10 ] virtual/exotic-color-space/images/feature-policy-max-downscaling-image.html [ Failure ]
+crbug.com/889185 [ Win10 ] virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases.html [ Failure ]
+crbug.com/889185 [ Win10 ] virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout.html [ Failure ]
+crbug.com/889185 [ Win10 ] virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image.html [ Failure ]
+crbug.com/889185 [ Win10 ] virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles.html [ Failure ]
+crbug.com/889185 [ Win10 ] virtual/gpu-rasterization/images/feature-policy-max-downscaling-image.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/001.worker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/001.worker-expected.txt
index ca574aaa..842154f 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/001.worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/001.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 61 tests; 58 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 63 tests; 60 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS The WorkerGlobalScope interface object should be exposed.
 PASS The DedicatedWorkerGlobalScope interface object should be exposed.
 PASS The Worker interface object should be exposed.
@@ -11,6 +11,8 @@
 PASS The WorkerLocation interface object should be exposed.
 PASS The ImageData interface object should be exposed.
 PASS The ImageBitmap interface object should be exposed.
+PASS The CanvasGradient interface object should be exposed.
+PASS The CanvasPattern interface object should be exposed.
 FAIL The CanvasPath interface object should be exposed. assert_own_property: expected property "CanvasPath" missing
 PASS The Path2D interface object should be exposed.
 PASS The PromiseRejectionEvent interface object should be exposed.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/001.worker.js b/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/001.worker.js
index 23df42036..a2b09fd 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/001.worker.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/001.worker.js
@@ -12,6 +12,8 @@
   "WorkerLocation",
   "ImageData",
   "ImageBitmap",
+  "CanvasGradient",
+  "CanvasPattern",
   "CanvasPath",
   "Path2D",
   "PromiseRejectionEvent",
diff --git a/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/002.worker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/002.worker-expected.txt
deleted file mode 100644
index ec73f22..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/002.worker-expected.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-This is a testharness.js-based test.
-PASS The SharedWorkerGlobalScope interface object should not be exposed.
-PASS The AbstractView interface object should not be exposed.
-PASS The AbstractWorker interface object should not be exposed.
-PASS The ApplicationCache interface object should not be exposed.
-PASS The Location interface object should not be exposed.
-PASS The Navigator interface object should not be exposed.
-PASS The Audio interface object should not be exposed.
-PASS The HTMLCanvasElement interface object should not be exposed.
-PASS The Path interface object should not be exposed.
-PASS The TextMetrics interface object should not be exposed.
-PASS The CanvasProxy interface object should not be exposed.
-PASS The CanvasRenderingContext2D interface object should not be exposed.
-PASS The DrawingStyle interface object should not be exposed.
-FAIL The CanvasGradient interface object should not be exposed. assert_false: expected false got true
-FAIL The CanvasPattern interface object should not be exposed. assert_false: expected false got true
-PASS The BeforeUnloadEvent interface object should not be exposed.
-PASS The PopStateEvent interface object should not be exposed.
-PASS The HashChangeEvent interface object should not be exposed.
-PASS The PageTransitionEvent interface object should not be exposed.
-PASS The DOMImplementation interface object should not be exposed.
-PASS The ReadableStreamDefaultReader interface object should not be exposed.
-PASS The ReadableStreamBYOBReader interface object should not be exposed.
-PASS The ReadableStreamDefaultController interface object should not be exposed.
-PASS The ReadableByteStreamController interface object should not be exposed.
-PASS The WritableStreamDefaultWriter interface object should not be exposed.
-PASS The WritableStreamDefaultController interface object should not be exposed.
-PASS The IDBEnvironment interface object should not be exposed.
-PASS The Database interface object should not be exposed.
-PASS The UIEvent interface object should not be exposed.
-PASS The FocusEvent interface object should not be exposed.
-PASS The MouseEvent interface object should not be exposed.
-PASS The WheelEvent interface object should not be exposed.
-PASS The InputEvent interface object should not be exposed.
-PASS The KeyboardEvent interface object should not be exposed.
-PASS The CompositionEvent interface object should not be exposed.
-PASS The VTTCue interface object should not be exposed.
-PASS The VTTRegion interface object should not be exposed.
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/002.worker.js b/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/002.worker.js
index 2c5f5a5..27be443 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/002.worker.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/002.worker.js
@@ -14,8 +14,6 @@
   "CanvasProxy",
   "CanvasRenderingContext2D",
   "DrawingStyle",
-  "CanvasGradient",
-  "CanvasPattern",
   "BeforeUnloadEvent",
   "PopStateEvent",
   "HashChangeEvent",
diff --git a/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/003-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/003-expected.txt
new file mode 100644
index 0000000..b2b1f993
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/003-expected.txt
@@ -0,0 +1,68 @@
+This is a testharness.js-based test.
+Found 64 tests; 59 PASS, 5 FAIL, 0 TIMEOUT, 0 NOTRUN.
+FAIL The ApplicationCache interface object should be exposed assert_true: expected true got false
+PASS The WorkerGlobalScope interface object should be exposed
+PASS The SharedWorkerGlobalScope interface object should be exposed
+FAIL The Worker interface object should be exposed assert_true: expected true got false
+FAIL The SharedWorker interface object should be exposed assert_true: expected true got false
+PASS The MessagePort interface object should be exposed
+PASS The MessageEvent interface object should be exposed
+PASS The WorkerNavigator interface object should be exposed
+PASS The MessageChannel interface object should be exposed
+PASS The WorkerLocation interface object should be exposed
+PASS The ImageData interface object should be exposed
+PASS The ImageBitmap interface object should be exposed
+PASS The CanvasGradient interface object should be exposed
+PASS The CanvasPattern interface object should be exposed
+FAIL The CanvasPath interface object should be exposed assert_true: expected true got false
+PASS The Path2D interface object should be exposed
+PASS The PromiseRejectionEvent interface object should be exposed
+PASS The EventSource interface object should be exposed
+PASS The WebSocket interface object should be exposed
+PASS The CloseEvent interface object should be exposed
+PASS The BroadcastChannel interface object should be exposed
+PASS The ArrayBuffer interface object should be exposed
+PASS The Int8Array interface object should be exposed
+PASS The Uint8Array interface object should be exposed
+PASS The Uint8ClampedArray interface object should be exposed
+PASS The Int16Array interface object should be exposed
+PASS The Uint16Array interface object should be exposed
+PASS The Int32Array interface object should be exposed
+PASS The Uint32Array interface object should be exposed
+PASS The Float32Array interface object should be exposed
+PASS The Float64Array interface object should be exposed
+PASS The DataView interface object should be exposed
+PASS The XMLHttpRequestEventTarget interface object should be exposed
+PASS The XMLHttpRequestUpload interface object should be exposed
+PASS The XMLHttpRequest interface object should be exposed
+PASS The ProgressEvent interface object should be exposed
+PASS The FormData interface object should be exposed
+PASS The URL interface object should be exposed
+PASS The URLSearchParams interface object should be exposed
+PASS The File interface object should be exposed
+PASS The Blob interface object should be exposed
+PASS The FileList interface object should be exposed
+PASS The FileReader interface object should be exposed
+PASS The FileReaderSync interface object should be exposed
+PASS The EventTarget interface object should be exposed
+FAIL The ErrorEvent interface object should be exposed assert_true: expected true got false
+PASS The Event interface object should be exposed
+PASS The CustomEvent interface object should be exposed
+PASS The DOMException interface object should be exposed
+PASS The ReadableStream interface object should be exposed
+PASS The WritableStream interface object should be exposed
+PASS The ByteLengthQueuingStrategy interface object should be exposed
+PASS The CountQueuingStrategy interface object should be exposed
+PASS The IDBRequest interface object should be exposed
+PASS The IDBOpenDBRequest interface object should be exposed
+PASS The IDBVersionChangeEvent interface object should be exposed
+PASS The IDBFactory interface object should be exposed
+PASS The IDBDatabase interface object should be exposed
+PASS The IDBObjectStore interface object should be exposed
+PASS The IDBIndex interface object should be exposed
+PASS The IDBKeyRange interface object should be exposed
+PASS The IDBCursor interface object should be exposed
+PASS The IDBCursorWithValue interface object should be exposed
+PASS The IDBTransaction interface object should be exposed
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/003.html b/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/003.html
index 5277825d..1ed257f 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/003.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/003.html
@@ -14,6 +14,8 @@
     "WorkerLocation",
     "ImageData",
     "ImageBitmap",
+    "CanvasGradient",
+    "CanvasPattern",
     "CanvasPath",
     "Path2D",
     "PromiseRejectionEvent",
diff --git a/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/004.html b/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/004.html
index 2e4387e..0c3a3d8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/004.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/workers/semantics/interface-objects/004.html
@@ -15,8 +15,6 @@
     "CanvasProxy",
     "CanvasRenderingContext2D",
     "DrawingStyle",
-    "CanvasGradient",
-    "CanvasPattern",
     "PopStateEvent",
     "HashChangeEvent",
     "PageTransitionEvent",
diff --git a/third_party/WebKit/LayoutTests/http/tests/event-timing/event-timing-only-observe-firstInput.html b/third_party/WebKit/LayoutTests/http/tests/event-timing/event-timing-only-observe-firstInput.html
new file mode 100644
index 0000000..bff4b06
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/event-timing/event-timing-only-observe-firstInput.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<meta charset=utf-8 />
+<title>Event Timing: only observe the first input</title>
+<button id='button' onclick='1'>Generate a 'click' event</button>
+<script src=../../resources/testharness.js></script>
+<script src=../../resources/testharnessreport.js></script>
+<script src=./resources/event-timing-support.js></script>
+
+<script>
+  /* Test:
+     PerformanceObserver for firstInput is registered
+     Click 1
+     Click 2
+     Wait
+     Expected result:
+     PerformanceObserver should observe one and only one entry.
+  */
+  async_test(function(t) {
+    let hasObservedFirstInput = false;
+    new PerformanceObserver(t.step_func((entryList) => {
+        assert_false(hasObservedFirstInput);
+        hasObservedFirstInput = true;
+        const observedEntries = entryList.getEntries();
+        assert_equals(observedEntries.length, 1);
+        assert_equals(observedEntries[0].entryType, 'firstInput');
+        assert_equals(observedEntries[0].name, 'click');
+    })).observe({ entryTypes: ['firstInput'] });
+    on_event(window, 'load', () => {
+      clickAndBlockMain('button').then(wait).then(() => {
+        clickAndBlockMain('button').then(wait);
+        // After some wait, the PerformanceObserver should have processed both clicks.
+        // One and only one firstInput entry should have been dispatched, so
+        // |hasObservedFirstInput| should be true.
+        t.step_timeout( () => {
+          assert_true(hasObservedFirstInput);
+          t.done();
+        }, 100);
+      });
+    });
+  },
+  "Event Timing: check firstInput for a PerformanceObserver observing only firstInput."
+  );
+</script>
+</html>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression-expected.png b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression-expected.png
index 79cd43a9..b904d6ca 100644
--- a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression-expected.png
+++ b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png
index 53c23a7d..829f099 100644
--- a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png
+++ b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-legacy-formats-expected.png b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-legacy-formats-expected.png
index 9879b047..d83dcc1 100644
--- a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-legacy-formats-expected.png
+++ b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-legacy-formats-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-edge-cases-expected.png
index 14da788..e8dd3dc6 100644
--- a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-edge-cases-expected.png
+++ b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-expected.png
index 6e684585..36b2e05 100644
--- a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-expected.png
+++ b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-forced-layout-expected.html b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-forced-layout-expected.html
deleted file mode 100644
index 7bfaefa84..0000000
--- a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-forced-layout-expected.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<!DOCTYPE html>
-<style>img { filter: invert(1); }</style>
-<img src="resources/green-256x256.jpg" width="100" height="100">
-<img src="resources/green-256x256.jpg" style="width: 100px; height: 100px">
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-forced-layout-expected.png
new file mode 100644
index 0000000..e4804c5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-responsive-image-expected.html b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-responsive-image-expected.html
deleted file mode 100644
index 7187800..0000000
--- a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-responsive-image-expected.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <iframe id="simple" src="resources/frame-with-max-downscaling-image-responsive-images-expected.html" width="600" height="500">
-  </iframe>
-</body>
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-responsive-image-expected.png
new file mode 100644
index 0000000..a7af098f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-styles-expected.png
index 573934a7..434f0e6c 100644
--- a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-styles-expected.png
+++ b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/repaint-overlay/layers-overlay.html b/third_party/WebKit/LayoutTests/paint/invalidation/repaint-overlay/layers-overlay.html
index 8178d72..3e81111 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/repaint-overlay/layers-overlay.html
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/repaint-overlay/layers-overlay.html
@@ -82,7 +82,7 @@
         {
           "object": "LayoutBlockFlow (relative positioned) DIV id='child1'",
           "rect": [20, 20, 10, 10],
-          "reason": "style change"
+          "reason": "background"
         }
       ],
       "transform": 1
@@ -116,7 +116,7 @@
         {
           "object": "LayoutBlockFlow (relative positioned) DIV id='child2'",
           "rect": [20, 20, 10, 10],
-          "reason": "style change"
+          "reason": "background"
         }
       ],
       "transform": 4
@@ -216,7 +216,7 @@
         {
           "object": "LayoutBlockFlow (relative positioned) DIV id='child1'",
           "rect": [20, 20, 10, 10],
-          "reason": "style change"
+          "reason": "background"
         }
       ],
       "transform": 1
@@ -250,7 +250,7 @@
         {
           "object": "LayoutBlockFlow (relative positioned) DIV id='child2'",
           "rect": [20, 20, 10, 10],
-          "reason": "style change"
+          "reason": "background"
         }
       ],
       "transform": 4
diff --git a/third_party/WebKit/LayoutTests/platform/mac/http/tests/images/feature-policy-image-compression-expected.png b/third_party/WebKit/LayoutTests/platform/mac/http/tests/images/feature-policy-image-compression-expected.png
new file mode 100644
index 0000000..cb31f34
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/http/tests/images/feature-policy-image-compression-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png b/third_party/WebKit/LayoutTests/platform/mac/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png
new file mode 100644
index 0000000..2ef4b66
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/http/tests/images/feature-policy-legacy-formats-expected.png b/third_party/WebKit/LayoutTests/platform/mac/http/tests/images/feature-policy-legacy-formats-expected.png
new file mode 100644
index 0000000..cba9b3b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/http/tests/images/feature-policy-legacy-formats-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-edge-cases-expected.png
index 78127f89..66bfd22 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-edge-cases-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-expected.png
index b244b38..fa820b6 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-forced-layout-expected.png
new file mode 100644
index 0000000..39f7cae
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-responsive-image-expected.png
new file mode 100644
index 0000000..6714790
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-styles-expected.png
index feae69f..dcff8b12 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-styles-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png
index cf25228..94b52fab 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png
index 4c7b27e..1918867 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout-expected.png
new file mode 100644
index 0000000..b74075b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image-expected.png
new file mode 100644
index 0000000..5c09e0b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png
index c0b27e9..b1c8b5cc 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png
index 78127f89..e271cf09 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png
index 57fcd4f4..fe337ab 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout-expected.png
new file mode 100644
index 0000000..7dd5022f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image-expected.png
new file mode 100644
index 0000000..a23321d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles-expected.png
new file mode 100644
index 0000000..dc44b74
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-image-compression-expected.png b/third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-image-compression-expected.png
new file mode 100644
index 0000000..cb1b74d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-image-compression-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png b/third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png
new file mode 100644
index 0000000..f1a77c75
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-image-policies-with-border-radius-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-legacy-formats-expected.png b/third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-legacy-formats-expected.png
new file mode 100644
index 0000000..18281d5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-legacy-formats-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-edge-cases-expected.png
new file mode 100644
index 0000000..fbf990d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-expected.png
new file mode 100644
index 0000000..f05919a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-forced-layout-expected.png
new file mode 100644
index 0000000..4ba8b01
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-responsive-image-expected.png
new file mode 100644
index 0000000..91fa985
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-styles-expected.png
new file mode 100644
index 0000000..b53521f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png
new file mode 100644
index 0000000..d9378cd8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png
new file mode 100644
index 0000000..4708b520
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout-expected.png
new file mode 100644
index 0000000..4d2c82ad
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image-expected.png
new file mode 100644
index 0000000..d3e0bfb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png
new file mode 100644
index 0000000..49d269d7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png
new file mode 100644
index 0000000..cb02a7d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png
new file mode 100644
index 0000000..0fc9cab
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout-expected.png
new file mode 100644
index 0000000..af162d4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image-expected.png
new file mode 100644
index 0000000..529fb60f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles-expected.png
new file mode 100644
index 0000000..6de1f99bd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png
index 46d004b..9879c7d 100644
--- a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png
+++ b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png
index b802f56..aa60ad5 100644
--- a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png
+++ b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout-expected.png
new file mode 100644
index 0000000..ad4cdc6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image-expected.png
new file mode 100644
index 0000000..6cb1553
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png
index 629b5ba..21cb653 100644
--- a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png
+++ b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png
index 14da788..c37f9722 100644
--- a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png
+++ b/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png
index 459bad23..f87a7a83 100644
--- a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png
+++ b/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout-expected.png
new file mode 100644
index 0000000..69403d6f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image-expected.png
new file mode 100644
index 0000000..c7833a9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles-expected.png
new file mode 100644
index 0000000..f68898c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles-expected.png
Binary files differ
diff --git a/third_party/android_platform/README.chromium b/third_party/android_platform/README.chromium
index 3fbd782..f4ad2449 100644
--- a/third_party/android_platform/README.chromium
+++ b/third_party/android_platform/README.chromium
@@ -43,3 +43,6 @@
 function names, line numbers etc.
 
 Speedup symbolization by avoiding unnecessary APK manifest extraction loops.
+
+Changed the symbolizer to act in a passthrough mode when reading from stdin, so
+    adb logcat can be piped through it to symbolize on the fly.
diff --git a/third_party/android_platform/development/scripts/stack.py b/third_party/android_platform/development/scripts/stack.py
index 7902ded..3ba311a 100755
--- a/third_party/android_platform/development/scripts/stack.py
+++ b/third_party/android_platform/development/scripts/stack.py
@@ -201,42 +201,47 @@
   if not symbol.CHROME_SYMBOLS_DIR:
     constants.CheckOutputDirectory()
 
-  if not arguments or arguments[0] == "-":
-    print "Reading native crash info from stdin"
-    f = sys.stdin
-  else:
-    print "Searching for native crashes in: " + os.path.realpath(arguments[0])
-    f = open(arguments[0], "r")
-
-  lines = f.readlines()
-  f.close()
+  print ("Reading Android symbols from: "
+         + os.path.normpath(symbol.SYMBOLS_DIR))
+  chrome_search_path = symbol.GetLibrarySearchPaths()
+  print ("Searching for Chrome symbols from within: "
+         + ':'.join((os.path.normpath(d) for d in chrome_search_path)))
 
   rootdir = None
   if zip_arg:
     rootdir, symbol.SYMBOLS_DIR = UnzipSymbols(zip_arg)
 
-  version = stack_libs.GetTargetAndroidVersionNumber(lines)
-  if version is None:
-    print ("Unknown Android release, "
-           "consider passing --packed-lib.")
-  elif version < _ANDROID_M_MAJOR_VERSION and not packed_libs:
-    print ("Pre-M Android release detected, "
-           "but --packed-lib not specified. Stack symbolization may fail.")
-
-  if (version is None or version < _ANDROID_M_MAJOR_VERSION) and packed_libs:
-    load_vaddrs = stack_libs.GetLoadVaddrs(stripped_libs=packed_libs)
+  if not arguments or arguments[0] == "-":
+    print "Reading native crash info from stdin"
+    with llvm_symbolizer.LLVMSymbolizer() as symbolizer:
+      stack_core.StreamingConvertTrace(sys.stdin, {}, more_info,
+                                       fallback_monochrome, arch_defined,
+                                       symbolizer)
   else:
-    load_vaddrs = {}
+    print "Searching for native crashes in: " + os.path.realpath(arguments[0])
+    f = open(arguments[0], "r")
 
-  print ("Reading Android symbols from: "
-         + os.path.normpath(symbol.SYMBOLS_DIR))
-  chrome_search_path = symbol.GetLibrarySearchPaths()
+    lines = f.readlines()
+    f.close()
 
-  with llvm_symbolizer.LLVMSymbolizer() as symbolizer:
-    print ("Searching for Chrome symbols from within: "
-           + ':'.join((os.path.normpath(d) for d in chrome_search_path)))
-    stack_core.ConvertTrace(lines, load_vaddrs, more_info, fallback_monochrome,
-                            arch_defined, symbolizer)
+    version = stack_libs.GetTargetAndroidVersionNumber(lines)
+    if version is None:
+      print ("Unknown Android release, "
+             "consider passing --packed-lib.")
+    elif version < _ANDROID_M_MAJOR_VERSION and not packed_libs:
+      print ("Pre-M Android release detected, "
+             "but --packed-lib not specified. Stack symbolization may fail.")
+
+    if (version is None or version < _ANDROID_M_MAJOR_VERSION) and packed_libs:
+      load_vaddrs = stack_libs.GetLoadVaddrs(stripped_libs=packed_libs)
+    else:
+      load_vaddrs = {}
+
+    with llvm_symbolizer.LLVMSymbolizer() as symbolizer:
+      print ("Searching for Chrome symbols from within: "
+             + ':'.join((os.path.normpath(d) for d in chrome_search_path)))
+      stack_core.ConvertTrace(lines, load_vaddrs, more_info, fallback_monochrome,
+                              arch_defined, symbolizer)
 
   if rootdir:
     # be a good citizen and clean up...os.rmdir and os.removedirs() don't work
diff --git a/third_party/android_platform/development/scripts/stack_core.py b/third_party/android_platform/development/scripts/stack_core.py
index 93837af..9f8eef6c 100755
--- a/third_party/android_platform/development/scripts/stack_core.py
+++ b/third_party/android_platform/development/scripts/stack_core.py
@@ -23,6 +23,7 @@
 import re
 import struct
 import subprocess
+import sys
 import time
 import zipfile
 
@@ -158,6 +159,40 @@
   print
   print '-----------------------------------------------------\n'
 
+def StreamingConvertTrace(input, load_vaddrs, more_info, fallback_monochrome, arch_defined, llvm_symbolizer):
+  """Symbolize stacks on the fly as they are read from an input stream."""
+  InitWidthRelatedLineMatchers()
+
+  if fallback_monochrome:
+    global _FALLBACK_SO
+    _FALLBACK_SO = 'libmonochrome.so'
+  useful_lines = []
+  so_dirs = []
+  in_stack = False
+  for line in iter(sys.stdin.readline, b''):
+    print line,
+    maybe_line, maybe_so_dir = PreProcessLog(load_vaddrs)([line])
+    useful_lines.extend(maybe_line)
+    so_dirs.extend(maybe_so_dir)
+    if in_stack:
+      if not maybe_line:
+        print "Stack found. Symbolizing..."
+        if so_dirs:
+          UpdateLibrarySearchPath(so_dirs)
+        # if arch isn't defined in command line, find it from log
+        if not arch_defined:
+          arch = _FindAbi(useful_lines)
+          if arch:
+            print ('Find ABI:' + arch)
+            symbol.ARCH = arch
+        ResolveCrashSymbol(list(useful_lines), more_info, llvm_symbolizer)
+        so_dirs = []
+        useful_lines = []
+        in_stack = False
+    else:
+      if _TRACE_LINE.search(line):
+        in_stack = True
+
 def ConvertTrace(lines, load_vaddrs, more_info, fallback_monochrome, arch_defined, llvm_symbolizer):
   """Convert strings containing native crash to a stack."""
   InitWidthRelatedLineMatchers()
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 9e8a5d4..991ed52 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -536,6 +536,7 @@
     "//third_party/blink/public/common",
     "//third_party/webrtc/api:libjingle_peerconnection_api",
     "//third_party/webrtc/rtc_base:rtc_base",
+    "//ui/accessibility:ax_enums_mojo",
     "//url",
   ]
 
diff --git a/third_party/blink/renderer/build/scripts/make_qualified_names.py b/third_party/blink/renderer/build/scripts/make_qualified_names.py
index dc5e4b8..ec075ec 100755
--- a/third_party/blink/renderer/build/scripts/make_qualified_names.py
+++ b/third_party/blink/renderer/build/scripts/make_qualified_names.py
@@ -92,7 +92,7 @@
         cpp_namespace = self.namespace.lower() + '_names'
         namespace_prefix = self._metadata('namespacePrefix') or 'k'
         # TODO(tkent): Remove the following branch.  crbug.com/889726
-        if self.namespace in ('HTML', 'MathML', 'SVG'):
+        if self.namespace in ('HTML', 'SVG'):
             cpp_namespace = self.namespace + 'Names'
             MakeQualifiedNamesWriter.filters['symbol'] = _legacy_symbol
             namespace_prefix = self._metadata('namespacePrefix') or self.namespace.lower()
diff --git a/third_party/blink/renderer/core/animation/compositor_animations_test.cc b/third_party/blink/renderer/core/animation/compositor_animations_test.cc
index 28caf45..ad8bdbe 100644
--- a/third_party/blink/renderer/core/animation/compositor_animations_test.cc
+++ b/third_party/blink/renderer/core/animation/compositor_animations_test.cc
@@ -1918,4 +1918,52 @@
   EXPECT_EQ(host->GetCompositedAnimationsCountForTesting(), 0u);
 }
 
+TEST_F(AnimationCompositorAnimationsTest, HasCSSAnimationsWithFillMode) {
+  LoadTestData("css-animation-with-fill-mode.html");
+  Document* document = GetFrame()->GetDocument();
+  Element* target1 = document->getElementById("target1");
+  const ComputedStyle* style1 = target1->GetLayoutObject()->Style();
+  EXPECT_FALSE(style1->HasTransformAnimationWithForwardsOrBothFillMode());
+  EXPECT_FALSE(style1->HasTransformRelatedProperty());
+
+  Element* target2 = document->getElementById("target2");
+  const ComputedStyle* style2 = target2->GetLayoutObject()->Style();
+  EXPECT_TRUE(style2->HasTransformAnimationWithForwardsOrBothFillMode());
+  EXPECT_TRUE(style2->HasTransformRelatedProperty());
+
+  Element* target3 = document->getElementById("target3");
+  const ComputedStyle* style3 = target3->GetLayoutObject()->Style();
+  EXPECT_TRUE(style3->HasTransformAnimationWithForwardsOrBothFillMode());
+  EXPECT_TRUE(style3->HasTransformRelatedProperty());
+
+  Element* target4 = document->getElementById("target4");
+  const ComputedStyle* style4 = target4->GetLayoutObject()->Style();
+  EXPECT_FALSE(style4->HasTransformAnimationWithForwardsOrBothFillMode());
+  EXPECT_FALSE(style4->HasTransformRelatedProperty());
+}
+
+TEST_F(AnimationCompositorAnimationsTest, HasWebAnimationsWithFillMode) {
+  LoadTestData("web-animation-with-fill-mode.html");
+  Document* document = GetFrame()->GetDocument();
+  Element* target1 = document->getElementById("target1");
+  const ComputedStyle* style1 = target1->GetLayoutObject()->Style();
+  EXPECT_FALSE(style1->HasTransformAnimationWithForwardsOrBothFillMode());
+  EXPECT_FALSE(style1->HasTransformRelatedProperty());
+
+  Element* target2 = document->getElementById("target2");
+  const ComputedStyle* style2 = target2->GetLayoutObject()->Style();
+  EXPECT_TRUE(style2->HasTransformAnimationWithForwardsOrBothFillMode());
+  EXPECT_TRUE(style2->HasTransformRelatedProperty());
+
+  Element* target3 = document->getElementById("target3");
+  const ComputedStyle* style3 = target3->GetLayoutObject()->Style();
+  EXPECT_TRUE(style3->HasTransformAnimationWithForwardsOrBothFillMode());
+  EXPECT_TRUE(style3->HasTransformRelatedProperty());
+
+  Element* target4 = document->getElementById("target4");
+  const ComputedStyle* style4 = target4->GetLayoutObject()->Style();
+  EXPECT_FALSE(style4->HasTransformAnimationWithForwardsOrBothFillMode());
+  EXPECT_FALSE(style4->HasTransformRelatedProperty());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/element_animations.cc b/third_party/blink/renderer/core/animation/element_animations.cc
index 9c202260..5881747 100644
--- a/third_party/blink/renderer/core/animation/element_animations.cc
+++ b/third_party/blink/renderer/core/animation/element_animations.cc
@@ -38,14 +38,18 @@
 
 namespace {
 
+bool AffectsTransformRelatedProperty(const KeyframeEffect& effect) {
+  return effect.Affects(PropertyHandle(GetCSSPropertyTransform())) ||
+         effect.Affects(PropertyHandle(GetCSSPropertyRotate())) ||
+         effect.Affects(PropertyHandle(GetCSSPropertyScale())) ||
+         effect.Affects(PropertyHandle(GetCSSPropertyTranslate()));
+}
+
 void UpdateAnimationFlagsForEffect(const KeyframeEffect& effect,
                                    ComputedStyle& style) {
   if (effect.Affects(PropertyHandle(GetCSSPropertyOpacity())))
     style.SetHasCurrentOpacityAnimation(true);
-  if (effect.Affects(PropertyHandle(GetCSSPropertyTransform())) ||
-      effect.Affects(PropertyHandle(GetCSSPropertyRotate())) ||
-      effect.Affects(PropertyHandle(GetCSSPropertyScale())) ||
-      effect.Affects(PropertyHandle(GetCSSPropertyTranslate())))
+  if (AffectsTransformRelatedProperty(effect))
     style.SetHasCurrentTransformAnimation(true);
   if (effect.Affects(PropertyHandle(GetCSSPropertyFilter())))
     style.SetHasCurrentFilterAnimation(true);
@@ -89,6 +93,13 @@
     // FIXME: Needs to consider AnimationGroup once added.
     DCHECK(animation.effect()->IsKeyframeEffect());
     const KeyframeEffect& effect = *ToKeyframeEffect(animation.effect());
+    if (AffectsTransformRelatedProperty(effect)) {
+      CompositorKeyframeModel::FillMode mode =
+          animation.effect()->SpecifiedTiming().fill_mode;
+      if (mode == CompositorKeyframeModel::FillMode::FORWARDS ||
+          mode == CompositorKeyframeModel::FillMode::BOTH)
+        style.SetHasTransformAnimationWithForwardsOrBothFillMode(true);
+    }
     if (!effect.IsCurrent())
       continue;
     UpdateAnimationFlagsForEffect(effect, style);
diff --git a/third_party/blink/renderer/core/animation/test_data/css-animation-with-fill-mode.html b/third_party/blink/renderer/core/animation/test_data/css-animation-with-fill-mode.html
new file mode 100644
index 0000000..50e0326
--- /dev/null
+++ b/third_party/blink/renderer/core/animation/test_data/css-animation-with-fill-mode.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<style>
+@keyframes spin {
+  0% { transform: rotate(20deg); }
+  100% { transform: none; }
+}
+
+@keyframes opac {
+  0% { opacity: 1.0; }
+  100% { opacity: 0.2; }
+}
+
+.animate1 {
+  animation: opac 0s;
+  animation-fill-mode: both;
+}
+
+.animate2 {
+  animation: spin 0s;
+  animation-fill-mode: both;
+}
+
+.animate3 {
+  animation: spin 0s, opac 0s;
+  animation-fill-mode: both, none;
+}
+
+.animate4 {
+  animation: spin 0s, opac 0s;
+  animation-fill-mode: none, both;
+}
+
+.target {
+  width: 10px;
+  height: 10px;
+  background: red;
+}
+
+.positioned {
+  position: absolute;
+  top: 0;
+  left: 0;
+  height: 10px;
+  width: 10px;
+  background: lime;
+}
+</style>
+<div id="target1" class="animate1 target">
+  <div class="positioned"></div>
+</div>
+<div id="target2" class="animate2 target">
+  <div class="positioned"></div>
+</div>
+<div id="target3" class="animate3 target">
+  <div class="positioned"></div>
+</div>
+<div id="target4" class="animate4 target">
+  <div class="positioned"></div>
+</div>
+</html>
diff --git a/third_party/blink/renderer/core/animation/test_data/web-animation-with-fill-mode.html b/third_party/blink/renderer/core/animation/test_data/web-animation-with-fill-mode.html
new file mode 100644
index 0000000..e286d93
--- /dev/null
+++ b/third_party/blink/renderer/core/animation/test_data/web-animation-with-fill-mode.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<style>
+.target {
+  width: 10px;
+  height: 10px;
+  background: red;
+}
+
+.positioned {
+  position: absolute;
+  top: 0;
+  left: 0;
+  height: 10px;
+  width: 10px;
+  background: lime;
+}
+</style>
+<div id="target1" class="target">
+  <div class="positioned"></div>
+</div>
+<div id="target2" class="target">
+  <div class="positioned"></div>
+</div>
+<div id="target3" class="target">
+  <div class="positioned"></div>
+</div>
+<div id="target4" class="target">
+  <div class="positioned"></div>
+</div>
+
+<script>
+var keyframe1 = {opacity: [1, 0.2]};
+var keyframe2 = {transform: ['rotate(20deg)', 'none']};
+var option1 = {fill: 'both', duration: 0};
+var option2 = {fill: 'none', duration: 0};
+
+var target1 = document.getElementById('target1');
+target1.animate(keyframe1, option1);
+
+var target2 = document.getElementById('target2');
+target2.animate(keyframe2, option1);
+
+var target3 = document.getElementById('target3');
+target3.animate(keyframe2, option1);
+target3.animate(keyframe1, option2);
+
+var target4 = document.getElementById('target4');
+target4.animate(keyframe2, option2);
+target4.animate(keyframe1, option1);
+</script>
+</html>
diff --git a/third_party/blink/renderer/core/core_initializer.cc b/third_party/blink/renderer/core/core_initializer.cc
index 7e79f79..34bc385 100644
--- a/third_party/blink/renderer/core/core_initializer.cc
+++ b/third_party/blink/renderer/core/core_initializer.cc
@@ -89,10 +89,10 @@
   // TODO(mikhail.pozdnyakov@intel.com): We should generate static strings
   // initialization code.
   const unsigned kQualifiedNamesCount =
-      HTMLNames::kTagsCount + HTMLNames::kAttrsCount + MathMLNames::kTagsCount +
-      MathMLNames::kAttrsCount + SVGNames::kTagsCount + SVGNames::kAttrsCount +
-      xlink_names::kAttrsCount + xml_names::kAttrsCount +
-      xmlns_names::kAttrsCount;
+      HTMLNames::kTagsCount + HTMLNames::kAttrsCount +
+      mathml_names::kTagsCount + mathml_names::kAttrsCount +
+      SVGNames::kTagsCount + SVGNames::kAttrsCount + xlink_names::kAttrsCount +
+      xml_names::kAttrsCount + xmlns_names::kAttrsCount;
 
   const unsigned kCoreStaticStringsCount =
       kQualifiedNamesCount + EventNames::kNamesCount +
@@ -111,7 +111,7 @@
 
   HTMLNames::init();
   SVGNames::init();
-  MathMLNames::init();
+  mathml_names::init();
   xlink_names::init();
   xml_names::init();
   xmlns_names::init();
diff --git a/third_party/blink/renderer/core/css/computed_style_extra_fields.json5 b/third_party/blink/renderer/core/css/computed_style_extra_fields.json5
index 96b002d..d53a94f 100644
--- a/third_party/blink/renderer/core/css/computed_style_extra_fields.json5
+++ b/third_party/blink/renderer/core/css/computed_style_extra_fields.json5
@@ -754,6 +754,13 @@
       default_value: "false",
     },
     {
+      name: "HasTransformAnimationWithForwardsOrBothFillMode",
+      field_template: "primitive",
+      type_name: "bool",
+      field_group: "*",
+      default_value: "false",
+    },
+    {
       name: "HasCurrentFilterAnimation",
       field_template: "primitive",
       type_name: "bool",
diff --git a/third_party/blink/renderer/core/css/css_default_style_sheets.cc b/third_party/blink/renderer/core/css/css_default_style_sheets.cc
index 8e34a4c..9f56557 100644
--- a/third_party/blink/renderer/core/css/css_default_style_sheets.cc
+++ b/third_party/blink/renderer/core/css/css_default_style_sheets.cc
@@ -173,7 +173,7 @@
   }
 
   // FIXME: We should assert that the sheet only styles MathML elements.
-  if (element.namespaceURI() == MathMLNames::mathmlNamespaceURI &&
+  if (element.namespaceURI() == mathml_names::kNamespaceURI &&
       !mathml_style_sheet_) {
     mathml_style_sheet_ =
         ParseUASheet(GetDataResourceAsASCIIString("mathml.css"));
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index f5b9733..70040973 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -585,7 +585,7 @@
                                        .IsViewportScrollCallback(callback);
 
   disable_custom_callbacks |=
-      !RootScrollerUtil::IsGlobal(this) &&
+      !root_scroller_util::IsGlobal(this) &&
       RuntimeEnabledFeatures::ScrollCustomizationEnabled() &&
       !GetScrollCustomizationCallbacks().InScrollPhase(this);
 
@@ -630,7 +630,7 @@
                                        ->GlobalRootScrollerController()
                                        .IsViewportScrollCallback(callback);
   disable_custom_callbacks |=
-      !RootScrollerUtil::IsGlobal(this) &&
+      !root_scroller_util::IsGlobal(this) &&
       RuntimeEnabledFeatures::ScrollCustomizationEnabled() &&
       !GetScrollCustomizationCallbacks().InScrollPhase(this);
 
@@ -1436,7 +1436,7 @@
 bool Node::IsStyledElement() const {
   return IsHTMLElement() || IsSVGElement() ||
          (IsElementNode() &&
-          ToElement(this)->namespaceURI() == MathMLNames::mathmlNamespaceURI);
+          ToElement(this)->namespaceURI() == mathml_names::kNamespaceURI);
 }
 
 bool Node::CanParticipateInFlatTree() const {
diff --git a/third_party/blink/renderer/core/editing/selection_controller.cc b/third_party/blink/renderer/core/editing/selection_controller.cc
index 69f51a3..9d36b0bf19 100644
--- a/third_party/blink/renderer/core/editing/selection_controller.cc
+++ b/third_party/blink/renderer/core/editing/selection_controller.cc
@@ -1158,13 +1158,13 @@
   Node* inner_node = result.InnerNode();
   if (!inner_node || !inner_node->GetLayoutObject())
     return false;
-  VisiblePosition pos = CreateVisiblePosition(
-      inner_node->GetLayoutObject()->PositionForPoint(result.LocalPoint()));
-  if (pos.IsNull())
+  PositionWithAffinity pos_with_affinity =
+      inner_node->GetLayoutObject()->PositionForPoint(result.LocalPoint());
+  if (pos_with_affinity.IsNull())
     return false;
   // TODO(xiaochengh): Don't use |ParentAnchoredEquivalent()|.
   const Position marker_position =
-      pos.DeepEquivalent().ParentAnchoredEquivalent();
+      pos_with_affinity.GetPosition().ParentAnchoredEquivalent();
   if (!SpellChecker::IsSpellCheckingEnabledAt(marker_position))
     return false;
   return SpellCheckMarkerAtPosition(inner_node->GetDocument().Markers(),
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 5f1bf2c0..9ab9830a 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -3553,7 +3553,7 @@
   if (!LayoutViewport())
     return false;
 
-  return RootScrollerUtil::ScrollableAreaForRootScroller(
+  return root_scroller_util::ScrollableAreaForRootScroller(
              controller.GlobalRootScroller()) == LayoutViewport();
 }
 
diff --git a/third_party/blink/renderer/core/html/custom/v0_custom_element.cc b/third_party/blink/renderer/core/html/custom/v0_custom_element.cc
index 4901ae8..4e10b8db 100644
--- a/third_party/blink/renderer/core/html/custom/v0_custom_element.cc
+++ b/third_party/blink/renderer/core/html/custom/v0_custom_element.cc
@@ -73,7 +73,7 @@
     DEFINE_STATIC_LOCAL(Vector<AtomicString>, reserved_names, ());
     if (reserved_names.IsEmpty()) {
       // FIXME(crbug.com/426605): We should be able to remove this.
-      reserved_names.push_back(MathMLNames::annotation_xmlTag.LocalName());
+      reserved_names.push_back(mathml_names::kAnnotationXmlTag.LocalName());
     }
 
     if (kNotFound == reserved_names.Find(name))
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index 824ff70..0f3e0e75 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -115,7 +115,8 @@
     return true;
   if (IsSVGSVGElement(node))
     return true;
-  if (node.IsElementNode() && ToElement(node).HasTagName(MathMLNames::mathTag))
+  if (node.IsElementNode() &&
+      ToElement(node).HasTagName(mathml_names::kMathTag))
     return true;
   return !node.IsElementNode() && node.parentNode()->IsHTMLElement();
 }
diff --git a/third_party/blink/renderer/core/html/parser/html_element_stack.cc b/third_party/blink/renderer/core/html/parser/html_element_stack.cc
index d1ed859..878a97d 100644
--- a/third_party/blink/renderer/core/html/parser/html_element_stack.cc
+++ b/third_party/blink/renderer/core/html/parser/html_element_stack.cc
@@ -48,12 +48,12 @@
   return item->HasTagName(appletTag) || item->HasTagName(captionTag) ||
          item->HasTagName(marqueeTag) || item->HasTagName(objectTag) ||
          item->HasTagName(tableTag) || item->HasTagName(tdTag) ||
-         item->HasTagName(thTag) || item->HasTagName(MathMLNames::miTag) ||
-         item->HasTagName(MathMLNames::moTag) ||
-         item->HasTagName(MathMLNames::mnTag) ||
-         item->HasTagName(MathMLNames::msTag) ||
-         item->HasTagName(MathMLNames::mtextTag) ||
-         item->HasTagName(MathMLNames::annotation_xmlTag) ||
+         item->HasTagName(thTag) || item->HasTagName(mathml_names::kMiTag) ||
+         item->HasTagName(mathml_names::kMoTag) ||
+         item->HasTagName(mathml_names::kMnTag) ||
+         item->HasTagName(mathml_names::kMsTag) ||
+         item->HasTagName(mathml_names::kMtextTag) ||
+         item->HasTagName(mathml_names::kAnnotationXmlTag) ||
          item->HasTagName(SVGNames::foreignObjectTag) ||
          item->HasTagName(SVGNames::descTag) ||
          item->HasTagName(SVGNames::titleTag) ||
@@ -229,20 +229,20 @@
 bool HTMLElementStack::IsMathMLTextIntegrationPoint(HTMLStackItem* item) {
   if (!item->IsElementNode())
     return false;
-  return item->HasTagName(MathMLNames::miTag) ||
-         item->HasTagName(MathMLNames::moTag) ||
-         item->HasTagName(MathMLNames::mnTag) ||
-         item->HasTagName(MathMLNames::msTag) ||
-         item->HasTagName(MathMLNames::mtextTag);
+  return item->HasTagName(mathml_names::kMiTag) ||
+         item->HasTagName(mathml_names::kMoTag) ||
+         item->HasTagName(mathml_names::kMnTag) ||
+         item->HasTagName(mathml_names::kMsTag) ||
+         item->HasTagName(mathml_names::kMtextTag);
 }
 
 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#html-integration-point
 bool HTMLElementStack::IsHTMLIntegrationPoint(HTMLStackItem* item) {
   if (!item->IsElementNode())
     return false;
-  if (item->HasTagName(MathMLNames::annotation_xmlTag)) {
+  if (item->HasTagName(mathml_names::kAnnotationXmlTag)) {
     Attribute* encoding_attr =
-        item->GetAttributeItem(MathMLNames::encodingAttr);
+        item->GetAttributeItem(mathml_names::kEncodingAttr);
     if (encoding_attr) {
       const String& encoding = encoding_attr->Value();
       return DeprecatedEqualIgnoringCase(encoding, "text/html") ||
diff --git a/third_party/blink/renderer/core/html/parser/html_stack_item.h b/third_party/blink/renderer/core/html/parser/html_stack_item.h
index 9cd28ab..05c3bb97 100644
--- a/third_party/blink/renderer/core/html/parser/html_stack_item.h
+++ b/third_party/blink/renderer/core/html/parser/html_stack_item.h
@@ -116,10 +116,10 @@
 
   // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#special
   bool IsSpecialNode() const {
-    if (HasTagName(MathMLNames::miTag) || HasTagName(MathMLNames::moTag) ||
-        HasTagName(MathMLNames::mnTag) || HasTagName(MathMLNames::msTag) ||
-        HasTagName(MathMLNames::mtextTag) ||
-        HasTagName(MathMLNames::annotation_xmlTag) ||
+    if (HasTagName(mathml_names::kMiTag) || HasTagName(mathml_names::kMoTag) ||
+        HasTagName(mathml_names::kMnTag) || HasTagName(mathml_names::kMsTag) ||
+        HasTagName(mathml_names::kMtextTag) ||
+        HasTagName(mathml_names::kAnnotationXmlTag) ||
         HasTagName(SVGNames::foreignObjectTag) ||
         HasTagName(SVGNames::descTag) || HasTagName(SVGNames::titleTag))
       return true;
diff --git a/third_party/blink/renderer/core/html/parser/html_tree_builder.cc b/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
index fc8d5c2..0af2338 100644
--- a/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
+++ b/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
@@ -496,7 +496,7 @@
 
 // https://html.spec.whatwg.org/multipage/parsing.html#adjust-mathml-attributes
 static void AdjustMathMLAttributes(AtomicHTMLToken* token) {
-  AdjustAttributes<MathMLNames::GetAttrs, MathMLNames::kAttrsCount>(token);
+  AdjustAttributes<mathml_names::GetAttrs, mathml_names::kAttrsCount>(token);
 }
 
 static void AddNamesWithPrefix(PrefixedNameToQualifiedNameMap* map,
@@ -823,11 +823,11 @@
     tree_.InsertHTMLElement(token);
     return;
   }
-  if (token->GetName() == MathMLNames::mathTag.LocalName()) {
+  if (token->GetName() == mathml_names::kMathTag.LocalName()) {
     tree_.ReconstructTheActiveFormattingElements();
     AdjustMathMLAttributes(token);
     AdjustForeignAttributes(token);
-    tree_.InsertForeignElement(token, MathMLNames::mathmlNamespaceURI);
+    tree_.InsertForeignElement(token, mathml_names::kNamespaceURI);
     return;
   }
   if (token->GetName() == SVGNames::svgTag.LocalName()) {
@@ -2591,13 +2591,13 @@
     return false;
   if (HTMLElementStack::IsMathMLTextIntegrationPoint(adjusted_current_node)) {
     if (token->GetType() == HTMLToken::kStartTag &&
-        token->GetName() != MathMLNames::mglyphTag &&
-        token->GetName() != MathMLNames::malignmarkTag)
+        token->GetName() != mathml_names::kMglyphTag &&
+        token->GetName() != mathml_names::kMalignmarkTag)
       return false;
     if (token->GetType() == HTMLToken::kCharacter)
       return false;
   }
-  if (adjusted_current_node->HasTagName(MathMLNames::annotation_xmlTag) &&
+  if (adjusted_current_node->HasTagName(mathml_names::kAnnotationXmlTag) &&
       token->GetType() == HTMLToken::kStartTag &&
       token->GetName() == SVGNames::svgTag)
     return false;
@@ -2666,7 +2666,7 @@
       }
       const AtomicString& current_namespace =
           adjusted_current_node->NamespaceURI();
-      if (current_namespace == MathMLNames::mathmlNamespaceURI)
+      if (current_namespace == mathml_names::kNamespaceURI)
         AdjustMathMLAttributes(token);
       if (current_namespace == SVGNames::svgNamespaceURI) {
         AdjustSVGTagNameCase(token);
diff --git a/third_party/blink/renderer/core/html/parser/html_tree_builder_simulator.cc b/third_party/blink/renderer/core/html/parser/html_tree_builder_simulator.cc
index 0d84be7..44c1e1a 100644
--- a/third_party/blink/renderer/core/html/parser/html_tree_builder_simulator.cc
+++ b/third_party/blink/renderer/core/html/parser/html_tree_builder_simulator.cc
@@ -87,11 +87,11 @@
   // FIXME: This is copied from HTMLElementStack::isMathMLTextIntegrationPoint
   // and changed to use threadSafeMatch.
   const String& tag_name = token.Data();
-  return ThreadSafeMatch(tag_name, MathMLNames::miTag) ||
-         ThreadSafeMatch(tag_name, MathMLNames::moTag) ||
-         ThreadSafeMatch(tag_name, MathMLNames::mnTag) ||
-         ThreadSafeMatch(tag_name, MathMLNames::msTag) ||
-         ThreadSafeMatch(tag_name, MathMLNames::mtextTag);
+  return ThreadSafeMatch(tag_name, mathml_names::kMiTag) ||
+         ThreadSafeMatch(tag_name, mathml_names::kMoTag) ||
+         ThreadSafeMatch(tag_name, mathml_names::kMnTag) ||
+         ThreadSafeMatch(tag_name, mathml_names::kMsTag) ||
+         ThreadSafeMatch(tag_name, mathml_names::kMtextTag);
 }
 
 static bool TokenExitsInSelect(const CompactHTMLToken& token) {
@@ -118,7 +118,7 @@
     Namespace current_namespace = HTML;
     if (record->NamespaceURI() == SVGNames::svgNamespaceURI)
       current_namespace = SVG;
-    else if (record->NamespaceURI() == MathMLNames::mathmlNamespaceURI)
+    else if (record->NamespaceURI() == mathml_names::kNamespaceURI)
       current_namespace = kMathML;
 
     if (namespace_stack.IsEmpty() ||
@@ -138,7 +138,7 @@
     const String& tag_name = token.Data();
     if (ThreadSafeMatch(tag_name, SVGNames::svgTag))
       namespace_stack_.push_back(SVG);
-    if (ThreadSafeMatch(tag_name, MathMLNames::mathTag))
+    if (ThreadSafeMatch(tag_name, mathml_names::kMathTag))
       namespace_stack_.push_back(kMathML);
     if (InForeignContent() && TokenExitsForeignContent(token))
       namespace_stack_.pop_back();
@@ -212,7 +212,7 @@
     if ((namespace_stack_.back() == SVG &&
          ThreadSafeMatch(tag_name, SVGNames::svgTag)) ||
         (namespace_stack_.back() == kMathML &&
-         ThreadSafeMatch(tag_name, MathMLNames::mathTag)) ||
+         ThreadSafeMatch(tag_name, mathml_names::kMathTag)) ||
         IsHTMLIntegrationPointForEndTag(token) ||
         (namespace_stack_.Contains(kMathML) &&
          namespace_stack_.back() == HTML && TokenExitsMath(token))) {
@@ -243,9 +243,9 @@
   Namespace tokens_ns = namespace_stack_.back();
   const String& tag_name = token.Data();
   if (tokens_ns == kMathML) {
-    if (!ThreadSafeMatch(tag_name, MathMLNames::annotation_xmlTag))
+    if (!ThreadSafeMatch(tag_name, mathml_names::kAnnotationXmlTag))
       return false;
-    if (auto* encoding = token.GetAttributeItem(MathMLNames::encodingAttr)) {
+    if (auto* encoding = token.GetAttributeItem(mathml_names::kEncodingAttr)) {
       return EqualIgnoringASCIICase(encoding->Value(), "text/html") ||
              EqualIgnoringASCIICase(encoding->Value(), "application/xhtml+xml");
     }
@@ -277,7 +277,7 @@
 
   const String& tag_name = token.Data();
   if (tokens_ns == kMathML)
-    return ThreadSafeMatch(tag_name, MathMLNames::annotation_xmlTag);
+    return ThreadSafeMatch(tag_name, mathml_names::kAnnotationXmlTag);
   if (tokens_ns == SVG) {
     // FIXME: It's very fragile that we special case foreignObject here to be
     // case-insensitive.
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc
index e254f06..50ce9d5 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.cc
+++ b/third_party/blink/renderer/core/input/scroll_manager.cc
@@ -282,7 +282,7 @@
     ScrollableArea* scrollable_area = nullptr;
 
     // The global root scroller must be scrolled by the RootFrameViewport.
-    if (RootScrollerUtil::IsGlobal(*box)) {
+    if (root_scroller_util::IsGlobal(*box)) {
       LocalFrame& main_frame = frame_->LocalFrameRoot();
       // The local root must be the main frame if we have the global root
       // scroller since it can't yet be set on OOPIFs.
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index ada797bd..6fc55c4c 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -5316,7 +5316,7 @@
     parameters
       string url
       # Base64-encoded data
-      string data
+      binary data
 
   # Clears seeded compilation cache.
   experimental command clearCompilationCache
@@ -5484,7 +5484,7 @@
     parameters
       string url
       # Base64-encoded data
-      string data
+      binary data
 
 
 domain Performance
@@ -5567,6 +5567,8 @@
       MixedContentType mixedContentType
       # Page certificate.
       array of string certificate
+      # Recommendations to fix any issues.
+      optional array of string recommendations
 
   # Information about insecure content on the page.
   type InsecureContentStatus extends object
diff --git a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
index 1c898a2..7a4d4bd3 100644
--- a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
@@ -1286,10 +1286,9 @@
   auto it = compilation_cache_.find(source.Url().GetString());
   if (it == compilation_cache_.end())
     return;
-  const Vector<char>& data = it->value;
+  const protocol::Binary& data = it->value;
   *cached_data = new v8::ScriptCompiler::CachedData(
-      reinterpret_cast<const uint8_t*>(data.data()), data.size(),
-      v8::ScriptCompiler::CachedData::BufferNotOwned);
+      data.data(), data.size(), v8::ScriptCompiler::CachedData::BufferNotOwned);
 }
 
 void InspectorPageAgent::ProduceCompilationCache(const ScriptSourceCode& source,
@@ -1313,9 +1312,11 @@
   std::unique_ptr<v8::ScriptCompiler::CachedData> cached_data(
       v8::ScriptCompiler::CreateCodeCache(script->GetUnboundScript()));
   if (cached_data) {
-    String base64data = Base64Encode(
-        reinterpret_cast<const char*>(cached_data->data), cached_data->length);
-    GetFrontend()->compilationCacheProduced(url_string, base64data);
+    // CachedData produced by CreateCodeCache always owns its buffer.
+    CHECK_EQ(cached_data->buffer_policy,
+             v8::ScriptCompiler::CachedData::BufferOwned);
+    GetFrontend()->compilationCacheProduced(
+        url_string, protocol::Binary::fromCachedData(std::move(cached_data)));
   }
 }
 
@@ -1325,11 +1326,8 @@
 }
 
 Response InspectorPageAgent::addCompilationCache(const String& url,
-                                                 const String& base64data) {
-  Vector<char> data;
-  if (!Base64Decode(base64data, data))
-    return Response::Error("data should be base64-encoded");
-  compilation_cache_.Set(url, std::move(data));
+                                                 const protocol::Binary& data) {
+  compilation_cache_.Set(url, data);
   return Response::OK();
 }
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_page_agent.h b/third_party/blink/renderer/core/inspector/inspector_page_agent.h
index 65325e4..2e9457b 100644
--- a/third_party/blink/renderer/core/inspector/inspector_page_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_page_agent.h
@@ -164,7 +164,7 @@
 
   protocol::Response setProduceCompilationCache(bool enabled) override;
   protocol::Response addCompilationCache(const String& url,
-                                         const String& data) override;
+                                         const protocol::Binary& data) override;
   protocol::Response clearCompilationCache() override;
 
   // InspectorInstrumentation API
@@ -236,7 +236,7 @@
   std::unique_ptr<protocol::Page::FrameResourceTree> BuildObjectForResourceTree(
       LocalFrame*);
   Member<InspectedFrames> inspected_frames_;
-  HashMap<String, Vector<char>> compilation_cache_;
+  HashMap<String, protocol::Binary> compilation_cache_;
   v8_inspector::V8InspectorSession* v8_session_;
   Client* client_;
   String pending_script_to_evaluate_on_load_once_;
diff --git a/third_party/blink/renderer/core/layout/layout_image.cc b/third_party/blink/renderer/core/layout/layout_image.cc
index 972b84e..b06bcb8f 100644
--- a/third_party/blink/renderer/core/layout/layout_image.cc
+++ b/third_party/blink/renderer/core/layout/layout_image.cc
@@ -273,7 +273,6 @@
 
   // Check for optimized image policies.
   if (View() && View()->GetFrameView()) {
-    bool old_flag = ShouldInvertColor();
     const LocalFrame& frame = View()->GetFrameView()->GetFrame();
     is_legacy_format_or_compressed_image_ =
         CheckForOptimizedImagePolicy(frame, this, new_image);
@@ -281,8 +280,6 @@
       is_downscaled_image_ =
           CheckForMaxDownscalingImagePolicy(frame, new_image, this);
     }
-    if (old_flag != ShouldInvertColor())
-      UpdateShouldInvertColor();
   }
 
   if (new_image == image_resource_->CachedImage()) {
@@ -483,23 +480,10 @@
   return ToSVGImageOrNull(cached_image->GetImage());
 }
 
-bool LayoutImage::ShouldInvertColor() const {
+bool LayoutImage::IsImagePolicyViolated() const {
   return is_downscaled_image_ || is_legacy_format_or_compressed_image_;
 }
 
-void LayoutImage::UpdateShouldInvertColor() {
-  SetNeedsPaintPropertyUpdate();
-  // If composited image, update compositing layer.
-  if (Layer())
-    Layer()->SetNeedsCompositingInputsUpdate();
-}
-
-void LayoutImage::UpdateShouldInvertColorForTest(bool value) {
-  is_downscaled_image_ = value;
-  is_legacy_format_or_compressed_image_ = value;
-  UpdateShouldInvertColor();
-}
-
 void LayoutImage::UpdateAfterLayout() {
   LayoutBox::UpdateAfterLayout();
   Node* node = GetNode();
@@ -509,11 +493,8 @@
 
       if (image_resource_ && image_resource_->CachedImage()) {
         // Check for optimized image policies.
-        bool old_flag = ShouldInvertColor();
         is_downscaled_image_ = CheckForMaxDownscalingImagePolicy(
             frame, image_resource_->CachedImage(), this);
-        if (old_flag != ShouldInvertColor())
-          UpdateShouldInvertColor();
       }
     }
 
diff --git a/third_party/blink/renderer/core/layout/layout_image.h b/third_party/blink/renderer/core/layout/layout_image.h
index 0ce310d4..cc59319 100644
--- a/third_party/blink/renderer/core/layout/layout_image.h
+++ b/third_party/blink/renderer/core/layout/layout_image.h
@@ -88,11 +88,9 @@
   const char* GetName() const override { return "LayoutImage"; }
 
   // When an image element violates feature policy optimized image policies, it
-  // should be rendered with inverted color.
+  // should be rendered with a placeholder image.
   // https://github.com/WICG/feature-policy/blob/master/policies/optimized-images.md
-  bool ShouldInvertColor() const;
-  void UpdateShouldInvertColor();
-  void UpdateShouldInvertColorForTest(bool);
+  bool IsImagePolicyViolated() const;
 
   void UpdateAfterLayout() override;
 
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
index fb8c05e..a6e7a0b 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
@@ -415,7 +415,8 @@
 }
 
 PaintLayer* RootScrollerController::RootScrollerPaintLayer() const {
-  return RootScrollerUtil::PaintLayerForRootScroller(effective_root_scroller_);
+  return root_scroller_util::PaintLayerForRootScroller(
+      effective_root_scroller_);
 }
 
 bool RootScrollerController::ScrollsViewport(const Element& element) const {
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_util.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_util.cc
index c2111cc2..322ce06d 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_util.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_util.cc
@@ -19,7 +19,7 @@
 
 namespace blink {
 
-namespace RootScrollerUtil {
+namespace root_scroller_util {
 
 ScrollableArea* ScrollableAreaForRootScroller(const Node* node) {
   if (!node)
@@ -97,6 +97,6 @@
              .GlobalRootScroller() == node;
 }
 
-}  // namespace RootScrollerUtil
+}  // namespace root_scroller_util
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_util.h b/third_party/blink/renderer/core/page/scrolling/root_scroller_util.h
index 8e6796b83..152aba0d 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_util.h
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_util.h
@@ -12,7 +12,7 @@
 class PaintLayer;
 class ScrollableArea;
 
-namespace RootScrollerUtil {
+namespace root_scroller_util {
 
 // Returns the ScrollableArea that's associated with the root scroller Node.
 // For the <html> element and document Node this will be the FrameView or root
@@ -28,7 +28,7 @@
 bool IsGlobal(const PaintLayer&);
 bool IsGlobal(const Node*);
 
-}  // namespace RootScrollerUtil
+}  // namespace root_scroller_util
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.cc b/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.cc
index b439f2c..fc1d86a 100644
--- a/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.cc
+++ b/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.cc
@@ -56,7 +56,8 @@
 }
 
 ScrollableArea* TopDocumentRootScrollerController::RootScrollerArea() const {
-  return RootScrollerUtil::ScrollableAreaForRootScroller(GlobalRootScroller());
+  return root_scroller_util::ScrollableAreaForRootScroller(
+      GlobalRootScroller());
 }
 
 IntSize TopDocumentRootScrollerController::RootScrollerVisibleArea() const {
@@ -114,7 +115,7 @@
     return;
 
   ScrollableArea* area =
-      RootScrollerUtil::ScrollableAreaForRootScroller(element);
+      root_scroller_util::ScrollableAreaForRootScroller(element);
 
   if (!area || !area->Layer())
     return;
@@ -140,7 +141,7 @@
     return;
 
   ScrollableArea* target_scroller =
-      RootScrollerUtil::ScrollableAreaForRootScroller(target);
+      root_scroller_util::ScrollableAreaForRootScroller(target);
 
   if (!target_scroller)
     return;
@@ -167,8 +168,8 @@
   SetNeedsCompositingUpdateOnAncestors(old_root_scroller);
   SetNeedsCompositingUpdateOnAncestors(target);
 
-  if (ScrollableArea* area =
-          RootScrollerUtil::ScrollableAreaForRootScroller(old_root_scroller)) {
+  if (ScrollableArea* area = root_scroller_util::ScrollableAreaForRootScroller(
+          old_root_scroller)) {
     if (old_root_scroller->GetDocument().IsActive())
       area->DidChangeGlobalRootScroller();
   }
@@ -237,7 +238,7 @@
 
 GraphicsLayer* TopDocumentRootScrollerController::RootScrollerLayer() const {
   ScrollableArea* area =
-      RootScrollerUtil::ScrollableAreaForRootScroller(global_root_scroller_);
+      root_scroller_util::ScrollableAreaForRootScroller(global_root_scroller_);
 
   if (!area)
     return nullptr;
@@ -253,13 +254,13 @@
 
 GraphicsLayer* TopDocumentRootScrollerController::RootContainerLayer() const {
   ScrollableArea* area =
-      RootScrollerUtil::ScrollableAreaForRootScroller(global_root_scroller_);
+      root_scroller_util::ScrollableAreaForRootScroller(global_root_scroller_);
 
   return area ? area->LayerForContainer() : nullptr;
 }
 
 PaintLayer* TopDocumentRootScrollerController::RootScrollerPaintLayer() const {
-  return RootScrollerUtil::PaintLayerForRootScroller(global_root_scroller_);
+  return root_scroller_util::PaintLayerForRootScroller(global_root_scroller_);
 }
 
 Element* TopDocumentRootScrollerController::GlobalRootScroller() const {
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
index 2b4375a..560a608a 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
@@ -313,12 +313,6 @@
   CompositorFilterOperations operations;
   OwningLayer().UpdateCompositorFilterOperationsForFilter(operations);
 
-  // If the image violates some feature policy optimized image policies, render
-  // with inverted color.
-  if (GetLayoutObject().IsLayoutImage() &&
-      ToLayoutImage(GetLayoutObject()).ShouldInvertColor())
-    operations.AppendInvertFilter(1.0f);
-
   graphics_layer_->SetFilters(std::move(operations));
 }
 
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc
index 396daff..158faf8 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc
@@ -2721,42 +2721,4 @@
             squashed->GraphicsLayerBacking()->GetPosition());
 }
 
-TEST_F(CompositedLayerMappingTest, ImageWithInvertFilterLayer) {
-  SetBodyInnerHTML("<img id='image' style='will-change: transform;' src='x'>");
-  ToLayoutImage(GetLayoutObjectByElementId("image"))
-      ->UpdateShouldInvertColorForTest(true);
-  GetDocument().View()->UpdateAllLifecyclePhases();
-  cc::FilterOperations filters;
-  filters.Append(cc::FilterOperation::CreateInvertFilter(1.0f));
-  EXPECT_EQ(filters, ToLayoutBoxModelObject(GetLayoutObjectByElementId("image"))
-                         ->Layer()
-                         ->GraphicsLayerBacking()
-                         ->CcLayer()
-                         ->filters());
-}
-
-TEST_F(CompositedLayerMappingTest, ImageWithInvertFilterLayerUpdated) {
-  SetBodyInnerHTML("<img id='image' style='will-change: transform;' src='x'>");
-  ToLayoutImage(GetLayoutObjectByElementId("image"))
-      ->UpdateShouldInvertColorForTest(true);
-  GetDocument().View()->UpdateAllLifecyclePhases();
-  cc::FilterOperations filters0, filters1;
-  filters0.Append(cc::FilterOperation::CreateInvertFilter(1.0f));
-  EXPECT_EQ(filters0,
-            ToLayoutBoxModelObject(GetLayoutObjectByElementId("image"))
-                ->Layer()
-                ->GraphicsLayerBacking()
-                ->CcLayer()
-                ->filters());
-  ToLayoutImage(GetLayoutObjectByElementId("image"))
-      ->UpdateShouldInvertColorForTest(false);
-  GetDocument().View()->UpdateAllLifecyclePhases();
-  EXPECT_EQ(filters1,
-            ToLayoutBoxModelObject(GetLayoutObjectByElementId("image"))
-                ->Layer()
-                ->GraphicsLayerBacking()
-                ->CcLayer()
-                ->filters());
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
index 877168d..d377682e 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
@@ -257,7 +257,7 @@
   const auto& settings = *layer.GetLayoutObject().GetDocument().GetSettings();
   if (!settings.GetAcceleratedCompositingEnabled())
     return false;
-  return RootScrollerUtil::IsGlobal(layer);
+  return root_scroller_util::IsGlobal(layer);
 }
 
 bool CompositingReasonFinder::RequiresCompositingForScrollDependentPosition(
diff --git a/third_party/blink/renderer/core/paint/image_painter.cc b/third_party/blink/renderer/core/paint/image_painter.cc
index 5b393b7..d064c56 100644
--- a/third_party/blink/renderer/core/paint/image_painter.cc
+++ b/third_party/blink/renderer/core/paint/image_painter.cc
@@ -22,6 +22,7 @@
 #include "third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
 #include "third_party/blink/renderer/platform/graphics/path.h"
+#include "third_party/blink/renderer/platform/graphics/placeholder_image.h"
 #include "third_party/blink/renderer/platform/graphics/scoped_interpolation_quality.h"
 
 namespace blink {
@@ -186,6 +187,11 @@
           ? 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);
+  }
   context.DrawImage(
       image.get(), decode_mode, FloatRect(pixel_snapped_dest_rect), &src_rect,
       SkBlendMode::kSrcOver,
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index c648909..eb9e489 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -2325,7 +2325,7 @@
   const TopDocumentRootScrollerController& controller =
       GetLayoutBox()->GetDocument().GetPage()->GlobalRootScrollerController();
 
-  return RootScrollerUtil::ScrollableAreaForRootScroller(
+  return root_scroller_util::ScrollableAreaForRootScroller(
              controller.GlobalRootScroller()) == this;
 }
 
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 88acf33..d256624 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -986,14 +986,10 @@
 
 static bool NeedsFilter(const LayoutObject& object) {
   // TODO(trchen): SVG caches filters in SVGResources. Implement it.
-  if (object.IsBoxModelObject() && ToLayoutBoxModelObject(object).Layer() &&
-      (object.StyleRef().HasFilter() || object.HasReflection() ||
-       CompositingReasonFinder::RequiresCompositingForFilterAnimation(
-           object.StyleRef())))
-    return true;
-  if (object.IsLayoutImage() && ToLayoutImage(object).ShouldInvertColor())
-    return true;
-  return false;
+  return (object.IsBoxModelObject() && ToLayoutBoxModelObject(object).Layer() &&
+          (object.StyleRef().HasFilter() || object.HasReflection() ||
+           CompositingReasonFinder::RequiresCompositingForFilterAnimation(
+               object.StyleRef())));
 }
 
 void FragmentPaintPropertyTreeBuilder::UpdateFilter() {
@@ -1006,23 +1002,13 @@
       state.local_transform_space = context_.current.transform;
       state.filters_origin = FloatPoint(context_.current.paint_offset);
 
-      auto* layer = ToLayoutBoxModelObject(object_).Layer();
-      if (layer) {
+      if (auto* layer = ToLayoutBoxModelObject(object_).Layer()) {
         // Try to use the cached filter.
         if (properties_->Filter())
           state.filter = properties_->Filter()->Filter();
 
-        if (object_.IsLayoutImage() &&
-            ToLayoutImage(object_).ShouldInvertColor())
-          state.filter.AppendInvertFilter(1.0f);
-
         layer->UpdateCompositorFilterOperationsForFilter(state.filter);
         layer->ClearFilterOnEffectNodeDirty();
-      } else {
-        DCHECK(object_.IsLayoutImage() &&
-               ToLayoutImage(object_).ShouldInvertColor());
-        state.filter = CompositorFilterOperations();
-        state.filter.AppendInvertFilter(1.0f);
       }
 
       // The CSS filter spec didn't specify how filters interact with overflow
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index 3be04493..5e4f024 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -6005,72 +6005,6 @@
   EXPECT_EQ(1u, NumFragments(foot->FirstRow()->FirstCell()));
 }
 
-TEST_P(PaintPropertyTreeBuilderTest, ImageWithInvertFilter) {
-  SetBodyInnerHTML(R"HTML(
-    <img id='img' src='x'>
-  )HTML");
-  ToLayoutImage(GetLayoutObjectByElementId("img"))
-      ->UpdateShouldInvertColorForTest(true);
-  GetDocument().View()->UpdateAllLifecyclePhases();
-  const auto* filters = PaintPropertiesForElement("img")->Filter();
-  ASSERT_NE(nullptr, filters);
-  CompositorFilterOperations filters_expect;
-  filters_expect.AppendInvertFilter(1.0f);
-  EXPECT_EQ(filters_expect, filters->Filter());
-}
-
-TEST_P(PaintPropertyTreeBuilderTest, ImageWithInvertFilterUpdated) {
-  SetBodyInnerHTML(R"HTML(
-    <img id='img' src='x'>
-  )HTML");
-
-  ToLayoutImage(GetLayoutObjectByElementId("img"))
-      ->UpdateShouldInvertColorForTest(true);
-  GetDocument().View()->UpdateAllLifecyclePhases();
-  const auto* filters = PaintPropertiesForElement("img")->Filter();
-  ASSERT_NE(nullptr, filters);
-  CompositorFilterOperations filters_expect;
-  filters_expect.AppendInvertFilter(1.0f);
-  EXPECT_EQ(filters_expect, filters->Filter());
-  ToLayoutImage(GetLayoutObjectByElementId("img"))
-      ->UpdateShouldInvertColorForTest(false);
-  GetDocument().View()->UpdateAllLifecyclePhases();
-  EXPECT_FALSE(PaintPropertiesForElement("img"));
-}
-
-TEST_P(PaintPropertyTreeBuilderTest, LayeredImageWithInvertFilter) {
-  SetBodyInnerHTML(R"HTML(
-    <img id='img' style='position: relative;' src='x'>
-  )HTML");
-  ToLayoutImage(GetLayoutObjectByElementId("img"))
-      ->UpdateShouldInvertColorForTest(true);
-  GetDocument().View()->UpdateAllLifecyclePhases();
-  const auto* filters = PaintPropertiesForElement("img")->Filter();
-  ASSERT_NE(nullptr, filters);
-  CompositorFilterOperations filters_expect;
-  filters_expect.AppendInvertFilter(1.0f);
-  EXPECT_EQ(filters_expect, filters->Filter());
-}
-
-TEST_P(PaintPropertyTreeBuilderTest, LayeredImageWithInvertFilterUpdated) {
-  SetBodyInnerHTML(R"HTML(
-    <img id='img' style='position: relative;' src='x'>
-  )HTML");
-
-  ToLayoutImage(GetLayoutObjectByElementId("img"))
-      ->UpdateShouldInvertColorForTest(true);
-  GetDocument().View()->UpdateAllLifecyclePhases();
-  const auto* filters = PaintPropertiesForElement("img")->Filter();
-  ASSERT_NE(nullptr, filters);
-  CompositorFilterOperations filters_expect;
-  filters_expect.AppendInvertFilter(1.0f);
-  EXPECT_EQ(filters_expect, filters->Filter());
-  ToLayoutImage(GetLayoutObjectByElementId("img"))
-      ->UpdateShouldInvertColorForTest(false);
-  GetDocument().View()->UpdateAllLifecyclePhases();
-  EXPECT_FALSE(PaintPropertiesForElement("img"));
-}
-
 TEST_P(PaintPropertyTreeBuilderTest,
        FloatPaintOffsetInContainerWithScrollbars) {
   SetBodyInnerHTML(R"HTML(
diff --git a/third_party/blink/renderer/core/paint/replaced_painter.cc b/third_party/blink/renderer/core/paint/replaced_painter.cc
index 9e07504e..09f7d3c2 100644
--- a/third_party/blink/renderer/core/paint/replaced_painter.cc
+++ b/third_party/blink/renderer/core/paint/replaced_painter.cc
@@ -65,14 +65,6 @@
     property_changed = true;
   }
 
-  // Check filter for optimized image policy violation highlights, which
-  // may be applied locally.
-  if (paint_properties->Filter() &&
-      (!replaced.HasLayer() || !replaced.Layer()->IsSelfPaintingLayer())) {
-    new_properties.SetEffect(paint_properties->Filter());
-    property_changed = true;
-  }
-
   if (property_changed) {
     chunk_properties_.emplace(input_paint_info_.context.GetPaintController(),
                               new_properties, replaced,
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index cea95d7b..a332fd9 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -1340,7 +1340,7 @@
   bool HasWillChangeOpacityHint() const {
     return WillChangeProperties().Contains(CSSPropertyOpacity);
   }
-  bool HasWillChangeTransformHint() const;
+  CORE_EXPORT bool HasWillChangeTransformHint() const;
 
   // Hyphen utility functions.
   Hyphenation* GetHyphenation() const;
@@ -2072,9 +2072,10 @@
   // will-change:transform should result in the same rendering behavior as
   // having a transform, including the creation of a containing block for fixed
   // position descendants.
-  bool HasTransformRelatedProperty() const {
+  CORE_EXPORT bool HasTransformRelatedProperty() const {
     return HasTransform() || Preserves3D() || HasPerspective() ||
-           HasWillChangeTransformHint();
+           HasWillChangeTransformHint() ||
+           HasTransformAnimationWithForwardsOrBothFillMode();
   }
 
   // Paint utility functions.
diff --git a/third_party/blink/renderer/core/timing/event_timing.cc b/third_party/blink/renderer/core/timing/event_timing.cc
index 8d8816c..bd694a3 100644
--- a/third_party/blink/renderer/core/timing/event_timing.cc
+++ b/third_party/blink/renderer/core/timing/event_timing.cc
@@ -38,7 +38,8 @@
   if ((performance_->ShouldBufferEventTiming() &&
        !performance_->IsEventTimingBufferFull()) ||
       performance_->HasObserverFor(PerformanceEntry::kEvent)
-      || performance_->HasObserverFor(PerformanceEntry::kFirstInput)) {
+      || (performance_->HasObserverFor(PerformanceEntry::kFirstInput)
+         && !performance_->FirstInputDetected())) {
     processing_start_ = CurrentTimeTicks();
     finished_will_dispatch_event_ = true;
   }
diff --git a/third_party/blink/renderer/core/timing/window_performance.h b/third_party/blink/renderer/core/timing/window_performance.h
index d937e83c..5d9490a 100644
--- a/third_party/blink/renderer/core/timing/window_performance.h
+++ b/third_party/blink/renderer/core/timing/window_performance.h
@@ -69,6 +69,8 @@
 
   bool ShouldBufferEventTiming();
 
+  bool FirstInputDetected() const { return first_input_detected_; }
+
   // This method creates a PerformanceEventTiming and if needed creates a swap
   // promise to calculate the |duration| attribute when such promise is
   // resolved.
diff --git a/third_party/blink/renderer/devtools/front_end/performance_monitor/PerformanceMonitor.js b/third_party/blink/renderer/devtools/front_end/performance_monitor/PerformanceMonitor.js
index b711eb94..f8930c7 100644
--- a/third_party/blink/renderer/devtools/front_end/performance_monitor/PerformanceMonitor.js
+++ b/third_party/blink/renderer/devtools/front_end/performance_monitor/PerformanceMonitor.js
@@ -14,7 +14,7 @@
     /** @type {!Array<!{timestamp: number, metrics: !Map<string, number>}>} */
     this._metricsBuffer = [];
     /** @const */
-    this._pixelsPerMs = 20 / 1000;
+    this._pixelsPerMs = 10 / 1000;
     /** @const */
     this._pollIntervalMs = 500;
     /** @const */
@@ -144,8 +144,9 @@
    * @param {!CanvasRenderingContext2D} ctx
    */
   _drawHorizontalGrid(ctx) {
+    const labelDistanceSeconds = 10;
     const lightGray = UI.themeSupport.patchColorText('rgba(0, 0, 0, 0.02)', UI.ThemeSupport.ColorUsage.Foreground);
-    ctx.font = '9px ' + Host.fontFamily();
+    ctx.font = '10px ' + Host.fontFamily();
     ctx.fillStyle = UI.themeSupport.patchColorText('rgba(0, 0, 0, 0.3)', UI.ThemeSupport.ColorUsage.Foreground);
     const currentTime = Date.now() / 1000;
     for (let sec = Math.ceil(currentTime);; --sec) {
@@ -155,9 +156,9 @@
       ctx.beginPath();
       ctx.moveTo(Math.round(x) + 0.5, 0);
       ctx.lineTo(Math.round(x) + 0.5, this._height);
-      if (sec >= 0 && sec % 5 === 0)
+      if (sec >= 0 && sec % labelDistanceSeconds === 0)
         ctx.fillText(new Date(sec * 1000).toLocaleTimeString(), Math.round(x) + 4, 12);
-      ctx.strokeStyle = sec % 5 ? lightGray : this._gridColor;
+      ctx.strokeStyle = sec % labelDistanceSeconds ? lightGray : this._gridColor;
       ctx.stroke();
     }
   }
diff --git a/third_party/blink/renderer/devtools/front_end/security/SecurityPanel.js b/third_party/blink/renderer/devtools/front_end/security/SecurityPanel.js
index 44f890f..13927983 100644
--- a/third_party/blink/renderer/devtools/front_end/security/SecurityPanel.js
+++ b/third_party/blink/renderer/devtools/front_end/security/SecurityPanel.js
@@ -649,6 +649,12 @@
       text.appendChild(Security.SecurityPanel.createCertificateViewerButtonForCert(
           Common.UIString('View certificate'), explanation.certificate));
     }
+
+    if (explanation.recommendations && explanation.recommendations.length) {
+      const recommendationList = text.createChild('ul', 'security-explanation-recommendations');
+      for (const recommendation of explanation.recommendations)
+        recommendationList.createChild('li').textContent = recommendation;
+    }
     return text;
   }
 
diff --git a/third_party/blink/renderer/devtools/front_end/security/mainView.css b/third_party/blink/renderer/devtools/front_end/security/mainView.css
index 03a8f32..411048d 100644
--- a/third_party/blink/renderer/devtools/front_end/security/mainView.css
+++ b/third_party/blink/renderer/devtools/front_end/security/mainView.css
@@ -173,3 +173,11 @@
 .security-mixed-content {
     margin-top: 8px;
 }
+
+.security-explanation-recommendations {
+    padding-inline-start: 16px;
+}
+
+.security-explanation-recommendations > li {
+    margin-bottom: 4px;
+}
diff --git a/third_party/blink/renderer/platform/heap/heap_test.cc b/third_party/blink/renderer/platform/heap/heap_test.cc
index 9bae406..fa987f1 100644
--- a/third_party/blink/renderer/platform/heap/heap_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_test.cc
@@ -6814,9 +6814,7 @@
   EXPECT_NE(after, before);
 }
 
-// TODO(https://crbug.com/897400): Leaks |gc_phase_| state, potentially
-// crashing later tests run in the same process.
-TEST(HeapTest, DISABLED_ShrinkVector) {
+TEST(HeapTest, ShrinkVector) {
   // Regression test: https://crbug.com/823289
 
   HeapVector<Member<IntWrapper>> vector;
diff --git a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
index f9987a3d..b30c431 100644
--- a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
+++ b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/platform/heap/heap_compact.h"
 #include "third_party/blink/renderer/platform/heap/heap_terminated_array.h"
 #include "third_party/blink/renderer/platform/heap/heap_terminated_array_builder.h"
+#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
@@ -85,6 +86,10 @@
  public:
   explicit IncrementalMarkingScopeBase(ThreadState* thread_state)
       : thread_state_(thread_state), heap_(thread_state_->Heap()) {
+    if (thread_state_->IsMarkingInProgress() ||
+        thread_state_->IsSweepingInProgress()) {
+      PreciselyCollectGarbage();
+    }
     heap_.CommitCallbackStacks();
   }
 
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index 8c89eff..b7bab87 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -223,11 +223,12 @@
             'event_util::.+',
             'media_constraints_impl::.+',
             'network_utils::.+',
+            'root_scroller_util::.+',
             'scheduler::.+',
             'touch_action_util::.+',
             'vector_math::.+',
             'xpath::.+',
-            '(xlink|xml|xmlns)_names::.+',
+            '[a-z_]+_names::.+',
 
             # Third-party libraries that don't depend on non-Blink Chrome code
             # are OK.
diff --git a/third_party/google_input_tools/README.chromium b/third_party/google_input_tools/README.chromium
index f0ec0c8..724040d 100644
--- a/third_party/google_input_tools/README.chromium
+++ b/third_party/google_input_tools/README.chromium
@@ -31,3 +31,5 @@
 
 Modified chrome/os/inputview/common.css to use Noto Serif/Sans CJK instead of
 the proprietary fonts that do not exist on Chrome OS anymore.
+
+Modified chrome/os/inputview/common.css to fix language codes and update fonts.
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/common.css b/third_party/google_input_tools/src/chrome/os/inputview/common.css
index 914ebe3..965ef2c 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/common.css
+++ b/third_party/google_input_tools/src/chrome/os/inputview/common.css
@@ -86,62 +86,62 @@
   cursor: default;
 }
 .inputview-font {
-  font-family: Roboto2, Roboto, Noto Sans, Sans-Serif;
+  font-family: Roboto, Noto Sans, Sans-Serif;
 }
 .inputview-font[lang=am] {
-  font-family: Roboto2, Roboto, Noto Sans Armenian, Sans-Serif;
+  font-family: Roboto, Noto Sans Armenian, Noto Sans Fallback, Sans-Serif;
 }
-.inputview-font[lang=il] {
-  font-family: Roboto2, Roboto, Noto Sans Hebrew, Sans-Serif;
+.inputview-font[lang=he] {
+  font-family: Roboto, Noto Sans Hebrew, Noto Sans Fallback, Sans-Serif;
 }
-.inputview-font[lang=jp] {
-  font-family: Roboto2, Roboto, Noto Sans CJK JP, Sans-Serif;
+.inputview-font[lang=ja] {
+  font-family: Roboto, Noto Sans CJK JP, Noto Sans Fallback, Sans-Serif;
 }
 .inputview-font[lang=ar] {
-  font-family: Roboto2, Roboto, Noto Arabic Naskh ui, Sans-Serif;
+  font-family: Roboto, Noto Arabic Naskh ui, Noto Sans Fallback, Sans-Serif;
 }
 .inputview-font[lang=bn] {
-  font-family: Roboto2, Roboto, Noto Sans Bengali, Sans-Serif;
+  font-family: Roboto, Noto Sans Bengali, Noto Sans Fallback, Sans-Serif;
 }
 .inputview-font[lang=ethi] {
-  font-family: Roboto2, Roboto, Noto Sans Ethiopic, Sans-Serif;
+  font-family: Roboto, Noto Sans Ethiopic, Noto Sans Fallback, Sans-Serif;
 }
 .inputview-font[lang=fa] {
-  font-family: Roboto2, Roboto, Noto Arabic Naskh ui, Sans-Serif;
+  font-family: Roboto, Noto Arabic Naskh ui, Noto Sans Fallback, Sans-Serif;
 }
 .inputview-font[lang=gu] {
-  font-family: Roboto2, Roboto, Noto Sans Gujarati, Lohit Gujarati, Sans-Serif;
+  font-family: Roboto, Noto Sans Gujarati, Lohit Gujarati, Noto Sans Fallback, Sans-Serif;
 }
 .inputview-font[lang=kn] {
-  font-family: Roboto2, Roboto, Noto Sans Kannada, Lohit Kannada, Sans-Serif;
+  font-family: Roboto, Noto Sans Kannada, Lohit Kannada, Noto Sans Fallback, Sans-Serif;
 }
 .inputview-font[lang=ml] {
-  font-family: Roboto2, Roboto, Noto Sans Malayalam, Lohit Malayalam, Sans-Serif;
+  font-family: Roboto, Noto Sans Malayalam, Lohit Malayalam, Noto Sans Fallback, Sans-Serif;
 }
 .inputview-font[lang=ta] {
-  font-family: Roboto2, Roboto, Noto Sans Tamil, Lohit Tamil, Sans-Serif;
+  font-family: Roboto, Noto Sans Tamil, Lohit Tamil, Noto Sans Fallback, Sans-Serif;
 }
 .inputview-font[lang=te] {
-  font-family: Roboto2, Roboto, Noto Sans Telugu, Lohit Telugu, Sans-Serif;
+  font-family: Roboto, Noto Sans Telugu, Lohit Telugu, Noto Sans Fallback, Sans-Serif;
 }
 .inputview-font[lang=th] {
-  font-family: Roboto2, Roboto, Noto Sans Thai, Sans-Serif;
+  font-family: Roboto, Noto Sans Thai, Noto Sans Fallback, Sans-Serif;
 }
 .inputview-font[lang=zh-CN] {
-  font-family: Roboto2, Roboto, Noto Sans CJK SC, Droid Sans Fallback, Sans-Serif;
+  font-family: Roboto, Noto Sans CJK SC, Noto Sans Fallback, Sans-Serif;
 }
 .inputview-font[lang=zh-TW],
 .inputview-font[lang=zh-HK] {
-  font-family: Roboto2, Roboto, Noto Sans CJK TC, Droid Sans Fallback, Sans-Serif;
+  font-family: Roboto, Noto Sans CJK TC, Noto Sans Fallback, Sans-Serif;
 }
-.inputview-font[lang=kr] {
-  font-family: Roboto2, Roboto, Noto Sans CJK KR, Sans-Serif;
+.inputview-font[lang=ko] {
+  font-family: Roboto, Noto Sans CJK KR, Noto Sans Fallback, Sans-Serif;
 }
 .inputview-emoji-font {
-  font-family: Noto Color Emoji, Noto Emoji, Droid Emoji;
+  font-family: Noto Color Emoji;
 }
 .inputview-emoticon {
-  font-family: Noto Color Emoji, Noto Emoji, Droid Emoji;
+  font-family: Noto Sans-Serif;
 }
 .inputview-modifier {
   display: table-cell;
@@ -849,4 +849,3 @@
   height: 17px;
   width: 16.5px;
 }
-
diff --git a/third_party/minizip/OWNERS b/third_party/minizip/OWNERS
index 77f7614..73220a8 100644
--- a/third_party/minizip/OWNERS
+++ b/third_party/minizip/OWNERS
@@ -1,2 +1 @@
-yawano@chromium.org
-mtomasz@chromium.org
+file://ui/file_manager/OWNERS
diff --git a/tools/determinism/deterministic_build_whitelist.pyl b/tools/determinism/deterministic_build_whitelist.pyl
index 412151c1..84fb67f 100644
--- a/tools/determinism/deterministic_build_whitelist.pyl
+++ b/tools/determinism/deterministic_build_whitelist.pyl
@@ -18,7 +18,10 @@
   # https://crbug.com/383340
   'android': [
     # https://crbug.com/897969
+    'apks/ChromePublicTest.apk',
     'apks/VrNfcSimulator.apk',
+    'browser_tests_apk/browser_tests-debug.apk',
+    'unit_tests_apk/unit_tests-debug.apk',
 
     # https://crbug.com/897970
     'lib.java/chromium_commands.dex.jar',
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 7cf42e7..c8f5d1fc 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -41116,6 +41116,13 @@
   <int value="7" label="ResourceLoadingHints"/>
 </enum>
 
+<enum name="PreviewsUserOmniboxAction">
+  <int value="0" label="User opted out of the preview"/>
+  <int value="1" label="User opened the page info dialog"/>
+  <int value="2" label="The Lite Page badge was displayed at commit"/>
+  <int value="3" label="The Lite Page badge was displayed at page load finish"/>
+</enum>
+
 <enum name="PreviewsUserOptedOut">
   <int value="0" label="The user did not choose to reload the full page."/>
   <int value="1" label="The user chose to reload the full page."/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 3b26df2..0adc2e5 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -11677,7 +11677,8 @@
   <summary>A bubble was given to the bubble manager but not shown.</summary>
 </histogram>
 
-<histogram name="CachedImageFetcher.Events" enum="CachedImageFetcherEvent">
+<histogram name="CachedImageFetcher.Events" enum="CachedImageFetcherEvent"
+    expires_after="2019-12-01">
   <owner>fgorski@chromium.org</owner>
   <owner>wylieb@chromium.org</owner>
   <summary>
@@ -11687,7 +11688,8 @@
   </summary>
 </histogram>
 
-<histogram name="CachedImageFetcher.ImageLoadFromCacheTime" units="ms">
+<histogram name="CachedImageFetcher.ImageLoadFromCacheTime" units="ms"
+    expires_after="2019-12-01">
   <owner>fgorski@chromium.org</owner>
   <owner>wylieb@chromium.org</owner>
   <summary>
@@ -11696,7 +11698,7 @@
 </histogram>
 
 <histogram name="CachedImageFetcher.ImageLoadFromNetworkAfterCacheHit"
-    units="ms">
+    units="ms" expires_after="2019-12-01">
   <owner>fgorski@chromium.org</owner>
   <owner>wylieb@chromium.org</owner>
   <summary>
@@ -11705,7 +11707,8 @@
   </summary>
 </histogram>
 
-<histogram name="CachedImageFetcher.ImageLoadFromNetworkTime" units="ms">
+<histogram name="CachedImageFetcher.ImageLoadFromNetworkTime" units="ms"
+    expires_after="2019-12-01">
   <owner>fgorski@chromium.org</owner>
   <owner>wylieb@chromium.org</owner>
   <summary>
@@ -11714,6 +11717,17 @@
   </summary>
 </histogram>
 
+<histogram name="CachedImageFetcher.TimeSinceLastCacheLRUEviction" units="ms"
+    expires_after="2019-12-01">
+  <owner>fgorski@chromium.org</owner>
+  <owner>wylieb@chromium.org</owner>
+  <summary>
+    The time since the last LRU eviction from the image cache. Recorded when two
+    LRU evictions occur within closure proximity to one another. Will be used to
+    determine if LRU eviction is happening too frequently.
+  </summary>
+</histogram>
+
 <histogram name="Canvas.ContextType" enum="CanvasContextType">
   <obsolete>
     Replaced with Blink.Canvas.ContextType in 10/2018.
@@ -82707,6 +82721,11 @@
   </summary>
 </histogram>
 
+<histogram name="Previews.OmniboxAction" enum="PreviewsUserOmniboxAction">
+  <owner>robertogden@chromium.org</owner>
+  <summary>User interactions with the Previews Android Omnibox UI.</summary>
+</histogram>
+
 <histogram base="true" name="Previews.OptimizationFilterStatus"
     enum="PreviewsOptimizationFilterStatus">
   <owner>dougarnett@chromium.org</owner>
@@ -132252,6 +132271,7 @@
       label="Resource loading hints based previews"/>
   <affected-histogram name="Previews.EligibilityReason"/>
   <affected-histogram name="Previews.InfoBarAction"/>
+  <affected-histogram name="Previews.OmniboxAction"/>
   <affected-histogram name="Previews.OptimizationFilterStatus"/>
   <affected-histogram name="Previews.OptOut.UserOptedOut"/>
 </histogram_suffixes>
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc
index 423d2b6..7ec601c 100644
--- a/ui/accessibility/ax_node_data.cc
+++ b/ui/accessibility/ax_node_data.cc
@@ -311,9 +311,8 @@
 
 const std::string& AXNodeData::GetStringAttribute(
     ax::mojom::StringAttribute attribute) const {
-  static base::NoDestructor<std::string> empty_string;
   auto iter = FindInVectorOfPairs(attribute, string_attributes);
-  return iter != string_attributes.end() ? iter->second : *empty_string;
+  return iter != string_attributes.end() ? iter->second : base::EmptyString();
 }
 
 bool AXNodeData::GetStringAttribute(ax::mojom::StringAttribute attribute,
@@ -352,7 +351,7 @@
 
 const std::vector<int32_t>& AXNodeData::GetIntListAttribute(
     ax::mojom::IntListAttribute attribute) const {
-  static base::NoDestructor<std::vector<int32_t>> empty_vector;
+  static const base::NoDestructor<std::vector<int32_t>> empty_vector;
   auto iter = FindInVectorOfPairs(attribute, intlist_attributes);
   if (iter != intlist_attributes.end())
     return iter->second;
@@ -378,7 +377,7 @@
 
 const std::vector<std::string>& AXNodeData::GetStringListAttribute(
     ax::mojom::StringListAttribute attribute) const {
-  static base::NoDestructor<std::vector<std::string>> empty_vector;
+  static const base::NoDestructor<std::vector<std::string>> empty_vector;
   auto iter = FindInVectorOfPairs(attribute, stringlist_attributes);
   if (iter != stringlist_attributes.end())
     return iter->second;
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
index 1e48ac6..2757628 100644
--- a/ui/accessibility/platform/ax_platform_node_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -28,7 +28,7 @@
 }
 
 const AXNodeData& AXPlatformNodeBase::GetData() const {
-  static base::NoDestructor<AXNodeData> empty_data;
+  static const base::NoDestructor<AXNodeData> empty_data;
   if (delegate_)
     return delegate_->GetData();
   return *empty_data;
@@ -190,9 +190,8 @@
 
 const std::string& AXPlatformNodeBase::GetStringAttribute(
     ax::mojom::StringAttribute attribute) const {
-  static base::NoDestructor<std::string> empty_data;
   if (!delegate_)
-    return *empty_data;
+    return base::EmptyString();
   return GetData().GetStringAttribute(attribute);
 }
 
@@ -228,7 +227,7 @@
 
 const std::vector<int32_t>& AXPlatformNodeBase::GetIntListAttribute(
     ax::mojom::IntListAttribute attribute) const {
-  static base::NoDestructor<std::vector<int32_t>> empty_data;
+  static const base::NoDestructor<std::vector<int32_t>> empty_data;
   if (!delegate_)
     return *empty_data;
   return GetData().GetIntListAttribute(attribute);
diff --git a/ui/aura/BUILD.gn b/ui/aura/BUILD.gn
index 79b1e66..1b39f76 100644
--- a/ui/aura/BUILD.gn
+++ b/ui/aura/BUILD.gn
@@ -254,6 +254,7 @@
     "test/mus/test_window_tree_client_delegate.h",
     "test/mus/test_window_tree_client_setup.cc",
     "test/mus/test_window_tree_client_setup.h",
+    "test/mus/test_window_tree_delegate.h",
     "test/mus/window_port_mus_test_helper.cc",
     "test/mus/window_port_mus_test_helper.h",
     "test/mus/window_tree_client_private.cc",
diff --git a/ui/aura/env.cc b/ui/aura/env.cc
index 05ef2c7d..1a8fd01f 100644
--- a/ui/aura/env.cc
+++ b/ui/aura/env.cc
@@ -236,8 +236,7 @@
 }
 
 WindowOcclusionTracker* Env::GetWindowOcclusionTracker() {
-  // TODO(https://crbug.com/867150): DCHECK to ensure LOCAL aura after mus
-  //     code path is wired up.
+  DCHECK_EQ(Mode::LOCAL, mode_);
   if (!window_occlusion_tracker_) {
     // Use base::WrapUnique + new because of the constructor is private.
     window_occlusion_tracker_ = base::WrapUnique(new WindowOcclusionTracker());
@@ -246,6 +245,34 @@
   return window_occlusion_tracker_.get();
 }
 
+void Env::PauseWindowOcclusionTracking() {
+  switch (mode_) {
+    case Mode::LOCAL:
+      GetWindowOcclusionTracker()->Pause();
+      break;
+    case Mode::MUS:
+      // |window_tree_client_| could be null in tests.
+      // e.g. WindowTreeClientDestructionTest.*
+      if (window_tree_client_)
+        window_tree_client_->PauseWindowOcclusionTracking();
+      break;
+  }
+}
+
+void Env::UnpauseWindowOcclusionTracking() {
+  switch (mode_) {
+    case Mode::LOCAL:
+      GetWindowOcclusionTracker()->Unpause();
+      break;
+    case Mode::MUS:
+      // |window_tree_client_| could be null in tests.
+      // e.g. WindowTreeClientDestructionTest.*
+      if (window_tree_client_)
+        window_tree_client_->UnpauseWindowOcclusionTracking();
+      break;
+  }
+}
+
 void Env::AddEventObserver(ui::EventObserver* observer,
                            ui::EventTarget* target,
                            const std::set<ui::EventType>& types) {
diff --git a/ui/aura/env.h b/ui/aura/env.h
index 0ce9495..43f3c7c 100644
--- a/ui/aura/env.h
+++ b/ui/aura/env.h
@@ -197,9 +197,15 @@
       mojo::InterfacePtr<ws::mojom::WindowTreeClient> client,
       base::OnceCallback<void(const base::UnguessableToken&)> callback);
 
-  // Get WindowOcclusionTracker instance. Create it if it is not yet created.
+  // Get WindowOcclusionTracker instance. Create one if not yet created.
   WindowOcclusionTracker* GetWindowOcclusionTracker();
 
+  // Pause/unpause window occlusion tracking. It hides the detail of where
+  // WindowOcclusionTracker lives. It calls the tracker for LOCAL aura and calls
+  // Window Service to access the tracker there for MUS aura.
+  void PauseWindowOcclusionTracking();
+  void UnpauseWindowOcclusionTracking();
+
   // Add, remove, or notify EventObservers. EventObservers are essentially
   // pre-target EventHandlers that can not modify the events nor alter dispatch.
   // On Chrome OS, observers receive system-wide events if |target| is this Env.
diff --git a/ui/aura/local/window_port_local.cc b/ui/aura/local/window_port_local.cc
index 9dcc7d8..aeb8c37e 100644
--- a/ui/aura/local/window_port_local.cc
+++ b/ui/aura/local/window_port_local.cc
@@ -15,6 +15,7 @@
 #include "ui/aura/hit_test_data_provider_aura.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
+#include "ui/aura/window_occlusion_tracker.h"
 #include "ui/base/layout.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
@@ -226,6 +227,10 @@
   return true;
 }
 
+void WindowPortLocal::TrackOcclusionState() {
+  window_->env()->GetWindowOcclusionTracker()->Track(window_);
+}
+
 void WindowPortLocal::OnFirstSurfaceActivation(
     const viz::SurfaceInfo& surface_info) {
   DCHECK_EQ(surface_info.id().frame_sink_id(), window_->GetFrameSinkId());
diff --git a/ui/aura/local/window_port_local.h b/ui/aura/local/window_port_local.h
index 7c81448..d29e186 100644
--- a/ui/aura/local/window_port_local.h
+++ b/ui/aura/local/window_port_local.h
@@ -59,6 +59,7 @@
   base::TimeTicks GetLocalSurfaceIdAllocationTime() const override;
   void OnEventTargetingPolicyChanged() override;
   bool ShouldRestackTransientChildren() override;
+  void TrackOcclusionState() override;
 
   // viz::HostFrameSinkClient:
   void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override;
diff --git a/ui/aura/mus/property_utils.cc b/ui/aura/mus/property_utils.cc
index 94546d1..d261320a 100644
--- a/ui/aura/mus/property_utils.cc
+++ b/ui/aura/mus/property_utils.cc
@@ -4,12 +4,12 @@
 
 #include "ui/aura/mus/property_utils.h"
 
+#include "base/logging.h"
 #include "services/ws/public/cpp/property_type_converters.h"
 #include "services/ws/public/mojom/window_manager.mojom.h"
 #include "services/ws/public/mojom/window_tree_constants.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/window_types.h"
-#include "ui/aura/window.h"
 
 namespace aura {
 namespace {
@@ -57,4 +57,36 @@
       mojo::ConvertTo<int32_t>(iter->second));
 }
 
+ws::mojom::OcclusionState WindowOcclusionStateToMojom(
+    Window::OcclusionState input) {
+  switch (input) {
+    case Window::OcclusionState::UNKNOWN:
+      return ws::mojom::OcclusionState::kUnknown;
+    case Window::OcclusionState::VISIBLE:
+      return ws::mojom::OcclusionState::kVisible;
+    case Window::OcclusionState::OCCLUDED:
+      return ws::mojom::OcclusionState::kOccluded;
+    case Window::OcclusionState::HIDDEN:
+      return ws::mojom::OcclusionState::kHidden;
+  }
+  NOTREACHED();
+  return ws::mojom::OcclusionState::kUnknown;
+}
+
+Window::OcclusionState WindowOcclusionStateFromMojom(
+    ws::mojom::OcclusionState input) {
+  switch (input) {
+    case ws::mojom::OcclusionState::kUnknown:
+      return Window::OcclusionState::UNKNOWN;
+    case ws::mojom::OcclusionState::kVisible:
+      return aura::Window::OcclusionState::VISIBLE;
+    case ws::mojom::OcclusionState::kOccluded:
+      return Window::OcclusionState::OCCLUDED;
+    case ws::mojom::OcclusionState::kHidden:
+      return Window::OcclusionState::HIDDEN;
+  }
+  NOTREACHED();
+  return Window::OcclusionState::UNKNOWN;
+}
+
 }  // namespace aura
diff --git a/ui/aura/mus/property_utils.h b/ui/aura/mus/property_utils.h
index eb16e2ef..0416f18 100644
--- a/ui/aura/mus/property_utils.h
+++ b/ui/aura/mus/property_utils.h
@@ -11,17 +11,17 @@
 #include <vector>
 
 #include "ui/aura/aura_export.h"
+#include "ui/aura/window.h"
 
 namespace ws {
 namespace mojom {
+enum class OcclusionState;
 enum class WindowType;
 }
 }
 
 namespace aura {
 
-class Window;
-
 // Configures the two window type properties on |window|. Specifically this
 // sets the property client::kWindowTypeKey as well as calling SetType().
 // This *must* be called before Init(). No-op for WindowType::UNKNOWN.
@@ -32,6 +32,12 @@
 AURA_EXPORT ws::mojom::WindowType GetWindowTypeFromProperties(
     const std::map<std::string, std::vector<uint8_t>>& properties);
 
+// Helpers to map Window::OcclusionState to/from its ws::mojom equivalent.
+AURA_EXPORT ws::mojom::OcclusionState WindowOcclusionStateToMojom(
+    Window::OcclusionState input);
+AURA_EXPORT Window::OcclusionState WindowOcclusionStateFromMojom(
+    ws::mojom::OcclusionState input);
+
 }  // namespace aura
 
 #endif  // UI_AURA_MUS_PROPERTY_UTILS_H_
diff --git a/ui/aura/mus/window_port_mus.cc b/ui/aura/mus/window_port_mus.cc
index 3bf4e272..dab140d 100644
--- a/ui/aura/mus/window_port_mus.cc
+++ b/ui/aura/mus/window_port_mus.cc
@@ -4,7 +4,11 @@
 
 #include "ui/aura/mus/window_port_mus.h"
 
+#include <utility>
+
 #include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/callback.h"
 #include "cc/mojo_embedder/async_layer_tree_frame_sink.h"
 #include "components/viz/client/hit_test_data_provider_draw_quad.h"
 #include "components/viz/client/local_surface_id_provider.h"
@@ -15,9 +19,9 @@
 #include "ui/aura/env.h"
 #include "ui/aura/mus/client_surface_embedder.h"
 #include "ui/aura/mus/property_converter.h"
+#include "ui/aura/mus/property_utils.h"
 #include "ui/aura/mus/window_tree_client.h"
 #include "ui/aura/mus/window_tree_client_delegate.h"
-#include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
 #include "ui/aura/window_observer.h"
 #include "ui/base/class_property.h"
@@ -37,6 +41,46 @@
 
 WindowPortMus::WindowMusChangeDataImpl::~WindowMusChangeDataImpl() = default;
 
+class WindowPortMus::VisibilityTracker : public WindowObserver {
+ public:
+  using Callback = base::RepeatingCallback<void(bool visible)>;
+  VisibilityTracker(Window* target, Callback callback)
+      : target_(target),
+        callback_(std::move(callback)),
+        last_visible_(target->IsVisible()) {
+    target_->AddObserver(this);
+  }
+
+  ~VisibilityTracker() override {
+    if (target_)
+      target_->RemoveObserver(this);
+  }
+
+ private:
+  // WindowObserver:
+  void OnWindowVisibilityChanged(Window* window, bool visible) override {
+    // Checking visibility change here instead of in OnVisibilityChanged to
+    // capture the change from window ancestors in addition to the window
+    // itself.
+    bool new_visible = target_->IsVisible();
+    if (new_visible == last_visible_)
+      return;
+
+    last_visible_ = new_visible;
+    callback_.Run(new_visible);
+  }
+  void OnWindowDestroyed(Window* window) override {
+    DCHECK_EQ(target_, window);
+    target_ = nullptr;
+  }
+
+  Window* target_;
+  Callback callback_;
+  bool last_visible_;
+
+  DISALLOW_COPY_AND_ASSIGN(VisibilityTracker);
+};
+
 // static
 WindowMus* WindowMus::Get(Window* window) {
   return WindowPortMus::Get(window);
@@ -674,6 +718,15 @@
   window_tree_client_->UnregisterFrameSinkId(this);
 }
 
+void WindowPortMus::TrackOcclusionState() {
+  // base::Unretained because |this| owns |visibility_tracker_|.
+  visibility_tracker_ = std::make_unique<VisibilityTracker>(
+      window_, base::BindRepeating(
+                   &WindowPortMus::UpdateOcclusionStateAfterVisiblityChange,
+                   base::Unretained(this)));
+  window_tree_client_->TrackOcclusionState(this);
+}
+
 void WindowPortMus::UpdatePrimarySurfaceId() {
   if (window_mus_type() != WindowMusType::LOCAL)
     return;
@@ -693,4 +746,64 @@
   client_surface_embedder_->UpdateSizeAndGutters();
 }
 
+void WindowPortMus::SetOcclusionStateFromServer(
+    ws::mojom::OcclusionState occlusion_state) {
+  const Window::OcclusionState new_state =
+      WindowOcclusionStateFromMojom(occlusion_state);
+  const Window::OcclusionState old_state = window_->occlusion_state();
+
+  if (old_state == new_state)
+    return;
+
+  // Filter HIDDEN/VISIBLE state that does not match window target visibility.
+  // This happens when the client makes visibility changes without waiting for
+  // server's occlusion state update. The stale occlusion state is still
+  // received and should be dropped to avoid unnecessary state change.
+  // e.g.
+  //   CLIENT: Hide()
+  //   CLIENT: Show()
+  //   SERVER: Receives Hide() and sends back HIDDEN
+  //   SERVER: Receives Show() and sends back VISIBLE
+  //   CLIENT: Receives HIDDEN and drops it because local state is visible.
+  //   CLIENT: Receives VISIBLE and accepts it.
+  const bool visible = window_->IsVisible();
+  if ((visible && new_state == Window::OcclusionState::HIDDEN) ||
+      (!visible && new_state == Window::OcclusionState::VISIBLE)) {
+    return;
+  }
+
+  UpdateOcclusionState(new_state);
+}
+
+void WindowPortMus::UpdateOcclusionState(Window::OcclusionState new_state) {
+  const Window::OcclusionState old_state = window_->occlusion_state();
+
+  if (new_state == Window::OcclusionState::HIDDEN &&
+      old_state != Window::OcclusionState::UNKNOWN) {
+    occlusion_state_before_hidden_ = old_state;
+  } else {
+    occlusion_state_before_hidden_.reset();
+  }
+
+  window_->SetOcclusionState(new_state);
+}
+
+void WindowPortMus::UpdateOcclusionStateAfterVisiblityChange(bool visible) {
+  DCHECK_EQ(visible, window_->IsVisible());
+
+  // No occlusion state update if |window_| is not added a root window.
+  if (!window_->GetRootWindow())
+    return;
+
+  if (!visible) {
+    // Set HIDDEN early when |window_| becomes hidden.
+    UpdateOcclusionState(Window::OcclusionState::HIDDEN);
+  } else {
+    // Restore to before-hidden state or VISIBLE when |window_| becomes visible.
+    UpdateOcclusionState(occlusion_state_before_hidden_
+                             ? occlusion_state_before_hidden_.value()
+                             : Window::OcclusionState::VISIBLE);
+  }
+}
+
 }  // namespace aura
diff --git a/ui/aura/mus/window_port_mus.h b/ui/aura/mus/window_port_mus.h
index 53ca3a3..4ba02e4 100644
--- a/ui/aura/mus/window_port_mus.h
+++ b/ui/aura/mus/window_port_mus.h
@@ -23,6 +23,7 @@
 #include "ui/aura/aura_export.h"
 #include "ui/aura/mus/mus_types.h"
 #include "ui/aura/mus/window_mus.h"
+#include "ui/aura/window.h"
 #include "ui/aura/window_port.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/platform_window/mojo/text_input_state.mojom.h"
@@ -45,7 +46,6 @@
 
 class ClientSurfaceEmbedder;
 class PropertyConverter;
-class Window;
 class WindowTreeClient;
 class WindowTreeClientPrivate;
 class WindowTreeHostMus;
@@ -205,6 +205,11 @@
     std::unique_ptr<ScopedServerChange> change;
   };
 
+  // Derived from WindowObserver to update local occlusion state. Not using
+  // OnVisibilityChanged because occlusion state is based on Window::IsVisible
+  // and needs to consider ancestors' visibility as well.
+  class VisibilityTracker;
+
   // Creates and adds a ServerChange to |server_changes_|. Returns the id
   // assigned to the ServerChange.
   ServerChangeIdType ScheduleChange(const ServerChangeType type,
@@ -298,9 +303,21 @@
   bool ShouldRestackTransientChildren() override;
   void RegisterFrameSinkId(const viz::FrameSinkId& frame_sink_id) override;
   void UnregisterFrameSinkId(const viz::FrameSinkId& frame_sink_id) override;
+  void TrackOcclusionState() override;
 
   void UpdatePrimarySurfaceId();
 
+  // Called by WindowTreeClient to update window occlusion state.
+  void SetOcclusionStateFromServer(ws::mojom::OcclusionState occlusion_state);
+
+  // Updates |window_| occlusion state to |new_state|.
+  void UpdateOcclusionState(Window::OcclusionState new_state);
+
+  // Update the local occlusion state after visibility of |window_| is changed.
+  // This is called from VisibilityTracker when window_->IsVisible changes to
+  // capture the visibility change from |window_| and its ancestors.
+  void UpdateOcclusionStateAfterVisiblityChange(bool visible);
+
   WindowTreeClient* window_tree_client_;
 
   Window* window_ = nullptr;
@@ -336,6 +353,15 @@
   // the local surface id when necessary.
   base::WeakPtr<cc::LayerTreeFrameSink> local_layer_tree_frame_sink_;
 
+  // Tracks |window_->IsVisible()| change and update local occlusion state.
+  std::unique_ptr<VisibilityTracker> visibility_tracker_;
+
+  // The occlusion state that is not UNKNOWN before changing to HIDDEN. If the
+  // value is set, it will be used when |window_| becomes visible again. This
+  // allows synchronous occlusion state change when making |window_| visible.
+  // Window Service will send back the real occlusion state later.
+  base::Optional<Window::OcclusionState> occlusion_state_before_hidden_;
+
   base::WeakPtrFactory<WindowPortMus> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(WindowPortMus);
diff --git a/ui/aura/mus/window_port_mus_unittest.cc b/ui/aura/mus/window_port_mus_unittest.cc
index 296b119..e330bf5 100644
--- a/ui/aura/mus/window_port_mus_unittest.cc
+++ b/ui/aura/mus/window_port_mus_unittest.cc
@@ -4,12 +4,15 @@
 
 #include "ui/aura/mus/window_port_mus.h"
 
+#include "base/optional.h"
+#include "base/run_loop.h"
 #include "cc/mojo_embedder/async_layer_tree_frame_sink.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/mus/client_surface_embedder.h"
 #include "ui/aura/test/aura_mus_test_base.h"
 #include "ui/aura/test/aura_test_base.h"
 #include "ui/aura/test/mus/test_window_tree.h"
+#include "ui/aura/test/mus/test_window_tree_delegate.h"
 #include "ui/aura/test/mus/window_port_mus_test_helper.h"
 #include "ui/aura/window.h"
 #include "ui/base/ui_base_features.h"
@@ -18,6 +21,43 @@
 
 using WindowPortMusTest = test::AuraMusClientTestBase;
 
+namespace {
+
+class TrackOcclusionStateCallWaiter : public TestWindowTreeDelegate {
+ public:
+  explicit TrackOcclusionStateCallWaiter(TestWindowTree* test_window_tree)
+      : test_window_tree_(test_window_tree) {
+    test_window_tree_->set_delegate(this);
+  }
+
+  ~TrackOcclusionStateCallWaiter() override {
+    test_window_tree_->set_delegate(nullptr);
+  }
+
+  // Don't wait twice since the Runloop can only be used once.
+  void Wait() { run_loop_.Run(); }
+
+  // TestWindowTreeDelegate:
+  void TrackOcclusionState(ws::Id window_id) override {
+    track_occlusion_state_received_ = true;
+    run_loop_.Quit();
+  }
+
+  const base::Optional<bool>& track_occlusion_state_call_received() const {
+    return track_occlusion_state_received_;
+  }
+
+ private:
+  base::RunLoop run_loop_;
+  TestWindowTree* const test_window_tree_;
+
+  base::Optional<bool> track_occlusion_state_received_;
+
+  DISALLOW_COPY_AND_ASSIGN(TrackOcclusionStateCallWaiter);
+};
+
+}  // namespace
+
 // TODO(sadrul): https://crbug.com/842361.
 TEST_F(WindowPortMusTest,
        DISABLED_LayerTreeFrameSinkGetsCorrectLocalSurfaceId) {
@@ -104,4 +144,57 @@
   EXPECT_EQ(updated_id, *(window_tree()->last_local_surface_id()));
 }
 
+// Tests that Window::TrackOcclusionState calls into WindowTree under mus.
+TEST_F(WindowPortMusTest, TrackOcclusionState) {
+  Window window(nullptr);
+  window.Init(ui::LAYER_NOT_DRAWN);
+  window.SetBounds(gfx::Rect(400, 300));
+
+  TrackOcclusionStateCallWaiter waiter(window_tree());
+  window.TrackOcclusionState();
+  waiter.Wait();
+  EXPECT_TRUE(waiter.track_occlusion_state_call_received() == true);
+}
+
+TEST_F(WindowPortMusTest, LocalOcclusionStateFromVisibility) {
+  Window window(nullptr);
+  window.Init(ui::LAYER_NOT_DRAWN);
+  window.set_owned_by_parent(false);
+  window.SetBounds(gfx::Rect(400, 300));
+
+  root_window()->AddChild(&window);
+  window.TrackOcclusionState();
+  ASSERT_EQ(Window::OcclusionState::HIDDEN, window.occlusion_state());
+
+  // Use a dummy waiter to disable simulated WindowOcclusionTracker behavior.
+  TrackOcclusionStateCallWaiter waiter(window_tree());
+
+  // Single window case.
+  window.Show();
+  EXPECT_EQ(Window::OcclusionState::VISIBLE, window.occlusion_state());
+
+  window.Hide();
+  EXPECT_EQ(Window::OcclusionState::HIDDEN, window.occlusion_state());
+
+  // Window has a parent.
+  Window parent(nullptr);
+  parent.Init(ui::LAYER_NOT_DRAWN);
+  parent.set_owned_by_parent(false);
+  parent.SetBounds(gfx::Rect(400, 300));
+  root_window()->AddChild(&parent);
+  parent.AddChild(&window);
+
+  // Stays HIDDEN because parent is not shown.
+  window.Show();
+  EXPECT_EQ(Window::OcclusionState::HIDDEN, window.occlusion_state());
+
+  // Changes to VISIBLE when window.IsVisible() is true.
+  parent.Show();
+  EXPECT_EQ(Window::OcclusionState::VISIBLE, window.occlusion_state());
+
+  // Changes to HIDDEN when parent hides.
+  parent.Hide();
+  EXPECT_EQ(Window::OcclusionState::HIDDEN, window.occlusion_state());
+}
+
 }  // namespace aura
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index 4e7bcc3..aaa2a3e 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -259,6 +259,21 @@
   tree_->SetHitTestInsets(window->server_id(), mouse, touch);
 }
 
+void WindowTreeClient::TrackOcclusionState(WindowMus* window) {
+  DCHECK(tree_);
+  tree_->TrackOcclusionState(window->server_id());
+}
+
+void WindowTreeClient::PauseWindowOcclusionTracking() {
+  DCHECK(tree_);
+  tree_->PauseWindowOcclusionTracking();
+}
+
+void WindowTreeClient::UnpauseWindowOcclusionTracking() {
+  DCHECK(tree_);
+  tree_->UnpauseWindowOcclusionTracking();
+}
+
 void WindowTreeClient::RegisterFrameSinkId(
     WindowMus* window,
     const viz::FrameSinkId& frame_sink_id) {
@@ -1475,6 +1490,17 @@
   screen_provider_observer_binding_.Bind(std::move(observer));
 }
 
+void WindowTreeClient::OnOcclusionStateChanged(
+    ws::Id window_id,
+    ws::mojom::OcclusionState occlusion_state) {
+  WindowMus* window = GetWindowByServerId(window_id);
+  if (!window)
+    return;
+
+  WindowPortMus::Get(window->GetWindow())
+      ->SetOcclusionStateFromServer(occlusion_state);
+}
+
 void WindowTreeClient::OnDisplaysChanged(
     std::vector<ws::mojom::WsDisplayPtr> ws_displays,
     int64_t primary_display_id,
diff --git a/ui/aura/mus/window_tree_client.h b/ui/aura/mus/window_tree_client.h
index f7480ed..f9b21f5 100644
--- a/ui/aura/mus/window_tree_client.h
+++ b/ui/aura/mus/window_tree_client.h
@@ -141,6 +141,9 @@
   void SetHitTestInsets(WindowMus* window,
                         const gfx::Insets& mouse,
                         const gfx::Insets& touch);
+  void TrackOcclusionState(WindowMus* window);
+  void PauseWindowOcclusionTracking();
+  void UnpauseWindowOcclusionTracking();
 
   // See WindowPort for details on these.
   void RegisterFrameSinkId(WindowMus* window,
@@ -450,6 +453,9 @@
   void RequestClose(ws::Id window_id) override;
   void GetScreenProviderObserver(
       ws::mojom::ScreenProviderObserverAssociatedRequest observer) override;
+  void OnOcclusionStateChanged(
+      ws::Id window_id,
+      ws::mojom::OcclusionState occlusion_state) override;
 
   // ws::mojom::ScreenProviderObserver:
   void OnDisplaysChanged(std::vector<ws::mojom::WsDisplayPtr> ws_displays,
diff --git a/ui/aura/mus/window_tree_client_unittest.cc b/ui/aura/mus/window_tree_client_unittest.cc
index 26b24bd..d4a538a 100644
--- a/ui/aura/mus/window_tree_client_unittest.cc
+++ b/ui/aura/mus/window_tree_client_unittest.cc
@@ -2607,4 +2607,54 @@
   EXPECT_TRUE(last_result);
 }
 
+// Verifies occlusion state from server is applied to underlying window.
+TEST_F(WindowTreeClientTest, OcclusionStateFromServer) {
+  struct {
+    const char* name;
+    bool window_is_visible;
+    ws::mojom::OcclusionState changed_state_from_server;
+    Window::OcclusionState expected_state;
+  } kTestCases[] = {
+      // VISIBLE is set when window is visible.
+      {"visible-set", true, ws::mojom::OcclusionState::kVisible,
+       Window::OcclusionState::VISIBLE},
+      // VISIBLE is not set when window hidden.
+      {"visible-not-set", false, ws::mojom::OcclusionState::kVisible,
+       Window::OcclusionState::HIDDEN},
+
+      // OCCLUDED is always set.
+      {"occluded-with-visible", true, ws::mojom::OcclusionState::kOccluded,
+       Window::OcclusionState::OCCLUDED},
+      {"occluded-with-invisible", false, ws::mojom::OcclusionState::kOccluded,
+       Window::OcclusionState::OCCLUDED},
+
+      // HIDDEN is set when window target visibility is false.
+      {"hidden-set", false, ws::mojom::OcclusionState::kHidden,
+       Window::OcclusionState::HIDDEN},
+      // HIDDEN is not set when window target visibility is true.
+      {"hidden-not-set", true, ws::mojom::OcclusionState::kHidden,
+       Window::OcclusionState::VISIBLE},
+  };
+
+  for (const auto& test : kTestCases) {
+    Window window(nullptr);
+    window.Init(ui::LAYER_NOT_DRAWN);
+    window.set_owned_by_parent(false);
+    root_window()->AddChild(&window);
+
+    window.TrackOcclusionState();
+    ASSERT_EQ(Window::OcclusionState::HIDDEN, window.occlusion_state());
+
+    if (test.window_is_visible && !window.IsVisible()) {
+      window.Show();
+    } else if (!test.window_is_visible && window.IsVisible()) {
+      window.Hide();
+    }
+
+    window_tree_client()->OnOcclusionStateChanged(
+        server_id(&window), test.changed_state_from_server);
+    EXPECT_EQ(test.expected_state, window.occlusion_state()) << test.name;
+  }
+}
+
 }  // namespace aura
diff --git a/ui/aura/test/mus/test_window_tree.cc b/ui/aura/test/mus/test_window_tree.cc
index 85c1df6..8afc070 100644
--- a/ui/aura/test/mus/test_window_tree.cc
+++ b/ui/aura/test/mus/test_window_tree.cc
@@ -8,12 +8,13 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/test/mus/test_window_tree_delegate.h"
 
 namespace aura {
 
-TestWindowTree::TestWindowTree() {}
+TestWindowTree::TestWindowTree() = default;
 
-TestWindowTree::~TestWindowTree() {}
+TestWindowTree::~TestWindowTree() = default;
 
 bool TestWindowTree::WasEventAcked(uint32_t event_id) const {
   for (const AckedEvent& acked_event : acked_events_) {
@@ -410,4 +411,21 @@
   last_transfer_should_cancel_ = should_cancel;
 }
 
+void TestWindowTree::TrackOcclusionState(ws::Id window_id) {
+  DCHECK(delegate_);
+  delegate_->TrackOcclusionState(window_id);
+}
+
+void TestWindowTree::PauseWindowOcclusionTracking() {
+  // |delegate_| could reset during shutdown.
+  if (delegate_)
+    delegate_->PauseWindowOcclusionTracking();
+}
+
+void TestWindowTree::UnpauseWindowOcclusionTracking() {
+  // |delegate_| could reset during shutdown.
+  if (delegate_)
+    delegate_->UnpauseWindowOcclusionTracking();
+}
+
 }  // namespace aura
diff --git a/ui/aura/test/mus/test_window_tree.h b/ui/aura/test/mus/test_window_tree.h
index 7463b841..4b50337 100644
--- a/ui/aura/test/mus/test_window_tree.h
+++ b/ui/aura/test/mus/test_window_tree.h
@@ -16,6 +16,8 @@
 
 namespace aura {
 
+class TestWindowTreeDelegate;
+
 enum class WindowTreeChangeType {
   ADD_TRANSIENT,
   BOUNDS,
@@ -50,6 +52,8 @@
 
   void set_client(ws::mojom::WindowTreeClient* client) { client_ = client; }
 
+  void set_delegate(TestWindowTreeDelegate* delegate) { delegate_ = delegate; }
+
   uint32_t window_id() const { return window_id_; }
 
   bool WasEventAcked(uint32_t event_id) const;
@@ -262,6 +266,9 @@
   void TransferGestureEventsTo(ws::Id current_id,
                                ws::Id new_id,
                                bool should_cancel) override;
+  void TrackOcclusionState(ws::Id window_id) override;
+  void PauseWindowOcclusionTracking() override;
+  void UnpauseWindowOcclusionTracking() override;
 
   struct AckedEvent {
     uint32_t event_id;
@@ -274,7 +281,7 @@
 
   std::vector<Change> changes_;
 
-  ws::mojom::WindowTreeClient* client_;
+  ws::mojom::WindowTreeClient* client_ = nullptr;
 
   base::Optional<base::flat_map<std::string, std::vector<uint8_t>>>
       last_new_window_properties_;
@@ -299,6 +306,8 @@
   // Support only one scheduled embed in test.
   base::UnguessableToken scheduled_embed_;
 
+  TestWindowTreeDelegate* delegate_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(TestWindowTree);
 };
 
diff --git a/ui/aura/test/mus/test_window_tree_client_setup.cc b/ui/aura/test/mus/test_window_tree_client_setup.cc
index de1c3fd8..7474a2e 100644
--- a/ui/aura/test/mus/test_window_tree_client_setup.cc
+++ b/ui/aura/test/mus/test_window_tree_client_setup.cc
@@ -6,13 +6,18 @@
 
 #include "ui/aura/test/mus/test_window_tree.h"
 #include "ui/aura/test/mus/window_tree_client_private.h"
+#include "ui/aura/test/window_occlusion_tracker_test_api.h"
+#include "ui/aura/window_occlusion_tracker.h"
 #include "ui/display/display.h"
 
 namespace aura {
 
-TestWindowTreeClientSetup::TestWindowTreeClientSetup() {}
+TestWindowTreeClientSetup::TestWindowTreeClientSetup() = default;
 
-TestWindowTreeClientSetup::~TestWindowTreeClientSetup() {}
+TestWindowTreeClientSetup::~TestWindowTreeClientSetup() {
+  if (window_tree_)
+    window_tree_->set_delegate(nullptr);
+}
 
 void TestWindowTreeClientSetup::Init(
     WindowTreeClientDelegate* window_tree_delegate) {
@@ -41,9 +46,27 @@
 void TestWindowTreeClientSetup::CommonInit(
     WindowTreeClientDelegate* window_tree_delegate) {
   window_tree_ = std::make_unique<TestWindowTree>();
+  window_tree_->set_delegate(this);
   window_tree_client_ =
       WindowTreeClientPrivate::CreateWindowTreeClient(window_tree_delegate);
   window_tree_->set_client(window_tree_client_.get());
+
+  window_occlusion_tracker_ = test::WindowOcclusionTrackerTestApi::Create();
+}
+
+void TestWindowTreeClientSetup::TrackOcclusionState(ws::Id window_id) {
+  window_occlusion_tracker_->Track(
+      WindowTreeClientPrivate(window_tree_client_.get())
+          .GetWindowByServerId(window_id));
+}
+
+void TestWindowTreeClientSetup::PauseWindowOcclusionTracking() {
+  test::WindowOcclusionTrackerTestApi(window_occlusion_tracker_.get()).Pause();
+}
+
+void TestWindowTreeClientSetup::UnpauseWindowOcclusionTracking() {
+  test::WindowOcclusionTrackerTestApi(window_occlusion_tracker_.get())
+      .Unpause();
 }
 
 }  // namespace aura
diff --git a/ui/aura/test/mus/test_window_tree_client_setup.h b/ui/aura/test/mus/test_window_tree_client_setup.h
index c373248..5c462bc1 100644
--- a/ui/aura/test/mus/test_window_tree_client_setup.h
+++ b/ui/aura/test/mus/test_window_tree_client_setup.h
@@ -9,18 +9,20 @@
 
 #include "base/macros.h"
 #include "ui/aura/mus/window_tree_client.h"
+#include "ui/aura/test/mus/test_window_tree_delegate.h"
 
 namespace aura {
 
 class TestWindowTree;
+class WindowOcclusionTracker;
 class WindowTreeClientDelegate;
 
 // TestWindowTreeClientSetup is used to create a WindowTreeClient that is not
 // connected to mus.
-class TestWindowTreeClientSetup {
+class TestWindowTreeClientSetup : public TestWindowTreeDelegate {
  public:
   TestWindowTreeClientSetup();
-  ~TestWindowTreeClientSetup();
+  ~TestWindowTreeClientSetup() override;
 
   // Initializes the WindowTreeClient.
   void Init(WindowTreeClientDelegate* window_tree_delegate);
@@ -39,6 +41,15 @@
   // Called by both implementations of init to perform common initialization.
   void CommonInit(WindowTreeClientDelegate* window_tree_delegate);
 
+  // TestWindowTreeDelegate:
+  void TrackOcclusionState(ws::Id window_id) override;
+  void PauseWindowOcclusionTracking() override;
+  void UnpauseWindowOcclusionTracking() override;
+
+  // Provide occlusion tracking for simulated server behavior. Needs to be
+  // released after |window_tree_client_|.
+  std::unique_ptr<WindowOcclusionTracker> window_occlusion_tracker_;
+
   std::unique_ptr<TestWindowTree> window_tree_;
 
   std::unique_ptr<WindowTreeClient> window_tree_client_;
diff --git a/ui/aura/test/mus/test_window_tree_delegate.h b/ui/aura/test/mus/test_window_tree_delegate.h
new file mode 100644
index 0000000..1e83978
--- /dev/null
+++ b/ui/aura/test/mus/test_window_tree_delegate.h
@@ -0,0 +1,28 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_TEST_MUS_TEST_WINDOW_TREE_DELEGATE_H_
+#define UI_AURA_TEST_MUS_TEST_WINDOW_TREE_DELEGATE_H_
+
+#include "services/ws/common/types.h"
+
+namespace aura {
+
+// Interface to delegate operations that TestWindowTree could not perform alone.
+class TestWindowTreeDelegate {
+ public:
+  // Invoked to start tracking occlusion state of window.
+  virtual void TrackOcclusionState(ws::Id window_id) {}
+
+  // Invoked to pause/unpause window occlusion state computation.
+  virtual void PauseWindowOcclusionTracking() {}
+  virtual void UnpauseWindowOcclusionTracking() {}
+
+ protected:
+  virtual ~TestWindowTreeDelegate() = default;
+};
+
+}  // namespace aura
+
+#endif  // UI_AURA_TEST_MUS_TEST_WINDOW_TREE_DELEGATE_H_
diff --git a/ui/aura/test/window_occlusion_tracker_test_api.cc b/ui/aura/test/window_occlusion_tracker_test_api.cc
index f32330b..a2693de 100644
--- a/ui/aura/test/window_occlusion_tracker_test_api.cc
+++ b/ui/aura/test/window_occlusion_tracker_test_api.cc
@@ -4,20 +4,40 @@
 
 #include "ui/aura/test/window_occlusion_tracker_test_api.h"
 
-#include "ui/aura/env.h"
+#include "base/memory/ptr_util.h"
 #include "ui/aura/window_occlusion_tracker.h"
 
 namespace aura {
 namespace test {
 
-WindowOcclusionTrackerTestApi::WindowOcclusionTrackerTestApi(Env* env)
-    : tracker_(env->GetWindowOcclusionTracker()) {}
+WindowOcclusionTrackerTestApi::WindowOcclusionTrackerTestApi(
+    WindowOcclusionTracker* tracker)
+    : tracker_(tracker) {}
 
 WindowOcclusionTrackerTestApi::~WindowOcclusionTrackerTestApi() = default;
 
+// static
+std::unique_ptr<WindowOcclusionTracker>
+WindowOcclusionTrackerTestApi::Create() {
+  // Use base::WrapUnique + new because of the constructor is private.
+  return base::WrapUnique(new WindowOcclusionTracker());
+}
+
 int WindowOcclusionTrackerTestApi::GetNumTimesOcclusionRecomputed() const {
   return tracker_->num_times_occlusion_recomputed_;
 }
 
+void WindowOcclusionTrackerTestApi::Pause() {
+  tracker_->Pause();
+}
+
+void WindowOcclusionTrackerTestApi::Unpause() {
+  tracker_->Unpause();
+}
+
+bool WindowOcclusionTrackerTestApi::IsPaused() const {
+  return tracker_->num_pause_occlusion_tracking_;
+}
+
 }  // namespace test
 }  // namespace aura
diff --git a/ui/aura/test/window_occlusion_tracker_test_api.h b/ui/aura/test/window_occlusion_tracker_test_api.h
index 71ce75e..e49db2d 100644
--- a/ui/aura/test/window_occlusion_tracker_test_api.h
+++ b/ui/aura/test/window_occlusion_tracker_test_api.h
@@ -5,25 +5,37 @@
 #ifndef UI_AURA_TEST_WINDOW_OCCLUSION_TRACKER_TEST_API_H_
 #define UI_AURA_TEST_WINDOW_OCCLUSION_TRACKER_TEST_API_H_
 
+#include <memory>
+
 #include "base/macros.h"
 
 namespace aura {
 
-class Env;
 class WindowOcclusionTracker;
 
 namespace test {
 
 class WindowOcclusionTrackerTestApi {
  public:
-  explicit WindowOcclusionTrackerTestApi(Env* env);
+  explicit WindowOcclusionTrackerTestApi(WindowOcclusionTracker* tracker);
   ~WindowOcclusionTrackerTestApi();
 
+  // Creates a WindowOcclusionTracker for TestWindowTreeClientSetup to simulate
+  // server side behavior. In most cases, tests should NOT call this and use the
+  // instance in Env instead.
+  static std::unique_ptr<WindowOcclusionTracker> Create();
+
   // Returns the number of times that occlusion was recomputed in this process.
   int GetNumTimesOcclusionRecomputed() const;
 
+  void Pause();
+  void Unpause();
+
+  bool IsPaused() const;
+
  private:
   WindowOcclusionTracker* const tracker_;
+
   DISALLOW_COPY_AND_ASSIGN(WindowOcclusionTrackerTestApi);
 };
 
diff --git a/ui/aura/window.cc b/ui/aura/window.cc
index 31072e7..e2f4bd7bdb 100644
--- a/ui/aura/window.cc
+++ b/ui/aura/window.cc
@@ -1166,7 +1166,7 @@
 }
 
 void Window::TrackOcclusionState() {
-  env_->GetWindowOcclusionTracker()->Track(this);
+  port_->TrackOcclusionState();
 }
 
 bool Window::RequiresDoubleTapGestureEvents() const {
diff --git a/ui/aura/window.h b/ui/aura/window.h
index 79b2560..9da9d3b1 100644
--- a/ui/aura/window.h
+++ b/ui/aura/window.h
@@ -115,7 +115,9 @@
     // - It's not transparent (transparent()).
     // - It's transform, bounds and opacity aren't animated.
     // - Its combined opacity is 1 (GetCombinedOpacity()).
-    // - The type of its layer is not ui::LAYER_NOT_DRAWN.
+    // - It has content to draw. Either the type of its layer is not
+    //     ui::LAYER_NOT_DRAWN, or it is a server window hosting remote client
+    //     content in Window Service.
     //
     // TODO(fdoray): A window that clips its children shouldn't be VISIBLE just
     // because it has an animated child.
@@ -467,6 +469,7 @@
   friend class WindowOcclusionTracker;
   friend class WindowPort;
   friend class WindowPortForShutdown;
+  friend class WindowPortMus;
   friend class WindowTargeter;
   friend class test::WindowTestApi;
 
diff --git a/ui/aura/window_occlusion_tracker.cc b/ui/aura/window_occlusion_tracker.cc
index 16a1d22..035b5da7 100644
--- a/ui/aura/window_occlusion_tracker.cc
+++ b/ui/aura/window_occlusion_tracker.cc
@@ -41,17 +41,6 @@
   return false;
 }
 
-// Returns true if |window| opaquely fills its bounds. |window| must be visible.
-bool VisibleWindowIsOpaque(Window* window) {
-  DCHECK(window->IsVisible());
-  DCHECK(window->layer());
-  return !window->transparent() &&
-         window->layer()->type() != ui::LAYER_NOT_DRAWN &&
-         window->layer()->GetCombinedOpacity() == 1.0f &&
-         // For simplicity, a shaped window is not considered opaque.
-         !WindowOrParentHasShape(window);
-}
-
 // Returns the transform of |window| relative to its root.
 // |parent_transform_relative_to_root| is the transform of |window->parent()|
 // relative to its root.
@@ -109,15 +98,12 @@
 
 }  // namespace
 
-WindowOcclusionTracker::ScopedPause::ScopedPause(Env* env)
-    : tracker_(env->GetWindowOcclusionTracker()) {
-  ++tracker_->num_pause_occlusion_tracking_;
+WindowOcclusionTracker::ScopedPause::ScopedPause(Env* env) : env_(env) {
+  env_->PauseWindowOcclusionTracking();
 }
 
 WindowOcclusionTracker::ScopedPause::~ScopedPause() {
-  --tracker_->num_pause_occlusion_tracking_;
-  DCHECK_GE(tracker_->num_pause_occlusion_tracking_, 0);
-  tracker_->MaybeComputeOcclusion();
+  env_->UnpauseWindowOcclusionTracking();
 }
 
 void WindowOcclusionTracker::Track(Window* window) {
@@ -264,6 +250,25 @@
   return true;
 }
 
+bool WindowOcclusionTracker::VisibleWindowIsOpaque(Window* window) const {
+  DCHECK(window->IsVisible());
+  DCHECK(window->layer());
+  return !window->transparent() && WindowHasContent(window) &&
+         window->layer()->GetCombinedOpacity() == 1.0f &&
+         // For simplicity, a shaped window is not considered opaque.
+         !WindowOrParentHasShape(window);
+}
+
+bool WindowOcclusionTracker::WindowHasContent(Window* window) const {
+  if (window->layer()->type() != ui::LAYER_NOT_DRAWN)
+    return true;
+
+  if (window_has_content_callback_)
+    return window_has_content_callback_.Run(window);
+
+  return false;
+}
+
 void WindowOcclusionTracker::CleanupAnimatedWindows() {
   base::EraseIf(animated_windows_, [=](Window* window) {
     ui::LayerAnimator* const animator = window->layer()->GetAnimator();
@@ -395,7 +400,7 @@
       WindowIsAnimated(window)) {
     return false;
   }
-  if (!window->transparent() && window->layer()->type() != ui::LAYER_NOT_DRAWN)
+  if (!window->transparent() && WindowHasContent(window))
     return true;
   for (Window* child_window : window->children()) {
     if (WindowOrDescendantIsOpaque(child_window, true))
@@ -468,6 +473,16 @@
     AddObserverToWindowAndDescendants(child_window);
 }
 
+void WindowOcclusionTracker::Pause() {
+  ++num_pause_occlusion_tracking_;
+}
+
+void WindowOcclusionTracker::Unpause() {
+  --num_pause_occlusion_tracking_;
+  DCHECK_GE(num_pause_occlusion_tracking_, 0);
+  MaybeComputeOcclusion();
+}
+
 void WindowOcclusionTracker::OnLayerAnimationEnded(
     ui::LayerAnimationSequence* sequence) {
   CleanupAnimatedWindows();
diff --git a/ui/aura/window_occlusion_tracker.h b/ui/aura/window_occlusion_tracker.h
index 2b546a5..18183873 100644
--- a/ui/aura/window_occlusion_tracker.h
+++ b/ui/aura/window_occlusion_tracker.h
@@ -6,7 +6,9 @@
 #define UI_AURA_WINDOW_OCCLUSION_TRACKER_H_
 
 #include <memory>
+#include <utility>
 
+#include "base/callback.h"
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/macros.h"
@@ -50,19 +52,27 @@
   // that could cause window occlusion states to change occurs within the scope
   // of a ScopedPause, window occlusion state computations are delayed until all
   // ScopedPause objects have been destroyed.
+  // TODO(crbug.com/867150): Pause the tracker in Window Service under mus.
   class AURA_EXPORT ScopedPause {
    public:
     explicit ScopedPause(Env* env);
     ~ScopedPause();
 
    private:
-    WindowOcclusionTracker* const tracker_;
+    Env* const env_;
     DISALLOW_COPY_AND_ASSIGN(ScopedPause);
   };
 
   // Start tracking the occlusion state of |window|.
   void Track(Window* window);
 
+  // Set a callback to determine whether a window has content to draw in
+  // addition to layer type check (window layer type != ui::LAYER_NOT_DRAWN).
+  using WindowHasContentCallback = base::RepeatingCallback<bool(const Window*)>;
+  void set_window_has_content_callback(WindowHasContentCallback callback) {
+    window_has_content_callback_ = std::move(callback);
+  }
+
  private:
   friend class test::WindowOcclusionTrackerTestApi;
   friend class Env;
@@ -96,6 +106,13 @@
       const SkIRect* clipped_bounds,
       SkRegion* occluded_region);
 
+  // Returns true if |window| opaquely fills its bounds. |window| must be
+  // visible.
+  bool VisibleWindowIsOpaque(Window* window) const;
+
+  // Returns true if |window| has content.
+  bool WindowHasContent(Window* window) const;
+
   // Removes windows whose bounds and transform are not animated from
   // |animated_windows_|. Marks the root of those windows as dirty.
   void CleanupAnimatedWindows();
@@ -167,6 +184,10 @@
   // Add |this| to the observer list of |window| and its descendants.
   void AddObserverToWindowAndDescendants(Window* window);
 
+  // Pauses/unpauses the occlusion state computation.
+  void Pause();
+  void Unpause();
+
   // ui::LayerAnimationObserver:
   void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override;
   void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override;
@@ -222,6 +243,9 @@
   // Tracks the observed windows.
   ScopedObserver<Window, WindowObserver> window_observer_{this};
 
+  // Callback to be invoked for additional window has content check.
+  WindowHasContentCallback window_has_content_callback_;
+
   DISALLOW_COPY_AND_ASSIGN(WindowOcclusionTracker);
 };
 
diff --git a/ui/aura/window_occlusion_tracker_unittest.cc b/ui/aura/window_occlusion_tracker_unittest.cc
index ee9247f..394c0206 100644
--- a/ui/aura/window_occlusion_tracker_unittest.cc
+++ b/ui/aura/window_occlusion_tracker_unittest.cc
@@ -4,13 +4,14 @@
 
 #include "ui/aura/window_occlusion_tracker.h"
 
+#include "base/bind_helpers.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/gtest_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/env.h"
 #include "ui/aura/test/aura_test_base.h"
-#include "ui/aura/test/env_test_helper.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/test/test_windows.h"
 #include "ui/aura/test/window_occlusion_tracker_test_api.h"
@@ -88,8 +89,6 @@
   }
 
  private:
-  test::EnvTestHelper env_test_helper_;
-
   DISALLOW_COPY_AND_ASSIGN(WindowOcclusionTrackerTest);
 };
 
@@ -861,6 +860,43 @@
   delete window_b;
 }
 
+// Verify that occlusion tracking with customized WindowHasContent callback.
+TEST_F(WindowOcclusionTrackerTest, CustomizedWindowHasContent) {
+  // Create window a. Expect it to be non-occluded.
+  MockWindowDelegate* delegate_a = new MockWindowDelegate();
+  delegate_a->set_expectation(Window::OcclusionState::VISIBLE);
+  CreateTrackedWindow(delegate_a, gfx::Rect(0, 0, 10, 10));
+  EXPECT_FALSE(delegate_a->is_expecting_call());
+
+  // Create window b with layer type LAYER_NOT_DRAWN. Occlusion state of a is
+  // not changed.
+  MockWindowDelegate* delegate_b = new MockWindowDelegate();
+  Window* window_b = new Window(delegate_b);
+  delegate_b->set_window(window_b);
+  window_b->Init(ui::LAYER_NOT_DRAWN);
+  window_b->SetBounds(gfx::Rect(0, 0, 10, 10));
+  root_window()->AddChild(window_b);
+  delegate_b->set_expectation(Window::OcclusionState::HIDDEN);
+  window_b->TrackOcclusionState();
+  EXPECT_FALSE(delegate_b->is_expecting_call());
+
+  // Use customized WindowHasContent callback to mark b as opaque.
+  window_b->env()->GetWindowOcclusionTracker()->set_window_has_content_callback(
+      base::BindLambdaForTesting([window_b](const Window* window) -> bool {
+        return window == window_b;
+      }));
+
+  // Show window b to trigger a occlusion compute and window a is occluded.
+  delegate_a->set_expectation(Window::OcclusionState::OCCLUDED);
+  delegate_b->set_expectation(Window::OcclusionState::VISIBLE);
+  window_b->Show();
+  EXPECT_FALSE(delegate_a->is_expecting_call());
+  EXPECT_FALSE(delegate_b->is_expecting_call());
+
+  window_b->env()->GetWindowOcclusionTracker()->set_window_has_content_callback(
+      base::NullCallback());
+}
+
 // Verify that when a tracked window is removed and re-added to a root,
 // occlusion states are still tracked.
 TEST_F(WindowOcclusionTrackerTest, RemoveAndAddTrackedToRoot) {
@@ -1450,7 +1486,8 @@
 // Verify that if a window changes its visibility every time it is notified that
 // its occlusion state changed, a DCHECK occurs.
 TEST_F(WindowOcclusionTrackerTest, OcclusionStatesDontBecomeStable) {
-  test::WindowOcclusionTrackerTestApi test_api(root_window()->env());
+  test::WindowOcclusionTrackerTestApi test_api(
+      root_window()->env()->GetWindowOcclusionTracker());
 
   // Create 2 superposed tracked windows.
   MockWindowDelegate* delegate_a = new MockWindowDelegate();
@@ -1650,7 +1687,8 @@
 // to be recomputed.
 TEST_F(WindowOcclusionTrackerTest,
        HideWindowWithHiddenParentOnOcclusionChange) {
-  test::WindowOcclusionTrackerTestApi test_api(root_window()->env());
+  test::WindowOcclusionTrackerTestApi test_api(
+      root_window()->env()->GetWindowOcclusionTracker());
 
   auto* delegate_a = new WindowDelegateAddingAndHidingChild(this);
   delegate_a->set_expectation(Window::OcclusionState::VISIBLE);
diff --git a/ui/aura/window_port.h b/ui/aura/window_port.h
index d1d28c0..fa32ae2 100644
--- a/ui/aura/window_port.h
+++ b/ui/aura/window_port.h
@@ -136,6 +136,9 @@
   virtual void RegisterFrameSinkId(const viz::FrameSinkId& frame_sink_id) {}
   virtual void UnregisterFrameSinkId(const viz::FrameSinkId& frame_sink_id) {}
 
+  // Called to start occlusion state tracking.
+  virtual void TrackOcclusionState() {}
+
  protected:
   explicit WindowPort(Type type);
 
diff --git a/ui/base/cocoa/menu_controller.h b/ui/base/cocoa/menu_controller.h
index b525671..69bea16 100644
--- a/ui/base/cocoa/menu_controller.h
+++ b/ui/base/cocoa/menu_controller.h
@@ -38,9 +38,6 @@
 // |-initWithModel:useWithPopUpButtonCell:| or after the first call to |-menu|.
 @property(nonatomic, assign) BOOL useWithPopUpButtonCell;
 
-+ (base::string16)elideMenuTitle:(const base::string16&)title
-                         toWidth:(int)width;
-
 // NIB-based initializer. This does not create a menu. Clients can set the
 // properties of the object and the menu will be created upon the first call to
 // |-menu|. Note that the menu will be immutable after creation.
diff --git a/ui/base/cocoa/menu_controller.mm b/ui/base/cocoa/menu_controller.mm
index 54e763c..2dfefc53 100644
--- a/ui/base/cocoa/menu_controller.mm
+++ b/ui/base/cocoa/menu_controller.mm
@@ -16,7 +16,6 @@
 #import "ui/events/event_utils.h"
 #include "ui/gfx/font_list.h"
 #include "ui/gfx/image/image.h"
-#include "ui/gfx/text_elider.h"
 #include "ui/strings/grit/ui_strings.h"
 
 namespace {
@@ -96,13 +95,6 @@
 @synthesize useWithPopUpButtonCell = useWithPopUpButtonCell_;
 @synthesize postItemSelectedAsTask = postItemSelectedAsTask_;
 
-+ (base::string16)elideMenuTitle:(const base::string16&)title
-                         toWidth:(int)width {
-  NSFont* nsfont = [NSFont menuBarFontOfSize:0];  // 0 means "default"
-  return gfx::ElideText(title, gfx::FontList(gfx::Font(nsfont)), width,
-                        gfx::ELIDE_TAIL, gfx::Typesetter::NATIVE);
-}
-
 - (instancetype)init {
   self = [super init];
   return self;
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn
index fbc6b93..54001ac 100644
--- a/ui/events/BUILD.gn
+++ b/ui/events/BUILD.gn
@@ -613,6 +613,7 @@
 
     if (is_fuchsia) {
       sources += [ "fuchsia/input_event_dispatcher_unittest.cc" ]
+      deps += [ "//third_party/fuchsia-sdk/sdk:input" ]
     }
   }
 }
diff --git a/ui/events/test/event_generator.cc b/ui/events/test/event_generator.cc
index 1355604..25ba46e 100644
--- a/ui/events/test/event_generator.cc
+++ b/ui/events/test/event_generator.cc
@@ -121,7 +121,6 @@
 }
 
 EventGenerator::~EventGenerator() {
-  pending_events_.clear();
   ui::SetEventTickClockForTesting(nullptr);
 }
 
@@ -565,43 +564,6 @@
   Dispatch(&fling_start);
 }
 
-void EventGenerator::ScrollSequence(const gfx::Point& start,
-                                    const base::TimeDelta& step_delay,
-                                    const std::vector<gfx::PointF>& offsets,
-                                    int num_fingers) {
-  size_t steps = offsets.size();
-  base::TimeTicks timestamp = ui::EventTimeForNow();
-  ui::ScrollEvent fling_cancel(ui::ET_SCROLL_FLING_CANCEL,
-                               start,
-                               timestamp,
-                               0,
-                               0, 0,
-                               0, 0,
-                               num_fingers);
-  Dispatch(&fling_cancel);
-
-  for (size_t i = 0; i < steps; ++i) {
-    timestamp += step_delay;
-    ui::ScrollEvent scroll(ui::ET_SCROLL,
-                           start,
-                           timestamp,
-                           0,
-                           offsets[i].x(), offsets[i].y(),
-                           offsets[i].x(), offsets[i].y(),
-                           num_fingers);
-    Dispatch(&scroll);
-  }
-
-  ui::ScrollEvent fling_start(ui::ET_SCROLL_FLING_START,
-                              start,
-                              timestamp,
-                              0,
-                              offsets[steps - 1].x(), offsets[steps - 1].y(),
-                              offsets[steps - 1].x(), offsets[steps - 1].y(),
-                              num_fingers);
-  Dispatch(&fling_start);
-}
-
 void EventGenerator::GenerateTrackpadRest() {
   int num_fingers = 2;
   ui::ScrollEvent scroll(ui::ET_SCROLL, current_location_,
@@ -627,7 +589,19 @@
 }
 
 void EventGenerator::Dispatch(ui::Event* event) {
-  DoDispatchEvent(event, async_);
+  if (event->IsTouchEvent()) {
+    ui::TouchEvent* touch_event = static_cast<ui::TouchEvent*>(event);
+    touch_pointer_details_.id = touch_event->pointer_details().id;
+    touch_event->SetPointerDetailsForTest(touch_pointer_details_);
+  }
+
+  if (!event->handled()) {
+    ui::EventSource* event_source = delegate()->GetEventSource(current_target_);
+    ui::EventSourceTestApi event_source_test(event_source);
+    ui::EventDispatchDetails details = event_source_test.SendEventToSink(event);
+    if (details.dispatcher_destroyed)
+      current_target_ = nullptr;
+  }
 }
 
 void EventGenerator::Init(gfx::NativeWindow root_window,
@@ -722,41 +696,5 @@
   return delegate()->CenterOfTarget(window);
 }
 
-void EventGenerator::DoDispatchEvent(ui::Event* event, bool async) {
-  if (event->IsTouchEvent()) {
-    ui::TouchEvent* touch_event = static_cast<ui::TouchEvent*>(event);
-    touch_pointer_details_.id = touch_event->pointer_details().id;
-    touch_event->SetPointerDetailsForTest(touch_pointer_details_);
-  }
-
-  if (async) {
-    std::unique_ptr<ui::Event> pending_event = ui::Event::Clone(*event);
-    if (pending_events_.empty()) {
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(&EventGenerator::DispatchNextPendingEvent,
-                                    base::Unretained(this)));
-    }
-    pending_events_.push_back(std::move(pending_event));
-  } else if (!event->handled()) {
-    ui::EventSource* event_source = delegate()->GetEventSource(current_target_);
-    ui::EventSourceTestApi event_source_test(event_source);
-    ui::EventDispatchDetails details = event_source_test.SendEventToSink(event);
-    if (details.dispatcher_destroyed)
-      current_target_ = nullptr;
-  }
-}
-
-void EventGenerator::DispatchNextPendingEvent() {
-  DCHECK(!pending_events_.empty());
-  ui::Event* event = pending_events_.front().get();
-  DoDispatchEvent(event, false);
-  pending_events_.pop_front();
-  if (!pending_events_.empty()) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(&EventGenerator::DispatchNextPendingEvent,
-                                  base::Unretained(this)));
-  }
-}
-
 }  // namespace test
 }  // namespace ui
diff --git a/ui/events/test/event_generator.h b/ui/events/test/event_generator.h
index fca27dee..2f7937a 100644
--- a/ui/events/test/event_generator.h
+++ b/ui/events/test/event_generator.h
@@ -5,7 +5,6 @@
 #ifndef UI_EVENTS_TEST_EVENT_GENERATOR_H_
 #define UI_EVENTS_TEST_EVENT_GENERATOR_H_
 
-#include <list>
 #include <memory>
 #include <vector>
 
@@ -18,10 +17,6 @@
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/native_widget_types.h"
 
-namespace gfx {
-class PointF;
-}
-
 namespace ui {
 class EventSource;
 class EventTarget;
@@ -138,9 +133,6 @@
   }
   const gfx::Point& current_location() const { return current_location_; }
 
-  void set_async(bool async) { async_ = async; }
-  bool async() const { return async_; }
-
   // Events could be dispatched using different methods. The choice is a
   // tradeoff between test robustness and coverage of OS internals that affect
   // event dispatch.
@@ -411,13 +403,6 @@
                       int steps,
                       int num_fingers);
 
-  // Generates scroll sequences of a FlingCancel, Scrolls, FlingStart, sending
-  // scrolls of each of the values in |offsets|.
-  void ScrollSequence(const gfx::Point& start,
-                      const base::TimeDelta& step_delay,
-                      const std::vector<gfx::PointF>& offsets,
-                      int num_fingers);
-
   // Generate a TrackPad "rest" event. That is, a user resting fingers on the
   // trackpad without moving. This may then be followed by a ScrollSequence(),
   // or a CancelTrackpadRest().
@@ -463,9 +448,6 @@
   gfx::Point GetLocationInCurrentRoot() const;
   gfx::Point CenterOfWindow(const EventTarget* window) const;
 
-  void DispatchNextPendingEvent();
-  void DoDispatchEvent(Event* event, bool async);
-
   std::unique_ptr<EventGeneratorDelegate> delegate_;
   gfx::Point current_location_;
   EventTarget* current_target_ = nullptr;
@@ -474,11 +456,6 @@
 
   ui::PointerDetails touch_pointer_details_;
 
-  std::list<std::unique_ptr<Event>> pending_events_;
-
-  // Set to true to cause events to be posted asynchronously.
-  bool async_ = false;
-
   // Whether to skip mapping of coordinates from the root window to a hit window
   // when dispatching events.
   bool assume_window_at_origin_ = true;
diff --git a/ui/file_manager/file_manager/common/js/file_type.js b/ui/file_manager/file_manager/common/js/file_type.js
index 62ba359..a83fe6f 100644
--- a/ui/file_manager/file_manager/common/js/file_type.js
+++ b/ui/file_manager/file_manager/common/js/file_type.js
@@ -372,8 +372,9 @@
     subtype: 'Word',
     pattern: /\.(doc|docx)$/i,
     mimePattern: new RegExp(
-        '/application\\/(msword|vnd\\.openxmlformats\\-' +
-        'officedocument\\./wordprocessingml\\.document)/i')
+        'application/(msword|vnd\\.' +
+            'openxmlformats-officedocument\\.wordprocessingml\\.document)',
+        'i')
   },
   {
     type: 'document',
@@ -382,8 +383,9 @@
     subtype: 'PPT',
     pattern: /\.(ppt|pptx)$/i,
     mimePattern: new RegExp(
-        '/application\\/(vnd\\.ms-powerpoint|\\.' +
-        'openxmlformats\\-/officedocument\\.wordprocessingml\\.presentation)/i')
+        'application/vnd\\.(ms-powerpoint|' +
+            'openxmlformats-officedocument\\.presentationml\\.presentation)',
+        'i')
   },
   {
     type: 'document',
@@ -392,8 +394,9 @@
     subtype: 'Excel',
     pattern: /\.(xls|xlsx)$/i,
     mimePattern: new RegExp(
-        '/application\\/(vnd\\.ms-excel|\\.' +
-        'openxmlformats\\-/officedocument\\.wordprocessingml\\.sheet)/i')
+        'application/vnd\\.(ms-excel|' +
+            'openxmlformats-officedocument\\.spreadsheetml\\.sheet)',
+        'i')
   }
 ];
 
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js
index 6ca5439..c2baf6c 100644
--- a/ui/file_manager/file_manager/common/js/util.js
+++ b/ui/file_manager/file_manager/common/js/util.js
@@ -1395,21 +1395,6 @@
 };
 
 /**
- * Returns if the My Files navigation should be disabled.
- * @return {!Promise<boolean>} Resolves with true if flag
- * "disable-my-files-navigation" is set to true.
- */
-util.isMyFilesNavigationDisabled = function() {
-  return new Promise(resolve => {
-    chrome.commandLinePrivate.hasSwitch(
-        'disable-my-files-navigation', isDisabled => {
-          resolve(isDisabled);
-        });
-  });
-};
-
-
-/**
  * Retrieves all entries inside the given |rootEntry|.
  * @param {!DirectoryEntry} rootEntry
  * @param {function(!Array<!Entry>)} entriesCallback Called when some chunk of
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index 4c80976..f23e2716 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -517,11 +517,8 @@
     return Promise
         .all([
           this.appStateController_.loadInitialViewOptions(),
-          util.isMyFilesNavigationDisabled(),
         ])
         .then(values => {
-          this.commandLineFlags_['disable-my-files-navigation'] =
-              /** @type {boolean} */ (values[1]);
           metrics.recordInterval('Load.InitSettings');
         });
   };
@@ -1209,8 +1206,7 @@
                     str('RECENT_ROOT_LABEL'),
                     VolumeManagerCommon.RootType.RECENT,
                     this.getSourceRestriction_())) :
-            null,
-        this.commandLineFlags_['disable-my-files-navigation']);
+            null);
     this.setupCrostini_();
     this.ui_.initDirectoryTree(directoryTree);
   };
diff --git a/ui/file_manager/file_manager/foreground/js/navigation_list_model.js b/ui/file_manager/file_manager/foreground/js/navigation_list_model.js
index 5a8b4fc..e68ab47a 100644
--- a/ui/file_manager/file_manager/foreground/js/navigation_list_model.js
+++ b/ui/file_manager/file_manager/foreground/js/navigation_list_model.js
@@ -146,15 +146,11 @@
  * @param {(!cr.ui.ArrayDataModel|!FolderShortcutsDataModel)} shortcutListModel
  *     The list of folder shortcut.
  * @param {NavigationModelFakeItem} recentModelItem Recent folder.
- * @param {boolean=} opt_disableMyFilesNavigation true if should use the new
- *     navigation style, value should come from flag
- *     disable-my-files-navigation.
  * @constructor
  * @extends {cr.EventTarget}
  */
 function NavigationListModel(
-    volumeManager, shortcutListModel, recentModelItem,
-    opt_disableMyFilesNavigation) {
+    volumeManager, shortcutListModel, recentModelItem) {
   cr.EventTarget.call(this);
 
   /**
@@ -234,9 +230,6 @@
     this.shortcutList_.push(entryToModelItem(shortcutEntry));
   }
 
-  // True if the flag disable-my-files-navigation is enabled.
-  this.disableMyFilesNavigation_ = !!opt_disableMyFilesNavigation;
-
   // Reorder volumes, shortcuts, and optional items for initial display.
   this.reorderNavigationItems_();
 
@@ -381,10 +374,6 @@
     this.linuxFilesItem_ = item;
     this.reorderNavigationItems_();
   },
-  /** @type {boolean} */
-  get disableMyFilesNavigation() {
-    return this.disableMyFilesNavigation_;
-  }
 };
 
 /**
@@ -393,11 +382,7 @@
  * it's disabled it has a flat structure with Linux files after Recent menu.
  */
 NavigationListModel.prototype.reorderNavigationItems_ = function() {
-  if (!this.disableMyFilesNavigation_) {
-    return this.orderAndNestItems_();
-  } else {
-    return this.flatNavigationItems_();
-  }
+  return this.orderAndNestItems_();
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js b/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js
index 245ded7..1103dbe 100644
--- a/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.js
@@ -42,18 +42,30 @@
   var recentItem = new NavigationModelFakeItem(
       'recent-label', NavigationModelItemType.RECENT,
       {toURL: () => 'fake-entry://recent'});
-  var model = new NavigationListModel(
-      volumeManager, shortcutListModel, recentItem, true);
+  var model =
+      new NavigationListModel(volumeManager, shortcutListModel, recentItem);
   model.linuxFilesItem = new NavigationModelFakeItem(
       'linux-files-label', NavigationModelItemType.CROSTINI,
-      {toURL: () => 'fake-entry://linux-files'});
+      new FakeEntry(
+          'linux-files-label', VolumeManagerCommon.RootType.CROSTINI));
 
-  assertEquals(5, model.length);
-  assertEquals('drive', model.item(0).volumeInfo.volumeId);
-  assertEquals('downloads', model.item(1).volumeInfo.volumeId);
-  assertEquals('fake-entry://recent', model.item(2).entry.toURL());
-  assertEquals('fake-entry://linux-files', model.item(3).entry.toURL());
-  assertEquals('/root/shortcut', model.item(4).entry.fullPath);
+  assertEquals(4, model.length);
+  console.log(model.item(0).label);
+  console.log(model.item(1).label);
+  console.log(model.item(2).label);
+  console.log(model.item(3).label);
+  assertEquals('fake-entry://recent', model.item(0).entry.toURL());
+  assertEquals('/root/shortcut', model.item(1).entry.fullPath);
+  assertEquals('My files', model.item(2).label);
+  assertEquals('drive', model.item(3).volumeInfo.volumeId);
+
+  // Downloads and Crostini are displayed within My files.
+  const myFilesEntry = model.item(2).entry;
+  console.log(myFilesEntry.children[0].name);
+  console.log(myFilesEntry.children[1].name);
+  assertEquals(2, myFilesEntry.children.length);
+  assertEquals('Downloads', myFilesEntry.children[0].name);
+  assertEquals('linux-files-label', myFilesEntry.children[1].name);
 }
 
 function testNoRecentOrLinuxFiles() {
@@ -61,13 +73,13 @@
   var shortcutListModel = new MockFolderShortcutDataModel(
       [new MockFileEntry(drive, '/root/shortcut')]);
   var recentItem = null;
-  var model = new NavigationListModel(
-      volumeManager, shortcutListModel, recentItem, true);
+  var model =
+      new NavigationListModel(volumeManager, shortcutListModel, recentItem);
 
   assertEquals(3, model.length);
-  assertEquals('drive', model.item(0).volumeInfo.volumeId);
-  assertEquals('downloads', model.item(1).volumeInfo.volumeId);
-  assertEquals('/root/shortcut', model.item(2).entry.fullPath);
+  assertEquals('/root/shortcut', model.item(0).entry.fullPath);
+  assertEquals('My files', model.item(1).label);
+  assertEquals('drive', model.item(2).volumeInfo.volumeId);
 }
 
 function testAddAndRemoveShortcuts() {
@@ -75,33 +87,34 @@
   var shortcutListModel = new MockFolderShortcutDataModel(
       [new MockFileEntry(drive, '/root/shortcut')]);
   var recentItem = null;
-  var model = new NavigationListModel(
-      volumeManager, shortcutListModel, recentItem, true);
+  var model =
+      new NavigationListModel(volumeManager, shortcutListModel, recentItem);
 
   assertEquals(3, model.length);
 
-  // Add a shortcut at the tail.
+  // Add a shortcut at the tail, shortcuts are sorted by their label.
   shortcutListModel.splice(1, 0, new MockFileEntry(drive, '/root/shortcut2'));
   assertEquals(4, model.length);
-  assertEquals('/root/shortcut2', model.item(3).entry.fullPath);
+  assertEquals('shortcut', model.item(0).label);
+  assertEquals('shortcut2', model.item(1).label);
 
   // Add a shortcut at the head.
   shortcutListModel.splice(0, 0, new MockFileEntry(drive, '/root/hoge'));
   assertEquals(5, model.length);
-  assertEquals('/root/hoge', model.item(2).entry.fullPath);
-  assertEquals('/root/shortcut', model.item(3).entry.fullPath);
-  assertEquals('/root/shortcut2', model.item(4).entry.fullPath);
+  assertEquals('hoge', model.item(0).label);
+  assertEquals('shortcut', model.item(1).label);
+  assertEquals('shortcut2', model.item(2).label);
 
   // Remove the last shortcut.
   shortcutListModel.splice(2, 1);
   assertEquals(4, model.length);
-  assertEquals('/root/hoge', model.item(2).entry.fullPath);
-  assertEquals('/root/shortcut', model.item(3).entry.fullPath);
+  assertEquals('hoge', model.item(0).label);
+  assertEquals('shortcut', model.item(1).label);
 
   // Remove the first shortcut.
   shortcutListModel.splice(0, 1);
   assertEquals(3, model.length);
-  assertEquals('/root/shortcut', model.item(2).entry.fullPath);
+  assertEquals('shortcut', model.item(0).label);
 }
 
 function testAddAndRemoveVolumes() {
@@ -109,8 +122,8 @@
   var shortcutListModel = new MockFolderShortcutDataModel(
       [new MockFileEntry(drive, '/root/shortcut')]);
   var recentItem = null;
-  var model = new NavigationListModel(
-      volumeManager, shortcutListModel, recentItem, true);
+  var model =
+      new NavigationListModel(volumeManager, shortcutListModel, recentItem);
 
   assertEquals(3, model.length);
 
@@ -118,31 +131,31 @@
   volumeManager.volumeInfoList.add(MockVolumeManager.createMockVolumeInfo(
       VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:hoge'));
   assertEquals(4, model.length);
-  assertEquals('drive', model.item(0).volumeInfo.volumeId);
-  assertEquals('downloads', model.item(1).volumeInfo.volumeId);
-  assertEquals('removable:hoge', model.item(2).volumeInfo.volumeId);
-  assertEquals('/root/shortcut', model.item(3).entry.fullPath);
+  assertEquals('/root/shortcut', model.item(0).entry.fullPath);
+  assertEquals('My files', model.item(1).label);
+  assertEquals('drive', model.item(2).volumeInfo.volumeId);
+  assertEquals('removable:hoge', model.item(3).volumeInfo.volumeId);
 
   // Removable volume 'fuga' is mounted.
   volumeManager.volumeInfoList.add(MockVolumeManager.createMockVolumeInfo(
       VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:fuga'));
   assertEquals(5, model.length);
-  assertEquals('drive', model.item(0).volumeInfo.volumeId);
-  assertEquals('downloads', model.item(1).volumeInfo.volumeId);
-  assertEquals('removable:hoge', model.item(2).volumeInfo.volumeId);
-  assertEquals('removable:fuga', model.item(3).volumeInfo.volumeId);
-  assertEquals('/root/shortcut', model.item(4).entry.fullPath);
+  assertEquals('/root/shortcut', model.item(0).entry.fullPath);
+  assertEquals('My files', model.item(1).label);
+  assertEquals('drive', model.item(2).volumeInfo.volumeId);
+  assertEquals('removable:hoge', model.item(3).volumeInfo.volumeId);
+  assertEquals('removable:fuga', model.item(4).volumeInfo.volumeId);
 
   // A shortcut is created on the 'hoge' volume.
   shortcutListModel.splice(
       1, 0, new MockFileEntry(hoge, '/shortcut2'));
   assertEquals(6, model.length);
-  assertEquals('drive', model.item(0).volumeInfo.volumeId);
-  assertEquals('downloads', model.item(1).volumeInfo.volumeId);
-  assertEquals('removable:hoge', model.item(2).volumeInfo.volumeId);
-  assertEquals('removable:fuga', model.item(3).volumeInfo.volumeId);
-  assertEquals('/root/shortcut', model.item(4).entry.fullPath);
-  assertEquals('/shortcut2', model.item(5).entry.fullPath);
+  assertEquals('/root/shortcut', model.item(0).entry.fullPath);
+  assertEquals('/shortcut2', model.item(1).entry.fullPath);
+  assertEquals('My files', model.item(2).label);
+  assertEquals('drive', model.item(3).volumeInfo.volumeId);
+  assertEquals('removable:hoge', model.item(4).volumeInfo.volumeId);
+  assertEquals('removable:fuga', model.item(5).volumeInfo.volumeId);
 }
 
 /**
@@ -211,8 +224,8 @@
   // 15.  provided:"zip" - mounted as provided: $zipVolumeId
 
   // Constructor already calls orderAndNestItems_.
-  const model = new NavigationListModel(
-      volumeManager, shortcutListModel, recentItem, false);
+  const model =
+      new NavigationListModel(volumeManager, shortcutListModel, recentItem);
 
   // Check items order and that MTP/Archive/Removable respect the original
   // order.
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
index 99887e59..a1eae49b 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -500,6 +500,17 @@
 };
 
 /**
+ * Default sorting for DirectoryItem sub-dirrectories.
+ * @param {!Array<!Entry>} entries Entries to be sorted.
+ * @returns {!Array<!Entry>}
+ */
+DirectoryItem.prototype.sortEntries = function(entries) {
+  entries.sort(util.compareName);
+  const filter = this.fileFilter_.filter.bind(this.fileFilter_);
+  return entries.filter(filter);
+};
+
+/**
  * Retrieves the latest subdirectories and update them on the tree.
  * @param {boolean} recursive True if the update is recursively.
  * @param {function()=} opt_successCallback Callback called on success.
@@ -507,40 +518,34 @@
  */
 DirectoryItem.prototype.updateSubDirectories = function(
     recursive, opt_successCallback, opt_errorCallback) {
-  if (!this.entry || util.isFakeEntry(this.entry)) {
-    if (opt_errorCallback)
-      opt_errorCallback();
+  if (!this.entry || this.entry.createReader === undefined) {
+    opt_errorCallback && opt_errorCallback();
     return;
   }
 
-  var sortEntries = function(fileFilter, entries) {
-    entries.sort(util.compareName);
-    return entries.filter(fileFilter.filter.bind(fileFilter));
-  };
-
-  var onSuccess = function(entries) {
+  const onSuccess = (entries) => {
     this.entries_ = entries;
     this.updateSubElementsFromList(recursive);
     opt_successCallback && opt_successCallback();
-  }.bind(this);
+  };
 
-  var reader = this.entry.createReader();
-  var entries = [];
-  var readEntry = function() {
-    reader.readEntries(function(results) {
+  const reader = this.entry.createReader();
+  const entries = [];
+  const readEntry = () => {
+    reader.readEntries((results) => {
       if (!results.length) {
-        onSuccess(sortEntries(this.fileFilter_, entries));
+        onSuccess(this.sortEntries(entries));
         return;
       }
 
-      for (var i = 0; i < results.length; i++) {
-        var entry = results[i];
+      for (let i = 0; i < results.length; i++) {
+        const entry = results[i];
         if (entry.isDirectory)
           entries.push(entry);
       }
       readEntry();
-    }.bind(this));
-  }.bind(this);
+    });
+  };
   readEntry();
 };
 
@@ -761,26 +766,42 @@
  */
 EntryListItem.prototype.updateSubDirectories = function(
     recursive, opt_successCallback, opt_errorCallback) {
-  if (!this.entry) {
+  if (!this.entry || this.entry.createReader === undefined) {
     opt_errorCallback && opt_errorCallback();
     return;
   }
   this.entries_ = [];
-  if (this.entry && this.entry.children) {
-    for (let childEntry of this.entry.children) {
-      if (childEntry instanceof VolumeEntry) {
-        // For VolumeEntry we want to display its root.
-        this.entries_.push(childEntry.rootEntry);
-      } else {
-        this.entries_.push(childEntry);
+
+  const onSuccess = (entries) => {
+    this.entries_ = entries;
+    this.updateSubElementsFromList(recursive);
+    opt_successCallback && opt_successCallback();
+  };
+
+  const reader = this.entry.createReader();
+  const entries = [];
+  const readEntry = () => {
+    reader.readEntries((results) => {
+      if (!results.length) {
+        onSuccess(this.sortEntries(entries));
+        return;
       }
-    }
-  }
-  if (this.entries_.length > 0) {
-    this.expanded = true;
-  }
-  this.updateSubElementsFromList(recursive);
-  opt_successCallback && opt_successCallback();
+
+      for (let i = 0; i < results.length; i++) {
+        const entry = results[i];
+        if (entry.isDirectory) {
+          // For VolumeEntry we want to display its root.
+          if (entry instanceof VolumeEntry) {
+            entries.push(entry.rootEntry);
+          } else {
+            entries.push(entry);
+          }
+        }
+      }
+      readEntry();
+    });
+  };
+  readEntry();
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1235,10 +1256,6 @@
       item.updateSubDirectories(false);
     }
   }
-  // When My files is disabled Drive should be expanded by default.
-  // TODO(crbug.com/850348): Remove this once flag is removed.
-  if (this.parentTree_.dataModel.disableMyFilesNavigation)
-    this.expanded = true;
 };
 
 /**
diff --git a/ui/file_manager/integration_tests/file_manager/create_new_folder.js b/ui/file_manager/integration_tests/file_manager/create_new_folder.js
index b5baae1a..82ed1176 100644
--- a/ui/file_manager/integration_tests/file_manager/create_new_folder.js
+++ b/ui/file_manager/integration_tests/file_manager/create_new_folder.js
@@ -210,7 +210,7 @@
           })
           .then(function() {
             return remoteCall.navigateWithDirectoryTree(
-                appId, '/photos', 'Downloads');
+                appId, '/photos', 'My files/Downloads');
           })
           .then(function() {
             return remoteCall.waitForFiles(
diff --git a/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js b/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
index 597387a8..94284f3 100644
--- a/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
+++ b/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
@@ -76,7 +76,7 @@
 function navigateToDestinationDirectoryAndTestPaste(windowId) {
   // Navigates to destination directory.
   return remoteCall
-      .navigateWithDirectoryTree(windowId, '/destination', 'Downloads')
+      .navigateWithDirectoryTree(windowId, '/destination', 'My files/Downloads')
       .then(function() {
         // Confirm files before paste.
         return remoteCall.waitForFiles(
@@ -127,13 +127,13 @@
   return setupForDirectoryTreeContextMenuTest().then(function(id) {
     windowId = id;
     return remoteCall.navigateWithDirectoryTree(
-        windowId, '/photos', 'Downloads');
+        windowId, '/photos', 'My files/Downloads');
   }).then(function() {
     return renamePhotosDirectoryTo(windowId, 'New photos', useKeyboardShortcut);
   }).then(function() {
     // Confirm that current directory has moved to new folder.
     return remoteCall.waitUntilCurrentDirectoryIsChanged(
-        windowId, '/Downloads/New photos');
+        windowId, '/My files/Downloads/New photos');
   });
 }
 
@@ -145,7 +145,7 @@
   return setupForDirectoryTreeContextMenuTest().then(function(id) {
     windowId = id;
     return remoteCall.navigateWithDirectoryTree(
-        windowId, '/photos', 'Downloads');
+        windowId, '/photos', 'My files/Downloads');
   }).then(function() {
     return renamePhotosDirectoryTo(windowId, newName, false);
   }).then(function() {
@@ -165,7 +165,7 @@
 
     if (changeCurrentDirectory)
       return remoteCall.navigateWithDirectoryTree(
-          windowId, '/photos', 'Downloads');
+          windowId, '/photos', 'My files/Downloads');
     else
       return remoteCall.expandDownloadVolumeInDirectoryTree(windowId);
   }).then(function() {
@@ -192,11 +192,13 @@
   }).then(function() {
     // Confirm that current directory is not changed at this timing.
     return remoteCall.waitUntilCurrentDirectoryIsChanged(
-        windowId, changeCurrentDirectory ? '/Downloads/photos' : '/Downloads');
+        windowId,
+        changeCurrentDirectory ? '/My files/Downloads/photos' :
+                                 '/My files/Downloads');
   }).then(function() {
     // Confirm that new directory is actually created by navigating to it.
     return remoteCall.navigateWithDirectoryTree(
-        windowId, '/photos/test', 'Downloads');
+        windowId, '/photos/test', 'My files/Downloads');
   });
 }
 
@@ -208,7 +210,7 @@
   testPromise(setupForDirectoryTreeContextMenuTest().then(function(id) {
     windowId = id;
     return remoteCall.navigateWithDirectoryTree(
-        windowId, '/photos', 'Downloads');
+        windowId, '/photos', 'My files/Downloads');
   }).then(function() {
     return clickDirectoryTreeContextMenuItem(windowId, '/photos', 'copy');
   }).then(function() {
@@ -224,7 +226,7 @@
   testPromise(setupForDirectoryTreeContextMenuTest().then(function(id) {
     windowId = id;
     return remoteCall.navigateWithDirectoryTree(
-        windowId, '/photos', 'Downloads');
+        windowId, '/photos', 'My files/Downloads');
   }).then(function() {
     // Press Ctrl+C.
     return remoteCall.callRemoteTestUtil('fakeKeyDown', windowId,
@@ -257,7 +259,7 @@
   testPromise(setupForDirectoryTreeContextMenuTest().then(function(id) {
     windowId = id;
     return remoteCall.navigateWithDirectoryTree(
-        windowId, '/photos', 'Downloads');
+        windowId, '/photos', 'My files/Downloads');
   }).then(function() {
     return clickDirectoryTreeContextMenuItem(windowId, '/photos', 'cut');
   }).then(function() {
@@ -277,7 +279,7 @@
   testPromise(setupForDirectoryTreeContextMenuTest().then(function(id) {
     windowId = id;
     return remoteCall.navigateWithDirectoryTree(
-        windowId, '/photos', 'Downloads');
+        windowId, '/photos', 'My files/Downloads');
   }).then(function() {
     // Press Ctrl+X.
     return remoteCall.callRemoteTestUtil('fakeKeyDown', windowId,
@@ -320,7 +322,7 @@
             // Copy photos directory as a test data.
             windowId = id;
             return remoteCall.navigateWithDirectoryTree(
-                windowId, '/photos', 'Downloads');
+                windowId, '/photos', 'My files/Downloads');
           })
           .then(function() {
             return remoteCall.callRemoteTestUtil(
@@ -329,7 +331,7 @@
           })
           .then(function() {
             return remoteCall.navigateWithDirectoryTree(
-                windowId, '/destination', 'Downloads');
+                windowId, '/destination', 'My files/Downloads');
           })
           .then(function() {
             // Confirm files before paste.
@@ -423,7 +425,7 @@
           .then(function() {
             // Navigate to child folder.
             return remoteCall.navigateWithDirectoryTree(
-                appId, '/photos/child-folder', 'Downloads');
+                appId, '/photos/child-folder', 'My files/Downloads');
           })
           .then(function() {
             // Rename parent folder.
@@ -452,14 +454,14 @@
             // /Downloads/photos/child-folder, since its path/parent has been
             // renamed.
             return remoteCall.waitUntilCurrentDirectoryIsChanged(
-                appId, '/Downloads');
+                appId, '/My files/Downloads');
           })
           .then(function() {
             // Navigate to child-folder using the new path.
             // |navigateWithDirectoryTree| already checks for breadcrumbs to
             // match the path.
             return remoteCall.navigateWithDirectoryTree(
-                appId, '/photos-new/child-folder', 'Downloads');
+                appId, '/photos-new/child-folder', 'My files/Downloads');
           }));
 };
 
diff --git a/ui/file_manager/integration_tests/file_manager/drive_specific.js b/ui/file_manager/integration_tests/file_manager/drive_specific.js
index f2e9945..97a0739 100644
--- a/ui/file_manager/integration_tests/file_manager/drive_specific.js
+++ b/ui/file_manager/integration_tests/file_manager/drive_specific.js
@@ -664,7 +664,7 @@
     function() {
       return remoteCall
           .navigateWithDirectoryTree(
-              appId, '/Recovered files from Google Drive', 'Downloads')
+              appId, '/Recovered files from Google Drive', 'My files/Downloads')
           .then(this.next);
     },
     // Ensure it contains never-sync.txt and never-sync (1).txt.
diff --git a/ui/file_manager/integration_tests/file_manager/metadata.js b/ui/file_manager/integration_tests/file_manager/metadata.js
index e294d113..7b5bcd2 100644
--- a/ui/file_manager/integration_tests/file_manager/metadata.js
+++ b/ui/file_manager/integration_tests/file_manager/metadata.js
@@ -167,7 +167,8 @@
     function(result) {
       appId = result.windowId;
       remoteCall
-          .navigateWithDirectoryTree(appId, '/photos1/folder1', 'Downloads')
+          .navigateWithDirectoryTree(
+              appId, '/photos1/folder1', 'My files/Downloads')
           .then(this.next);
     },
     // Fetch the metadata stats.
diff --git a/ui/file_manager/integration_tests/remote_call.js b/ui/file_manager/integration_tests/remote_call.js
index cc7b888..ceabdec1 100644
--- a/ui/file_manager/integration_tests/remote_call.js
+++ b/ui/file_manager/integration_tests/remote_call.js
@@ -451,17 +451,14 @@
 RemoteCallFilesApp.prototype.waitUntilCurrentDirectoryIsChanged = function(
     windowId, expectedPath) {
   var caller = getCaller();
-  return repeatUntil(function () {
-    return this.callRemoteTestUtil('getBreadcrumbPath', windowId, []).then(
-      function(path) {
-        // TODO(lucmult): Remove this once MyFiles flag is removed.
-        // https://crbug.com/850348.
-        const myFilesExpectedPath = '/My files' + expectedPath;
-        if(!(path === expectedPath || path === myFilesExpectedPath)) {
-          return pending(
-              caller, 'Expected path is %s got %s', expectedPath, path);
-        }
-      });
+  return repeatUntil(function() {
+    return this.callRemoteTestUtil('getBreadcrumbPath', windowId, [])
+        .then(function(path) {
+          if (path !== expectedPath) {
+            return pending(
+                caller, 'Expected path is %s got %s', expectedPath, path);
+          }
+        });
   }.bind(this));
 };
 
diff --git a/ui/message_center/views/message_view.cc b/ui/message_center/views/message_view.cc
index c99255b..0708aaa 100644
--- a/ui/message_center/views/message_view.cc
+++ b/ui/message_center/views/message_view.cc
@@ -121,17 +121,6 @@
 
   SetBorder(views::CreateRoundedRectBorder(
       kNotificationBorderThickness, kNotificationCornerRadius, kBorderColor));
-
-  auto* control_buttons_view = GetControlButtonsView();
-  if (control_buttons_view) {
-    int control_button_count =
-        (control_buttons_view->settings_button() ? 1 : 0) +
-        (control_buttons_view->snooze_button() ? 1 : 0);
-    if (control_button_count)
-      slide_out_controller_.EnableSwipeControl(control_button_count);
-    // TODO(crbug.com/1177464): support updating the swipe control when
-    // should_show_setting_buttons is changed after notification creation.
-  }
 }
 
 void MessageView::CloseSwipeControl() {
@@ -392,6 +381,10 @@
   slide_out_controller_.set_slide_mode(CalculateSlideMode());
 }
 
+void MessageView::SetSlideButtonWidth(int control_button_width) {
+  slide_out_controller_.SetSwipeControlWidth(control_button_width);
+}
+
 void MessageView::OnCloseButtonPressed() {
   MessageCenter::Get()->RemoveNotification(notification_id_,
                                            true /* by_user */);
diff --git a/ui/message_center/views/message_view.h b/ui/message_center/views/message_view.h
index 34a9dfa..1332544 100644
--- a/ui/message_center/views/message_view.h
+++ b/ui/message_center/views/message_view.h
@@ -148,6 +148,9 @@
   // mode.
   void DisableSlideForcibly(bool disable);
 
+  // Updates the width of the buttons which are hidden and avail by swipe.
+  void SetSlideButtonWidth(int coutrol_button_width);
+
   void set_scroller(views::ScrollView* scroller) { scroller_ = scroller; }
   std::string notification_id() const { return notification_id_; }
 
diff --git a/ui/message_center/views/slide_out_controller.cc b/ui/message_center/views/slide_out_controller.cc
index c42f677..e8c2c17 100644
--- a/ui/message_center/views/slide_out_controller.cc
+++ b/ui/message_center/views/slide_out_controller.cc
@@ -163,12 +163,9 @@
   delegate_->OnSlideOut();
 }
 
-void SlideOutController::EnableSwipeControl(int button_count) {
-  DCHECK(button_count > 0);
-  swipe_control_width_ =
-      kSwipeControlButtonSize * button_count +
-      kSwipeControlButtonHorizontalMargin * (button_count + 1);
-  has_swipe_control_ = true;
+void SlideOutController::SetSwipeControlWidth(int swipe_control_width) {
+  swipe_control_width_ = swipe_control_width;
+  has_swipe_control_ = (swipe_control_width != 0);
 }
 
 void SlideOutController::CloseSwipeControl() {
diff --git a/ui/message_center/views/slide_out_controller.h b/ui/message_center/views/slide_out_controller.h
index 767169a..e429a4f 100644
--- a/ui/message_center/views/slide_out_controller.h
+++ b/ui/message_center/views/slide_out_controller.h
@@ -53,9 +53,10 @@
   // ui::ImplicitAnimationObserver
   void OnImplicitAnimationsCompleted() override;
 
-  // Enables the swipe control. Buttons will appea behind the view as user
-  // slides it partially and it's kept open after the gesture.
-  void EnableSwipeControl(int button_count);
+  // Enables the swipe control with specifying the width of buttons. Buttons
+  // will appear behind the view as user slides it partially and it's kept open
+  // after the gesture.
+  void SetSwipeControlWidth(int swipe_control_width);
   float GetGestureAmount() const { return gesture_amount_; }
 
   // Moves slide back to the center position to closes the swipe control.
diff --git a/ui/touch_selection/touch_selection_menu_runner.h b/ui/touch_selection/touch_selection_menu_runner.h
index f4e05da..c82e05e 100644
--- a/ui/touch_selection/touch_selection_menu_runner.h
+++ b/ui/touch_selection/touch_selection_menu_runner.h
@@ -6,6 +6,7 @@
 #define UI_TOUCH_SELECTION_TOUCH_SELECTION_MENU_RUNNER_H_
 
 #include "base/macros.h"
+#include "base/strings/string_util.h"
 #include "ui/touch_selection/ui_touch_selection_export.h"
 
 namespace aura {
@@ -32,6 +33,12 @@
   // menu to show up in which case the menu will run asynchronously at a later
   // time.
   virtual void RunContextMenu() = 0;
+
+  // Whether the Quick Menu should be opened.
+  virtual bool ShouldShowQuickMenu() = 0;
+
+  // Returns the current text selection.
+  virtual base::string16 GetSelectedText() = 0;
 };
 
 // An interface for the singleton object responsible for running touch selection
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.cc b/ui/views/bubble/bubble_dialog_delegate_view.cc
index f539f676..b142981 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -164,6 +164,12 @@
   return kViewClassName;
 }
 
+void BubbleDialogDelegateView::AddedToWidget() {
+  DialogDelegateView::AddedToWidget();
+  if (GetAnchorView())
+    EnableFocusTraversalFromAnchorView();
+}
+
 void BubbleDialogDelegateView::OnWidgetDestroying(Widget* widget) {
   if (anchor_widget() == widget)
     SetAnchorView(NULL);
@@ -268,6 +274,7 @@
 void BubbleDialogDelegateView::EnableFocusTraversalFromAnchorView() {
   DCHECK(GetWidget());
   DCHECK(GetAnchorView());
+  DCHECK(anchor_widget());
   GetWidget()->SetFocusTraversableParent(
       anchor_widget()->GetFocusTraversable());
   GetWidget()->SetFocusTraversableParentView(GetAnchorView());
@@ -342,8 +349,13 @@
 void BubbleDialogDelegateView::Init() {}
 
 void BubbleDialogDelegateView::SetAnchorView(View* anchor_view) {
-  if (GetAnchorView())
+  if (GetAnchorView()) {
+    if (GetWidget()) {
+      GetWidget()->SetFocusTraversableParent(nullptr);
+      GetWidget()->SetFocusTraversableParentView(nullptr);
+    }
     GetAnchorView()->ClearProperty(kAnchoredDialogKey);
+  }
 
   // When the anchor view gets set the associated anchor widget might
   // change as well.
@@ -377,6 +389,8 @@
     // bounds when |anchor_view| is NULL, the bubble won't move.)
     OnAnchorBoundsChanged();
 
+    // Make sure that focus can move into here from the anchor view. If there's
+    // no widget yet, focus traversal will be set up in ::AddedToWidget().
     EnableFocusTraversalFromAnchorView();
   }
 }
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.h b/ui/views/bubble/bubble_dialog_delegate_view.h
index 8d65614..733bd48 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.h
+++ b/ui/views/bubble/bubble_dialog_delegate_view.h
@@ -49,6 +49,7 @@
   static Widget* CreateBubble(BubbleDialogDelegateView* bubble_delegate);
 
   // DialogDelegateView:
+  void AddedToWidget() override;
   BubbleDialogDelegateView* AsBubbleDialogDelegate() override;
   bool ShouldShowCloseButton() const override;
   ClientView* CreateClientView(Widget* widget) override;
diff --git a/ui/views/controls/native/native_view_host_aura.cc b/ui/views/controls/native/native_view_host_aura.cc
index 3be7f53..7efa51a3 100644
--- a/ui/views/controls/native/native_view_host_aura.cc
+++ b/ui/views/controls/native/native_view_host_aura.cc
@@ -134,8 +134,12 @@
 
 void NativeViewHostAura::RemovedFromWidget() {
   if (host_->native_view()) {
-    host_->native_view()->Hide();
+    // Clear kHostWindowKey before Hide() because it could be accessed during
+    // the call. In MUS aura, the hosting window could be destroyed at this
+    // point.
     host_->native_view()->ClearProperty(aura::client::kHostWindowKey);
+
+    host_->native_view()->Hide();
     if (host_->native_view()->parent())
       host_->native_view()->parent()->RemoveChild(host_->native_view());
     RemoveClippingWindow();
diff --git a/ui/views/focus/focus_manager_unittest.cc b/ui/views/focus/focus_manager_unittest.cc
index 25e7a34..31222b0 100644
--- a/ui/views/focus/focus_manager_unittest.cc
+++ b/ui/views/focus/focus_manager_unittest.cc
@@ -931,6 +931,13 @@
   GetWidget()->GetRootView()->AddChildView(parent3);
   GetWidget()->GetRootView()->AddChildView(parent4);
 
+  // Add an unfocusable child view to the dialog anchor view. This is a
+  // regression test that makes sure focus is able to navigate past unfocusable
+  // children and try to go into the anchored dialog. |kAnchoredDialogKey| was
+  // previously not checked if a recursive search to find a focusable child view
+  // was attempted (and failed), so the dialog would previously be skipped.
+  parent3->AddChildView(new View());
+
   BubbleDialogDelegateView* bubble_delegate =
       new TestBubbleDialogDelegateView(parent3);
   test::WidgetTest::WidgetAutoclosePtr bubble_widget(
diff --git a/ui/views/focus/focus_search.cc b/ui/views/focus/focus_search.cc
index c0e33176..8351f8f 100644
--- a/ui/views/focus/focus_search.cc
+++ b/ui/views/focus/focus_search.cc
@@ -183,15 +183,14 @@
           focus_traversable_view);
       if (v || *focus_traversable)
         return v;
-    } else {
-      // Check to see if we should navigate into a dialog anchored at this view.
-      BubbleDialogDelegateView* bubble =
-          starting_view->GetProperty(kAnchoredDialogKey);
-      if (bubble) {
-        *focus_traversable = bubble->GetWidget()->GetFocusTraversable();
-        *focus_traversable_view = starting_view;
-        return nullptr;
-      }
+    }
+    // Check to see if we should navigate into a dialog anchored at this view.
+    BubbleDialogDelegateView* bubble =
+        starting_view->GetProperty(kAnchoredDialogKey);
+    if (bubble) {
+      *focus_traversable = bubble->GetWidget()->GetFocusTraversable();
+      *focus_traversable_view = starting_view;
+      return nullptr;
     }
   }
 
diff --git a/ui/views/touchui/touch_selection_controller_impl.cc b/ui/views/touchui/touch_selection_controller_impl.cc
index 050064b9..93e0b4b 100644
--- a/ui/views/touchui/touch_selection_controller_impl.cc
+++ b/ui/views/touchui/touch_selection_controller_impl.cc
@@ -6,6 +6,7 @@
 
 #include <set>
 
+#include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/time/time.h"
 #include "ui/aura/client/cursor_client.h"
@@ -118,7 +119,7 @@
 
 // Return the appropriate handle image based on the bound's type
 gfx::Image* GetHandleImage(gfx::SelectionBound::Type bound_type) {
-  switch(bound_type) {
+  switch (bound_type) {
     case gfx::SelectionBound::LEFT:
       return GetLeftHandleImage();
     case gfx::SelectionBound::CENTER:
@@ -128,7 +129,7 @@
     default:
       NOTREACHED() << "Invalid touch handle bound type: " << bound_type;
       return nullptr;
-  };
+  }
 }
 
 // Calculates the bounds of the widget containing the selection handle based
@@ -157,7 +158,7 @@
     default:
       NOTREACHED() << "Undefined bound type.";
       break;
-  };
+  }
   return gfx::Rect(
       widget_left, bound.edge_top_rounded().y(), widget_width, widget_height);
 }
@@ -616,6 +617,16 @@
   client_view_->OpenContextMenu(anchor);
 }
 
+bool TouchSelectionControllerImpl::ShouldShowQuickMenu() {
+  NOTREACHED();
+  return false;
+}
+
+base::string16 TouchSelectionControllerImpl::GetSelectedText() {
+  NOTREACHED();
+  return base::string16();
+}
+
 void TouchSelectionControllerImpl::OnWidgetClosing(Widget* widget) {
   DCHECK_EQ(client_widget_, widget);
   client_widget_->RemoveObserver(this);
diff --git a/ui/views/touchui/touch_selection_controller_impl.h b/ui/views/touchui/touch_selection_controller_impl.h
index f749d210..c2b283c 100644
--- a/ui/views/touchui/touch_selection_controller_impl.h
+++ b/ui/views/touchui/touch_selection_controller_impl.h
@@ -5,6 +5,8 @@
 #ifndef UI_VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_
 #define UI_VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_
 
+#include <memory>
+
 #include "base/macros.h"
 #include "base/timer/timer.h"
 #include "ui/base/touch/touch_editing_controller.h"
@@ -73,6 +75,8 @@
   bool IsCommandIdEnabled(int command_id) const override;
   void ExecuteCommand(int command_id, int event_flags) override;
   void RunContextMenu() override;
+  bool ShouldShowQuickMenu() override;
+  base::string16 GetSelectedText() override;
 
   // WidgetObserver:
   void OnWidgetClosing(Widget* widget) override;
diff --git a/ui/views/touchui/touch_selection_menu_runner_views.cc b/ui/views/touchui/touch_selection_menu_runner_views.cc
index 7a99a8f..2c03131 100644
--- a/ui/views/touchui/touch_selection_menu_runner_views.cc
+++ b/ui/views/touchui/touch_selection_menu_runner_views.cc
@@ -19,8 +19,6 @@
 #include "ui/gfx/text_utils.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
-#include "ui/views/controls/button/button.h"
-#include "ui/views/controls/button/label_button.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/style/typography.h"
 
@@ -39,56 +37,8 @@
 
 }  // namespace
 
-// A bubble that contains actions available for the selected text. An object of
-// this type, as a BubbleDialogDelegateView, manages its own lifetime.
-class TouchSelectionMenuRunnerViews::Menu : public BubbleDialogDelegateView,
-                                            public ButtonListener {
- public:
-  Menu(TouchSelectionMenuRunnerViews* owner,
-       ui::TouchSelectionMenuClient* client,
-       const gfx::Rect& anchor_rect,
-       const gfx::Size& handle_image_size,
-       aura::Window* context);
-
-  // Checks whether there is any command available to show in the menu.
-  static bool IsMenuAvailable(const ui::TouchSelectionMenuClient* client);
-
-  // Closes the menu. This will eventually self-destroy the object.
-  void CloseMenu();
-
- private:
-  friend class TouchSelectionMenuRunnerViews::TestApi;
-
-  ~Menu() override;
-
-  // Queries the |client_| for what commands to show in the menu and sizes the
-  // menu appropriately.
-  void CreateButtons();
-
-  // Helper method to create a single button.
-  Button* CreateButton(const base::string16& title, int tag);
-
-  // Helper to disconnect this menu object from its owning menu runner.
-  void DisconnectOwner();
-
-  // BubbleDialogDelegateView:
-  void OnPaint(gfx::Canvas* canvas) override;
-  void WindowClosing() override;
-  int GetDialogButtons() const override;
-
-  // ButtonListener:
-  void ButtonPressed(Button* sender, const ui::Event& event) override;
-
-  TouchSelectionMenuRunnerViews* owner_;
-  ui::TouchSelectionMenuClient* const client_;
-
-  DISALLOW_COPY_AND_ASSIGN(Menu);
-};
-
 TouchSelectionMenuRunnerViews::Menu::Menu(TouchSelectionMenuRunnerViews* owner,
                                           ui::TouchSelectionMenuClient* client,
-                                          const gfx::Rect& anchor_rect,
-                                          const gfx::Size& handle_image_size,
                                           aura::Window* context)
     : BubbleDialogDelegateView(nullptr, BubbleBorder::BOTTOM_CENTER),
       owner_(owner),
@@ -105,6 +55,11 @@
 
   SetLayoutManager(std::make_unique<BoxLayout>(
       BoxLayout::kHorizontal, gfx::Insets(), kSpacingBetweenButtons));
+}
+
+void TouchSelectionMenuRunnerViews::Menu::ShowMenu(
+    const gfx::Rect& anchor_rect,
+    const gfx::Size& handle_image_size) {
   CreateButtons();
 
   // After buttons are created, check if there is enough room between handles to
@@ -170,7 +125,7 @@
   Layout();
 }
 
-Button* TouchSelectionMenuRunnerViews::Menu::CreateButton(
+LabelButton* TouchSelectionMenuRunnerViews::Menu::CreateButton(
     const base::string16& title,
     int tag) {
   base::string16 label =
@@ -243,9 +198,9 @@
   return menu ? menu->GetAnchorRect() : gfx::Rect();
 }
 
-Button* TouchSelectionMenuRunnerViews::TestApi::GetFirstButton() const {
+LabelButton* TouchSelectionMenuRunnerViews::TestApi::GetFirstButton() const {
   TouchSelectionMenuRunnerViews::Menu* menu = menu_runner_->menu_;
-  return menu ? static_cast<Button*>(menu->child_at(0)) : nullptr;
+  return menu ? static_cast<LabelButton*>(menu->child_at(0)) : nullptr;
 }
 
 Widget* TouchSelectionMenuRunnerViews::TestApi::GetWidget() const {
@@ -260,6 +215,14 @@
   CloseMenu();
 }
 
+void TouchSelectionMenuRunnerViews::ShowMenu(
+    Menu* menu,
+    const gfx::Rect& anchor_rect,
+    const gfx::Size& handle_image_size) {
+  menu_ = menu;
+  menu_->ShowMenu(anchor_rect, handle_image_size);
+}
+
 bool TouchSelectionMenuRunnerViews::IsMenuAvailable(
     const ui::TouchSelectionMenuClient* client) const {
   return TouchSelectionMenuRunnerViews::Menu::IsMenuAvailable(client);
@@ -272,8 +235,11 @@
     aura::Window* context) {
   CloseMenu();
 
-  if (TouchSelectionMenuRunnerViews::Menu::IsMenuAvailable(client))
-    menu_ = new Menu(this, client, anchor_rect, handle_image_size, context);
+  if (!TouchSelectionMenuRunnerViews::Menu::IsMenuAvailable(client))
+    return;
+
+  menu_ = new Menu(this, client, context);
+  menu_->ShowMenu(anchor_rect, handle_image_size);
 }
 
 void TouchSelectionMenuRunnerViews::CloseMenu() {
diff --git a/ui/views/touchui/touch_selection_menu_runner_views.h b/ui/views/touchui/touch_selection_menu_runner_views.h
index 0ce40600..438413a 100644
--- a/ui/views/touchui/touch_selection_menu_runner_views.h
+++ b/ui/views/touchui/touch_selection_menu_runner_views.h
@@ -9,16 +9,21 @@
 
 #include "base/macros.h"
 #include "ui/touch_selection/touch_selection_menu_runner.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/button/label_button.h"
 #include "ui/views/views_export.h"
 
 namespace views {
-class Button;
+class LabelButton;
 class Widget;
 
 // Views implementation for TouchSelectionMenuRunner.
 class VIEWS_EXPORT TouchSelectionMenuRunnerViews
     : public ui::TouchSelectionMenuRunner {
  public:
+  class Menu;
+
   // Test API to access internal state in tests.
   class VIEWS_EXPORT TestApi {
    public:
@@ -26,7 +31,7 @@
     ~TestApi();
 
     gfx::Rect GetAnchorRect() const;
-    Button* GetFirstButton() const;
+    LabelButton* GetFirstButton() const;
     Widget* GetWidget() const;
 
    private:
@@ -38,20 +43,23 @@
   TouchSelectionMenuRunnerViews();
   ~TouchSelectionMenuRunnerViews() override;
 
- private:
-  friend class TouchSelectionMenuRunnerViewsTestApi;
-  class Menu;
+ protected:
+  // Sets the menu as the currently runner menu and shows it.
+  void ShowMenu(Menu* menu,
+                const gfx::Rect& anchor_rect,
+                const gfx::Size& handle_image_size);
 
   // ui::TouchSelectionMenuRunner:
   bool IsMenuAvailable(
       const ui::TouchSelectionMenuClient* client) const override;
+  void CloseMenu() override;
   void OpenMenu(ui::TouchSelectionMenuClient* client,
                 const gfx::Rect& anchor_rect,
                 const gfx::Size& handle_image_size,
                 aura::Window* context) override;
-  void CloseMenu() override;
   bool IsRunning() const override;
 
+ private:
   // A pointer to the currently running menu, or |nullptr| if no menu is
   // running. The menu manages its own lifetime and deletes itself when closed.
   Menu* menu_;
@@ -59,6 +67,54 @@
   DISALLOW_COPY_AND_ASSIGN(TouchSelectionMenuRunnerViews);
 };
 
+// A bubble that contains actions available for the selected text. An object of
+// this type, as a BubbleDialogDelegateView, manages its own lifetime.
+class TouchSelectionMenuRunnerViews::Menu : public BubbleDialogDelegateView,
+                                            public ButtonListener {
+ public:
+  Menu(TouchSelectionMenuRunnerViews* owner,
+       ui::TouchSelectionMenuClient* client,
+       aura::Window* context);
+
+  void ShowMenu(const gfx::Rect& anchor_rect,
+                const gfx::Size& handle_image_size);
+
+  // Checks whether there is any command available to show in the menu.
+  static bool IsMenuAvailable(const ui::TouchSelectionMenuClient* client);
+
+  // Closes the menu. This will eventually self-destroy the object.
+  void CloseMenu();
+
+ protected:
+  ~Menu() override;
+
+  // Queries the |client_| for what commands to show in the menu and sizes the
+  // menu appropriately.
+  virtual void CreateButtons();
+
+  // Helper method to create a single button.
+  LabelButton* CreateButton(const base::string16& title, int tag);
+
+  // ButtonListener:
+  void ButtonPressed(Button* sender, const ui::Event& event) override;
+
+ private:
+  friend class TouchSelectionMenuRunnerViews::TestApi;
+
+  // Helper to disconnect this menu object from its owning menu runner.
+  void DisconnectOwner();
+
+  // BubbleDialogDelegateView:
+  void OnPaint(gfx::Canvas* canvas) override;
+  void WindowClosing() override;
+  int GetDialogButtons() const override;
+
+  TouchSelectionMenuRunnerViews* owner_;
+  ui::TouchSelectionMenuClient* const client_;
+
+  DISALLOW_COPY_AND_ASSIGN(Menu);
+};
+
 }  // namespace views
 
 #endif  // UI_VIEWS_TOUCHUI_TOUCH_SELECTION_MENU_RUNNER_VIEWS_H_
diff --git a/ui/views/touchui/touch_selection_menu_runner_views_unittest.cc b/ui/views/touchui/touch_selection_menu_runner_views_unittest.cc
index 281ebd7..cf555e3 100644
--- a/ui/views/touchui/touch_selection_menu_runner_views_unittest.cc
+++ b/ui/views/touchui/touch_selection_menu_runner_views_unittest.cc
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ui/views/touchui/touch_selection_menu_runner_views.h"
+
 #include "base/macros.h"
 #include "ui/events/event_utils.h"
 #include "ui/touch_selection/touch_selection_menu_runner.h"
-#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/button/label_button.h"
 #include "ui/views/test/views_test_base.h"
-#include "ui/views/touchui/touch_selection_menu_runner_views.h"
 
 namespace views {
 namespace {
@@ -53,6 +54,10 @@
 
   void RunContextMenu() override {}
 
+  base::string16 GetSelectedText() override { return base::string16(); }
+
+  bool ShouldShowQuickMenu() override { return false; }
+
   // When set to true, no command would be available and menu should not be
   // shown.
   bool no_command_available_;
@@ -152,7 +157,7 @@
 
   // Tap the first action on the menu and check taht the menu is closed
   // properly.
-  Button* button = test_api.GetFirstButton();
+  LabelButton* button = test_api.GetFirstButton();
   DCHECK(button);
   gfx::Point button_center = button->bounds().CenterPoint();
   ui::GestureEventDetails details(ui::ET_GESTURE_TAP);
diff --git a/ui/views/views_delegate.cc b/ui/views/views_delegate.cc
index 4c73337..ec2f941 100644
--- a/ui/views/views_delegate.cc
+++ b/ui/views/views_delegate.cc
@@ -4,6 +4,8 @@
 
 #include "ui/views/views_delegate.h"
 
+#include <utility>
+
 #include "base/command_line.h"
 #include "build/build_config.h"
 #include "ui/views/views_touch_selection_controller_factory.h"
@@ -133,4 +135,11 @@
   return true;
 }
 
+#if defined(USE_AURA)
+void ViewsDelegate::SetTouchSelectionMenuRunner(
+    std::unique_ptr<TouchSelectionMenuRunnerViews> menu_runner) {
+  touch_selection_menu_runner_ = std::move(menu_runner);
+}
+#endif
+
 }  // namespace views
diff --git a/ui/views/views_delegate.h b/ui/views/views_delegate.h
index bf6718f8..8d313323 100644
--- a/ui/views/views_delegate.h
+++ b/ui/views/views_delegate.h
@@ -203,6 +203,11 @@
  protected:
   ViewsDelegate();
 
+#if defined(USE_AURA)
+  void SetTouchSelectionMenuRunner(
+      std::unique_ptr<TouchSelectionMenuRunnerViews> menu_runner);
+#endif
+
  private:
   std::unique_ptr<ui::TouchEditingControllerFactory>
       editing_controller_factory_;
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc
index 2514071e..e4a1d240 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc
@@ -14,6 +14,7 @@
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/client/focus_client.h"
 #include "ui/aura/client/window_parenting_client.h"
+#include "ui/aura/env.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/test/window_occlusion_tracker_test_api.h"
 #include "ui/aura/window.h"
@@ -335,7 +336,7 @@
 
   // Reorder child views. Expect occlusion to only be recomputed once.
   aura::test::WindowOcclusionTrackerTestApi window_occlusion_tracker_test_api(
-      parent_window->env());
+      parent_window->env()->GetWindowOcclusionTracker());
   const int num_times_occlusion_recomputed =
       window_occlusion_tracker_test_api.GetNumTimesOcclusionRecomputed();
   contents_view->ReorderChildView(host_view3, 0);
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc
index 2f3f85b..130919de 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc
@@ -189,8 +189,9 @@
   DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11Test);
 };
 
+// https://crbug.com/898742: Test is flaky.
 // Tests that the shape is properly set on the x window.
-TEST_F(DesktopWindowTreeHostX11Test, Shape) {
+TEST_F(DesktopWindowTreeHostX11Test, DISABLED_Shape) {
   if (!ui::IsShapeExtensionAvailable())
     return;