diff --git a/BUILD.gn b/BUILD.gn
index 7265673..b4c3eff5 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -70,6 +70,7 @@
 
   deps = [
     ":gn_visibility",
+    "//base:base_perftests",
     "//base:base_unittests",
     "//chrome/installer",
     "//net:net_unittests",
@@ -418,7 +419,6 @@
   if (is_ios || is_win || (is_linux && !is_chromeos)) {
     deps += [
       "//base:base_i18n_perftests",
-      "//base:base_perftests",
       "//google_apis:google_apis_unittests",
     ]
   }
diff --git a/DEPS b/DEPS
index 005b6e6..81b1ae8 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,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': '38702ab43b2857f2fe06afb8dad1339d76a2fd84',
+  'skia_revision': '11f844e559bc536e2b1cb09d126af8fffe56f31c',
   # 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': '7245c9da4622180b78ee142a7e4f7fcc9151fb04',
+  'v8_revision': 'f274206578777b85cf4fc6c662e7f783d6f7456e',
   # 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.
@@ -56,7 +56,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
-  'buildtools_revision': '84fdc992430562c77356707e9a047c7c691b7c3e',
+  'buildtools_revision': 'cbc33b9c0a9d1bb913895a4319a742c504a2d541',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -400,7 +400,7 @@
 
     # For Linux and Chromium OS.
     'src/third_party/cros_system_api':
-      Var('chromium_git') + '/chromiumos/platform/system_api.git' + '@' + '27810c6ba2d3f136811e516fe07d5562704a92ee',
+      Var('chromium_git') + '/chromiumos/platform/system_api.git' + '@' + 'a8a31cea143fb6bf2a06c91d5a5349ae51c067b4',
 
     # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
     'src/third_party/chromite':
diff --git a/android_webview/browser/aw_contents.cc b/android_webview/browser/aw_contents.cc
index b827a89a..f423c897 100644
--- a/android_webview/browser/aw_contents.cc
+++ b/android_webview/browser/aw_contents.cc
@@ -888,6 +888,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   gfx::Size size(w, h);
   web_contents_->GetNativeView()->OnPhysicalBackingSizeChanged(size);
+  web_contents_->GetNativeView()->OnSizeChanged(w, h);
   browser_view_renderer_.OnSizeChanged(w, h);
 }
 
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 262c3ab1..066155e4 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -103,6 +103,8 @@
     "display/mouse_warp_controller.h",
     "display/null_mouse_warp_controller.cc",
     "display/null_mouse_warp_controller.h",
+    "display/overscan_calibrator.cc",
+    "display/overscan_calibrator.h",
     "display/projecting_observer_chromeos.cc",
     "display/projecting_observer_chromeos.h",
     "display/resolution_notification_controller.cc",
@@ -119,6 +121,10 @@
     "display/shared_display_edge_indicator.h",
     "display/shutdown_observer_chromeos.cc",
     "display/shutdown_observer_chromeos.h",
+    "display/touch_calibrator_controller.cc",
+    "display/touch_calibrator_controller.h",
+    "display/touch_calibrator_view.cc",
+    "display/touch_calibrator_view.h",
     "display/unified_mouse_warp_controller.cc",
     "display/unified_mouse_warp_controller.h",
     "display/window_tree_host_manager.cc",
@@ -599,6 +605,8 @@
     "system/web_notification/ash_popup_alignment_delegate.h",
     "system/web_notification/fullscreen_notification_blocker.cc",
     "system/web_notification/fullscreen_notification_blocker.h",
+    "system/web_notification/inactive_user_notification_blocker.cc",
+    "system/web_notification/inactive_user_notification_blocker.h",
     "system/web_notification/login_state_notification_blocker.cc",
     "system/web_notification/login_state_notification_blocker.h",
     "system/web_notification/message_center_bubble.cc",
@@ -864,6 +872,7 @@
     "//base/third_party/dynamic_annotations",
     "//cc",
     "//cc/debug",
+    "//cc/paint:paint",
     "//chromeos",
     "//chromeos:power_manager_proto",
     "//components/device_event_log",
@@ -1261,6 +1270,7 @@
     "system/update/tray_update_unittest.cc",
     "system/user/tray_user_unittest.cc",
     "system/web_notification/ash_popup_alignment_delegate_unittest.cc",
+    "system/web_notification/inactive_user_notification_blocker_unittest.cc",
     "system/web_notification/login_state_notification_blocker_unittest.cc",
     "system/web_notification/web_notification_tray_unittest.cc",
     "test/ash_test_helper_unittest.cc",
@@ -1377,6 +1387,7 @@
     "display/root_window_transformers_unittest.cc",
     "display/screen_ash_unittest.cc",
     "display/screen_position_controller_unittest.cc",
+    "display/touch_calibrator_controller_unittest.cc",
     "display/unified_mouse_warp_controller_unittest.cc",
     "display/window_tree_host_manager_unittest.cc",
 
diff --git a/ash/accessibility/accessibility_controller.cc b/ash/accessibility/accessibility_controller.cc
index d2b07e5..9318e6e 100644
--- a/ash/accessibility/accessibility_controller.cc
+++ b/ash/accessibility/accessibility_controller.cc
@@ -21,6 +21,8 @@
 #include "services/ui/public/interfaces/constants.mojom.h"
 #include "ui/base/cursor/cursor_type.h"
 
+using session_manager::SessionState;
+
 namespace ash {
 namespace {
 
@@ -96,9 +98,23 @@
          prefs->GetBoolean(prefs::kAccessibilityScreenMagnifierEnabled);
 }
 
+void AccessibilityController::OnSigninScreenPrefServiceInitialized(
+    PrefService* prefs) {
+  ObservePrefs(prefs);
+}
+
 void AccessibilityController::OnActiveUserPrefServiceChanged(
     PrefService* prefs) {
-  // Watch for pref updates from webui settings.
+  ObservePrefs(prefs);
+}
+
+void AccessibilityController::SetPrefServiceForTest(PrefService* prefs) {
+  pref_service_for_test_ = prefs;
+  ObservePrefs(prefs);
+}
+
+void AccessibilityController::ObservePrefs(PrefService* prefs) {
+  // Watch for pref updates from webui settings and policy.
   pref_change_registrar_ = base::MakeUnique<PrefChangeRegistrar>();
   pref_change_registrar_->Init(prefs);
   pref_change_registrar_->Add(
@@ -114,20 +130,23 @@
       base::Bind(&AccessibilityController::UpdateHighContrastFromPref,
                  base::Unretained(this)));
 
+  // Load current state.
   UpdateLargeCursorFromPref();
   UpdateHighContrastFromPref();
 }
 
-void AccessibilityController::SetPrefServiceForTest(PrefService* prefs) {
-  pref_service_for_test_ = prefs;
-  OnActiveUserPrefServiceChanged(prefs);
-}
-
 PrefService* AccessibilityController::GetActivePrefService() const {
   if (pref_service_for_test_)
     return pref_service_for_test_;
 
-  return Shell::Get()->session_controller()->GetLastActiveUserPrefService();
+  SessionController* session = Shell::Get()->session_controller();
+  // Use the active user prefs once they become available. Check the PrefService
+  // object instead of session state because prefs load is async after login.
+  PrefService* user_prefs = session->GetLastActiveUserPrefService();
+  if (user_prefs)
+    return user_prefs;
+
+  return session->GetSigninScreenPrefService();
 }
 
 void AccessibilityController::UpdateLargeCursorFromPref() {
diff --git a/ash/accessibility/accessibility_controller.h b/ash/accessibility/accessibility_controller.h
index db7206d..75e4ddb 100644
--- a/ash/accessibility/accessibility_controller.h
+++ b/ash/accessibility/accessibility_controller.h
@@ -44,11 +44,16 @@
   static bool RequiresCursorCompositing(PrefService* prefs);
 
   // SessionObserver:
+  void OnSigninScreenPrefServiceInitialized(PrefService* prefs) override;
   void OnActiveUserPrefServiceChanged(PrefService* prefs) override;
 
   void SetPrefServiceForTest(PrefService* prefs);
 
  private:
+  // Observes either the signin screen prefs or active user prefs and loads
+  // initial settings.
+  void ObservePrefs(PrefService* prefs);
+
   // Before login returns the signin screen profile prefs. After login returns
   // the active user profile prefs. Returns null early during startup.
   PrefService* GetActivePrefService() const;
diff --git a/ash/accessibility/accessibility_controller_unittest.cc b/ash/accessibility/accessibility_controller_unittest.cc
index ff12e1f..a3e95d5 100644
--- a/ash/accessibility/accessibility_controller_unittest.cc
+++ b/ash/accessibility/accessibility_controller_unittest.cc
@@ -106,4 +106,34 @@
   Shell::Get()->system_tray_notifier()->RemoveAccessibilityObserver(&observer);
 }
 
+using AccessibilityControllerSigninTest = NoSessionAshTestBase;
+
+TEST_F(AccessibilityControllerSigninTest, SigninScreenPrefs) {
+  AccessibilityController* accessibility =
+      Shell::Get()->accessibility_controller();
+
+  SessionController* session = Shell::Get()->session_controller();
+  EXPECT_EQ(session_manager::SessionState::LOGIN_PRIMARY,
+            session->GetSessionState());
+  EXPECT_FALSE(accessibility->IsLargeCursorEnabled());
+
+  // Verify that toggling prefs at the signin screen changes the signin setting.
+  PrefService* signin_prefs = session->GetSigninScreenPrefService();
+  using prefs::kAccessibilityLargeCursorEnabled;
+  EXPECT_FALSE(signin_prefs->GetBoolean(kAccessibilityLargeCursorEnabled));
+  accessibility->SetLargeCursorEnabled(true);
+  EXPECT_TRUE(accessibility->IsLargeCursorEnabled());
+  EXPECT_TRUE(signin_prefs->GetBoolean(kAccessibilityLargeCursorEnabled));
+
+  // Verify that toggling prefs after signin changes the user setting.
+  SimulateUserLogin("user1@test.com");
+  PrefService* user_prefs = session->GetLastActiveUserPrefService();
+  EXPECT_NE(signin_prefs, user_prefs);
+  EXPECT_FALSE(accessibility->IsLargeCursorEnabled());
+  EXPECT_FALSE(user_prefs->GetBoolean(kAccessibilityLargeCursorEnabled));
+  accessibility->SetLargeCursorEnabled(true);
+  EXPECT_TRUE(accessibility->IsLargeCursorEnabled());
+  EXPECT_TRUE(user_prefs->GetBoolean(kAccessibilityLargeCursorEnabled));
+}
+
 }  // namespace ash
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 83bef477..9bee866 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -983,4 +983,15 @@
   ASSERT_EQ(app_list::AppListView::CLOSED, view->app_list_state());
 }
 
+TEST_F(FullscreenAppListPresenterDelegateTest,
+       MouseWheelFromAppListPresenterImplTransitionsAppListState) {
+  app_list_presenter_impl()->Show(GetPrimaryDisplayId());
+  app_list::AppListView* view = app_list_presenter_impl()->GetView();
+  EXPECT_EQ(app_list::AppListView::PEEKING, view->app_list_state());
+
+  app_list_presenter_impl()->ProcessMouseWheelOffset(-30);
+
+  ASSERT_EQ(app_list::AppListView::FULLSCREEN_ALL_APPS, view->app_list_state());
+}
+
 }  // namespace ash
diff --git a/ash/display/DEPS b/ash/display/DEPS
index 5ca4b3a5..d68ef13f 100644
--- a/ash/display/DEPS
+++ b/ash/display/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+ash/host",
+  "+cc/paint",
 ]
 
 specific_include_rules = {
diff --git a/chrome/browser/chromeos/display/overscan_calibrator.cc b/ash/display/overscan_calibrator.cc
similarity index 92%
rename from chrome/browser/chromeos/display/overscan_calibrator.cc
rename to ash/display/overscan_calibrator.cc
index 06cff92..173f5bd 100644
--- a/chrome/browser/chromeos/display/overscan_calibrator.cc
+++ b/ash/display/overscan_calibrator.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/display/overscan_calibrator.h"
+#include "ash/display/overscan_calibrator.h"
 
 #include <stdint.h>
 
@@ -21,7 +21,7 @@
 #include "ui/display/manager/managed_display_info.h"
 #include "ui/gfx/canvas.h"
 
-namespace chromeos {
+namespace ash {
 namespace {
 
 // The opacity for the arrows of the overscan calibration.
@@ -117,10 +117,8 @@
 }
 
 void OverscanCalibrator::UpdateInsets(const gfx::Insets& insets) {
-  insets_.Set(std::max(insets.top(), 0),
-              std::max(insets.left(), 0),
-              std::max(insets.bottom(), 0),
-              std::max(insets.right(), 0));
+  insets_.Set(std::max(insets.top(), 0), std::max(insets.left(), 0),
+              std::max(insets.bottom(), 0), std::max(insets.right(), 0));
   calibration_layer_->SchedulePaint(calibration_layer_->bounds());
 }
 
@@ -145,13 +143,11 @@
 }
 
 void OverscanCalibrator::OnDelegatedFrameDamage(
-    const gfx::Rect& damage_rect_in_dip) {
-}
+    const gfx::Rect& damage_rect_in_dip) {}
 
-void OverscanCalibrator::OnDeviceScaleFactorChanged(
-    float device_scale_factor) {
+void OverscanCalibrator::OnDeviceScaleFactorChanged(float device_scale_factor) {
   // TODO(mukai): Cancel the overscan calibration when the device
   // configuration has changed.
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/chromeos/display/overscan_calibrator.h b/ash/display/overscan_calibrator.h
similarity index 86%
rename from chrome/browser/chromeos/display/overscan_calibrator.h
rename to ash/display/overscan_calibrator.h
index 94765e8..8f1b5fd1 100644
--- a/chrome/browser/chromeos/display/overscan_calibrator.h
+++ b/ash/display/overscan_calibrator.h
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_DISPLAY_OVERSCAN_CALIBRATOR_H_
-#define CHROME_BROWSER_CHROMEOS_DISPLAY_OVERSCAN_CALIBRATOR_H_
+#ifndef ASH_DISPLAY_OVERSCAN_CALIBRATOR_H_
+#define ASH_DISPLAY_OVERSCAN_CALIBRATOR_H_
 
 #include <memory>
 
+#include "ash/ash_export.h"
 #include "base/macros.h"
 #include "ui/compositor/layer_delegate.h"
 #include "ui/display/display.h"
@@ -17,11 +18,11 @@
 class Layer;
 }
 
-namespace chromeos {
+namespace ash {
 
 // This is used to show the visible feedback to the user's operations for
 // calibrating display overscan settings.
-class OverscanCalibrator : public ui::LayerDelegate {
+class ASH_EXPORT OverscanCalibrator : public ui::LayerDelegate {
  public:
   OverscanCalibrator(const display::Display& target_display,
                      const gfx::Insets& initial_insets);
@@ -64,6 +65,6 @@
   DISALLOW_COPY_AND_ASSIGN(OverscanCalibrator);
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROME_BROWSER_CHROMEOS_DISPLAY_OVERSCAN_CALIBRATOR_H_
+#endif  // ASH_DISPLAY_OVERSCAN_CALIBRATOR_H_
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.cc b/ash/display/touch_calibrator_controller.cc
similarity index 87%
rename from chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.cc
rename to ash/display/touch_calibrator_controller.cc
index 58e1f8c..2671bc30 100644
--- a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.cc
+++ b/ash/display/touch_calibrator_controller.cc
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.h"
+#include "ash/display/touch_calibrator_controller.h"
 
+#include "ash/display/touch_calibrator_view.h"
 #include "ash/shell.h"
 #include "ash/touch/ash_touch_transform_controller.h"
 #include "base/memory/ptr_util.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h"
 #include "ui/display/screen.h"
 #include "ui/events/event.h"
 #include "ui/events/event_constants.h"
 
-namespace chromeos {
+namespace ash {
 
 // Time interval after a touch event during which all other touch events are
 // ignored during calibration.
@@ -39,7 +39,7 @@
   is_calibrating_ = true;
   callback_ = callback;
 
-  ash::Shell::Get()->window_tree_host_manager()->AddObserver(this);
+  Shell::Get()->window_tree_host_manager()->AddObserver(this);
   target_display_ = target_display;
 
   // Clear all touch calibrator views used in any previous calibration.
@@ -57,10 +57,10 @@
         base::MakeUnique<TouchCalibratorView>(display, is_primary_view);
   }
 
-  ash::Shell::Get()->touch_transformer_controller()->SetForCalibration(true);
+  Shell::Get()->touch_transformer_controller()->SetForCalibration(true);
 
   // Add self as an event handler target.
-  ash::Shell::Get()->AddPreTargetHandler(this);
+  Shell::Get()->AddPreTargetHandler(this);
 }
 
 void TouchCalibratorController::StopCalibration() {
@@ -68,12 +68,12 @@
     return;
   is_calibrating_ = false;
 
-  ash::Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
+  Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
 
-  ash::Shell::Get()->touch_transformer_controller()->SetForCalibration(false);
+  Shell::Get()->touch_transformer_controller()->SetForCalibration(false);
 
   // Remove self as the event handler.
-  ash::Shell::Get()->RemovePreTargetHandler(this);
+  Shell::Get()->RemovePreTargetHandler(this);
 
   // Transition all touch calibrator views to their final state for a graceful
   // exit.
@@ -120,7 +120,7 @@
       callback_.Reset();
     }
     StopCalibration();
-    ash::Shell::Get()->display_manager()->SetTouchCalibrationData(
+    Shell::Get()->display_manager()->SetTouchCalibrationData(
         target_display_.id(), touch_point_quad_,
         target_screen_calibration_view->size());
     return;
@@ -162,4 +162,4 @@
   target_screen_calibration_view->AdvanceToNextState();
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.h b/ash/display/touch_calibrator_controller.h
similarity index 85%
rename from chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.h
rename to ash/display/touch_calibrator_controller.h
index ffa0ab5..9d731693 100644
--- a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.h
+++ b/ash/display/touch_calibrator_controller.h
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_CONTROLLER_H_
-#define CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_CONTROLLER_H_
+#ifndef ASH_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_CONTROLLER_H_
+#define ASH_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_CONTROLLER_H_
 
 #include <map>
 
+#include "ash/ash_export.h"
 #include "ash/display/window_tree_host_manager.h"
 #include "base/time/time.h"
 #include "ui/display/display.h"
@@ -16,9 +17,9 @@
 namespace ui {
 class KeyEvent;
 class TouchEvent;
-}
+}  // namespace ui
 
-namespace chromeos {
+namespace ash {
 
 class TouchCalibratorView;
 
@@ -26,8 +27,9 @@
 // associated data from the user. It instantiates TouchCalibratorView classes to
 // present an interface the user can interact with for calibration.
 // Touch calibration is restricted to calibrate only one display at a time.
-class TouchCalibratorController : public ui::EventHandler,
-                                  public ash::WindowTreeHostManager::Observer {
+class ASH_EXPORT TouchCalibratorController
+    : public ui::EventHandler,
+      public WindowTreeHostManager::Observer {
  public:
   using CalibrationPointPairQuad =
       display::TouchCalibrationData::CalibrationPointPairQuad;
@@ -86,5 +88,5 @@
   DISALLOW_COPY_AND_ASSIGN(TouchCalibratorController);
 };
 
-}  // namespace chromeos
-#endif  // CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_CONTROLLER_H_
+}  // namespace ash
+#endif  // ASH_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_CONTROLLER_H_
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller_unittest.cc b/ash/display/touch_calibrator_controller_unittest.cc
similarity index 91%
rename from chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller_unittest.cc
rename to ash/display/touch_calibrator_controller_unittest.cc
index 7d085cb..29da72dd 100644
--- a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller_unittest.cc
+++ b/ash/display/touch_calibrator_controller_unittest.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.h"
+#include "ash/display/touch_calibrator_controller.h"
 
 #include <vector>
 
+#include "ash/display/touch_calibrator_view.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "base/stl_util.h"
-#include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h"
 #include "ui/display/display.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/events/base_event_utils.h"
@@ -19,9 +19,9 @@
 
 using namespace display;
 
-namespace chromeos {
+namespace ash {
 
-class TouchCalibratorControllerTest : public ash::AshTestBase {
+class TouchCalibratorControllerTest : public AshTestBase {
  public:
   TouchCalibratorControllerTest() {}
 
@@ -75,7 +75,7 @@
   TouchCalibratorController touch_calibrator_controller;
   StartCalibrationChecks(&touch_calibrator_controller, touch_display);
 
-  ui::EventTargetTestApi test_api(ash::Shell::Get());
+  ui::EventTargetTestApi test_api(Shell::Get());
   const ui::EventHandlerList& handlers = test_api.pre_target_handlers();
   EXPECT_TRUE(base::ContainsValue(handlers, &touch_calibrator_controller));
 }
@@ -119,4 +119,4 @@
             touch_calibrator_controller.last_touch_timestamp_);
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc b/ash/display/touch_calibrator_view.cc
similarity index 97%
rename from chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc
rename to ash/display/touch_calibrator_view.cc
index 49d7709..fc4551e 100644
--- a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc
+++ b/ash/display/touch_calibrator_view.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h"
+#include "ash/display/touch_calibrator_view.h"
 
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/resources/vector_icons/vector_icons.h"
@@ -21,7 +21,7 @@
 #include "ui/views/controls/label.h"
 #include "ui/views/widget/widget.h"
 
-namespace chromeos {
+namespace ash {
 
 namespace {
 
@@ -92,8 +92,8 @@
   params.activatable = views::Widget::InitParams::ACTIVATABLE_NO;
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
-  params.parent = ash::Shell::GetContainer(
-      root_window, ash::kShellWindowId_OverlayContainer);
+  params.parent =
+      Shell::GetContainer(root_window, kShellWindowId_OverlayContainer);
   return params;
 }
 
@@ -240,8 +240,7 @@
       icon_width_(bounds.width() * 0.5f) {
   SetBoundsRect(bounds);
 
-  hand_icon_ =
-      gfx::CreateVectorIcon(ash::kTouchCalibrationHandIcon, kHandIconColor);
+  hand_icon_ = gfx::CreateVectorIcon(kTouchCalibrationHandIcon, kHandIconColor);
 }
 
 TouchTargetThrobberView::~TouchTargetThrobberView() {}
@@ -442,8 +441,8 @@
 
   // crbug/676513 moves this file to src/ash which will require an ash icon
   // file.
-  check_icon_ = gfx::CreateVectorIcon(ash::kTouchCalibrationCompleteCheckIcon,
-                                      SK_ColorWHITE);
+  check_icon_ =
+      gfx::CreateVectorIcon(kTouchCalibrationCompleteCheckIcon, SK_ColorWHITE);
 
   flags_.setColor(SK_ColorWHITE);
   flags_.setStyle(cc::PaintFlags::kFill_Style);
@@ -470,7 +469,7 @@
       throbber_circle_(nullptr),
       hint_box_view_(nullptr),
       touch_point_view_(nullptr) {
-  aura::Window* root = ash::Shell::GetRootWindowForDisplayId(display_.id());
+  aura::Window* root = Shell::GetRootWindowForDisplayId(display_.id());
   widget_.reset(new views::Widget);
   widget_->Init(GetWidgetParams(root));
   widget_->SetContentsView(this);
@@ -827,4 +826,4 @@
   }
 }
 
-}  // namespace chromeos
+}  // namespace ash
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h b/ash/display/touch_calibrator_view.h
similarity index 91%
rename from chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h
rename to ash/display/touch_calibrator_view.h
index f4d0255..6a0497c4 100644
--- a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h
+++ b/ash/display/touch_calibrator_view.h
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_VIEW_H_
-#define CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_VIEW_H_
+#ifndef ASH_DISPLAY_TOUCH_CALIBRATOR_VIEW_H_
+#define ASH_DISPLAY_TOUCH_CALIBRATOR_VIEW_H_
 
+#include "ash/ash_export.h"
 #include "base/macros.h"
 #include "cc/paint/paint_flags.h"
 #include "ui/compositor/layer_animation_observer.h"
@@ -15,18 +16,18 @@
 namespace views {
 class Label;
 class Widget;
-}
+}  // namespace views
 
 namespace gfx {
 class Animation;
 class LinearAnimation;
-}
+}  // namespace gfx
 
 namespace ui {
 class LayerAnimationSequence;
-}
+}  // namespace ui
 
-namespace chromeos {
+namespace ash {
 
 class CircularThrobberView;
 
@@ -36,9 +37,9 @@
 // touch calibration view.
 // |TouchCalibratorView| acts as a state machine and has an API to toggle its
 // state or get the current state.
-class TouchCalibratorView : public views::View,
-                            public gfx::AnimationDelegate,
-                            public ui::LayerAnimationObserver {
+class ASH_EXPORT TouchCalibratorView : public views::View,
+                                       public gfx::AnimationDelegate,
+                                       public ui::LayerAnimationObserver {
  public:
   // Different states of |TouchCalibratorView| in order.
   enum State {
@@ -152,6 +153,6 @@
   DISALLOW_COPY_AND_ASSIGN(TouchCalibratorView);
 };
 
-}  // namespace chromeos
+}  // namespace ash
 
-#endif  // CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_VIEW_H_
+#endif  // ASH_DISPLAY_TOUCH_CALIBRATOR_VIEW_H_
diff --git a/ash/fast_ink/fast_ink_pointer_controller.cc b/ash/fast_ink/fast_ink_pointer_controller.cc
index 6d8699a..21f9635 100644
--- a/ash/fast_ink/fast_ink_pointer_controller.cc
+++ b/ash/fast_ink/fast_ink_pointer_controller.cc
@@ -52,6 +52,15 @@
   // while it is being animated away.
 }
 
+bool FastInkPointerController::CanStartNewGesture(ui::TouchEvent* event) {
+  // 1) The stylus is pressed
+  // 2) The stylus is moving, but the pointer session has not started yet
+  // (most likely because the preceding press event was consumed by another
+  // handler).
+  return (event->type() == ui::ET_TOUCH_PRESSED ||
+          (event->type() == ui::ET_TOUCH_MOVED && !GetPointerView()));
+}
+
 void FastInkPointerController::OnTouchEvent(ui::TouchEvent* event) {
   if (!enabled_)
     return;
@@ -71,13 +80,7 @@
   aura::Window* root_window =
       static_cast<aura::Window*>(event->target())->GetRootWindow();
 
-  // Start a new pointer session in one of the two cases:
-  // 1) The stylus is pressed
-  // 2) The stylus is moving, but the pointer session has not started yet
-  // (most likely because the preceding press event was consumed by another
-  // handler).
-  if ((event->type() == ui::ET_TOUCH_PRESSED) ||
-      (event->type() == ui::ET_TOUCH_MOVED && !GetPointerView())) {
+  if (CanStartNewGesture(event)) {
     // Ignore events over the palette.
     if (palette_utils::PaletteContainsPointInScreen(event->root_location()))
       return;
diff --git a/ash/fast_ink/fast_ink_pointer_controller.h b/ash/fast_ink/fast_ink_pointer_controller.h
index 6db873d..74e1f2f 100644
--- a/ash/fast_ink/fast_ink_pointer_controller.h
+++ b/ash/fast_ink/fast_ink_pointer_controller.h
@@ -31,6 +31,10 @@
   // the pointer.
   virtual void SetEnabled(bool enabled);
 
+ protected:
+  // Whether the controller is ready to start handling a new gesture.
+  virtual bool CanStartNewGesture(ui::TouchEvent* event);
+
  private:
   // ui::EventHandler:
   void OnTouchEvent(ui::TouchEvent* event) override;
diff --git a/ash/fast_ink/fast_ink_points.cc b/ash/fast_ink/fast_ink_points.cc
index 65a08c5..6be420c 100644
--- a/ash/fast_ink/fast_ink_points.cc
+++ b/ash/fast_ink/fast_ink_points.cc
@@ -27,6 +27,13 @@
   points_.push_back(new_point);
 }
 
+void FastInkPoints::AddGap() {
+  // Not doing anything special regarding prediction, as in real usage there
+  // will be a gap in timestamps, and the prediction algorithm will reject the
+  // points that are too old.
+  points_.back().gap_after = true;
+}
+
 void FastInkPoints::MoveForwardToTime(const base::TimeTicks& latest_time) {
   DCHECK_GE(latest_time, collection_latest_time_);
   collection_latest_time_ = latest_time;
diff --git a/ash/fast_ink/fast_ink_points.h b/ash/fast_ink/fast_ink_points.h
index b264cee..63ed3b46 100644
--- a/ash/fast_ink/fast_ink_points.h
+++ b/ash/fast_ink/fast_ink_points.h
@@ -16,14 +16,16 @@
 
 namespace ash {
 
-// FastInkPoints is a helper class used for displaying low-latency palette tools
-// It keeps track of the points needed to render the image.
+// FastInkPoints is a helper class used for displaying low-latency palette
+// tools. It contains a collection of points representing one or more
+// contiguous trajectory segments.
 class ASH_EXPORT FastInkPoints {
  public:
   // Struct to describe each point.
   struct FastInkPoint {
     gfx::PointF location;
     base::TimeTicks time;
+    bool gap_after = false;  // True when there is a gap after this point.
   };
 
   // Constructor with a parameter to choose the fade out time of the points in
@@ -33,6 +35,9 @@
 
   // Adds a point.
   void AddPoint(const gfx::PointF& point, const base::TimeTicks& time);
+  // Adds a gap after the most recent point. This is useful for multi-stroke
+  // gesture handling (e.g. strokes going over the bezel).
+  void AddGap();
   // Updates the collection latest time. Automatically clears points that are
   // too old.
   void MoveForwardToTime(const base::TimeTicks& latest_time);
diff --git a/ash/fast_ink/fast_ink_points_unittest.cc b/ash/fast_ink/fast_ink_points_unittest.cc
index ea22aa1..81a99f6 100644
--- a/ash/fast_ink/fast_ink_points_unittest.cc
+++ b/ash/fast_ink/fast_ink_points_unittest.cc
@@ -231,4 +231,25 @@
   // Not testing with non-zero jerk, as the current prediction implementation
   // is not maintaining constant jerk on purpose.
 }
+
+// Test the interrupted stroke support.
+TEST_F(FastInkPointsTest, AddGap) {
+  points_.AddPoint(gfx::PointF(0, 0), base::TimeTicks());
+  points_.AddPoint(gfx::PointF(1, 1), base::TimeTicks());
+  points_.AddGap();
+  points_.AddPoint(gfx::PointF(2, 2), base::TimeTicks());
+  points_.AddPoint(gfx::PointF(3, 3), base::TimeTicks());
+  points_.AddPoint(gfx::PointF(4, 4), base::TimeTicks());
+  points_.AddGap();
+  points_.AddPoint(gfx::PointF(5, 5), base::TimeTicks());
+
+  auto points = points_.points();
+
+  EXPECT_FALSE(points[0].gap_after);
+  EXPECT_TRUE(points[1].gap_after);
+  EXPECT_FALSE(points[2].gap_after);
+  EXPECT_FALSE(points[3].gap_after);
+  EXPECT_TRUE(points[4].gap_after);
+  EXPECT_FALSE(points[5].gap_after);
+}
 }  // namespace ash
diff --git a/ash/highlighter/highlighter_controller.cc b/ash/highlighter/highlighter_controller.cc
index 5d38fc90..803d820e 100644
--- a/ash/highlighter/highlighter_controller.cc
+++ b/ash/highlighter/highlighter_controller.cc
@@ -19,6 +19,11 @@
 
 namespace {
 
+// Bezel stroke detection margin, in DP.
+const int kScreenEdgeMargin = 2;
+
+const int kInterruptedStrokeTimeoutMs = 500;
+
 // Adjust the height of the bounding box to match the pen tip height,
 // while keeping the same vertical center line. Adjust the width to
 // account for the pen tip width.
@@ -88,70 +93,100 @@
 }
 
 void HighlighterController::UpdatePointerView(ui::TouchEvent* event) {
+  interrupted_stroke_timer_.reset();
+
   highlighter_view_->AddNewPoint(event->root_location_f(), event->time_stamp());
 
-  if (event->type() == ui::ET_TOUCH_RELEASED) {
-    aura::Window* current_window =
-        highlighter_view_->GetWidget()->GetNativeWindow()->GetRootWindow();
+  if (event->type() != ui::ET_TOUCH_RELEASED)
+    return;
 
-    const FastInkPoints& points = highlighter_view_->points();
-    gfx::RectF box = points.GetBoundingBoxF();
+  gfx::Rect bounds = highlighter_view_->GetWidget()
+                         ->GetNativeWindow()
+                         ->GetRootWindow()
+                         ->bounds();
+  bounds.Inset(kScreenEdgeMargin, kScreenEdgeMargin);
 
-    const HighlighterGestureType gesture_type =
-        DetectHighlighterGesture(box, HighlighterView::kPenTipSize, points);
-
-    if (gesture_type == HighlighterGestureType::kHorizontalStroke) {
-      UMA_HISTOGRAM_COUNTS_10000(
-          "Ash.Shelf.Palette.Assistant.HighlighterLength",
-          static_cast<int>(box.width()));
-
-      box = AdjustHorizontalStroke(box, HighlighterView::kPenTipSize);
-    } else if (gesture_type == HighlighterGestureType::kClosedShape) {
-      const float fraction = box.width() * box.height() /
-                             (current_window->bounds().width() *
-                              current_window->bounds().height());
-      UMA_HISTOGRAM_PERCENTAGE("Ash.Shelf.Palette.Assistant.CircledPercentage",
-                               static_cast<int>(fraction * 100));
-    }
-
-    highlighter_view_->Animate(
-        box.CenterPoint(), gesture_type,
-        base::Bind(&HighlighterController::DestroyHighlighterView,
-                   base::Unretained(this)));
-
-    if (gesture_type != HighlighterGestureType::kNotRecognized) {
-      if (observer_) {
-        observer_->HandleSelection(gfx::ToEnclosingRect(
-            gfx::ScaleRect(box, GetScreenshotScale(current_window))));
-      }
-
-      result_view_ = base::MakeUnique<HighlighterResultView>(current_window);
-      result_view_->Animate(
-          box, gesture_type,
-          base::Bind(&HighlighterController::DestroyResultView,
-                     base::Unretained(this)));
-
-      recognized_gesture_counter_++;
-    }
-
-    gesture_counter_++;
-
-    const base::TimeTicks gesture_start = points.GetOldest().time;
-    if (gesture_counter_ > 1) {
-      // Up to 3 minutes.
-      UMA_HISTOGRAM_MEDIUM_TIMES("Ash.Shelf.Palette.Assistant.GestureInterval",
-                                 gesture_start - previous_gesture_end_);
-    }
-    previous_gesture_end_ = points.GetNewest().time;
-
-    // Up to 10 seconds.
-    UMA_HISTOGRAM_TIMES("Ash.Shelf.Palette.Assistant.GestureDuration",
-                        points.GetNewest().time - gesture_start);
-
-    UMA_HISTOGRAM_ENUMERATION("Ash.Shelf.Palette.Assistant.GestureType",
-                              gesture_type,
-                              HighlighterGestureType::kGestureCount);
+  const gfx::PointF pos = highlighter_view_->points().GetNewest().location;
+  if (bounds.Contains(
+          gfx::Point(static_cast<int>(pos.x()), static_cast<int>(pos.y())))) {
+    // The stroke has ended far enough from the screen edge, process it
+    // immediately.
+    RecognizeGesture();
+    return;
   }
+
+  // The stroke has ended close to the screen edge. Delay gesture recognition
+  // a little to give the pen a chance to re-enter the screen.
+  highlighter_view_->AddGap();
+
+  interrupted_stroke_timer_ = base::MakeUnique<base::OneShotTimer>();
+  interrupted_stroke_timer_->Start(
+      FROM_HERE, base::TimeDelta::FromMilliseconds(kInterruptedStrokeTimeoutMs),
+      base::Bind(&HighlighterController::RecognizeGesture,
+                 base::Unretained(this)));
+}
+
+void HighlighterController::RecognizeGesture() {
+  interrupted_stroke_timer_.reset();
+
+  aura::Window* current_window =
+      highlighter_view_->GetWidget()->GetNativeWindow()->GetRootWindow();
+
+  const FastInkPoints& points = highlighter_view_->points();
+  gfx::RectF box = points.GetBoundingBoxF();
+
+  const HighlighterGestureType gesture_type =
+      DetectHighlighterGesture(box, HighlighterView::kPenTipSize, points);
+
+  if (gesture_type == HighlighterGestureType::kHorizontalStroke) {
+    UMA_HISTOGRAM_COUNTS_10000("Ash.Shelf.Palette.Assistant.HighlighterLength",
+                               static_cast<int>(box.width()));
+
+    box = AdjustHorizontalStroke(box, HighlighterView::kPenTipSize);
+  } else if (gesture_type == HighlighterGestureType::kClosedShape) {
+    const float fraction =
+        box.width() * box.height() /
+        (current_window->bounds().width() * current_window->bounds().height());
+    UMA_HISTOGRAM_PERCENTAGE("Ash.Shelf.Palette.Assistant.CircledPercentage",
+                             static_cast<int>(fraction * 100));
+  }
+
+  highlighter_view_->Animate(
+      box.CenterPoint(), gesture_type,
+      base::Bind(&HighlighterController::DestroyHighlighterView,
+                 base::Unretained(this)));
+
+  if (gesture_type != HighlighterGestureType::kNotRecognized) {
+    if (observer_) {
+      observer_->HandleSelection(gfx::ToEnclosingRect(
+          gfx::ScaleRect(box, GetScreenshotScale(current_window))));
+    }
+
+    result_view_ = base::MakeUnique<HighlighterResultView>(current_window);
+    result_view_->Animate(box, gesture_type,
+                          base::Bind(&HighlighterController::DestroyResultView,
+                                     base::Unretained(this)));
+
+    recognized_gesture_counter_++;
+  }
+
+  gesture_counter_++;
+
+  const base::TimeTicks gesture_start = points.GetOldest().time;
+  if (gesture_counter_ > 1) {
+    // Up to 3 minutes.
+    UMA_HISTOGRAM_MEDIUM_TIMES("Ash.Shelf.Palette.Assistant.GestureInterval",
+                               gesture_start - previous_gesture_end_);
+  }
+  previous_gesture_end_ = points.GetNewest().time;
+
+  // Up to 10 seconds.
+  UMA_HISTOGRAM_TIMES("Ash.Shelf.Palette.Assistant.GestureDuration",
+                      points.GetNewest().time - gesture_start);
+
+  UMA_HISTOGRAM_ENUMERATION("Ash.Shelf.Palette.Assistant.GestureType",
+                            gesture_type,
+                            HighlighterGestureType::kGestureCount);
 }
 
 void HighlighterController::DestroyPointerView() {
@@ -159,8 +194,16 @@
   DestroyResultView();
 }
 
+bool HighlighterController::CanStartNewGesture(ui::TouchEvent* event) {
+  return !interrupted_stroke_timer_ &&
+         FastInkPointerController::CanStartNewGesture(event);
+}
+
 void HighlighterController::DestroyHighlighterView() {
   highlighter_view_.reset();
+  // |interrupted_stroke_timer_| should never be non null when
+  // |highlighter_view_| is null.
+  interrupted_stroke_timer_.reset();
 }
 
 void HighlighterController::DestroyResultView() {
diff --git a/ash/highlighter/highlighter_controller.h b/ash/highlighter/highlighter_controller.h
index 955b719d..2455bd4 100644
--- a/ash/highlighter/highlighter_controller.h
+++ b/ash/highlighter/highlighter_controller.h
@@ -9,6 +9,10 @@
 
 #include "ash/fast_ink/fast_ink_pointer_controller.h"
 
+namespace base {
+class OneShotTimer;
+}
+
 namespace ash {
 
 class HighlighterResultView;
@@ -38,6 +42,11 @@
                          aura::Window* root_window) override;
   void UpdatePointerView(ui::TouchEvent* event) override;
   void DestroyPointerView() override;
+  bool CanStartNewGesture(ui::TouchEvent* event) override;
+
+  // Performs gesture recognition, initiates appropriate visual effects,
+  // notifies the observer if necessary.
+  void RecognizeGesture();
 
   // Destroys |highlighter_view_|, if it exists.
   void DestroyHighlighterView();
@@ -70,6 +79,10 @@
   // Recognized gesture counter withing a session.
   int recognized_gesture_counter_ = 0;
 
+  // Not null while waiting for the next event to continue an interrupted
+  // stroke.
+  std::unique_ptr<base::OneShotTimer> interrupted_stroke_timer_;
+
   DISALLOW_COPY_AND_ASSIGN(HighlighterController);
 };
 
diff --git a/ash/highlighter/highlighter_controller_test_api.cc b/ash/highlighter/highlighter_controller_test_api.cc
index b7837393..485d776 100644
--- a/ash/highlighter/highlighter_controller_test_api.cc
+++ b/ash/highlighter/highlighter_controller_test_api.cc
@@ -28,6 +28,11 @@
   instance_->DestroyPointerView();
 }
 
+void HighlighterControllerTestApi::SimulateInterruptedStrokeTimeout() {
+  instance_->interrupted_stroke_timer_->Stop();
+  instance_->RecognizeGesture();
+}
+
 bool HighlighterControllerTestApi::IsShowingHighlighter() const {
   return instance_->highlighter_view_.get();
 }
@@ -40,6 +45,11 @@
   return instance_->result_view_.get();
 }
 
+bool HighlighterControllerTestApi::IsWaitingToResumeStroke() const {
+  return instance_->interrupted_stroke_timer_ &&
+         instance_->interrupted_stroke_timer_->IsRunning();
+}
+
 const FastInkPoints& HighlighterControllerTestApi::points() const {
   return instance_->highlighter_view_->points_;
 }
diff --git a/ash/highlighter/highlighter_controller_test_api.h b/ash/highlighter/highlighter_controller_test_api.h
index ba03dd8..734ef7eb1 100644
--- a/ash/highlighter/highlighter_controller_test_api.h
+++ b/ash/highlighter/highlighter_controller_test_api.h
@@ -24,8 +24,10 @@
 
   void SetEnabled(bool enabled);
   void DestroyPointerView();
+  void SimulateInterruptedStrokeTimeout();
   bool IsShowingHighlighter() const;
   bool IsFadingAway() const;
+  bool IsWaitingToResumeStroke() const;
   bool IsShowingSelectionResult() const;
   const FastInkPoints& points() const;
   const FastInkPoints& predicted_points() const;
diff --git a/ash/highlighter/highlighter_controller_unittest.cc b/ash/highlighter/highlighter_controller_unittest.cc
index 1464743..b3f4c84a 100644
--- a/ash/highlighter/highlighter_controller_unittest.cc
+++ b/ash/highlighter/highlighter_controller_unittest.cc
@@ -207,15 +207,15 @@
 
   // A closed diamond shape is recognized
   controller_test_api_->ResetSelection();
-  GetEventGenerator().MoveTouch(gfx::Point(100, 0));
+  GetEventGenerator().MoveTouch(gfx::Point(100, 50));
   GetEventGenerator().PressTouch();
   GetEventGenerator().MoveTouch(gfx::Point(200, 150));
-  GetEventGenerator().MoveTouch(gfx::Point(100, 300));
+  GetEventGenerator().MoveTouch(gfx::Point(100, 250));
   GetEventGenerator().MoveTouch(gfx::Point(0, 150));
-  GetEventGenerator().MoveTouch(gfx::Point(100, 0));
+  GetEventGenerator().MoveTouch(gfx::Point(100, 50));
   GetEventGenerator().ReleaseTouch();
   EXPECT_TRUE(controller_test_api_->handle_selection_called());
-  EXPECT_EQ("0,0 200x300", controller_test_api_->selection().ToString());
+  EXPECT_EQ("0,50 200x200", controller_test_api_->selection().ToString());
 }
 
 // Test that stylus gesture recognition correctly handles display scaling
@@ -290,4 +290,48 @@
   EXPECT_EQ("600,200 300x400", controller_test_api_->selection().ToString());
 }
 
+// Test that a stroke interrupted close to the screen edge is treated as
+// contiguous.
+TEST_F(HighlighterControllerTest, InterruptedStroke) {
+  controller_test_api_->SetEnabled(true);
+  GetEventGenerator().EnterPenPointerMode();
+
+  UpdateDisplay("1500x1000");
+
+  // An interrupted stroke close to the screen edge should be recognized as a
+  // contiguous stroke.
+  controller_test_api_->ResetSelection();
+  GetEventGenerator().MoveTouch(gfx::Point(300, 100));
+  GetEventGenerator().PressTouch();
+  GetEventGenerator().MoveTouch(gfx::Point(0, 100));
+  GetEventGenerator().ReleaseTouch();
+  EXPECT_TRUE(controller_test_api_->IsWaitingToResumeStroke());
+  EXPECT_FALSE(controller_test_api_->handle_selection_called());
+  EXPECT_FALSE(controller_test_api_->IsFadingAway());
+
+  GetEventGenerator().MoveTouch(gfx::Point(0, 200));
+  GetEventGenerator().PressTouch();
+  GetEventGenerator().MoveTouch(gfx::Point(300, 200));
+  GetEventGenerator().ReleaseTouch();
+  EXPECT_FALSE(controller_test_api_->IsWaitingToResumeStroke());
+  EXPECT_TRUE(controller_test_api_->handle_selection_called());
+  EXPECT_EQ("0,100 300x100", controller_test_api_->selection().ToString());
+
+  // Repeat the same gesture, but simulate a timeout after the gap. This should
+  // force the gesture completion.
+  controller_test_api_->ResetSelection();
+  GetEventGenerator().MoveTouch(gfx::Point(300, 100));
+  GetEventGenerator().PressTouch();
+  GetEventGenerator().MoveTouch(gfx::Point(0, 100));
+  GetEventGenerator().ReleaseTouch();
+  EXPECT_TRUE(controller_test_api_->IsWaitingToResumeStroke());
+  EXPECT_FALSE(controller_test_api_->handle_selection_called());
+  EXPECT_FALSE(controller_test_api_->IsFadingAway());
+
+  controller_test_api_->SimulateInterruptedStrokeTimeout();
+  EXPECT_FALSE(controller_test_api_->IsWaitingToResumeStroke());
+  EXPECT_TRUE(controller_test_api_->handle_selection_called());
+  EXPECT_TRUE(controller_test_api_->IsFadingAway());
+}
+
 }  // namespace ash
diff --git a/ash/highlighter/highlighter_view.cc b/ash/highlighter/highlighter_view.cc
index 71acb6cd..3a1a9ad 100644
--- a/ash/highlighter/highlighter_view.cc
+++ b/ash/highlighter/highlighter_view.cc
@@ -109,6 +109,10 @@
   RequestRedraw();
 }
 
+void HighlighterView::AddGap() {
+  points_.AddGap();
+}
+
 void HighlighterView::Animate(const gfx::PointF& pivot,
                               HighlighterGestureType gesture_type,
                               const base::Closure& done) {
@@ -179,23 +183,24 @@
   // is exactly kPenTipHeight.
   const int height = kPenTipHeight - kPenTipWidth;
 
-  gfx::PointF previous_point;
+  FastInkPoints::FastInkPoint previous_point;
 
   for (int i = 0; i < num_points; ++i) {
-    gfx::PointF current_point;
+    FastInkPoints::FastInkPoint current_point;
     if (i < points_.GetNumberOfPoints()) {
-      current_point = points_.points()[i].location;
+      current_point = points_.points()[i];
     } else {
       current_point =
-          predicted_points_.points()[i - points_.GetNumberOfPoints()].location;
+          predicted_points_.points()[i - points_.GetNumberOfPoints()];
     }
 
-    if (i != 0) {
+    if (i != 0 && !previous_point.gap_after) {
       gfx::Rect damage_rect = InflateDamageRect(gfx::ToEnclosingRect(
-          gfx::BoundingRect(previous_point, current_point)));
+          gfx::BoundingRect(previous_point.location, current_point.location)));
       // Only draw the segment if it is touching the clip rect.
       if (clip_rect.Intersects(damage_rect)) {
-        DrawSegment(canvas, previous_point, current_point, height, flags);
+        DrawSegment(canvas, previous_point.location, current_point.location,
+                    height, flags);
       }
     }
 
diff --git a/ash/highlighter/highlighter_view.h b/ash/highlighter/highlighter_view.h
index 4c146f0a..36e8e4f8 100644
--- a/ash/highlighter/highlighter_view.h
+++ b/ash/highlighter/highlighter_view.h
@@ -41,6 +41,8 @@
 
   void AddNewPoint(const gfx::PointF& new_point, const base::TimeTicks& time);
 
+  void AddGap();
+
   void Animate(const gfx::PointF& pivot,
                HighlighterGestureType gesture_type,
                const base::Closure& done);
diff --git a/ash/rotator/screen_rotation_animator.cc b/ash/rotator/screen_rotation_animator.cc
index 326648c..8246dcf8 100644
--- a/ash/rotator/screen_rotation_animator.cc
+++ b/ash/rotator/screen_rotation_animator.cc
@@ -200,10 +200,11 @@
           rotation_request->mode) {
     StartSlowAnimation(std::move(rotation_request));
   } else {
-    std::unique_ptr<viz::CopyOutputRequest> copy_output_request =
-        viz::CopyOutputRequest::CreateRequest(
-            CreateAfterCopyCallbackBeforeRotation(std::move(rotation_request)));
-    RequestCopyScreenRotationContainerLayer(std::move(copy_output_request));
+    RequestCopyScreenRotationContainerLayer(
+        std::make_unique<viz::CopyOutputRequest>(
+            viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
+            CreateAfterCopyCallbackBeforeRotation(
+                std::move(rotation_request))));
     screen_rotation_state_ = COPY_REQUESTED;
   }
 }
@@ -278,9 +279,16 @@
   }
   // Abort animation and set the rotation to target rotation when the copy
   // request has been canceled or failed. It would fail if, for examples: a) The
-  // layer is removed from the compositor and destroye before committing the
+  // layer is removed from the compositor and destroyed before committing the
   // request to the compositor. b) The compositor is shutdown.
-  if (result->IsEmpty()) {
+  //
+  // Note that it is also possible that the compositor does not support texture
+  // mailboxes.
+  //
+  // TODO(miu): Use DCHECK(result->GetTextureMailbox()) here instead once legacy
+  // support support for Texture→SkBitmap fallback is removed.
+  // http://crbug.com/754872
+  if (result->IsEmpty() || !result->GetTextureMailbox()) {
     Shell::Get()->display_manager()->SetDisplayRotation(
         rotation_request->display_id, rotation_request->new_rotation,
         rotation_request->source);
@@ -292,10 +300,11 @@
   AddLayerAtTopOfWindowLayers(root_window_, old_layer_tree_owner_->root());
   SetRotation(rotation_request->display_id, rotation_request->old_rotation,
               rotation_request->new_rotation, rotation_request->source);
-  std::unique_ptr<viz::CopyOutputRequest> copy_output_request =
-      viz::CopyOutputRequest::CreateRequest(
-          CreateAfterCopyCallbackAfterRotation(std::move(rotation_request)));
-  RequestCopyScreenRotationContainerLayer(std::move(copy_output_request));
+
+  RequestCopyScreenRotationContainerLayer(
+      std::make_unique<viz::CopyOutputRequest>(
+          viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
+          CreateAfterCopyCallbackAfterRotation(std::move(rotation_request))));
 }
 
 void ScreenRotationAnimator::OnScreenRotationContainerLayerCopiedAfterRotation(
@@ -310,9 +319,10 @@
   // for examples: a) The layer is removed from the compositor and destroye
   // before committing the request to the compositor. b) The compositor is
   // shutdown.
+  // 4) the compositor does not support texture mailboxes.
   if (RootWindowChangedForDisplayId(root_window_,
                                     rotation_request->display_id) ||
-      result->IsEmpty()) {
+      result->IsEmpty() || !result->GetTextureMailbox()) {
     ProcessAnimationQueue();
     return;
   }
@@ -332,7 +342,10 @@
     std::unique_ptr<viz::CopyOutputResult> result) {
   viz::TextureMailbox texture_mailbox;
   std::unique_ptr<viz::SingleReleaseCallback> release_callback;
-  result->TakeTexture(&texture_mailbox, &release_callback);
+  if (auto* mailbox = result->GetTextureMailbox()) {
+    texture_mailbox = *mailbox;
+    release_callback = result->TakeTextureOwnership();
+  }
   DCHECK(texture_mailbox.IsTexture());
   const gfx::Rect rect(
       GetScreenRotationContainer(root_window_)->layer()->size());
diff --git a/ash/session/session_controller.cc b/ash/session/session_controller.cc
index af5b35b..053afbf2 100644
--- a/ash/session/session_controller.cc
+++ b/ash/session/session_controller.cc
@@ -228,6 +228,10 @@
     client_->ShowMultiProfileLogin();
 }
 
+PrefService* SessionController::GetSigninScreenPrefService() const {
+  return signin_screen_prefs_.get();
+}
+
 PrefService* SessionController::GetUserPrefServiceForUser(
     const AccountId& account_id) {
   auto it = per_user_prefs_.find(account_id);
@@ -395,6 +399,11 @@
   FlushMojoForTest();
 }
 
+void SessionController::SetSigninScreenPrefServiceForTest(
+    std::unique_ptr<PrefService> prefs) {
+  OnSigninScreenPrefServiceInitialized(std::move(prefs));
+}
+
 void SessionController::ProvideUserPrefServiceForTest(
     const AccountId& account_id,
     std::unique_ptr<PrefService> pref_service) {
@@ -559,16 +568,14 @@
   if (!pref_service)
     return;
 
+  DCHECK(!signin_screen_prefs_);
   signin_screen_prefs_ = std::move(pref_service);
 
   // The signin profile should be initialized before any user profile.
   DCHECK(!last_active_user_prefs_);
 
-  // Chrome's ProfileManager::GetActiveUserProfile() can return the signin
-  // screen profile, so do the same thing here with signin screen profile prefs.
-  last_active_user_prefs_ = signin_screen_prefs_.get();
   for (auto& observer : observers_)
-    observer.OnActiveUserPrefServiceChanged(last_active_user_prefs_);
+    observer.OnSigninScreenPrefServiceInitialized(signin_screen_prefs_.get());
 }
 
 void SessionController::OnProfilePrefServiceInitialized(
diff --git a/ash/session/session_controller.h b/ash/session/session_controller.h
index a80a28ce..5727cf0 100644
--- a/ash/session/session_controller.h
+++ b/ash/session/session_controller.h
@@ -139,6 +139,10 @@
   // Show the multi-profile login UI to add another user to this session.
   void ShowMultiProfileLogin();
 
+  // Returns the PrefService used at the signin screen, which is tied to an
+  // incognito profile in chrome and is valid until the browser exits.
+  PrefService* GetSigninScreenPrefService() const;
+
   // Returns the PrefService for |account_id| or null if one does not exist.
   PrefService* GetUserPrefServiceForUser(const AccountId& account_id);
 
@@ -173,6 +177,7 @@
   void ClearUserSessionsForTest();
   void FlushMojoForTest();
   void LockScreenAndFlushForTest();
+  void SetSigninScreenPrefServiceForTest(std::unique_ptr<PrefService> prefs);
   void ProvideUserPrefServiceForTest(const AccountId& account_id,
                                      std::unique_ptr<PrefService> pref_service);
 
diff --git a/ash/session/session_observer.h b/ash/session/session_observer.h
index 0217b5f7..e18e723 100644
--- a/ash/session/session_observer.h
+++ b/ash/session/session_observer.h
@@ -42,6 +42,9 @@
   // Called when the limit becomes available and when it changes.
   virtual void OnSessionLengthLimitChanged() {}
 
+  // Called when the signin screen profile |prefs| are ready.
+  virtual void OnSigninScreenPrefServiceInitialized(PrefService* prefs) {}
+
   // Called when the PrefService for the active user session changes. This can
   // be due to the active user session changing or the PrefService for the
   // currently-active user session becoming initialized. This is never called
diff --git a/ash/session/test_session_controller_client.cc b/ash/session/test_session_controller_client.cc
index 71619eeb..cb39dbf 100644
--- a/ash/session/test_session_controller_client.cc
+++ b/ash/session/test_session_controller_client.cc
@@ -62,6 +62,12 @@
 
   controller_->ClearUserSessionsForTest();
   controller_->SetSessionInfo(session_info_->Clone());
+
+  if (!controller_->GetSigninScreenPrefService()) {
+    auto pref_service = base::MakeUnique<TestingPrefServiceSimple>();
+    Shell::RegisterProfilePrefs(pref_service->registry(), true /* for_test */);
+    controller_->SetSigninScreenPrefServiceForTest(std::move(pref_service));
+  }
 }
 
 void TestSessionControllerClient::SetCanLockScreen(bool can_lock) {
diff --git a/ash/shelf/shelf.cc b/ash/shelf/shelf.cc
index 9ada51cf6..f914c58 100644
--- a/ash/shelf/shelf.cc
+++ b/ash/shelf/shelf.cc
@@ -17,6 +17,7 @@
 #include "ash/shell.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "ui/app_list/presenter/app_list.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -265,6 +266,10 @@
   return shelf_layout_manager_->ProcessGestureEvent(event);
 }
 
+void Shelf::ProcessMouseWheelEvent(const ui::MouseWheelEvent& event) {
+  Shell::Get()->app_list()->ProcessMouseWheelEvent(event);
+}
+
 void Shelf::AddObserver(ShelfObserver* observer) {
   observers_.AddObserver(observer);
 }
diff --git a/ash/shelf/shelf.h b/ash/shelf/shelf.h
index 9a5c4aa..662e6e9 100644
--- a/ash/shelf/shelf.h
+++ b/ash/shelf/shelf.h
@@ -23,6 +23,7 @@
 
 namespace ui {
 class GestureEvent;
+class MouseWheelEvent;
 }
 
 namespace ash {
@@ -123,6 +124,9 @@
   // Returns true if the event was handled.
   bool ProcessGestureEvent(const ui::GestureEvent& event);
 
+  // Handles a mousewheel scroll event coming from the shelf.
+  void ProcessMouseWheelEvent(const ui::MouseWheelEvent& event);
+
   void AddObserver(ShelfObserver* observer);
   void RemoveObserver(ShelfObserver* observer);
 
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 7340aec..57bd5dc 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -35,6 +35,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "ui/accessibility/ax_node_data.h"
+#include "ui/app_list/app_list_features.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/compositor/layer.h"
@@ -1551,6 +1552,14 @@
     event->StopPropagation();
 }
 
+bool ShelfView::OnMouseWheel(const ui::MouseWheelEvent& event) {
+  if (!app_list::features::IsFullscreenAppListEnabled())
+    return false;
+
+  shelf_->ProcessMouseWheelEvent(event);
+  return true;
+}
+
 void ShelfView::ShelfItemAdded(int model_index) {
   {
     base::AutoReset<bool> cancelling_drag(&cancelling_drag_model_changed_,
@@ -1777,9 +1786,9 @@
 
   // Get any custom entries; show the context menu in AfterGetContextMenuItems.
   model_->GetShelfItemDelegate(item->id)->GetContextMenuItems(
-      display_id,
-      base::Bind(&ShelfView::AfterGetContextMenuItems,
-                 weak_factory_.GetWeakPtr(), item->id, point, source_type));
+      display_id, base::Bind(&ShelfView::AfterGetContextMenuItems,
+                             weak_factory_.GetWeakPtr(), item->id,
+                             context_menu_point, source_type));
 }
 
 void ShelfView::ShowMenu(std::unique_ptr<ui::MenuModel> menu_model,
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index d2e473d6..3e79c053 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -301,6 +301,7 @@
 
   // Overridden from ui::EventHandler:
   void OnGestureEvent(ui::GestureEvent* event) override;
+  bool OnMouseWheel(const ui::MouseWheelEvent& event) override;
 
   // Overridden from ShelfModelObserver:
   void ShelfItemAdded(int model_index) override;
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 5a1a49c..ed6e1439 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -46,11 +46,15 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_mock_time_message_loop_task_runner.h"
 #include "base/test/user_action_tester.h"
 #include "base/time/time.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "ui/app_list/app_list_features.h"
 #include "ui/app_list/presenter/app_list.h"
+#include "ui/app_list/presenter/test/test_app_list_presenter.h"
+#include "ui/app_list/views/app_list_view.h"
 #include "ui/aura/test/aura_test_base.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
@@ -2036,6 +2040,73 @@
   EXPECT_TRUE(test_api_->CloseMenu());
 }
 
+TEST_F(ShelfViewTest, MouseWheelScrollOnShelfTransitionsAppList) {
+  // Enable the Fullscreen AppList.
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      app_list::features::kEnableFullscreenAppList);
+  TestAppListPresenterImpl app_list_presenter_impl;
+  app_list_presenter_impl.ShowAndRunLoop(GetPrimaryDisplayId());
+  app_list::test::TestAppListPresenter test_app_list_presenter;
+  Shell::Get()->app_list()->SetAppListPresenter(
+      test_app_list_presenter.CreateInterfacePtrAndBind());
+  ui::test::EventGenerator& generator = GetEventGenerator();
+  gfx::Point shelf_center = shelf_view_->GetBoundsInScreen().CenterPoint();
+  generator.MoveMouseTo(shelf_center);
+
+  // Mousewheel scroll on the shelf view.
+  generator.MoveMouseWheel(0, -1);
+  RunAllPendingInMessageLoop();
+
+  ASSERT_EQ(1u, test_app_list_presenter.process_mouse_wheel_offset_count());
+}
+
+TEST_F(ShelfViewTest, MouseWheelScrollOnApplistButtonTransitionsAppList) {
+  // Enable the fullscreen app list.
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      app_list::features::kEnableFullscreenAppList);
+  TestAppListPresenterImpl app_list_presenter_impl;
+  app_list_presenter_impl.ShowAndRunLoop(GetPrimaryDisplayId());
+  app_list::test::TestAppListPresenter test_app_list_presenter;
+  Shell::Get()->app_list()->SetAppListPresenter(
+      test_app_list_presenter.CreateInterfacePtrAndBind());
+  ui::test::EventGenerator& generator = GetEventGenerator();
+  gfx::Point app_list_button_center =
+      shelf_view_->GetAppListButton()->GetBoundsInScreen().CenterPoint();
+  generator.MoveMouseTo(app_list_button_center);
+
+  // Mousewheel scroll on the AppListButton.
+  generator.MoveMouseWheel(0, -1);
+  RunAllPendingInMessageLoop();
+
+  ASSERT_EQ(1u, test_app_list_presenter.process_mouse_wheel_offset_count());
+}
+
+TEST_F(ShelfViewTest, MouseWheelScrollOnAppIconTransitionsAppList) {
+  // Enable the Fullscreen AppList.
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      app_list::features::kEnableFullscreenAppList);
+  TestAppListPresenterImpl app_list_presenter_impl;
+  app_list_presenter_impl.ShowAndRunLoop(GetPrimaryDisplayId());
+  app_list::test::TestAppListPresenter test_app_list_presenter;
+  Shell::Get()->app_list()->SetAppListPresenter(
+      test_app_list_presenter.CreateInterfacePtrAndBind());
+  ui::test::EventGenerator& generator = GetEventGenerator();
+  // Add an app button
+  ShelfID id = AddApp();
+  gfx::Point button_center =
+      GetButtonByID(id)->GetBoundsInScreen().CenterPoint();
+  generator.MoveMouseTo(button_center);
+
+  // Mousewheel scroll on the app button.
+  generator.MoveMouseWheel(0, -1);
+  RunAllPendingInMessageLoop();
+
+  ASSERT_EQ(1u, test_app_list_presenter.process_mouse_wheel_offset_count());
+}
+
 class ShelfViewVisibleBoundsTest : public ShelfViewTest,
                                    public testing::WithParamInterface<bool> {
  public:
@@ -2802,8 +2873,8 @@
               ElementsAre(views::InkDropState::DEACTIVATED));
 }
 
-// Tests ink drop state transitions for the overflow button when the user clicks
-// on it.
+// Tests ink drop state transitions for the overflow button when the user
+// clicks on it.
 TEST_F(OverflowButtonInkDropTest, MouseActivate) {
   ui::test::EventGenerator& generator = GetEventGenerator();
   gfx::Point mouse_location = GetScreenPointInsideOverflowButton();
@@ -2885,8 +2956,8 @@
   ASSERT_TRUE(test_api_->IsShowingOverflowBubble());
 }
 
-// Tests ink drop state transitions for the overflow button when the user right
-// clicks on the button to show the context menu.
+// Tests ink drop state transitions for the overflow button when the user
+// right clicks on the button to show the context menu.
 TEST_F(OverflowButtonInkDropTest, MouseContextMenu) {
   ui::test::EventGenerator& generator = GetEventGenerator();
   generator.MoveMouseTo(GetScreenPointInsideOverflowButton());
@@ -3090,8 +3161,8 @@
   }
 }
 
-// Test fixture for testing material design ink drop on overflow button when it
-// is active.
+// Test fixture for testing material design ink drop on overflow button when
+// it is active.
 class OverflowButtonActiveInkDropTest : public OverflowButtonInkDropTest {
  public:
   OverflowButtonActiveInkDropTest() {}
@@ -3265,7 +3336,8 @@
 }
 
 // Tests ink drop state transitions for the overflow button when it is active
-// and the user taps down on it and drags it out of the button bounds and back.
+// and the user taps down on it and drags it out of the button bounds and
+// back.
 TEST_F(OverflowButtonActiveInkDropTest, TouchDragOutAndBack) {
   ui::test::EventGenerator& generator = GetEventGenerator();
   generator.set_current_location(GetScreenPointInsideOverflowButton());
diff --git a/ash/shell/example_app_list_presenter.cc b/ash/shell/example_app_list_presenter.cc
index 70e90a1..0627d05 100644
--- a/ash/shell/example_app_list_presenter.cc
+++ b/ash/shell/example_app_list_presenter.cc
@@ -77,5 +77,7 @@
 void ExampleAppListPresenter::EndDragFromShelf(
     app_list::mojom::AppListState app_list_state) {}
 
+void ExampleAppListPresenter::ProcessMouseWheelOffset(int offset) {}
+
 }  // namespace shell
 }  // namespace ash
diff --git a/ash/shell/example_app_list_presenter.h b/ash/shell/example_app_list_presenter.h
index 599b6fb..8452ed8 100644
--- a/ash/shell/example_app_list_presenter.h
+++ b/ash/shell/example_app_list_presenter.h
@@ -31,6 +31,7 @@
   void UpdateYPositionAndOpacity(int new_y_position,
                                  float background_opacity) override;
   void EndDragFromShelf(app_list::mojom::AppListState app_list_state) override;
+  void ProcessMouseWheelOffset(int offset) override;
 
  private:
   mojo::Binding<app_list::mojom::AppListPresenter> binding_;
diff --git a/ash/system/bluetooth/bluetooth_power_controller.h b/ash/system/bluetooth/bluetooth_power_controller.h
index fda86fd..c2622290 100644
--- a/ash/system/bluetooth/bluetooth_power_controller.h
+++ b/ash/system/bluetooth/bluetooth_power_controller.h
@@ -63,6 +63,10 @@
   void AdapterPresentChanged(device::BluetoothAdapter* adapter,
                              bool present) override;
 
+  device::BluetoothAdapter* bluetooth_adapter_for_test() {
+    return bluetooth_adapter_.get();
+  }
+
  private:
   friend class BluetoothPowerControllerTest;
 
diff --git a/ash/system/bluetooth/bluetooth_power_controller_unittest.cc b/ash/system/bluetooth/bluetooth_power_controller_unittest.cc
index 9ea3f063..70bca56 100644
--- a/ash/system/bluetooth/bluetooth_power_controller_unittest.cc
+++ b/ash/system/bluetooth/bluetooth_power_controller_unittest.cc
@@ -5,10 +5,12 @@
 #include "ash/system/bluetooth/bluetooth_power_controller.h"
 
 #include "ash/public/cpp/ash_pref_names.h"
+#include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
 #include "ash/test_shell_delegate.h"
+#include "base/run_loop.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
@@ -20,23 +22,39 @@
 constexpr char kUser1Email[] = "user1@bluetooth";
 constexpr bool kUserFirstLogin = true;
 
+void SetupBluetoothAdapter() {
+  // Set Bluetooth discovery simulation delay to 0 so the test doesn't have to
+  // wait or use timers.
+  bluez::FakeBluetoothAdapterClient* adapter_client =
+      static_cast<bluez::FakeBluetoothAdapterClient*>(
+          bluez::BluezDBusManager::Get()->GetBluetoothAdapterClient());
+  adapter_client->SetSimulationIntervalMs(0);
+
+  // Makes sure we get the callback from BluetoothAdapterFactory::GetAdapter
+  // first before running the remaining test.
+  base::RunLoop().RunUntilIdle();
+}
+
+BluetoothPowerController* GetController() {
+  return Shell::Get()->bluetooth_power_controller();
+}
+
+device::BluetoothAdapter* GetBluetoothAdapter() {
+  return GetController()->bluetooth_adapter_for_test();
+}
+
 }  // namespace
 
+// NOTE: Manually controls local state prefs and user prefs so to allow tests
+// of default pref values before the pref service initialization notifications.
 class BluetoothPowerControllerTest : public NoSessionAshTestBase {
  public:
-  BluetoothPowerControllerTest() {}
+  BluetoothPowerControllerTest() { disable_provide_local_state(); }
   ~BluetoothPowerControllerTest() override {}
 
   void SetUp() override {
     NoSessionAshTestBase::SetUp();
 
-    // Set Bluetooth discovery simulation delay to 0 so the test doesn't have to
-    // wait or use timers.
-    bluez::FakeBluetoothAdapterClient* adapter_client =
-        static_cast<bluez::FakeBluetoothAdapterClient*>(
-            bluez::BluezDBusManager::Get()->GetBluetoothAdapterClient());
-    adapter_client->SetSimulationIntervalMs(0);
-
     BluetoothPowerController::RegisterProfilePrefs(
         active_user_prefs_.registry());
     BluetoothPowerController::RegisterLocalStatePrefs(
@@ -44,9 +62,7 @@
 
     GetController()->local_state_pref_service_ = &local_state_prefs_;
 
-    // Makes sure we get the callback from BluetoothAdapterFactory::GetAdapter
-    // first before running the remaining test.
-    RunAllPendingInMessageLoop();
+    SetupBluetoothAdapter();
   }
 
   void AddUserSessionAndStartWatchingPrefsChanges(
@@ -65,20 +81,12 @@
   }
 
  protected:
-  BluetoothPowerController* GetController() {
-    return Shell::Get()->bluetooth_power_controller();
-  }
-
-  device::BluetoothAdapter* GetBluetoothAdapter() {
-    return Shell::Get()->bluetooth_power_controller()->bluetooth_adapter_.get();
-  }
-
   void ApplyBluetoothLocalStatePref() {
-    Shell::Get()->bluetooth_power_controller()->ApplyBluetoothLocalStatePref();
+    GetController()->ApplyBluetoothLocalStatePref();
   }
 
   void ApplyBluetoothPrimaryUserPref() {
-    Shell::Get()->bluetooth_power_controller()->ApplyBluetoothPrimaryUserPref();
+    GetController()->ApplyBluetoothPrimaryUserPref();
   }
 
   TestingPrefServiceSimple active_user_prefs_;
@@ -297,4 +305,34 @@
   EXPECT_TRUE(GetBluetoothAdapter()->IsPowered());
 }
 
+using BluetoothPowerControllerIntegrationTest = NoSessionAshTestBase;
+
+// An "integration" test using the PrefService objects provided by AshTestBase
+// and TestSessionControllerClient to provide a closer simulation of production
+// login behavior. http://crbug.com/762567
+TEST_F(BluetoothPowerControllerIntegrationTest, Basics) {
+  SetupBluetoothAdapter();
+  device::BluetoothAdapter* adapter = GetBluetoothAdapter();
+
+  // Verify toggling bluetooth before login.
+  PrefService* local_state = Shell::Get()->GetLocalStatePrefService();
+  GetController()->ToggleBluetoothEnabled();
+  EXPECT_TRUE(local_state->GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
+  EXPECT_TRUE(adapter->IsPowered());
+  GetController()->ToggleBluetoothEnabled();
+  EXPECT_FALSE(local_state->GetBoolean(prefs::kSystemBluetoothAdapterEnabled));
+  EXPECT_FALSE(adapter->IsPowered());
+
+  // Verify toggling bluetooth after login.
+  SimulateUserLogin(kUser1Email);
+  PrefService* user_prefs =
+      Shell::Get()->session_controller()->GetLastActiveUserPrefService();
+  GetController()->ToggleBluetoothEnabled();
+  EXPECT_TRUE(user_prefs->GetBoolean(prefs::kUserBluetoothAdapterEnabled));
+  EXPECT_TRUE(adapter->IsPowered());
+  GetController()->ToggleBluetoothEnabled();
+  EXPECT_FALSE(user_prefs->GetBoolean(prefs::kUserBluetoothAdapterEnabled));
+  EXPECT_FALSE(adapter->IsPowered());
+}
+
 }  // namespace ash
diff --git a/ash/system/network/network_state_list_detailed_view.cc b/ash/system/network/network_state_list_detailed_view.cc
index eccdb12..159a23d 100644
--- a/ash/system/network/network_state_list_detailed_view.cc
+++ b/ash/system/network/network_state_list_detailed_view.cc
@@ -365,7 +365,8 @@
 
 void NetworkStateListDetailedView::CallRequestScan() {
   VLOG(1) << "Requesting Network Scan.";
-  NetworkHandler::Get()->network_state_handler()->RequestScan();
+  NetworkHandler::Get()->network_state_handler()->RequestScan(
+      NetworkTypePattern::WiFi());
   // Periodically request a scan while this UI is open.
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
diff --git a/ash/system/web_notification/inactive_user_notification_blocker.cc b/ash/system/web_notification/inactive_user_notification_blocker.cc
new file mode 100644
index 0000000..88f05b86
--- /dev/null
+++ b/ash/system/web_notification/inactive_user_notification_blocker.cc
@@ -0,0 +1,59 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/web_notification/inactive_user_notification_blocker.h"
+
+#include "ash/session/session_controller.h"
+#include "ash/shell.h"
+#include "ash/shell_delegate.h"
+#include "ash/system/system_notifier.h"
+#include "components/signin/core/account_id/account_id.h"
+#include "ui/message_center/message_center.h"
+
+namespace ash {
+
+InactiveUserNotificationBlocker::InactiveUserNotificationBlocker(
+    message_center::MessageCenter* message_center)
+    : NotificationBlocker(message_center), scoped_observer_(this) {
+  auto* session = Shell::Get()->session_controller()->GetUserSession(0);
+  if (session)
+    active_account_id_ = session->user_info->account_id;
+}
+
+InactiveUserNotificationBlocker::~InactiveUserNotificationBlocker() = default;
+
+bool InactiveUserNotificationBlocker::ShouldShowNotification(
+    const message_center::Notification& notification) const {
+  if (!Shell::Get()->shell_delegate()->IsMultiProfilesEnabled())
+    return true;
+
+  if (system_notifier::IsAshSystemNotifier(notification.notifier_id()))
+    return true;
+
+  return AccountId::FromUserEmail(notification.notifier_id().profile_id) ==
+         active_account_id_;
+}
+
+bool InactiveUserNotificationBlocker::ShouldShowNotificationAsPopup(
+    const message_center::Notification& notification) const {
+  return ShouldShowNotification(notification);
+}
+
+void InactiveUserNotificationBlocker::OnActiveUserSessionChanged(
+    const AccountId& account_id) {
+  if (active_account_id_ == account_id)
+    return;
+
+  quiet_modes_[active_account_id_] = message_center()->IsQuietMode();
+  active_account_id_ = account_id;
+  std::map<AccountId, bool>::const_iterator iter =
+      quiet_modes_.find(active_account_id_);
+  if (iter != quiet_modes_.end() &&
+      iter->second != message_center()->IsQuietMode()) {
+    message_center()->SetQuietMode(iter->second);
+  }
+  NotifyBlockingStateChanged();
+}
+
+}  // namespace ash
diff --git a/ash/system/web_notification/inactive_user_notification_blocker.h b/ash/system/web_notification/inactive_user_notification_blocker.h
new file mode 100644
index 0000000..c8a8493
--- /dev/null
+++ b/ash/system/web_notification/inactive_user_notification_blocker.h
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_WEB_NOTIFICATION_INACTIVE_USER_NOTIFICATION_BLOCKER_H_
+#define ASH_SYSTEM_WEB_NOTIFICATION_INACTIVE_USER_NOTIFICATION_BLOCKER_H_
+
+#include <map>
+
+#include "ash/ash_export.h"
+#include "ash/session/session_observer.h"
+#include "base/macros.h"
+#include "components/signin/core/account_id/account_id.h"
+#include "ui/message_center/notification_blocker.h"
+
+namespace ash {
+
+// A notification blocker for per-profile stream switching.
+class ASH_EXPORT InactiveUserNotificationBlocker
+    : public message_center::NotificationBlocker,
+      public SessionObserver {
+ public:
+  explicit InactiveUserNotificationBlocker(
+      message_center::MessageCenter* message_center);
+  ~InactiveUserNotificationBlocker() override;
+
+  // message_center::NotificationBlocker:
+  bool ShouldShowNotification(
+      const message_center::Notification& notification) const override;
+  bool ShouldShowNotificationAsPopup(
+      const message_center::Notification& notification) const override;
+
+  // SessionObserver:
+  void OnActiveUserSessionChanged(const AccountId& account_id) override;
+
+ private:
+  AccountId active_account_id_;
+  std::map<AccountId, bool> quiet_modes_;
+  ScopedSessionObserver scoped_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(InactiveUserNotificationBlocker);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_WEB_NOTIFICATION_INACTIVE_USER_NOTIFICATION_BLOCKER_H_
diff --git a/ash/system/web_notification/inactive_user_notification_blocker_unittest.cc b/ash/system/web_notification/inactive_user_notification_blocker_unittest.cc
new file mode 100644
index 0000000..a544361
--- /dev/null
+++ b/ash/system/web_notification/inactive_user_notification_blocker_unittest.cc
@@ -0,0 +1,196 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/web_notification/inactive_user_notification_blocker.h"
+
+#include "ash/session/test_session_controller_client.h"
+#include "ash/shell.h"
+#include "ash/system/system_notifier.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/test_shell_delegate.h"
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/signin/core/account_id/account_id.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/notification.h"
+
+namespace ash {
+
+namespace {
+
+using base::UTF8ToUTF16;
+
+class InactiveUserNotificationBlockerTest
+    : public AshTestBase,
+      public message_center::NotificationBlocker::Observer {
+ public:
+  InactiveUserNotificationBlockerTest() {}
+  ~InactiveUserNotificationBlockerTest() override {}
+
+  // AshTestBase overrides:
+  void SetUp() override {
+    AshTestBase::SetUp();
+    TestShellDelegate* shell_delegate =
+        static_cast<TestShellDelegate*>(Shell::Get()->shell_delegate());
+    shell_delegate->set_multi_profiles_enabled(true);
+
+    test_session_controller_ = std::make_unique<TestSessionControllerClient>(
+        Shell::Get()->session_controller());
+    test_session_controller_->CreatePredefinedUserSessions(1);
+
+    blocker_ = std::make_unique<InactiveUserNotificationBlocker>(
+        message_center::MessageCenter::Get());
+    blocker_->AddObserver(this);
+  }
+
+  void TearDown() override {
+    blocker_->RemoveObserver(this);
+    blocker_.reset();
+    AshTestBase::TearDown();
+  }
+
+  // message_center::NotificationBlocker::Observer ovverrides:
+  void OnBlockingStateChanged(
+      message_center::NotificationBlocker* blocker) override {
+    state_changed_count_++;
+  }
+
+ protected:
+  const std::string GetDefaultUserId() { return "user0@tray"; }
+
+  void AddUserSession(const std::string& email) {
+    test_session_controller_->AddUserSession(email);
+  }
+
+  void SwitchActiveUser(const std::string& email) {
+    const AccountId account_id(AccountId::FromUserEmail(email));
+    test_session_controller_->SwitchActiveUser(account_id);
+  }
+
+  int GetStateChangedCountAndReset() {
+    int result = state_changed_count_;
+    state_changed_count_ = 0;
+    return result;
+  }
+
+  bool ShouldShowAsPopup(const message_center::NotifierId& notifier_id,
+                         const std::string& profile_id) {
+    message_center::NotifierId id_with_profile = notifier_id;
+    id_with_profile.profile_id = profile_id;
+
+    message_center::Notification notification(
+        message_center::NOTIFICATION_TYPE_SIMPLE, "popup-id",
+        UTF8ToUTF16("popup-title"), UTF8ToUTF16("popup-message"), gfx::Image(),
+        UTF8ToUTF16("popup-source"), GURL(), id_with_profile,
+        message_center::RichNotificationData(), nullptr);
+
+    return blocker_->ShouldShowNotificationAsPopup(notification);
+  }
+
+  bool ShouldShow(const message_center::NotifierId& notifier_id,
+                  const std::string& profile_id) {
+    message_center::NotifierId id_with_profile = notifier_id;
+    id_with_profile.profile_id = profile_id;
+
+    message_center::Notification notification(
+        message_center::NOTIFICATION_TYPE_SIMPLE, "notification-id",
+        UTF8ToUTF16("notification-title"), UTF8ToUTF16("notification-message"),
+        gfx::Image(), UTF8ToUTF16("notification-source"), GURL(),
+        id_with_profile, message_center::RichNotificationData(), nullptr);
+
+    return blocker_->ShouldShowNotification(notification);
+  }
+
+ private:
+  int state_changed_count_ = 0;
+
+  std::unique_ptr<InactiveUserNotificationBlocker> blocker_;
+
+  std::unique_ptr<TestSessionControllerClient> test_session_controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(InactiveUserNotificationBlockerTest);
+};
+
+TEST_F(InactiveUserNotificationBlockerTest, Basic) {
+  message_center::NotifierId notifier_id(
+      message_center::NotifierId::APPLICATION, "test-app");
+  // Only allowed the system notifier.
+  message_center::NotifierId ash_system_notifier(
+      message_center::NotifierId::SYSTEM_COMPONENT,
+      system_notifier::kNotifierDisplay);
+  // Other system notifiers should be treated as same as a normal notifier.
+  message_center::NotifierId random_system_notifier(
+      message_center::NotifierId::SYSTEM_COMPONENT, "random_system_component");
+
+  // Notifications from a user other than the active one (in this case, default)
+  // are generally blocked unless they're ash system notifications.
+  const std::string kInvalidUserId;
+  EXPECT_FALSE(ShouldShowAsPopup(notifier_id, kInvalidUserId));
+  EXPECT_TRUE(ShouldShowAsPopup(ash_system_notifier, kInvalidUserId));
+  EXPECT_FALSE(ShouldShowAsPopup(random_system_notifier, kInvalidUserId));
+  EXPECT_TRUE(ShouldShowAsPopup(notifier_id, GetDefaultUserId()));
+  EXPECT_FALSE(ShouldShow(notifier_id, kInvalidUserId));
+  EXPECT_TRUE(ShouldShow(ash_system_notifier, kInvalidUserId));
+  EXPECT_FALSE(ShouldShow(random_system_notifier, kInvalidUserId));
+  EXPECT_TRUE(ShouldShow(notifier_id, GetDefaultUserId()));
+  EXPECT_TRUE(ShouldShow(random_system_notifier, GetDefaultUserId()));
+
+  // Add a second user and try with a recognized but inactive user as well.
+  AddUserSession("user1@tray");
+  EXPECT_EQ(0, GetStateChangedCountAndReset());
+  EXPECT_FALSE(ShouldShowAsPopup(notifier_id, kInvalidUserId));
+  EXPECT_TRUE(ShouldShowAsPopup(ash_system_notifier, kInvalidUserId));
+  EXPECT_FALSE(ShouldShowAsPopup(random_system_notifier, kInvalidUserId));
+  EXPECT_TRUE(ShouldShowAsPopup(notifier_id, GetDefaultUserId()));
+  EXPECT_FALSE(ShouldShowAsPopup(notifier_id, "user1@tray"));
+  EXPECT_TRUE(ShouldShowAsPopup(random_system_notifier, GetDefaultUserId()));
+  EXPECT_FALSE(ShouldShowAsPopup(random_system_notifier, "user1@tray"));
+  EXPECT_FALSE(ShouldShow(notifier_id, kInvalidUserId));
+  EXPECT_TRUE(ShouldShow(ash_system_notifier, kInvalidUserId));
+  EXPECT_FALSE(ShouldShow(random_system_notifier, kInvalidUserId));
+  EXPECT_TRUE(ShouldShow(notifier_id, GetDefaultUserId()));
+  EXPECT_FALSE(ShouldShow(notifier_id, "user1@tray"));
+  EXPECT_TRUE(ShouldShow(random_system_notifier, GetDefaultUserId()));
+  EXPECT_FALSE(ShouldShow(random_system_notifier, "user1@tray"));
+
+  // Activate the second user and make sure the original/default user's
+  // notifications are now hidden.
+  SwitchActiveUser("user1@tray");
+  EXPECT_FALSE(ShouldShowAsPopup(notifier_id, kInvalidUserId));
+  EXPECT_TRUE(ShouldShowAsPopup(ash_system_notifier, kInvalidUserId));
+  EXPECT_FALSE(ShouldShowAsPopup(random_system_notifier, kInvalidUserId));
+  EXPECT_FALSE(ShouldShowAsPopup(notifier_id, GetDefaultUserId()));
+  EXPECT_TRUE(ShouldShowAsPopup(notifier_id, "user1@tray"));
+  EXPECT_FALSE(ShouldShowAsPopup(random_system_notifier, GetDefaultUserId()));
+  EXPECT_TRUE(ShouldShowAsPopup(random_system_notifier, "user1@tray"));
+  EXPECT_FALSE(ShouldShow(notifier_id, kInvalidUserId));
+  EXPECT_TRUE(ShouldShow(ash_system_notifier, kInvalidUserId));
+  EXPECT_FALSE(ShouldShow(random_system_notifier, kInvalidUserId));
+  EXPECT_FALSE(ShouldShow(notifier_id, GetDefaultUserId()));
+  EXPECT_TRUE(ShouldShow(notifier_id, "user1@tray"));
+  EXPECT_FALSE(ShouldShow(random_system_notifier, GetDefaultUserId()));
+  EXPECT_TRUE(ShouldShow(random_system_notifier, "user1@tray"));
+
+  // Switch back and verify the active user's notifications are once again
+  // shown.
+  SwitchActiveUser(GetDefaultUserId());
+  EXPECT_FALSE(ShouldShowAsPopup(notifier_id, kInvalidUserId));
+  EXPECT_TRUE(ShouldShowAsPopup(ash_system_notifier, kInvalidUserId));
+  EXPECT_FALSE(ShouldShowAsPopup(random_system_notifier, kInvalidUserId));
+  EXPECT_TRUE(ShouldShowAsPopup(notifier_id, GetDefaultUserId()));
+  EXPECT_FALSE(ShouldShowAsPopup(notifier_id, "user1@tray"));
+  EXPECT_TRUE(ShouldShowAsPopup(random_system_notifier, GetDefaultUserId()));
+  EXPECT_FALSE(ShouldShowAsPopup(random_system_notifier, "user1@tray"));
+  EXPECT_FALSE(ShouldShow(notifier_id, kInvalidUserId));
+  EXPECT_TRUE(ShouldShow(ash_system_notifier, kInvalidUserId));
+  EXPECT_FALSE(ShouldShow(random_system_notifier, kInvalidUserId));
+  EXPECT_TRUE(ShouldShow(notifier_id, GetDefaultUserId()));
+  EXPECT_FALSE(ShouldShow(notifier_id, "user1@tray"));
+  EXPECT_TRUE(ShouldShow(random_system_notifier, GetDefaultUserId()));
+  EXPECT_FALSE(ShouldShow(random_system_notifier, "user1@tray"));
+}
+
+}  // namespace
+
+}  // namespace ash
diff --git a/ash/system/web_notification/message_center_controller.cc b/ash/system/web_notification/message_center_controller.cc
index 72eecaa..58f2d7a 100644
--- a/ash/system/web_notification/message_center_controller.cc
+++ b/ash/system/web_notification/message_center_controller.cc
@@ -10,6 +10,7 @@
 
 MessageCenterController::MessageCenterController()
     : fullscreen_notification_blocker_(message_center::MessageCenter::Get()),
+      inactive_user_notification_blocker_(message_center::MessageCenter::Get()),
       login_notification_blocker_(message_center::MessageCenter::Get()) {}
 
 MessageCenterController::~MessageCenterController() {}
diff --git a/ash/system/web_notification/message_center_controller.h b/ash/system/web_notification/message_center_controller.h
index 257e0c3..bd6ffd49 100644
--- a/ash/system/web_notification/message_center_controller.h
+++ b/ash/system/web_notification/message_center_controller.h
@@ -6,6 +6,7 @@
 #define ASH_SYSTEM_WEB_NOTIFICATION_MESSAGE_CENTER_CONTROLLER_H_
 
 #include "ash/system/web_notification/fullscreen_notification_blocker.h"
+#include "ash/system/web_notification/inactive_user_notification_blocker.h"
 #include "ash/system/web_notification/login_state_notification_blocker.h"
 #include "base/macros.h"
 
@@ -22,6 +23,7 @@
 
  private:
   FullscreenNotificationBlocker fullscreen_notification_blocker_;
+  InactiveUserNotificationBlocker inactive_user_notification_blocker_;
   LoginStateNotificationBlocker login_notification_blocker_;
 
   DISALLOW_COPY_AND_ASSIGN(MessageCenterController);
diff --git a/base/hash.cc b/base/hash.cc
index 4af0862..ab5cebc 100644
--- a/base/hash.cc
+++ b/base/hash.cc
@@ -41,4 +41,64 @@
   return PersistentHash(str.data(), str.size());
 }
 
+// Implement hashing for pairs of at-most 32 bit integer values.
+// When size_t is 32 bits, we turn the 64-bit hash code into 32 bits by using
+// multiply-add hashing. This algorithm, as described in
+// Theorem 4.3.3 of the thesis "Über die Komplexität der Multiplikation in
+// eingeschränkten Branchingprogrammmodellen" by Woelfel, is:
+//
+//   h32(x32, y32) = (h64(x32, y32) * rand_odd64 + rand16 * 2^16) % 2^64 / 2^32
+//
+// Contact danakj@chromium.org for any questions.
+size_t HashInts32(uint32_t value1, uint32_t value2) {
+  uint64_t value1_64 = value1;
+  uint64_t hash64 = (value1_64 << 32) | value2;
+
+  if (sizeof(size_t) >= sizeof(uint64_t))
+    return static_cast<size_t>(hash64);
+
+  uint64_t odd_random = 481046412LL << 32 | 1025306955LL;
+  uint32_t shift_random = 10121U << 16;
+
+  hash64 = hash64 * odd_random + shift_random;
+  size_t high_bits =
+      static_cast<size_t>(hash64 >> (8 * (sizeof(uint64_t) - sizeof(size_t))));
+  return high_bits;
+}
+
+// Implement hashing for pairs of up-to 64-bit integer values.
+// We use the compound integer hash method to produce a 64-bit hash code, by
+// breaking the two 64-bit inputs into 4 32-bit values:
+// http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000
+// Then we reduce our result to 32 bits if required, similar to above.
+size_t HashInts64(uint64_t value1, uint64_t value2) {
+  uint32_t short_random1 = 842304669U;
+  uint32_t short_random2 = 619063811U;
+  uint32_t short_random3 = 937041849U;
+  uint32_t short_random4 = 3309708029U;
+
+  uint32_t value1a = static_cast<uint32_t>(value1 & 0xffffffff);
+  uint32_t value1b = static_cast<uint32_t>((value1 >> 32) & 0xffffffff);
+  uint32_t value2a = static_cast<uint32_t>(value2 & 0xffffffff);
+  uint32_t value2b = static_cast<uint32_t>((value2 >> 32) & 0xffffffff);
+
+  uint64_t product1 = static_cast<uint64_t>(value1a) * short_random1;
+  uint64_t product2 = static_cast<uint64_t>(value1b) * short_random2;
+  uint64_t product3 = static_cast<uint64_t>(value2a) * short_random3;
+  uint64_t product4 = static_cast<uint64_t>(value2b) * short_random4;
+
+  uint64_t hash64 = product1 + product2 + product3 + product4;
+
+  if (sizeof(size_t) >= sizeof(uint64_t))
+    return static_cast<size_t>(hash64);
+
+  uint64_t odd_random = 1578233944LL << 32 | 194370989LL;
+  uint32_t shift_random = 20591U << 16;
+
+  hash64 = hash64 * odd_random + shift_random;
+  size_t high_bits =
+      static_cast<size_t>(hash64 >> (8 * (sizeof(uint64_t) - sizeof(size_t))));
+  return high_bits;
+}
+
 }  // namespace base
diff --git a/base/hash.h b/base/hash.h
index e7771ef..a030dab1 100644
--- a/base/hash.h
+++ b/base/hash.h
@@ -36,65 +36,9 @@
 BASE_EXPORT uint32_t PersistentHash(const void* data, size_t length);
 BASE_EXPORT uint32_t PersistentHash(const std::string& str);
 
-// Implement hashing for pairs of at-most 32 bit integer values.
-// When size_t is 32 bits, we turn the 64-bit hash code into 32 bits by using
-// multiply-add hashing. This algorithm, as described in
-// Theorem 4.3.3 of the thesis "Über die Komplexität der Multiplikation in
-// eingeschränkten Branchingprogrammmodellen" by Woelfel, is:
-//
-//   h32(x32, y32) = (h64(x32, y32) * rand_odd64 + rand16 * 2^16) % 2^64 / 2^32
-//
-// Contact danakj@chromium.org for any questions.
-inline size_t HashInts32(uint32_t value1, uint32_t value2) {
-  uint64_t value1_64 = value1;
-  uint64_t hash64 = (value1_64 << 32) | value2;
-
-  if (sizeof(size_t) >= sizeof(uint64_t))
-    return static_cast<size_t>(hash64);
-
-  uint64_t odd_random = 481046412LL << 32 | 1025306955LL;
-  uint32_t shift_random = 10121U << 16;
-
-  hash64 = hash64 * odd_random + shift_random;
-  size_t high_bits =
-      static_cast<size_t>(hash64 >> (8 * (sizeof(uint64_t) - sizeof(size_t))));
-  return high_bits;
-}
-
-// Implement hashing for pairs of up-to 64-bit integer values.
-// We use the compound integer hash method to produce a 64-bit hash code, by
-// breaking the two 64-bit inputs into 4 32-bit values:
-// http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000
-// Then we reduce our result to 32 bits if required, similar to above.
-inline size_t HashInts64(uint64_t value1, uint64_t value2) {
-  uint32_t short_random1 = 842304669U;
-  uint32_t short_random2 = 619063811U;
-  uint32_t short_random3 = 937041849U;
-  uint32_t short_random4 = 3309708029U;
-
-  uint32_t value1a = static_cast<uint32_t>(value1 & 0xffffffff);
-  uint32_t value1b = static_cast<uint32_t>((value1 >> 32) & 0xffffffff);
-  uint32_t value2a = static_cast<uint32_t>(value2 & 0xffffffff);
-  uint32_t value2b = static_cast<uint32_t>((value2 >> 32) & 0xffffffff);
-
-  uint64_t product1 = static_cast<uint64_t>(value1a) * short_random1;
-  uint64_t product2 = static_cast<uint64_t>(value1b) * short_random2;
-  uint64_t product3 = static_cast<uint64_t>(value2a) * short_random3;
-  uint64_t product4 = static_cast<uint64_t>(value2b) * short_random4;
-
-  uint64_t hash64 = product1 + product2 + product3 + product4;
-
-  if (sizeof(size_t) >= sizeof(uint64_t))
-    return static_cast<size_t>(hash64);
-
-  uint64_t odd_random = 1578233944LL << 32 | 194370989LL;
-  uint32_t shift_random = 20591U << 16;
-
-  hash64 = hash64 * odd_random + shift_random;
-  size_t high_bits =
-      static_cast<size_t>(hash64 >> (8 * (sizeof(uint64_t) - sizeof(size_t))));
-  return high_bits;
-}
+// Hash pairs of 32-bit or 64-bit numbers.
+BASE_EXPORT size_t HashInts32(uint32_t value1, uint32_t value2);
+BASE_EXPORT size_t HashInts64(uint64_t value1, uint64_t value2);
 
 template <typename T1, typename T2>
 inline size_t HashInts(T1 value1, T2 value2) {
diff --git a/base/observer_list.h b/base/observer_list.h
index 0572ba6..1a07b95 100644
--- a/base/observer_list.h
+++ b/base/observer_list.h
@@ -76,7 +76,7 @@
   // Enumeration of which observers are notified.
   enum NotificationType {
     // Specifies that any observers added during notification are notified.
-    // This is the default type if non type is provided to the constructor.
+    // This is the default type if no type is provided to the constructor.
     NOTIFY_ALL,
 
     // Specifies that observers added while sending out notification are not
diff --git a/base/trace_event/heap_profiler_serialization_state.h b/base/trace_event/heap_profiler_serialization_state.h
index 3d388b20..53c5687 100644
--- a/base/trace_event/heap_profiler_serialization_state.h
+++ b/base/trace_event/heap_profiler_serialization_state.h
@@ -54,6 +54,11 @@
     return heap_profiler_breakdown_threshold_bytes_;
   }
 
+  bool is_initialized() const {
+    return stack_frame_deduplicator_ && type_name_deduplicator_ &&
+           heap_profiler_breakdown_threshold_bytes_;
+  }
+
  private:
   friend class RefCountedThreadSafe<HeapProfilerSerializationState>;
   ~HeapProfilerSerializationState();
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index 24f4921..7bba99d 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -18,6 +18,7 @@
 #include "base/debug/thread_heap_usage_tracker.h"
 #include "base/memory/ptr_util.h"
 #include "base/sequenced_task_runner.h"
+#include "base/strings/string_util.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/heap_profiler.h"
@@ -92,11 +93,53 @@
   GetterFunctPtr const getter_function;
 };
 
+void NotifyHeapProfilingEnabledOnMDPThread(
+    scoped_refptr<MemoryDumpProviderInfo> mdpinfo,
+    bool profiling_enabled) {
+  DCHECK(!mdpinfo->disabled);
+  mdpinfo->dump_provider->OnHeapProfilingEnabled(profiling_enabled);
+}
+
+inline bool ShouldEnableMDPAllocatorHooks(HeapProfilingMode mode) {
+  return (mode == kHeapProfilingModePseudo) ||
+         (mode == kHeapProfilingModeNative) ||
+         (mode == kHeapProfilingModeNoStack);
+}
+
+#if BUILDFLAG(USE_ALLOCATOR_SHIM) && !defined(OS_NACL)
 inline bool IsHeapProfilingModeEnabled(HeapProfilingMode mode) {
   return mode != kHeapProfilingModeDisabled &&
          mode != kHeapProfilingModeInvalid;
 }
 
+void EnableFilteringForPseudoStackProfiling() {
+  if (AllocationContextTracker::capture_mode() !=
+          AllocationContextTracker::CaptureMode::PSEUDO_STACK ||
+      (TraceLog::GetInstance()->enabled_modes() & TraceLog::FILTERING_MODE)) {
+    return;
+  }
+  // Create trace config with heap profiling filter.
+  std::string filter_string = JoinString(
+      {"*", TRACE_DISABLED_BY_DEFAULT("net"), TRACE_DISABLED_BY_DEFAULT("cc"),
+       MemoryDumpManager::kTraceCategory},
+      ",");
+  TraceConfigCategoryFilter category_filter;
+  category_filter.InitializeFromString(filter_string);
+
+  TraceConfig::EventFilterConfig heap_profiler_filter_config(
+      HeapProfilerEventFilter::kName);
+  heap_profiler_filter_config.SetCategoryFilter(category_filter);
+
+  TraceConfig::EventFilters filters;
+  filters.push_back(heap_profiler_filter_config);
+  TraceConfig filtering_trace_config;
+  filtering_trace_config.SetEventFilters(filters);
+
+  TraceLog::GetInstance()->SetEnabled(filtering_trace_config,
+                                      TraceLog::FILTERING_MODE);
+}
+#endif  // BUILDFLAG(USE_ALLOCATOR_SHIM) && !defined(OS_NACL)
+
 }  // namespace
 
 // static
@@ -139,11 +182,7 @@
     : is_coordinator_(false),
       tracing_process_id_(kInvalidTracingProcessId),
       dumper_registrations_ignored_for_testing_(false),
-      heap_profiling_mode_(kHeapProfilingModeDisabled) {
-  // At this point the command line may not be initialized but we try to
-  // enable the heap profiler to capture allocations as soon as possible.
-  EnableHeapProfilingIfNeeded();
-}
+      heap_profiling_mode_(kHeapProfilingModeDisabled) {}
 
 MemoryDumpManager::~MemoryDumpManager() {
   Thread* dump_thread = nullptr;
@@ -204,13 +243,6 @@
 #if BUILDFLAG(USE_ALLOCATOR_SHIM) && !defined(OS_NACL)
   bool notify_mdps = true;
 
-  // TODO(kraynov, ssid): Remove this when heap profiler will be capable to
-  // get enabled when tracing had already started.
-  if (heap_profiler_serialization_state_) {
-    LOG(ERROR) << "EnableHeapProfiling can't be used when tracing is enabled.";
-    return false;
-  }
-
   if (heap_profiling_mode_ == kHeapProfilingModeInvalid)
     return false;  // Disabled permanently.
 
@@ -225,6 +257,7 @@
     case kHeapProfilingModePseudo:
       AllocationContextTracker::SetCaptureMode(
           AllocationContextTracker::CaptureMode::PSEUDO_STACK);
+      EnableFilteringForPseudoStackProfiling();
       break;
 
     case kHeapProfilingModeNative:
@@ -250,6 +283,8 @@
         LOG(ERROR) << "ThreadHeapUsageTracker cannot be disabled.";
         return false;
       }
+      if (heap_profiling_mode_ == kHeapProfilingModePseudo)
+        TraceLog::GetInstance()->SetDisabled(TraceLog::FILTERING_MODE);
       AllocationContextTracker::SetCaptureMode(
           AllocationContextTracker::CaptureMode::DISABLED);
       heap_profiling_mode_ = kHeapProfilingModeInvalid;  // Disable permanently.
@@ -263,10 +298,18 @@
   if (heap_profiling_mode_ != kHeapProfilingModeInvalid)
     heap_profiling_mode_ = profiling_mode;
 
+  // In case tracing was already enabled, setup the serialization state before
+  // notifying mdps.
+  InitializeHeapProfilerStateIfNeededLocked();
   if (notify_mdps) {
-    for (auto mdp : dump_providers_) {
-      mdp->dump_provider->OnHeapProfilingEnabled(
-          IsHeapProfilingModeEnabled(heap_profiling_mode_));
+    bool enabled = IsHeapProfilingModeEnabled(heap_profiling_mode_);
+    for (const auto& mdpinfo : dump_providers_) {
+      const auto& task_runner = mdpinfo->task_runner
+                                    ? mdpinfo->task_runner
+                                    : GetOrCreateBgTaskRunnerLocked();
+      task_runner->PostTask(
+          FROM_HERE,
+          Bind(&NotifyHeapProfilingEnabledOnMDPThread, mdpinfo, enabled));
     }
   }
   return true;
@@ -304,34 +347,6 @@
 #endif
 
   TRACE_EVENT_WARMUP_CATEGORY(kTraceCategory);
-
-  // TODO(ssid): This should be done in EnableHeapProfiling so that we capture
-  // more allocations (crbug.com/625170).
-  if (AllocationContextTracker::capture_mode() ==
-          AllocationContextTracker::CaptureMode::PSEUDO_STACK &&
-      !(TraceLog::GetInstance()->enabled_modes() & TraceLog::FILTERING_MODE)) {
-    // Create trace config with heap profiling filter.
-    std::string filter_string = "*";
-    const char* const kFilteredCategories[] = {
-        TRACE_DISABLED_BY_DEFAULT("net"), TRACE_DISABLED_BY_DEFAULT("cc"),
-        MemoryDumpManager::kTraceCategory};
-    for (const char* cat : kFilteredCategories)
-      filter_string = filter_string + "," + cat;
-    TraceConfigCategoryFilter category_filter;
-    category_filter.InitializeFromString(filter_string);
-
-    TraceConfig::EventFilterConfig heap_profiler_filter_config(
-        HeapProfilerEventFilter::kName);
-    heap_profiler_filter_config.SetCategoryFilter(category_filter);
-
-    TraceConfig::EventFilters filters;
-    filters.push_back(heap_profiler_filter_config);
-    TraceConfig filtering_trace_config;
-    filtering_trace_config.SetEventFilters(filters);
-
-    TraceLog::GetInstance()->SetEnabled(filtering_trace_config,
-                                        TraceLog::FILTERING_MODE);
-  }
 }
 
 void MemoryDumpManager::RegisterDumpProvider(
@@ -390,7 +405,6 @@
                                      "polling must NOT be thread bound.";
   }
 
-  bool heap_profiling_enabled = false;
   {
     AutoLock lock(lock_);
     bool already_registered = !dump_providers_.insert(mdpinfo).second;
@@ -402,14 +416,15 @@
     if (options.is_fast_polling_supported)
       MemoryPeakDetector::GetInstance()->NotifyMemoryDumpProvidersChanged();
 
-    heap_profiling_enabled =
-        (heap_profiling_mode_ == kHeapProfilingModePseudo) ||
-        (heap_profiling_mode_ == kHeapProfilingModeNative) ||
-        (heap_profiling_mode_ == kHeapProfilingModeNoStack);
+    if (ShouldEnableMDPAllocatorHooks(heap_profiling_mode_)) {
+      const auto& mdp_task_runner = mdpinfo->task_runner
+                                        ? mdpinfo->task_runner
+                                        : GetOrCreateBgTaskRunnerLocked();
+      mdp_task_runner->PostTask(
+          FROM_HERE,
+          Bind(&NotifyHeapProfilingEnabledOnMDPThread, mdpinfo, true));
+    }
   }
-
-  if (heap_profiling_enabled)
-    mdp->OnHeapProfilingEnabled(true);
 }
 
 void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) {
@@ -538,9 +553,11 @@
     // MDM could have been disabled by this point destroying
     // |heap_profiler_serialization_state|. If heap profiling is enabled we
     // require session state so if heap profiling is on and session state is
-    // absent we fail the dump immediately.
+    // absent we fail the dump immediately. If heap profiler is enabled during
+    // the dump, then the dump succeeds since the dump was requested before, and
+    // the future process dumps will contain heap dumps.
     if (args.dump_type != MemoryDumpType::SUMMARY_ONLY &&
-        IsHeapProfilingModeEnabled(heap_profiling_mode_) &&
+        ShouldEnableMDPAllocatorHooks(heap_profiling_mode_) &&
         !heap_profiler_serialization_state_) {
       callback.Run(false /* success */, args.dump_guid, nullptr);
       return;
@@ -577,7 +594,7 @@
   TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported();
 
   if (pmd_async_state->pending_dump_providers.empty())
-    return FinalizeDumpAndAddToTrace(std::move(pmd_async_state));
+    return FinishAsyncProcessDump(std::move(pmd_async_state));
 
   // Read MemoryDumpProviderInfo thread safety considerations in
   // memory_dump_manager.h when accessing |mdpinfo| fields.
@@ -713,7 +730,7 @@
   SetupNextMemoryDump(std::move(pmd_async_state));
 }
 
-void MemoryDumpManager::FinalizeDumpAndAddToTrace(
+void MemoryDumpManager::FinishAsyncProcessDump(
     std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) {
   HEAP_PROFILER_SCOPED_IGNORE;
   DCHECK(pmd_async_state->pending_dump_providers.empty());
@@ -722,12 +739,12 @@
     scoped_refptr<SingleThreadTaskRunner> callback_task_runner =
         pmd_async_state->callback_task_runner;
     callback_task_runner->PostTask(
-        FROM_HERE, BindOnce(&MemoryDumpManager::FinalizeDumpAndAddToTrace,
+        FROM_HERE, BindOnce(&MemoryDumpManager::FinishAsyncProcessDump,
                             Unretained(this), Passed(&pmd_async_state)));
     return;
   }
 
-  TRACE_EVENT0(kTraceCategory, "MemoryDumpManager::FinalizeDumpAndAddToTrace");
+  TRACE_EVENT0(kTraceCategory, "MemoryDumpManager::FinishAsyncProcessDump");
 
   if (!pmd_async_state->callback.is_null()) {
     pmd_async_state->callback.Run(
@@ -743,40 +760,14 @@
 void MemoryDumpManager::SetupForTracing(
     const TraceConfig::MemoryDumpConfig& memory_dump_config) {
   AutoLock lock(lock_);
-
-  scoped_refptr<HeapProfilerSerializationState>
-      heap_profiler_serialization_state = new HeapProfilerSerializationState;
-  heap_profiler_serialization_state
+  heap_profiler_serialization_state_ = new HeapProfilerSerializationState();
+  heap_profiler_serialization_state_
       ->set_heap_profiler_breakdown_threshold_bytes(
           memory_dump_config.heap_profiler_options.breakdown_threshold_bytes);
-  if (IsHeapProfilingModeEnabled(heap_profiling_mode_)) {
-    // If heap profiling is enabled, the stack frame deduplicator and type name
-    // deduplicator will be in use. Add a metadata events to write the frames
-    // and type IDs.
-    heap_profiler_serialization_state->SetStackFrameDeduplicator(
-        WrapUnique(new StackFrameDeduplicator));
-
-    heap_profiler_serialization_state->SetTypeNameDeduplicator(
-        WrapUnique(new TypeNameDeduplicator));
-
-    TRACE_EVENT_API_ADD_METADATA_EVENT(
-        TraceLog::GetCategoryGroupEnabled("__metadata"), "stackFrames",
-        "stackFrames",
-        std::make_unique<SessionStateConvertableProxy<StackFrameDeduplicator>>(
-            heap_profiler_serialization_state,
-            &HeapProfilerSerializationState::stack_frame_deduplicator));
-
-    TRACE_EVENT_API_ADD_METADATA_EVENT(
-        TraceLog::GetCategoryGroupEnabled("__metadata"), "typeNames",
-        "typeNames",
-        std::make_unique<SessionStateConvertableProxy<TypeNameDeduplicator>>(
-            heap_profiler_serialization_state,
-            &HeapProfilerSerializationState::type_name_deduplicator));
-  }
+  InitializeHeapProfilerStateIfNeededLocked();
 
   // At this point we must have the ability to request global dumps.
   DCHECK(!request_dump_function_.is_null());
-  heap_profiler_serialization_state_ = heap_profiler_serialization_state;
 
   MemoryDumpScheduler::Config periodic_config;
   bool peak_detector_configured = false;
@@ -838,6 +829,35 @@
   heap_profiler_serialization_state_ = nullptr;
 }
 
+void MemoryDumpManager::InitializeHeapProfilerStateIfNeededLocked() {
+  lock_.AssertAcquired();
+  if (!ShouldEnableMDPAllocatorHooks(heap_profiling_mode_) ||
+      !heap_profiler_serialization_state_ ||
+      heap_profiler_serialization_state_->is_initialized()) {
+    return;
+  }
+  // If heap profiling is enabled, the stack frame deduplicator and type name
+  // deduplicator will be in use. Add a metadata events to write the frames
+  // and type IDs.
+  heap_profiler_serialization_state_->SetStackFrameDeduplicator(
+      WrapUnique(new StackFrameDeduplicator));
+  heap_profiler_serialization_state_->SetTypeNameDeduplicator(
+      WrapUnique(new TypeNameDeduplicator));
+
+  TRACE_EVENT_API_ADD_METADATA_EVENT(
+      TraceLog::GetCategoryGroupEnabled("__metadata"), "stackFrames",
+      "stackFrames",
+      std::make_unique<SessionStateConvertableProxy<StackFrameDeduplicator>>(
+          heap_profiler_serialization_state_,
+          &HeapProfilerSerializationState::stack_frame_deduplicator));
+
+  TRACE_EVENT_API_ADD_METADATA_EVENT(
+      TraceLog::GetCategoryGroupEnabled("__metadata"), "typeNames", "typeNames",
+      std::make_unique<SessionStateConvertableProxy<TypeNameDeduplicator>>(
+          heap_profiler_serialization_state_,
+          &HeapProfilerSerializationState::type_name_deduplicator));
+}
+
 MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState(
     MemoryDumpRequestArgs req_args,
     const MemoryDumpProviderInfo::OrderedSet& dump_providers,
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h
index 9512a813..a1983fa 100644
--- a/base/trace_event/memory_dump_manager.h
+++ b/base/trace_event/memory_dump_manager.h
@@ -215,7 +215,7 @@
     // Callback passed to the initial call to CreateProcessDump().
     ProcessMemoryDumpCallback callback;
 
-    // The thread on which FinalizeDumpAndAddToTrace() (and hence |callback|)
+    // The thread on which FinishAsyncProcessDump() (and hence |callback|)
     // should be invoked. This is the thread on which the initial
     // CreateProcessDump() request was called.
     const scoped_refptr<SingleThreadTaskRunner> callback_task_runner;
@@ -240,7 +240,7 @@
   static void SetInstanceForTesting(MemoryDumpManager* instance);
   static uint32_t GetDumpsSumKb(const std::string&, const ProcessMemoryDump*);
 
-  void FinalizeDumpAndAddToTrace(
+  void FinishAsyncProcessDump(
       std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state);
 
   // Lazily initializes dump_thread_ and returns its TaskRunner.
@@ -273,6 +273,10 @@
   void GetDumpProvidersForPolling(
       std::vector<scoped_refptr<MemoryDumpProviderInfo>>*);
 
+  // Initialize |heap_profiler_serialization_state_| when tracing and heap
+  // profiler are enabled.
+  void InitializeHeapProfilerStateIfNeededLocked();
+
   // Returns true if Initialize() has been called, false otherwise.
   bool is_initialized() const { return !request_dump_function_.is_null(); }
 
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index e892c2ac..d9228a1 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -839,7 +839,7 @@
   InitializeMemoryDumpManagerForInProcessTesting(false /* is_coordinator */);
   MockMemoryDumpProvider mdp1;
   MockMemoryDumpProvider mdp2;
-  RegisterDumpProvider(&mdp1, nullptr);
+  RegisterDumpProvider(&mdp1, ThreadTaskRunnerHandle::Get());
   {
     testing::InSequence sequence;
     EXPECT_CALL(mdp1, OnHeapProfilingEnabled(true)).Times(1);
@@ -852,13 +852,31 @@
   }
 
   EXPECT_TRUE(mdm_->EnableHeapProfiling(kHeapProfilingModePseudo));
+  RunLoop().RunUntilIdle();
   ASSERT_EQ(AllocationContextTracker::CaptureMode::PSEUDO_STACK,
             AllocationContextTracker::capture_mode());
   EXPECT_EQ(mdm_->GetHeapProfilingMode(), kHeapProfilingModePseudo);
-  RegisterDumpProvider(&mdp2, nullptr);
+  EXPECT_EQ(TraceLog::FILTERING_MODE, TraceLog::GetInstance()->enabled_modes());
+  RegisterDumpProvider(&mdp2, ThreadTaskRunnerHandle::Get());
+
+  TraceConfig::MemoryDumpConfig config;
+  config.heap_profiler_options.breakdown_threshold_bytes = 100;
+  mdm_->SetupForTracing(config);
+  EXPECT_EQ(config.heap_profiler_options.breakdown_threshold_bytes,
+            mdm_->heap_profiler_serialization_state_for_testing()
+                ->heap_profiler_breakdown_threshold_bytes());
+  EXPECT_TRUE(
+      mdm_->heap_profiler_serialization_state_for_testing()->is_initialized());
+  EXPECT_EQ(mdm_->GetHeapProfilingMode(), kHeapProfilingModePseudo);
+  mdm_->TeardownForTracing();
+  EXPECT_FALSE(mdm_->heap_profiler_serialization_state_for_testing());
+
   // Disable will permanently disable heap profiling.
   EXPECT_TRUE(mdm_->EnableHeapProfiling(kHeapProfilingModeDisabled));
+  RunLoop().RunUntilIdle();
   EXPECT_EQ(mdm_->GetHeapProfilingMode(), kHeapProfilingModeInvalid);
+  EXPECT_EQ(0, TraceLog::GetInstance()->enabled_modes());
+  EXPECT_FALSE(mdm_->heap_profiler_serialization_state_for_testing());
   ASSERT_EQ(AllocationContextTracker::CaptureMode::DISABLED,
             AllocationContextTracker::capture_mode());
   EXPECT_FALSE(mdm_->EnableHeapProfiling(kHeapProfilingModePseudo));
@@ -872,15 +890,28 @@
 TEST_F(MemoryDumpManagerTest, EnableHeapProfilingNoStack) {
   InitializeMemoryDumpManagerForInProcessTesting(true /* is_coordinator */);
   MockMemoryDumpProvider mdp1;
-  RegisterDumpProvider(&mdp1, nullptr);
+  RegisterDumpProvider(&mdp1, ThreadTaskRunnerHandle::Get());
   testing::InSequence sequence;
   EXPECT_CALL(mdp1, OnHeapProfilingEnabled(true)).Times(1);
   EXPECT_CALL(mdp1, OnHeapProfilingEnabled(false)).Times(1);
 
+  // Enable tracing before heap profiling.
+  TraceConfig::MemoryDumpConfig config;
+  config.heap_profiler_options.breakdown_threshold_bytes = 100;
+  mdm_->SetupForTracing(config);
+  EXPECT_EQ(config.heap_profiler_options.breakdown_threshold_bytes,
+            mdm_->heap_profiler_serialization_state_for_testing()
+                ->heap_profiler_breakdown_threshold_bytes());
+  EXPECT_FALSE(
+      mdm_->heap_profiler_serialization_state_for_testing()->is_initialized());
+
   EXPECT_TRUE(mdm_->EnableHeapProfiling(kHeapProfilingModeNoStack));
+  RunLoop().RunUntilIdle();
   ASSERT_EQ(AllocationContextTracker::CaptureMode::NO_STACK,
             AllocationContextTracker::capture_mode());
   EXPECT_EQ(mdm_->GetHeapProfilingMode(), kHeapProfilingModeNoStack);
+  EXPECT_TRUE(
+      mdm_->heap_profiler_serialization_state_for_testing()->is_initialized());
   // Do nothing when already enabled.
   EXPECT_FALSE(mdm_->EnableHeapProfiling(kHeapProfilingModeNoStack));
   EXPECT_FALSE(mdm_->EnableHeapProfiling(kHeapProfilingModePseudo));
@@ -889,6 +920,7 @@
   EXPECT_EQ(mdm_->GetHeapProfilingMode(), kHeapProfilingModeNoStack);
   // Disable will permanently disable heap profiling.
   EXPECT_TRUE(mdm_->EnableHeapProfiling(kHeapProfilingModeDisabled));
+  RunLoop().RunUntilIdle();
   ASSERT_EQ(AllocationContextTracker::CaptureMode::DISABLED,
             AllocationContextTracker::capture_mode());
   EXPECT_FALSE(mdm_->EnableHeapProfiling(kHeapProfilingModePseudo));
@@ -896,25 +928,30 @@
             AllocationContextTracker::capture_mode());
   EXPECT_EQ(mdm_->GetHeapProfilingMode(), kHeapProfilingModeInvalid);
   EXPECT_FALSE(mdm_->EnableHeapProfiling(kHeapProfilingModeDisabled));
+  RunLoop().RunUntilIdle();
   EXPECT_EQ(mdm_->GetHeapProfilingMode(), kHeapProfilingModeInvalid);
+  mdm_->TeardownForTracing();
+  EXPECT_FALSE(mdm_->heap_profiler_serialization_state_for_testing());
 }
 
 TEST_F(MemoryDumpManagerTest, EnableHeapProfilingTask) {
   InitializeMemoryDumpManagerForInProcessTesting(true /* is_coordinator */);
   MockMemoryDumpProvider mdp1;
   MockMemoryDumpProvider mdp2;
-  RegisterDumpProvider(&mdp1, nullptr);
+  RegisterDumpProvider(&mdp1, ThreadTaskRunnerHandle::Get());
   EXPECT_CALL(mdp1, OnHeapProfilingEnabled(_)).Times(0);
   EXPECT_CALL(mdp2, OnHeapProfilingEnabled(_)).Times(0);
 
   ASSERT_FALSE(base::debug::ThreadHeapUsageTracker::IsHeapTrackingEnabled());
   EXPECT_TRUE(mdm_->EnableHeapProfiling(kHeapProfilingModeTaskProfiler));
+  RunLoop().RunUntilIdle();
   ASSERT_EQ(AllocationContextTracker::CaptureMode::DISABLED,
             AllocationContextTracker::capture_mode());
-  RegisterDumpProvider(&mdp2, nullptr);
+  RegisterDumpProvider(&mdp2, ThreadTaskRunnerHandle::Get());
   EXPECT_EQ(mdm_->GetHeapProfilingMode(), kHeapProfilingModeTaskProfiler);
   ASSERT_TRUE(debug::ThreadHeapUsageTracker::IsHeapTrackingEnabled());
   TestingThreadHeapUsageTracker::DisableHeapTrackingForTesting();
+  ASSERT_FALSE(base::debug::ThreadHeapUsageTracker::IsHeapTrackingEnabled());
 }
 
 TEST_F(MemoryDumpManagerTest, EnableHeapProfilingDisableDisabled) {
@@ -928,7 +965,7 @@
 TEST_F(MemoryDumpManagerTest, EnableHeapProfilingIfNeeded) {
   InitializeMemoryDumpManagerForInProcessTesting(false /* is_coordinator */);
   MockMemoryDumpProvider mdp1;
-  RegisterDumpProvider(&mdp1, nullptr);
+  RegisterDumpProvider(&mdp1, ThreadTaskRunnerHandle::Get());
 
   // Should be noop.
   mdm_->EnableHeapProfilingIfNeeded();
@@ -946,9 +983,11 @@
   CommandLine* cmdline = CommandLine::ForCurrentProcess();
   cmdline->AppendSwitchASCII(switches::kEnableHeapProfiling, "");
   mdm_->EnableHeapProfilingIfNeeded();
+  RunLoop().RunUntilIdle();
   ASSERT_EQ(AllocationContextTracker::CaptureMode::PSEUDO_STACK,
             AllocationContextTracker::capture_mode());
   EXPECT_TRUE(mdm_->EnableHeapProfiling(kHeapProfilingModeDisabled));
+  RunLoop().RunUntilIdle();
   ASSERT_EQ(AllocationContextTracker::CaptureMode::DISABLED,
             AllocationContextTracker::capture_mode());
   EXPECT_FALSE(mdm_->EnableHeapProfiling(kHeapProfilingModeNoStack));
@@ -967,8 +1006,6 @@
   mdm_->EnableHeapProfilingIfNeeded();
   EXPECT_EQ(mdm_->GetHeapProfilingMode(), kHeapProfilingModeInvalid);
 #else
-  // EnableHeapProfilingIfNeeded should be called in |mdm_| constructor.
-  EXPECT_EQ(mdm_->GetHeapProfilingMode(), kHeapProfilingModeInvalid);
   mdm_->EnableHeapProfilingIfNeeded();
   EXPECT_EQ(mdm_->GetHeapProfilingMode(), kHeapProfilingModeInvalid);
 #endif  //  BUILDFLAG(USE_ALLOCATOR_SHIM) && !defined(OS_NACL)
diff --git a/base/trace_event/process_memory_dump.cc b/base/trace_event/process_memory_dump.cc
index f72ebe7..2ae8154 100644
--- a/base/trace_event/process_memory_dump.cc
+++ b/base/trace_event/process_memory_dump.cc
@@ -286,14 +286,9 @@
         metrics_by_context,
     base::trace_event::TraceEventMemoryOverhead& overhead,
     const char* allocator_name) {
-  if (!metrics_by_context.empty()) {
-    // We shouldn't end up here unless we're doing a detailed dump with
-    // heap profiling enabled and if that is the case tracing should be
-    // enabled which sets up the heap profiler serialization state.
-    if (!heap_profiler_serialization_state()) {
-      NOTREACHED();
-      return;
-    }
+  // The heap profiler serialization state can be null here if heap profiler was
+  // enabled when a process dump is in progress.
+  if (heap_profiler_serialization_state() && !metrics_by_context.empty()) {
     DCHECK_EQ(0ul, heap_dumps_.count(allocator_name));
     std::unique_ptr<TracedValue> heap_dump = ExportHeapDump(
         metrics_by_context, *heap_profiler_serialization_state());
diff --git a/build/android/pylib/gtest/gtest_test_instance.py b/build/android/pylib/gtest/gtest_test_instance.py
index 0020b28..3b4d0ec 100644
--- a/build/android/pylib/gtest/gtest_test_instance.py
+++ b/build/android/pylib/gtest/gtest_test_instance.py
@@ -188,7 +188,7 @@
         duration = 0 # Don't know.
 
     if log is not None:
-      if _STACK_LINE_RE.match(l):
+      if not matcher and _STACK_LINE_RE.match(l):
         stack.append(l)
       else:
         log.append(l)
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 2db4273..b8b736d 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -750,13 +750,8 @@
   import("//third_party/ijar/ijar.gni")
   import("//third_party/android_platform/config.gni")
 
-  rebased_android_sdk = rebase_path(android_sdk, root_build_dir)
-  rebased_android_sdk_build_tools =
-      rebase_path(android_sdk_build_tools, root_build_dir)
-
   android_sdk_jar = "$android_sdk/android.jar"
-  rebased_android_sdk_jar = rebase_path(android_sdk_jar, root_build_dir)
-  android_default_aapt_path = "$rebased_android_sdk_build_tools/aapt"
+  android_default_aapt_path = "$android_sdk_build_tools/aapt"
 
   template("android_lint") {
     action(target_name) {
@@ -870,23 +865,20 @@
       # http://crbug.com/725224. Fix for bots running out of memory.
       pool = "//build/toolchain:link_pool($default_toolchain)"
 
+      _output_jar_path = invoker.output_jar_path
+      _proguard_jar_path = _default_proguard_jar_path
       if (defined(invoker.proguard_jar_path)) {
         _proguard_jar_path = invoker.proguard_jar_path
-      } else {
-        _proguard_jar_path = _default_proguard_jar_path
       }
-      _output_jar_path = invoker.output_jar_path
+      _android_sdk_jar = android_sdk_jar
+      if (defined(invoker.alternative_android_sdk_jar)) {
+        _android_sdk_jar = invoker.alternative_android_sdk_jar
+      }
+
       inputs = [
+        _android_sdk_jar,
         _proguard_jar_path,
       ]
-      if (defined(invoker.alternative_android_sdk_jar)) {
-        inputs += [ invoker.alternative_android_sdk_jar ]
-        _rebased_android_sdk_jar =
-            rebase_path(invoker.alternative_android_sdk_jar)
-      } else {
-        inputs += [ android_sdk_jar ]
-        _rebased_android_sdk_jar = rebased_android_sdk_jar
-      }
       if (defined(invoker.inputs)) {
         inputs += invoker.inputs
       }
@@ -906,7 +898,7 @@
         "--output-path",
         rebase_path(_output_jar_path, root_build_dir),
         "--classpath",
-        _rebased_android_sdk_jar,
+        rebase_path(_android_sdk_jar, root_build_dir),
       ]
       if (proguard_verbose) {
         args += [ "--verbose" ]
@@ -1076,7 +1068,7 @@
           "--depfile",
           rebase_path(depfile, root_build_dir),
           "--android-sdk-tools",
-          rebased_android_sdk_build_tools,
+          rebase_path(android_sdk_build_tools, root_build_dir),
           "--main-dex-list-path",
           rebase_path(_main_dex_list_path, root_build_dir),
           "--main-dex-rules-path",
@@ -1128,7 +1120,7 @@
         "--depfile",
         rebase_path(depfile, root_build_dir),
         "--android-sdk-tools",
-        rebased_android_sdk_build_tools,
+        rebase_path(android_sdk_build_tools, root_build_dir),
         "--dex-path",
         rebased_output,
       ]
@@ -1662,36 +1654,36 @@
 
         script = "//build/android/gyp/package_resources.py"
         depfile = "${target_gen_dir}/${target_name}.d"
-        inputs = [
-          invoker.android_manifest,
-        ]
-        if (defined(_resources_zip)) {
-          inputs += [ _resources_zip ]
-        }
         outputs = [
           _resource_packaged_apk_path,
         ]
 
+        _android_aapt_path = android_default_aapt_path
         if (defined(invoker.android_aapt_path)) {
           _android_aapt_path = invoker.android_aapt_path
-        } else {
-          _android_aapt_path = android_default_aapt_path
         }
 
+        _android_sdk_jar = android_sdk_jar
         if (defined(invoker.alternative_android_sdk_jar)) {
-          _rebased_android_sdk_jar =
-              rebase_path(invoker.alternative_android_sdk_jar)
-        } else {
-          _rebased_android_sdk_jar = rebased_android_sdk_jar
+          _android_sdk_jar = invoker.alternative_android_sdk_jar
+        }
+
+        inputs = [
+          _android_aapt_path,
+          _android_sdk_jar,
+          invoker.android_manifest,
+        ]
+        if (defined(_resources_zip)) {
+          inputs += [ _resources_zip ]
         }
 
         args = [
           "--depfile",
           rebase_path(depfile, root_build_dir),
           "--android-sdk-jar",
-          _rebased_android_sdk_jar,
+          rebase_path(_android_sdk_jar, root_build_dir),
           "--aapt-path",
-          _android_aapt_path,
+          rebase_path(_android_aapt_path, root_build_dir),
           "--android-manifest",
           rebase_path(invoker.android_manifest, root_build_dir),
           "--version-code",
@@ -2791,35 +2783,34 @@
         sources += invoker.generated_resource_files
       }
 
+      _android_aapt_path = android_default_aapt_path
+      if (defined(invoker.android_aapt_path)) {
+        _android_aapt_path = invoker.android_aapt_path
+      }
+
+      _android_sdk_jar = android_sdk_jar
+      if (defined(invoker.alternative_android_sdk_jar)) {
+        _android_sdk_jar = invoker.alternative_android_sdk_jar
+      }
+
       inputs = [
         invoker.build_config,
         invoker.android_manifest,
+        _android_aapt_path,
+        _android_sdk_jar,
       ]
 
       _rebased_all_resource_dirs =
           rebase_path(_all_resource_dirs, root_build_dir)
       _rebased_build_config = rebase_path(invoker.build_config, root_build_dir)
 
-      if (defined(invoker.android_aapt_path)) {
-        _android_aapt_path = invoker.android_aapt_path
-      } else {
-        _android_aapt_path = android_default_aapt_path
-      }
-
-      if (defined(invoker.alternative_android_sdk_jar)) {
-        _rebased_android_sdk_jar =
-            rebase_path(invoker.alternative_android_sdk_jar)
-      } else {
-        _rebased_android_sdk_jar = rebased_android_sdk_jar
-      }
-
       args = [
         "--depfile",
         rebase_path(depfile, root_build_dir),
         "--android-sdk-jar",
-        _rebased_android_sdk_jar,
+        rebase_path(_android_sdk_jar, root_build_dir),
         "--aapt-path",
-        _android_aapt_path,
+        rebase_path(_android_aapt_path, root_build_dir),
         "--android-manifest",
         rebase_path(invoker.android_manifest, root_build_dir),
         "--resource-dirs=$_rebased_all_resource_dirs",
diff --git a/build/toolchain/win/midl.py b/build/toolchain/win/midl.py
index e2a3ffd6..1b9e1fc 100644
--- a/build/toolchain/win/midl.py
+++ b/build/toolchain/win/midl.py
@@ -14,16 +14,28 @@
 
 
 def ZapTimestamp(filename):
+  contents = open(filename, 'rb').read()
   # midl.exe writes timestamp 2147483647 (2^31 - 1) as creation date into its
   # outputs, but using the local timezone.  To make the output timezone-
   # independent, replace that date with a fixed string of the same length.
+  # Also blank out the minor version number.
   if filename.endswith('.tlb'):
-    lookbehind = 'Created by MIDL version 8\.01\.0622 '
+    contents = re.sub(
+        'Created by MIDL version 8\.\d\d\.\d{4} at ... Jan 1. ..:..:.. 2038',
+        'Created by MIDL version 8.xx.xxxx at a redacted point in time',
+        contents)
   else:
-    lookbehind = 'File created by MIDL compiler version 8\.01\.0622 \*/\r\n/\* '
-  contents = open(filename, 'rb').read()
-  contents = re.sub(r'(?<=%s)at ... Jan 1. ..:..:.. 2038' % lookbehind,
-                            'at a redacted point in time', contents)
+    contents = re.sub(
+        'File created by MIDL compiler version 8\.\d\d\.\d{4} \*/\r\n'
+        '/\* at ... Jan 1. ..:..:.. 2038',
+        'File created by MIDL compiler version 8.xx.xxxx */\r\n'
+        '/* at a redacted point in time',
+        contents)
+    contents = re.sub(
+        '    Oicf, W1, Zp8, env=(.....) \(32b run\), '
+        'target_arch=(AMD64|X86) 8\.\d\d\.\d{4}',
+        '    Oicf, W1, Zp8, env=\\1 (32b run), target_arch=\\2 8.xx.xxxx',
+        contents)
   open(filename, 'wb').write(contents)
 
 
@@ -36,11 +48,12 @@
 
   # Copy checked-in outputs to final location.
   THIS_DIR = os.path.abspath(os.path.dirname(__file__))
-  source = os.path.join(THIS_DIR, "..", "..", "..",
-      "third_party", "win_build_output", outdir.replace('gen/', 'midl/'))
+  source = os.path.join(THIS_DIR, '..', '..', '..',
+      'third_party', 'win_build_output', outdir.replace('gen/', 'midl/'))
   if os.path.isdir(os.path.join(source, os.path.basename(idl))):
     source = os.path.join(source, os.path.basename(idl))
   source = os.path.join(source, arch.split('.')[1])  # Append 'x86' or 'x64'.
+  source = os.path.normpath(source)
   if not is_chromoting:
     distutils.dir_util.copy_tree(source, outdir, preserve_times=False)
 
@@ -108,6 +121,8 @@
                                            open(tofile, 'U').readlines(),
                                            fromfile, tofile))
       delete_tmp_dir = False
+      print 'To rebaseline:'
+      print '  copy /y %s\* %s' % (tmp_dir, source)
       sys.exit(1)
     return 0
   finally:
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index cecbbddc..d7e388b 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -149,8 +149,6 @@
     "output/direct_renderer.h",
     "output/latency_info_swap_promise.cc",
     "output/latency_info_swap_promise.h",
-    "output/layer_quad.cc",
-    "output/layer_quad.h",
     "output/layer_tree_frame_sink.cc",
     "output/layer_tree_frame_sink.h",
     "output/layer_tree_frame_sink_client.h",
@@ -684,7 +682,6 @@
     "layers/viewport_unittest.cc",
     "output/bsp_tree_unittest.cc",
     "output/context_cache_controller_unittest.cc",
-    "output/layer_quad_unittest.cc",
     "output/layer_tree_frame_sink_unittest.cc",
     "output/overlay_unittest.cc",
     "output/renderer_pixeltest.cc",
diff --git a/cc/ipc/copy_output_result.mojom b/cc/ipc/copy_output_result.mojom
index 082cfc9..b410be7 100644
--- a/cc/ipc/copy_output_result.mojom
+++ b/cc/ipc/copy_output_result.mojom
@@ -9,10 +9,17 @@
 import "skia/public/interfaces/bitmap.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 
-// See cc/output/copy_output_result.h.
+// See components/viz/common/quads/copy_output_result.h.
+enum CopyOutputResultFormat {
+  RGBA_BITMAP,
+  RGBA_TEXTURE,
+};
+
+// See components/viz/common/quads/copy_output_result.h.
 struct CopyOutputResult {
-  gfx.mojom.Size size;
+  CopyOutputResultFormat format;
+  gfx.mojom.Rect rect;
   skia.mojom.Bitmap? bitmap;
-  TextureMailbox texture_mailbox;
+  TextureMailbox? texture_mailbox;
   TextureMailboxReleaser? releaser;
 };
diff --git a/cc/ipc/copy_output_result_struct_traits.cc b/cc/ipc/copy_output_result_struct_traits.cc
index a5e5629..1fb5d96c 100644
--- a/cc/ipc/copy_output_result_struct_traits.cc
+++ b/cc/ipc/copy_output_result_struct_traits.cc
@@ -16,7 +16,7 @@
 // attached to it goes away (i.e. StrongBinding is used).
 class TextureMailboxReleaserImpl : public cc::mojom::TextureMailboxReleaser {
  public:
-  TextureMailboxReleaserImpl(
+  explicit TextureMailboxReleaserImpl(
       std::unique_ptr<viz::SingleReleaseCallback> release_callback)
       : release_callback_(std::move(release_callback)) {
     DCHECK(release_callback_);
@@ -52,83 +52,75 @@
 namespace mojo {
 
 // static
-const SkBitmap& StructTraits<cc::mojom::CopyOutputResultDataView,
-                             std::unique_ptr<viz::CopyOutputResult>>::
-    bitmap(const std::unique_ptr<viz::CopyOutputResult>& result) {
-  static SkBitmap* null_bitmap = new SkBitmap();
-  if (!result->bitmap_)
-    return *null_bitmap;
-  return *result->bitmap_;
-}
-
-// static
 cc::mojom::TextureMailboxReleaserPtr
 StructTraits<cc::mojom::CopyOutputResultDataView,
              std::unique_ptr<viz::CopyOutputResult>>::
     releaser(const std::unique_ptr<viz::CopyOutputResult>& result) {
-  if (!result->release_callback_)
-    return {};
   cc::mojom::TextureMailboxReleaserPtr releaser;
-  auto impl = std::make_unique<TextureMailboxReleaserImpl>(
-      std::move(result->release_callback_));
-  MakeStrongBinding(std::move(impl), MakeRequest(&releaser));
+  if (HasTextureResult(*result)) {
+    MakeStrongBinding(std::make_unique<TextureMailboxReleaserImpl>(
+                          result->TakeTextureOwnership()),
+                      MakeRequest(&releaser));
+  }
   return releaser;
 }
 
 // static
 bool StructTraits<cc::mojom::CopyOutputResultDataView,
                   std::unique_ptr<viz::CopyOutputResult>>::
+    HasTextureResult(const viz::CopyOutputResult& result) {
+  return result.GetTextureMailbox() && result.GetTextureMailbox()->IsTexture();
+}
+
+// static
+bool StructTraits<cc::mojom::CopyOutputResultDataView,
+                  std::unique_ptr<viz::CopyOutputResult>>::
     Read(cc::mojom::CopyOutputResultDataView data,
          std::unique_ptr<viz::CopyOutputResult>* out_p) {
-  // We first read into local variables and then call the appropriate
-  // constructor of viz::CopyOutputResult.
-  gfx::Size size;
-  auto bitmap = std::make_unique<SkBitmap>();
-  viz::TextureMailbox texture_mailbox;
-  std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+  // First read into local variables, and then instantiate an appropriate
+  // implementation of viz::CopyOutputResult.
+  viz::CopyOutputResult::Format format;
+  gfx::Rect rect;
 
-  if (!data.ReadSize(&size))
+  if (!data.ReadFormat(&format) || !data.ReadRect(&rect))
     return false;
 
-  if (!data.ReadBitmap(bitmap.get()))
-    return false;
+  switch (format) {
+    case viz::CopyOutputResult::Format::RGBA_BITMAP: {
+      SkBitmap bitmap;
+      if (!rect.IsEmpty() &&
+          (!data.ReadBitmap(&bitmap) || !bitmap.readyToDraw())) {
+        return false;  // Missing image data!
+      }
+      out_p->reset(new viz::CopyOutputSkBitmapResult(rect, bitmap));
+      return true;
+    }
 
-  if (!data.ReadTextureMailbox(&texture_mailbox))
-    return false;
-
-  auto releaser = data.TakeReleaser<cc::mojom::TextureMailboxReleaserPtr>();
-  if (releaser) {
-    // CopyOutputResult does not have a TextureMailboxReleaserPtr member.
-    // We use base::Bind to turn TextureMailboxReleaser::Release into a
-    // viz::ReleaseCallback.
-    release_callback = viz::SingleReleaseCallback::Create(
-        base::Bind(Release, base::Passed(&releaser)));
+    case viz::CopyOutputResult::Format::RGBA_TEXTURE: {
+      base::Optional<viz::TextureMailbox> texture_mailbox;
+      if (!data.ReadTextureMailbox(&texture_mailbox))
+        return false;
+      if (texture_mailbox && texture_mailbox->IsTexture()) {
+        auto releaser =
+            data.TakeReleaser<cc::mojom::TextureMailboxReleaserPtr>();
+        if (!releaser)
+          return false;  // Illegal to provide texture without Releaser.
+        out_p->reset(new viz::CopyOutputTextureResult(
+            rect, *texture_mailbox,
+            viz::SingleReleaseCallback::Create(
+                base::Bind(Release, base::Passed(&releaser)))));
+      } else {
+        if (!rect.IsEmpty())
+          return false;  // Missing image data!
+        out_p->reset(new viz::CopyOutputTextureResult(
+            rect, viz::TextureMailbox(), nullptr));
+      }
+      return true;
+    }
   }
 
-  // Empty result.
-  if (bitmap->isNull() && !texture_mailbox.IsTexture()) {
-    *out_p = viz::CopyOutputResult::CreateEmptyResult();
-    return true;
-  }
-
-  // Bitmap result.
-  if (!bitmap->isNull()) {
-    // We can't have both a bitmap and a texture.
-    if (texture_mailbox.IsTexture())
-      return false;
-    *out_p = viz::CopyOutputResult::CreateBitmapResult(std::move(bitmap));
-    return true;
-  }
-
-  // Texture result.
-  DCHECK(texture_mailbox.IsTexture());
-  if (size.IsEmpty())
-    return false;
-  if (!release_callback)
-    return false;
-  *out_p = viz::CopyOutputResult::CreateTextureResult(
-      size, texture_mailbox, std::move(release_callback));
-  return true;
+  NOTREACHED();
+  return false;
 }
 
 }  // namespace mojo
diff --git a/cc/ipc/copy_output_result_struct_traits.h b/cc/ipc/copy_output_result_struct_traits.h
index 8ada87c..cd265bb 100644
--- a/cc/ipc/copy_output_result_struct_traits.h
+++ b/cc/ipc/copy_output_result_struct_traits.h
@@ -15,19 +15,58 @@
 namespace mojo {
 
 template <>
+struct EnumTraits<cc::mojom::CopyOutputResultFormat,
+                  viz::CopyOutputResult::Format> {
+  static cc::mojom::CopyOutputResultFormat ToMojom(
+      viz::CopyOutputResult::Format format) {
+    switch (format) {
+      case viz::CopyOutputResult::Format::RGBA_BITMAP:
+        return cc::mojom::CopyOutputResultFormat::RGBA_BITMAP;
+      case viz::CopyOutputResult::Format::RGBA_TEXTURE:
+        return cc::mojom::CopyOutputResultFormat::RGBA_TEXTURE;
+    }
+    NOTREACHED();
+    return cc::mojom::CopyOutputResultFormat::RGBA_BITMAP;
+  }
+
+  static bool FromMojom(cc::mojom::CopyOutputResultFormat input,
+                        viz::CopyOutputResult::Format* out) {
+    switch (input) {
+      case cc::mojom::CopyOutputResultFormat::RGBA_BITMAP:
+        *out = viz::CopyOutputResult::Format::RGBA_BITMAP;
+        return true;
+      case cc::mojom::CopyOutputResultFormat::RGBA_TEXTURE:
+        *out = viz::CopyOutputResult::Format::RGBA_TEXTURE;
+        return true;
+    }
+    return false;
+  }
+};
+
+template <>
 struct StructTraits<cc::mojom::CopyOutputResultDataView,
                     std::unique_ptr<viz::CopyOutputResult>> {
-  static const gfx::Size& size(
+  static viz::CopyOutputResult::Format format(
       const std::unique_ptr<viz::CopyOutputResult>& result) {
-    return result->size_;
+    return result->format();
+  }
+
+  static const gfx::Rect& rect(
+      const std::unique_ptr<viz::CopyOutputResult>& result) {
+    return result->rect();
   }
 
   static const SkBitmap& bitmap(
-      const std::unique_ptr<viz::CopyOutputResult>& result);
-
-  static const viz::TextureMailbox& texture_mailbox(
       const std::unique_ptr<viz::CopyOutputResult>& result) {
-    return result->texture_mailbox_;
+    return result->AsSkBitmap();
+  }
+
+  static base::Optional<viz::TextureMailbox> texture_mailbox(
+      const std::unique_ptr<viz::CopyOutputResult>& result) {
+    if (HasTextureResult(*result))
+      return *result->GetTextureMailbox();
+    else
+      return base::nullopt;
   }
 
   static cc::mojom::TextureMailboxReleaserPtr releaser(
@@ -35,6 +74,9 @@
 
   static bool Read(cc::mojom::CopyOutputResultDataView data,
                    std::unique_ptr<viz::CopyOutputResult>* out_p);
+
+ private:
+  static bool HasTextureResult(const viz::CopyOutputResult& result);
 };
 
 }  // namespace mojo
diff --git a/cc/ipc/struct_traits_unittest.cc b/cc/ipc/struct_traits_unittest.cc
index 046cb126..ecae5d38 100644
--- a/cc/ipc/struct_traits_unittest.cc
+++ b/cc/ipc/struct_traits_unittest.cc
@@ -54,78 +54,97 @@
   DISALLOW_COPY_AND_ASSIGN(StructTraitsTest);
 };
 
-void CopyOutputResultCallback(base::Closure quit_closure,
-                              const gpu::SyncToken& expected_sync_token,
-                              bool expected_is_lost,
-                              const gpu::SyncToken& sync_token,
-                              bool is_lost) {
-  EXPECT_EQ(expected_sync_token, sync_token);
-  EXPECT_EQ(expected_is_lost, is_lost);
-  quit_closure.Run();
-}
-
 }  // namespace
 
-TEST_F(StructTraitsTest, CopyOutputResult_Bitmap) {
-  auto bitmap = std::make_unique<SkBitmap>();
-  bitmap->allocN32Pixels(7, 8);
-  bitmap->eraseARGB(123, 213, 77, 33);
-  auto in_bitmap = std::make_unique<SkBitmap>();
-  in_bitmap->allocN32Pixels(7, 8);
-  in_bitmap->eraseARGB(123, 213, 77, 33);
-  auto input = viz::CopyOutputResult::CreateBitmapResult(std::move(bitmap));
-  auto size = input->size();
+TEST_F(StructTraitsTest, CopyOutputResult_Empty) {
+  auto input = std::make_unique<viz::CopyOutputResult>(
+      viz::CopyOutputResult::Format::RGBA_BITMAP, gfx::Rect());
 
   mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy();
   std::unique_ptr<viz::CopyOutputResult> output;
   proxy->EchoCopyOutputResult(std::move(input), &output);
 
-  EXPECT_TRUE(output->HasBitmap());
-  EXPECT_FALSE(output->HasTexture());
-  EXPECT_EQ(size, output->size());
+  EXPECT_TRUE(output->IsEmpty());
+  EXPECT_EQ(output->format(), viz::CopyOutputResult::Format::RGBA_BITMAP);
+  EXPECT_TRUE(output->rect().IsEmpty());
+  EXPECT_FALSE(output->AsSkBitmap().readyToDraw());
+  EXPECT_EQ(output->GetTextureMailbox(), nullptr);
+}
 
-  std::unique_ptr<SkBitmap> out_bitmap = output->TakeBitmap();
-  EXPECT_EQ(in_bitmap->getSize(), out_bitmap->getSize());
-  EXPECT_EQ(0, std::memcmp(in_bitmap->getPixels(), out_bitmap->getPixels(),
-                           in_bitmap->getSize()));
+TEST_F(StructTraitsTest, CopyOutputResult_Bitmap) {
+  const gfx::Rect result_rect(42, 43, 7, 8);
+  SkBitmap bitmap;
+  const sk_sp<SkColorSpace> adobe_rgb = SkColorSpace::MakeRGB(
+      SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kAdobeRGB_Gamut);
+  bitmap.allocN32Pixels(7, 8, adobe_rgb);
+  bitmap.eraseARGB(123, 213, 77, 33);
+  auto input =
+      std::make_unique<viz::CopyOutputSkBitmapResult>(result_rect, bitmap);
+
+  mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy();
+  std::unique_ptr<viz::CopyOutputResult> output;
+  proxy->EchoCopyOutputResult(std::move(input), &output);
+
+  EXPECT_FALSE(output->IsEmpty());
+  EXPECT_EQ(output->format(), viz::CopyOutputResult::Format::RGBA_BITMAP);
+  EXPECT_EQ(output->rect(), result_rect);
+  EXPECT_EQ(output->GetTextureMailbox(), nullptr);
+
+  const SkBitmap& out_bitmap = output->AsSkBitmap();
+  EXPECT_TRUE(out_bitmap.readyToDraw());
+  EXPECT_EQ(out_bitmap.width(), result_rect.width());
+  EXPECT_EQ(out_bitmap.height(), result_rect.height());
+
+  // Check that the pixels are the same as the input and the color spaces are
+  // equivalent.
+  SkBitmap expected_bitmap;
+  expected_bitmap.allocN32Pixels(7, 8, adobe_rgb);
+  expected_bitmap.eraseARGB(123, 213, 77, 33);
+  EXPECT_EQ(expected_bitmap.getSize(), out_bitmap.getSize());
+  EXPECT_EQ(0, std::memcmp(expected_bitmap.getPixels(), out_bitmap.getPixels(),
+                           expected_bitmap.getSize()));
+  EXPECT_TRUE(SkColorSpace::Equals(expected_bitmap.colorSpace(),
+                                   out_bitmap.colorSpace()));
 }
 
 TEST_F(StructTraitsTest, CopyOutputResult_Texture) {
-  const gfx::Size size(1234, 5678);
+  const gfx::Rect result_rect(12, 34, 56, 78);
   const int8_t mailbox_name[GL_MAILBOX_SIZE_CHROMIUM] = {
       0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 9, 7, 5, 3, 1, 3};
   const uint32_t target = 3;
   gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO, 0,
                             gpu::CommandBufferId::FromUnsafeValue(0x123),
                             71234838);
-  bool is_lost = true;
   base::RunLoop run_loop;
   auto callback = viz::SingleReleaseCallback::Create(base::Bind(
-      CopyOutputResultCallback, run_loop.QuitClosure(), sync_token, is_lost));
+      [](base::Closure quit_closure, const gpu::SyncToken& expected_sync_token,
+         const gpu::SyncToken& sync_token, bool is_lost) {
+        EXPECT_EQ(expected_sync_token, sync_token);
+        EXPECT_TRUE(is_lost);
+        quit_closure.Run();
+      },
+      run_loop.QuitClosure(), sync_token));
   gpu::Mailbox mailbox;
   mailbox.SetName(mailbox_name);
   viz::TextureMailbox texture_mailbox(mailbox, gpu::SyncToken(), target);
-
-  auto input = viz::CopyOutputResult::CreateTextureResult(size, texture_mailbox,
-                                                          std::move(callback));
+  auto input = std::make_unique<viz::CopyOutputTextureResult>(
+      result_rect, texture_mailbox, std::move(callback));
 
   mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy();
   std::unique_ptr<viz::CopyOutputResult> output;
   proxy->EchoCopyOutputResult(std::move(input), &output);
 
-  EXPECT_FALSE(output->HasBitmap());
-  EXPECT_TRUE(output->HasTexture());
-  EXPECT_EQ(size, output->size());
+  EXPECT_FALSE(output->IsEmpty());
+  EXPECT_EQ(output->format(), viz::CopyOutputResult::Format::RGBA_TEXTURE);
+  EXPECT_EQ(output->rect(), result_rect);
+  ASSERT_NE(output->GetTextureMailbox(), nullptr);
+  EXPECT_EQ(output->GetTextureMailbox()->mailbox(), mailbox);
 
-  viz::TextureMailbox out_mailbox;
-  std::unique_ptr<viz::SingleReleaseCallback> out_callback;
-  output->TakeTexture(&out_mailbox, &out_callback);
-  EXPECT_EQ(mailbox, out_mailbox.mailbox());
-  out_callback->Run(sync_token, is_lost);
-  // If CopyOutputResultCallback is called (which is the intended behaviour),
-  // this will exit. Otherwise, this test will time out and fail.
-  // In CopyOutputResultCallback we verify that the given sync_token and is_lost
-  // have their intended values.
+  std::unique_ptr<viz::SingleReleaseCallback> out_callback =
+      output->TakeTextureOwnership();
+  out_callback->Run(sync_token, true /* is_lost */);
+  // If the CopyOutputResult callback is called (which is the intended
+  // behaviour), this will exit. Otherwise, this test will time out and fail.
   run_loop.Run();
 }
 
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 850fe8c..972ee260 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -374,8 +374,6 @@
     if (it != inputs_.copy_requests.end())
       inputs_.copy_requests.erase(it);
   }
-  if (request->IsEmpty())
-    return;
   inputs_.copy_requests.push_back(std::move(request));
   SetSubtreePropertyChanged();
   SetPropertyTreesNeedRebuild();
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index e727164..5ba8c40 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -1320,11 +1320,13 @@
   // Create identical requests without the source being set, and expect the
   // layer does not abort either one.
   std::unique_ptr<viz::CopyOutputRequest> request =
-      viz::CopyOutputRequest::CreateRequest(
+      std::make_unique<viz::CopyOutputRequest>(
+          viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
           base::BindOnce(&ReceiveCopyOutputResult, &result_count));
   layer->RequestCopyOfOutput(std::move(request));
   EXPECT_EQ(0, result_count);
-  request = viz::CopyOutputRequest::CreateRequest(
+  request = std::make_unique<viz::CopyOutputRequest>(
+      viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
       base::BindOnce(&ReceiveCopyOutputResult, &result_count));
   layer->RequestCopyOfOutput(std::move(request));
   EXPECT_EQ(0, result_count);
@@ -1340,28 +1342,36 @@
   // the first request using |kArbitrarySourceId1| aborts immediately when
   // the second request using |kArbitrarySourceId1| is made.
   int did_receive_first_result_from_this_source = 0;
-  request = viz::CopyOutputRequest::CreateRequest(base::BindOnce(
-      &ReceiveCopyOutputResult, &did_receive_first_result_from_this_source));
+  request = std::make_unique<viz::CopyOutputRequest>(
+      viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+      base::BindOnce(&ReceiveCopyOutputResult,
+                     &did_receive_first_result_from_this_source));
   request->set_source(kArbitrarySourceId1);
   layer->RequestCopyOfOutput(std::move(request));
   EXPECT_EQ(0, did_receive_first_result_from_this_source);
   // Make a request from a different source.
   int did_receive_result_from_different_source = 0;
-  request = viz::CopyOutputRequest::CreateRequest(base::BindOnce(
-      &ReceiveCopyOutputResult, &did_receive_result_from_different_source));
+  request = std::make_unique<viz::CopyOutputRequest>(
+      viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+      base::BindOnce(&ReceiveCopyOutputResult,
+                     &did_receive_result_from_different_source));
   request->set_source(kArbitrarySourceId2);
   layer->RequestCopyOfOutput(std::move(request));
   EXPECT_EQ(0, did_receive_result_from_different_source);
   // Make a request without specifying the source.
   int did_receive_result_from_anonymous_source = 0;
-  request = viz::CopyOutputRequest::CreateRequest(base::BindOnce(
-      &ReceiveCopyOutputResult, &did_receive_result_from_anonymous_source));
+  request = std::make_unique<viz::CopyOutputRequest>(
+      viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+      base::BindOnce(&ReceiveCopyOutputResult,
+                     &did_receive_result_from_anonymous_source));
   layer->RequestCopyOfOutput(std::move(request));
   EXPECT_EQ(0, did_receive_result_from_anonymous_source);
   // Make the second request from |kArbitrarySourceId1|.
   int did_receive_second_result_from_this_source = 0;
-  request = viz::CopyOutputRequest::CreateRequest(base::BindOnce(
-      &ReceiveCopyOutputResult, &did_receive_second_result_from_this_source));
+  request = std::make_unique<viz::CopyOutputRequest>(
+      viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+      base::BindOnce(&ReceiveCopyOutputResult,
+                     &did_receive_second_result_from_this_source));
   request->set_source(kArbitrarySourceId1);
   layer->RequestCopyOfOutput(
       std::move(request));  // First request to be aborted.
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index 5e96ae20..3ee48e6 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -1489,6 +1489,13 @@
     return;
   }
 
+  // Make sure to union the rect from this invalidation with the update_rect
+  // instead of over-writing it. We don't want to reset the update that came
+  // from the main thread.
+  gfx::Rect new_update_rect = invalidation.bounds();
+  new_update_rect.Union(update_rect());
+  SetUpdateRect(new_update_rect);
+
   invalidation_.Union(invalidation);
   tilings_->Invalidate(invalidation);
   SetNeedsPushProperties();
diff --git a/cc/output/direct_renderer.cc b/cc/output/direct_renderer.cc
index be0f7c7..54363ed 100644
--- a/cc/output/direct_renderer.cc
+++ b/cc/output/direct_renderer.cc
@@ -507,7 +507,7 @@
     // we restore the state between readbacks. http://crbug.com/99393.
     if (!first_request)
       UseRenderPass(render_pass);
-    CopyCurrentRenderPassToBitmap(std::move(copy_request));
+    CopyDrawnRenderPass(std::move(copy_request));
     first_request = false;
   }
 }
diff --git a/cc/output/direct_renderer.h b/cc/output/direct_renderer.h
index 0cec4d42..a6c2b079 100644
--- a/cc/output/direct_renderer.h
+++ b/cc/output/direct_renderer.h
@@ -172,7 +172,7 @@
   virtual void EnsureScissorTestEnabled() = 0;
   virtual void EnsureScissorTestDisabled() = 0;
   virtual void DidChangeVisibility() = 0;
-  virtual void CopyCurrentRenderPassToBitmap(
+  virtual void CopyDrawnRenderPass(
       std::unique_ptr<viz::CopyOutputRequest> request) = 0;
   virtual void SetEnableDCLayers(bool enable) = 0;
 
diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc
index 37ccbf1..2fc72ea 100644
--- a/cc/output/software_renderer.cc
+++ b/cc/output/software_renderer.cc
@@ -564,21 +564,36 @@
                             current_paint_);
 }
 
-void SoftwareRenderer::CopyCurrentRenderPassToBitmap(
+void SoftwareRenderer::CopyDrawnRenderPass(
     std::unique_ptr<viz::CopyOutputRequest> request) {
+  // SoftwareRenderer supports RGBA_BITMAP only. For legacy reasons, if a
+  // RGBA_TEXTURE request is being made, clients are prepared to accept
+  // RGBA_BITMAP results.
+  //
+  // TODO(miu): Get rid of the legacy behavior and send empty results for
+  // RGBA_TEXTURE requests once tab capture is moved into VIZ.
+  // http://crbug.com/754872
+  switch (request->result_format()) {
+    case viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP:
+    case viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE:
+      break;
+  }
+
   gfx::Rect copy_rect = current_frame()->current_render_pass->output_rect;
   if (request->has_area())
     copy_rect.Intersect(request->area());
   gfx::Rect window_copy_rect = MoveFromDrawToWindowSpace(copy_rect);
 
-  std::unique_ptr<SkBitmap> bitmap(new SkBitmap);
-  bitmap->allocPixels(SkImageInfo::MakeN32Premul(window_copy_rect.width(),
-                                                 window_copy_rect.height()));
-  if (!current_canvas_->readPixels(*bitmap, window_copy_rect.x(),
+  SkBitmap bitmap;
+  bitmap.allocPixels(SkImageInfo::MakeN32Premul(
+      window_copy_rect.width(), window_copy_rect.height(),
+      current_canvas_->imageInfo().refColorSpace()));
+  if (!current_canvas_->readPixels(bitmap, window_copy_rect.x(),
                                    window_copy_rect.y()))
-    bitmap->reset();
+    return;  // |request| auto-sends empty result on out-of-scope.
 
-  request->SendBitmapResult(std::move(bitmap));
+  request->SendResult(
+      std::make_unique<viz::CopyOutputSkBitmapResult>(copy_rect, bitmap));
 }
 
 void SoftwareRenderer::SetEnableDCLayers(bool enable) {
diff --git a/cc/output/software_renderer.h b/cc/output/software_renderer.h
index d377da0..54fbbf32 100644
--- a/cc/output/software_renderer.h
+++ b/cc/output/software_renderer.h
@@ -49,7 +49,7 @@
   bool FlippedFramebuffer() const override;
   void EnsureScissorTestEnabled() override;
   void EnsureScissorTestDisabled() override;
-  void CopyCurrentRenderPassToBitmap(
+  void CopyDrawnRenderPass(
       std::unique_ptr<viz::CopyOutputRequest> request) override;
   void SetEnableDCLayers(bool enable) override;
   void DidChangeVisibility() override;
diff --git a/cc/output/software_renderer_unittest.cc b/cc/output/software_renderer_unittest.cc
index d643d82..167de3c 100644
--- a/cc/output/software_renderer_unittest.cc
+++ b/cc/output/software_renderer_unittest.cc
@@ -63,9 +63,11 @@
     base::RunLoop loop;
 
     list->back()->copy_requests.push_back(
-        viz::CopyOutputRequest::CreateBitmapRequest(base::BindOnce(
-            &SoftwareRendererTest::SaveBitmapResult,
-            base::Unretained(&bitmap_result), loop.QuitClosure())));
+        std::make_unique<viz::CopyOutputRequest>(
+            viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+            base::BindOnce(&SoftwareRendererTest::SaveBitmapResult,
+                           base::Unretained(&bitmap_result),
+                           loop.QuitClosure())));
 
     renderer()->DrawFrame(list, device_scale_factor, viewport_size);
     loop.Run();
@@ -75,8 +77,10 @@
   static void SaveBitmapResult(std::unique_ptr<SkBitmap>* bitmap_result,
                                const base::Closure& quit_closure,
                                std::unique_ptr<viz::CopyOutputResult> result) {
-    DCHECK(result->HasBitmap());
-    *bitmap_result = result->TakeBitmap();
+    DCHECK(!result->IsEmpty());
+    DCHECK_EQ(result->format(), viz::CopyOutputResult::Format::RGBA_BITMAP);
+    *bitmap_result = std::make_unique<SkBitmap>(result->AsSkBitmap());
+    DCHECK((*bitmap_result)->readyToDraw());
     quit_closure.Run();
   }
 
diff --git a/cc/output/vulkan_renderer.cc b/cc/output/vulkan_renderer.cc
index 78afcb5..019abe2d 100644
--- a/cc/output/vulkan_renderer.cc
+++ b/cc/output/vulkan_renderer.cc
@@ -78,7 +78,7 @@
   NOTIMPLEMENTED();
 }
 
-void VulkanRenderer::CopyCurrentRenderPassToBitmap(
+void VulkanRenderer::CopyDrawnRenderPass(
     DrawingFrame* frame,
     std::unique_ptr<viz::CopyOutputRequest> request) {
   NOTIMPLEMENTED();
diff --git a/cc/output/vulkan_renderer.h b/cc/output/vulkan_renderer.h
index 66b7831..584e86d 100644
--- a/cc/output/vulkan_renderer.h
+++ b/cc/output/vulkan_renderer.h
@@ -46,7 +46,7 @@
   bool FlippedFramebuffer(const DrawingFrame* frame) const override;
   void EnsureScissorTestEnabled() override;
   void EnsureScissorTestDisabled() override;
-  void CopyCurrentRenderPassToBitmap(
+  void CopyDrawnRenderPass(
       DrawingFrame* frame,
       std::unique_ptr<viz::CopyOutputRequest> request) override;
   bool CanPartialSwap() override;
diff --git a/cc/paint/paint_image.cc b/cc/paint/paint_image.cc
index ca109ec..359958df 100644
--- a/cc/paint/paint_image.cc
+++ b/cc/paint/paint_image.cc
@@ -143,7 +143,7 @@
     content_id = paint_record_content_id_;
 
   DCHECK_NE(content_id, kInvalidContentId);
-  return FrameKey(id_, content_id, frame_index, subset_rect_);
+  return FrameKey(content_id, frame_index, subset_rect_);
 }
 
 const std::vector<FrameMetadata>& PaintImage::GetFrameMetadata() const {
@@ -176,17 +176,14 @@
   return str.str();
 }
 
-PaintImage::FrameKey::FrameKey(Id paint_image_id,
-                               ContentId content_id,
+PaintImage::FrameKey::FrameKey(ContentId content_id,
                                size_t frame_index,
                                gfx::Rect subset_rect)
-    : paint_image_id_(paint_image_id),
-      content_id_(content_id),
+    : content_id_(content_id),
       frame_index_(frame_index),
       subset_rect_(subset_rect) {
-  size_t original_hash = base::HashInts(
-      static_cast<uint64_t>(base::HashInts(paint_image_id_, content_id_)),
-      static_cast<uint64_t>(frame_index_));
+  size_t original_hash = base::HashInts(static_cast<uint64_t>(content_id_),
+                                        static_cast<uint64_t>(frame_index_));
   if (subset_rect_.IsEmpty()) {
     hash_ = original_hash;
   } else {
@@ -200,8 +197,7 @@
 }
 
 bool PaintImage::FrameKey::operator==(const FrameKey& other) const {
-  return paint_image_id_ == other.paint_image_id_ &&
-         content_id_ == other.content_id_ &&
+  return content_id_ == other.content_id_ &&
          frame_index_ == other.frame_index_ &&
          subset_rect_ == other.subset_rect_;
 }
@@ -212,8 +208,7 @@
 
 std::string PaintImage::FrameKey::ToString() const {
   std::ostringstream str;
-  str << "paint_image_id: " << paint_image_id_ << ","
-      << "content_id: " << content_id_ << ","
+  str << "content_id: " << content_id_ << ","
       << "frame_index: " << frame_index_ << ","
       << "subset_rect: " << subset_rect_.ToString();
   return str.str();
diff --git a/cc/paint/paint_image.h b/cc/paint/paint_image.h
index db1856f..e1781c3 100644
--- a/cc/paint/paint_image.h
+++ b/cc/paint/paint_image.h
@@ -47,10 +47,7 @@
 
   class CC_PAINT_EXPORT FrameKey {
    public:
-    FrameKey(Id paint_image_id,
-             ContentId content_id,
-             size_t frame_index,
-             gfx::Rect subset_rect);
+    FrameKey(ContentId content_id, size_t frame_index, gfx::Rect subset_rect);
     bool operator==(const FrameKey& other) const;
     bool operator!=(const FrameKey& other) const;
 
@@ -58,7 +55,6 @@
     std::string ToString() const;
 
    private:
-    Id paint_image_id_;
     ContentId content_id_;
     size_t frame_index_;
     // TODO(khushalsagar): Remove this when callers take care of subsetting.
diff --git a/cc/paint/solid_color_analyzer.cc b/cc/paint/solid_color_analyzer.cc
index 79b3a43..fe341d2e 100644
--- a/cc/paint/solid_color_analyzer.cc
+++ b/cc/paint/solid_color_analyzer.cc
@@ -45,13 +45,9 @@
          flags.getStyle() == PaintFlags::kFill_Style;
 }
 
-// Returns true if the specified |drawn_shape| will cover the entire canvas
-// and that the canvas is not clipped (i.e. it covers ALL of the canvas).
-template <typename T>
-bool IsFullQuad(const SkCanvas& canvas, const T& drawn_shape) {
-  if (!canvas.isClipRect())
-    return false;
-
+// Returns true if the specified drawn_rect will cover the entire canvas, and
+// that the canvas is not clipped (i.e. it covers ALL of the canvas).
+bool IsFullQuad(const SkCanvas& canvas, const SkRect& drawn_rect) {
   SkIRect clip_irect;
   if (!canvas.getDeviceClipBounds(&clip_irect))
     return false;
@@ -66,13 +62,11 @@
   if (!matrix.rectStaysRect())
     return false;
 
-  SkMatrix inverse;
-  if (!matrix.invert(&inverse))
-    return false;
-
-  SkRect clip_rect = SkRect::Make(clip_irect);
-  inverse.mapRect(&clip_rect, clip_rect);
-  return drawn_shape.contains(clip_rect);
+  SkRect device_rect;
+  matrix.mapRect(&device_rect, drawn_rect);
+  SkRect clip_rect;
+  clip_rect.set(clip_irect);
+  return device_rect.contains(clip_rect);
 }
 
 void CheckIfSolidColor(const SkCanvas& canvas,
@@ -103,19 +97,18 @@
   }
 }
 
-template <typename T>
-void CheckIfSolidShape(const SkCanvas& canvas,
-                       const T& shape,
-                       const PaintFlags& flags,
-                       bool* is_solid_color,
-                       bool* is_transparent,
-                       SkColor* color) {
+void CheckIfSolidRect(const SkCanvas& canvas,
+                      const SkRect& rect,
+                      const PaintFlags& flags,
+                      bool* is_solid_color,
+                      bool* is_transparent,
+                      SkColor* color) {
   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
-               "SolidColorAnalyzer::CheckIfSolidShape");
+               "SolidColorAnalyzer::HandleDrawRect");
   if (flags.nothingToDraw())
     return;
 
-  bool does_cover_canvas = IsFullQuad(canvas, shape);
+  bool does_cover_canvas = IsFullQuad(canvas, rect);
   SkBlendMode blendmode = flags.getBlendMode();
   if (does_cover_canvas && ActsLikeClear(blendmode, flags.getAlpha()))
     *is_transparent = true;
@@ -130,13 +123,6 @@
   }
 }
 
-bool CheckIfRRectClipCoversCanvas(const SkCanvas& canvas,
-                                  const SkRRect& rrect) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
-               "SolidColorAnalyzer::CheckIfRRectClipCoversCanvas");
-  return IsFullQuad(canvas, rrect);
-}
-
 }  // namespace
 
 base::Optional<SkColor> SolidColorAnalyzer::DetermineIfSolidColor(
@@ -174,7 +160,7 @@
   stack.emplace_back(PaintOpBuffer::CompositeIterator(buffer, offsets),
                      canvas.getTotalMatrix(), canvas.getSaveCount());
 
-  int num_draw_ops = 0;
+  int num_ops = 0;
   while (!stack.empty()) {
     auto& frame = stack.back();
     if (!frame.iter) {
@@ -205,15 +191,7 @@
       case PaintOpType::DrawOval:
       case PaintOpType::DrawPath:
         return base::nullopt;
-      // TODO(vmpstr): Add more tests on exceeding max_ops_to_analyze.
-      case PaintOpType::DrawRRect: {
-        if (++num_draw_ops > max_ops_to_analyze)
-          return base::nullopt;
-        const DrawRRectOp* rrect_op = static_cast<const DrawRRectOp*>(op);
-        CheckIfSolidShape(canvas, rrect_op->rrect, rrect_op->flags, &is_solid,
-                          &is_transparent, &color);
-        break;
-      }
+      case PaintOpType::DrawRRect:
       case PaintOpType::DrawTextBlob:
       // Anything that has to do a save layer is probably not solid. As it will
       // likely need more than one draw op.
@@ -224,27 +202,18 @@
       // cover the canvas.
       // TODO(vmpstr): We could investigate handling these.
       case PaintOpType::ClipPath:
+      case PaintOpType::ClipRRect:
         return base::nullopt;
-      case PaintOpType::ClipRRect: {
-        const ClipRRectOp* rrect_op = static_cast<const ClipRRectOp*>(op);
-        bool does_cover_canvas =
-            CheckIfRRectClipCoversCanvas(canvas, rrect_op->rrect);
-        // If the clip covers the full canvas, we can treat it as if there's no
-        // clip at all and continue, otherwise this is no longer a solid color.
-        if (!does_cover_canvas)
-          return base::nullopt;
-        break;
-      }
       case PaintOpType::DrawRect: {
-        if (++num_draw_ops > max_ops_to_analyze)
+        if (++num_ops > max_ops_to_analyze)
           return base::nullopt;
         const DrawRectOp* rect_op = static_cast<const DrawRectOp*>(op);
-        CheckIfSolidShape(canvas, rect_op->rect, rect_op->flags, &is_solid,
-                          &is_transparent, &color);
+        CheckIfSolidRect(canvas, rect_op->rect, rect_op->flags, &is_solid,
+                         &is_transparent, &color);
         break;
       }
       case PaintOpType::DrawColor: {
-        if (++num_draw_ops > max_ops_to_analyze)
+        if (++num_ops > max_ops_to_analyze)
           return base::nullopt;
         const DrawColorOp* color_op = static_cast<const DrawColorOp*>(op);
         CheckIfSolidColor(canvas, color_op->color, color_op->mode, &is_solid,
diff --git a/cc/paint/solid_color_analyzer_unittest.cc b/cc/paint/solid_color_analyzer_unittest.cc
index c73cbe9..a944cc6 100644
--- a/cc/paint/solid_color_analyzer_unittest.cc
+++ b/cc/paint/solid_color_analyzer_unittest.cc
@@ -29,11 +29,6 @@
     buffer_ = nullptr;
   }
 
-  void Reset() {
-    TearDown();
-    SetUp();
-  }
-
   void Initialize(const gfx::Rect& rect = gfx::Rect(0, 0, 100, 100)) {
     canvas_.emplace(display_item_list_.get(), gfx::RectToSkRect(rect));
     rect_ = rect;
@@ -132,21 +127,6 @@
   EXPECT_EQ(color, GetColor());
 }
 
-// TODO(vmpstr): Generalize the DrawRect test cases so that we can test both
-// Rect and RRect.
-TEST_F(SolidColorAnalyzerTest, DrawRRect) {
-  SkRect rect = SkRect::MakeWH(200, 200);
-  SkRRect rrect;
-  rrect.setRectXY(rect, 5, 5);
-  gfx::Rect canvas_rect(5, 5, 190, 190);
-  Initialize(canvas_rect);
-  PaintFlags flags;
-  SkColor color = SkColorSetARGB(255, 11, 22, 33);
-  flags.setColor(color);
-  canvas()->drawRRect(rrect, flags);
-  EXPECT_EQ(color, GetColor());
-}
-
 TEST_F(SolidColorAnalyzerTest, DrawRectClipped) {
   Initialize();
   PaintFlags flags;
@@ -298,85 +278,5 @@
   EXPECT_FALSE(IsSolidColor());
 }
 
-TEST_F(SolidColorAnalyzerTest, ClipRRectCoversCanvas) {
-  SkVector radii[4] = {
-      SkVector::Make(10.0, 15.0), SkVector::Make(20.0, 25.0),
-      SkVector::Make(30.0, 35.0), SkVector::Make(40.0, 45.0),
-  };
-
-  SkVector radii_scale[4] = {
-      SkVector::Make(100.0, 150.0), SkVector::Make(200.0, 250.0),
-      SkVector::Make(300.0, 350.0), SkVector::Make(400.0, 450.0),
-  };
-
-  int rr_size = 600;
-  int canvas_size = 255;
-  gfx::Rect canvas_rect(canvas_size, canvas_size);
-  PaintFlags flags;
-  flags.setColor(SK_ColorWHITE);
-
-  struct {
-    SkVector offset;
-    SkVector offset_scale;
-    bool expected;
-  } cases[] = {
-      // Not within bounding box of |rr|.
-      {SkVector::Make(100, 100), SkVector::Make(100, 100), false},
-
-      // Intersects UL corner.
-      {SkVector::Make(0, 0), SkVector::Make(0, 0), false},
-
-      // Between UL and UR.
-      {SkVector::Make(-50, 0), SkVector::Make(-50, -15), true},
-
-      // Intersects UR corner.
-      {SkVector::Make(canvas_size - rr_size, 0),
-       SkVector::Make(canvas_size - rr_size, 0), false},
-
-      // Between UR and LR.
-      {SkVector::Make(canvas_size - rr_size, -50), SkVector::Make(-305, -80),
-       true},
-
-      // Intersects LR corner.
-      {SkVector::Make(canvas_size - rr_size, canvas_size - rr_size),
-       SkVector::Make(canvas_size - rr_size, canvas_size - rr_size), false},
-
-      // Between LL and LR
-      {SkVector::Make(-50, canvas_size - rr_size), SkVector::Make(-205, -310),
-       true},
-
-      // Intersects LL corner
-      {SkVector::Make(0, canvas_size - rr_size),
-       SkVector::Make(0, canvas_size - rr_size), false},
-
-      // Between UL and LL
-      {SkVector::Make(0, -50), SkVector::Make(-15, -60), true},
-
-      // In center
-      {SkVector::Make(-100, -100), SkVector::Make(-100, -100), true},
-  };
-
-  for (int case_scale = 0; case_scale < 2; ++case_scale) {
-    bool scaled = case_scale > 0;
-    for (size_t i = 0; i < arraysize(cases); ++i) {
-      Reset();
-      Initialize(canvas_rect);
-
-      SkRect bounding_rect = SkRect::MakeXYWH(
-          scaled ? cases[i].offset_scale.x() : cases[i].offset.x(),
-          scaled ? cases[i].offset_scale.y() : cases[i].offset.y(), rr_size,
-          rr_size);
-
-      SkRRect rr;
-      rr.setRectRadii(bounding_rect, scaled ? radii_scale : radii);
-
-      canvas()->clipRRect(rr, SkClipOp::kIntersect, false);
-      canvas()->drawRect(RectToSkRect(canvas_rect), flags);
-      EXPECT_EQ(cases[i].expected, IsSolidColor())
-          << "Case " << i << ", " << scaled << " failed.";
-    }
-  }
-}
-
 }  // namespace
 }  // namespace cc
diff --git a/cc/quads/render_pass_unittest.cc b/cc/quads/render_pass_unittest.cc
index 6c82df50..1c06312 100644
--- a/cc/quads/render_pass_unittest.cc
+++ b/cc/quads/render_pass_unittest.cc
@@ -87,7 +87,7 @@
                filters, background_filters, color_space,
                has_transparent_background, cache_render_pass,
                has_damage_from_contributing_content);
-  pass->copy_requests.push_back(viz::CopyOutputRequest::CreateEmptyRequest());
+  pass->copy_requests.push_back(viz::CopyOutputRequest::CreateStubForTesting());
 
   // Stick a quad in the pass, this should not get copied.
   viz::SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc
index f75966b4..494ecabe 100644
--- a/cc/scheduler/scheduler_state_machine.cc
+++ b/cc/scheduler/scheduler_state_machine.cc
@@ -246,34 +246,35 @@
   if (resourceless_draw_)
     return is_layer_tree_frame_sink_lost || !can_draw_;
 
-  // These are all the cases where we normally cannot or do not want to draw
-  // but, if needs_redraw_ is true and we do not draw to make forward progress,
-  // we might deadlock with the main thread.
-  // This should be a superset of PendingActivationsShouldBeForced() since
-  // activation of the pending tree is blocked by drawing of the active tree and
-  // the main thread might be blocked on activation of the most recent commit.
+  // These are all the cases where we normally cannot or do not want
+  // to draw but, if |needs_redraw_| is true and we do not draw to
+  // make forward progress, we might deadlock with the main
+  // thread. This should be a superset of ShouldAbortCurrentFrame()
+  // since activation of the pending tree is blocked by drawing of the
+  // active tree and the main thread might be blocked on activation of
+  // the most recent commit.
   return is_layer_tree_frame_sink_lost || !can_draw_ || !visible_ ||
          begin_frame_source_paused_;
 }
 
-bool SchedulerStateMachine::PendingActivationsShouldBeForced() const {
-  // There is no output surface to trigger our activations.
-  // If we do not force activations to make forward progress, we might deadlock
-  // with the main thread.
+bool SchedulerStateMachine::ShouldAbortCurrentFrame() const {
+  // Abort the frame if there is no output surface to trigger our
+  // activations, avoiding deadlock with the main thread.
   if (layer_tree_frame_sink_state_ == LAYER_TREE_FRAME_SINK_NONE)
     return true;
 
-  // If we're not visible, we should force activation.
-  // Since we set RequiresHighResToDraw when becoming visible, we ensure that we
-  // don't checkerboard until all visible resources are done. Furthermore, if we
-  // do keep the pending tree around, when becoming visible we might activate
-  // prematurely causing RequiresHighResToDraw flag to be reset. In all cases,
-  // we can simply activate on becoming invisible since we don't need to draw
+  // If we're not visible, we should just abort the frame. Since we
+  // set RequiresHighResToDraw when becoming visible, we ensure that
+  // we don't checkerboard until all visible resources are
+  // done. Furthermore, if we do keep the pending tree around, when
+  // becoming visible we might activate prematurely causing
+  // RequiresHighResToDraw flag to be reset. In all cases, we can
+  // simply activate on becoming invisible since we don't need to draw
   // the active tree when we're in this state.
   if (!visible_)
     return true;
 
-  // Force pending activations when viz::BeginFrameSource is paused to avoid
+  // Abort the frame when viz::BeginFrameSource is paused to avoid
   // deadlocking the main thread.
   if (begin_frame_source_paused_)
     return true;
@@ -366,8 +367,7 @@
   if (active_tree_needs_first_draw_)
     return false;
 
-  // If we want to force activation, do so ASAP.
-  if (PendingActivationsShouldBeForced())
+  if (ShouldAbortCurrentFrame())
     return true;
 
   // At this point, only activate if we are ready to activate.
@@ -1014,8 +1014,8 @@
 
 bool SchedulerStateMachine::ShouldTriggerBeginImplFrameDeadlineImmediately()
     const {
-  // If we just forced activation, we should end the deadline right now.
-  if (PendingActivationsShouldBeForced() && !has_pending_tree_)
+  // If we aborted the current frame we should end the deadline right now.
+  if (ShouldAbortCurrentFrame() && !has_pending_tree_)
     return true;
 
   // Throttle the deadline on CompositorFrameAck since we wont draw and submit
diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h
index 3370c63e..9985bfe6 100644
--- a/cc/scheduler/scheduler_state_machine.h
+++ b/cc/scheduler/scheduler_state_machine.h
@@ -305,9 +305,7 @@
   bool ShouldTriggerBeginImplFrameDeadlineImmediately() const;
   bool ShouldBlockDeadlineIndefinitely() const;
 
-  // True if we need to force activations to make forward progress.
-  // TODO(sunnyps): Rename this to ShouldAbortCurrentFrame or similar.
-  bool PendingActivationsShouldBeForced() const;
+  bool ShouldAbortCurrentFrame() const;
 
   bool ShouldBeginLayerTreeFrameSinkCreation() const;
   bool ShouldDraw() const;
diff --git a/cc/scheduler/scheduler_state_machine_unittest.cc b/cc/scheduler/scheduler_state_machine_unittest.cc
index 86516f56..73725f8 100644
--- a/cc/scheduler/scheduler_state_machine_unittest.cc
+++ b/cc/scheduler/scheduler_state_machine_unittest.cc
@@ -146,8 +146,8 @@
   bool CanDraw() const { return can_draw_; }
   bool Visible() const { return visible_; }
 
-  bool PendingActivationsShouldBeForced() const {
-    return SchedulerStateMachine::PendingActivationsShouldBeForced();
+  bool ShouldAbortCurrentFrame() const {
+    return SchedulerStateMachine::ShouldAbortCurrentFrame();
   }
 
   bool has_pending_tree() const { return has_pending_tree_; }
@@ -1711,7 +1711,7 @@
 }
 
 TEST(SchedulerStateMachineTest,
-     TestPendingActivationsShouldBeForcedAfterLostLayerTreeFrameSink) {
+     TestShouldAbortCurrentFrameAfterLostLayerTreeFrameSink) {
   SchedulerSettings default_scheduler_settings;
   StateMachine state(default_scheduler_settings);
   SET_UP_STATE(state)
@@ -1726,7 +1726,7 @@
   state.NotifyReadyToCommit();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
 
-  EXPECT_TRUE(state.PendingActivationsShouldBeForced());
+  EXPECT_TRUE(state.ShouldAbortCurrentFrame());
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE);
 
   EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
@@ -1816,7 +1816,7 @@
   // because we are not visible.
   state.NotifyBeginMainFrameStarted();
   state.NotifyReadyToCommit();
-  EXPECT_TRUE(state.PendingActivationsShouldBeForced());
+  EXPECT_TRUE(state.ShouldAbortCurrentFrame());
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE);
   EXPECT_TRUE(state.active_tree_needs_first_draw());
@@ -2066,7 +2066,7 @@
 
   state.SetVisible(false);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
-  EXPECT_TRUE(state.PendingActivationsShouldBeForced());
+  EXPECT_TRUE(state.ShouldAbortCurrentFrame());
   EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineImmediately());
 }
 
@@ -2086,7 +2086,7 @@
 
   state.SetBeginFrameSourcePaused(true);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
-  EXPECT_TRUE(state.PendingActivationsShouldBeForced());
+  EXPECT_TRUE(state.ShouldAbortCurrentFrame());
   EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineImmediately());
 }
 
diff --git a/cc/test/layer_test_common.cc b/cc/test/layer_test_common.cc
index aedaf2d..5bf5e48 100644
--- a/cc/test/layer_test_common.cc
+++ b/cc/test/layer_test_common.cc
@@ -232,12 +232,9 @@
                             render_pass_.get(), &data);
 }
 
-void EmptyCopyOutputCallback(std::unique_ptr<viz::CopyOutputResult> result) {}
-
 void LayerTestCommon::LayerImplTest::RequestCopyOfOutput() {
   root_layer_for_testing()->test_properties()->copy_requests.push_back(
-      viz::CopyOutputRequest::CreateRequest(
-          base::BindOnce(&EmptyCopyOutputCallback)));
+      viz::CopyOutputRequest::CreateStubForTesting());
 }
 
 }  // namespace cc
diff --git a/cc/test/layer_tree_pixel_test.cc b/cc/test/layer_tree_pixel_test.cc
index ee5866d5..ae47bac5 100644
--- a/cc/test/layer_tree_pixel_test.cc
+++ b/cc/test/layer_tree_pixel_test.cc
@@ -90,14 +90,18 @@
 
 std::unique_ptr<viz::CopyOutputRequest>
 LayerTreePixelTest::CreateCopyOutputRequest() {
-  return viz::CopyOutputRequest::CreateBitmapRequest(base::BindOnce(
-      &LayerTreePixelTest::ReadbackResult, base::Unretained(this)));
+  return std::make_unique<viz::CopyOutputRequest>(
+      viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+      base::BindOnce(&LayerTreePixelTest::ReadbackResult,
+                     base::Unretained(this)));
 }
 
 void LayerTreePixelTest::ReadbackResult(
     std::unique_ptr<viz::CopyOutputResult> result) {
-  ASSERT_TRUE(result->HasBitmap());
-  result_bitmap_ = result->TakeBitmap();
+  ASSERT_FALSE(result->IsEmpty());
+  EXPECT_EQ(result->format(), viz::CopyOutputResult::Format::RGBA_BITMAP);
+  result_bitmap_ = std::make_unique<SkBitmap>(result->AsSkBitmap());
+  EXPECT_TRUE(result_bitmap_->readyToDraw());
   EndTest();
 }
 
@@ -224,12 +228,14 @@
   LayerTreeTest::SetupTree();
 }
 
-std::unique_ptr<SkBitmap> LayerTreePixelTest::CopyTextureMailboxToBitmap(
+SkBitmap LayerTreePixelTest::CopyTextureMailboxToBitmap(
     const gfx::Size& size,
     const viz::TextureMailbox& texture_mailbox) {
   DCHECK(texture_mailbox.IsTexture());
+
+  SkBitmap bitmap;
   if (!texture_mailbox.IsTexture())
-    return nullptr;
+    return bitmap;
 
   std::unique_ptr<gpu::GLInProcessContext> context =
       CreateTestInProcessContext();
@@ -268,10 +274,11 @@
   gl->DeleteFramebuffers(1, &fbo);
   gl->DeleteTextures(1, &texture_id);
 
-  std::unique_ptr<SkBitmap> bitmap(new SkBitmap);
-  bitmap->allocN32Pixels(size.width(), size.height());
+  bitmap.allocN32Pixels(size.width(), size.height());
+  // TODO(miu): Provide color space in this allocN32Pixels() call.
+  // http://crbug.com/758057
 
-  uint8_t* out_pixels = static_cast<uint8_t*>(bitmap->getPixels());
+  uint8_t* out_pixels = static_cast<uint8_t*>(bitmap.getPixels());
 
   size_t row_bytes = size.width() * 4;
   size_t total_bytes = size.height() * row_bytes;
diff --git a/cc/test/layer_tree_pixel_test.h b/cc/test/layer_tree_pixel_test.h
index bad5fd67..d8cea55 100644
--- a/cc/test/layer_tree_pixel_test.h
+++ b/cc/test/layer_tree_pixel_test.h
@@ -80,7 +80,7 @@
                                       Layer* target,
                                       base::FilePath file_name);
 
-  std::unique_ptr<SkBitmap> CopyTextureMailboxToBitmap(
+  SkBitmap CopyTextureMailboxToBitmap(
       const gfx::Size& size,
       const viz::TextureMailbox& texture_mailbox);
 
diff --git a/cc/test/pixel_test.cc b/cc/test/pixel_test.cc
index ddffab4f..88d43d3 100644
--- a/cc/test/pixel_test.cc
+++ b/cc/test/pixel_test.cc
@@ -67,7 +67,8 @@
   base::RunLoop run_loop;
 
   std::unique_ptr<viz::CopyOutputRequest> request =
-      viz::CopyOutputRequest::CreateBitmapRequest(
+      std::make_unique<viz::CopyOutputRequest>(
+          viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
           base::BindOnce(&PixelTest::ReadbackResult, base::Unretained(this),
                          run_loop.QuitClosure()));
   if (copy_rect)
@@ -98,7 +99,8 @@
   RenderPass* target = pass_list->back().get();
 
   std::unique_ptr<viz::CopyOutputRequest> request =
-      viz::CopyOutputRequest::CreateBitmapRequest(
+      std::make_unique<viz::CopyOutputRequest>(
+          viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
           base::BindOnce(&PixelTest::ReadbackResult, base::Unretained(this),
                          run_loop.QuitClosure()));
   target->copy_requests.push_back(std::move(request));
@@ -130,8 +132,10 @@
 
 void PixelTest::ReadbackResult(base::Closure quit_run_loop,
                                std::unique_ptr<viz::CopyOutputResult> result) {
-  ASSERT_TRUE(result->HasBitmap());
-  result_bitmap_ = result->TakeBitmap();
+  ASSERT_FALSE(result->IsEmpty());
+  EXPECT_EQ(result->format(), viz::CopyOutputResult::Format::RGBA_BITMAP);
+  result_bitmap_ = std::make_unique<SkBitmap>(result->AsSkBitmap());
+  EXPECT_TRUE(result_bitmap_->readyToDraw());
   quit_run_loop.Run();
 }
 
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index 48ae628..1089ae5 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -4808,8 +4808,6 @@
   EXPECT_FALSE(grand_child_layer->contributes_to_drawn_render_surface());
 }
 
-void EmptyCopyOutputCallback(std::unique_ptr<viz::CopyOutputResult> result) {}
-
 TEST_F(LayerTreeHostCommonTest, SubtreeHiddenWithCopyRequest) {
   FakeImplTaskRunnerProvider task_runner_provider;
   TestTaskGraphRunner task_graph_runner;
@@ -4889,8 +4887,7 @@
   copy_grand_child_layer->test_properties()->hide_layer_and_subtree = true;
 
   copy_layer->test_properties()->copy_requests.push_back(
-      viz::CopyOutputRequest::CreateRequest(
-          base::BindOnce(&EmptyCopyOutputCallback)));
+      viz::CopyOutputRequest::CreateStubForTesting());
 
   RenderSurfaceList render_surface_list;
   LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs(
@@ -4986,8 +4983,7 @@
   copy_child->SetDrawsContent(true);
 
   copy_layer->test_properties()->copy_requests.push_back(
-      viz::CopyOutputRequest::CreateRequest(
-          base::BindOnce(&EmptyCopyOutputCallback)));
+      viz::CopyOutputRequest::CreateStubForTesting());
 
   copy_layer->test_properties()->AddChild(std::move(copy_child));
   copy_parent->test_properties()->AddChild(std::move(copy_layer));
@@ -5028,8 +5024,7 @@
   copy_layer->SetBounds(gfx::Size(100, 100));
   copy_layer->SetDrawsContent(true);
   copy_layer->test_properties()->copy_requests.push_back(
-      viz::CopyOutputRequest::CreateRequest(
-          base::BindOnce(&EmptyCopyOutputCallback)));
+      viz::CopyOutputRequest::CreateStubForTesting());
 
   LayerImpl* copy_child = AddChild<LayerImpl>(copy_layer);
   copy_child->SetBounds(gfx::Size(100, 100));
@@ -5089,8 +5084,7 @@
   copy_surface->test_properties()->force_render_surface = true;
 
   copy_layer->test_properties()->copy_requests.push_back(
-      viz::CopyOutputRequest::CreateRequest(
-          base::BindOnce(&EmptyCopyOutputCallback)));
+      viz::CopyOutputRequest::CreateStubForTesting());
 
   DCHECK(!copy_layer->test_properties()->copy_requests.empty());
   ExecuteCalculateDrawProperties(root);
@@ -5105,8 +5099,7 @@
   copy_layer->SetBounds(gfx::Size(50, 50));
   copy_layer->SetMasksToBounds(true);
   copy_layer->test_properties()->copy_requests.push_back(
-      viz::CopyOutputRequest::CreateRequest(
-          base::BindOnce(&EmptyCopyOutputCallback)));
+      viz::CopyOutputRequest::CreateStubForTesting());
   root->layer_tree_impl()->property_trees()->needs_rebuild = true;
 
   DCHECK(!copy_layer->test_properties()->copy_requests.empty());
@@ -5121,8 +5114,7 @@
   // Case 3: When there is device scale factor.
   float device_scale_factor = 2.f;
   copy_layer->test_properties()->copy_requests.push_back(
-      viz::CopyOutputRequest::CreateRequest(
-          base::BindOnce(&EmptyCopyOutputCallback)));
+      viz::CopyOutputRequest::CreateStubForTesting());
 
   DCHECK(!copy_layer->test_properties()->copy_requests.empty());
   ExecuteCalculateDrawProperties(root, device_scale_factor);
@@ -8584,8 +8576,6 @@
   EXPECT_EQ(gfx::Rect(0, 10, 25, 25), scroll_child->visible_layer_rect());
 }
 
-static void CopyOutputCallback(std::unique_ptr<viz::CopyOutputResult> result) {}
-
 TEST_F(LayerTreeHostCommonTest, HasCopyRequestsInTargetSubtree) {
   scoped_refptr<Layer> root = Layer::Create();
   scoped_refptr<Layer> child1 = Layer::Create();
@@ -8599,11 +8589,9 @@
   grandchild->AddChild(greatgrandchild);
   host()->SetRootLayer(root);
 
-  child1->RequestCopyOfOutput(viz::CopyOutputRequest::CreateBitmapRequest(
-      base::BindOnce(&CopyOutputCallback)));
+  child1->RequestCopyOfOutput(viz::CopyOutputRequest::CreateStubForTesting());
   greatgrandchild->RequestCopyOfOutput(
-      viz::CopyOutputRequest::CreateBitmapRequest(
-          base::BindOnce(&CopyOutputCallback)));
+      viz::CopyOutputRequest::CreateStubForTesting());
   child2->SetOpacity(0.f);
   ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root.get());
 
@@ -8689,8 +8677,8 @@
   // Now, even though child has zero opacity, we will configure |grandchild| and
   // |greatgrandchild| in several ways that should force the subtree to be
   // processed anyhow.
-  grandchild->RequestCopyOfOutput(viz::CopyOutputRequest::CreateBitmapRequest(
-      base::BindOnce(&CopyOutputCallback)));
+  grandchild->RequestCopyOfOutput(
+      viz::CopyOutputRequest::CreateStubForTesting());
   ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root.get());
   update_list = GetUpdateLayerList();
   EXPECT_TRUE(VerifyLayerInList(grandchild, update_list));
@@ -8803,7 +8791,7 @@
   // |greatgrandchild| in several ways that should force the subtree to be
   // processed anyhow.
   grandchild_ptr->test_properties()->copy_requests.push_back(
-      viz::CopyOutputRequest::CreateEmptyRequest());
+      viz::CopyOutputRequest::CreateStubForTesting());
   root_ptr->layer_tree_impl()->property_trees()->needs_rebuild = true;
   ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root_ptr);
   EXPECT_EQ(gfx::Rect(10, 10), grandchild_ptr->visible_layer_rect());
@@ -9012,8 +9000,7 @@
   parent->AddChild(child);
   host()->SetRootLayer(root);
 
-  child->RequestCopyOfOutput(viz::CopyOutputRequest::CreateRequest(
-      base::BindOnce(&EmptyCopyOutputCallback)));
+  child->RequestCopyOfOutput(viz::CopyOutputRequest::CreateStubForTesting());
 
   ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList(root.get());
   EXPECT_TRUE(root->has_copy_requests_in_target_subtree());
@@ -10116,8 +10103,7 @@
   // Need to persist the render surface after copy request is cleared.
   copy_layer->test_properties()->force_render_surface = true;
   copy_layer->test_properties()->copy_requests.push_back(
-      viz::CopyOutputRequest::CreateRequest(
-          base::Bind(&EmptyCopyOutputCallback)));
+      viz::CopyOutputRequest::CreateStubForTesting());
 
   clip_layer->SetDrawsContent(true);
   clip_layer->SetMasksToBounds(true);
@@ -10367,8 +10353,7 @@
   // Case 4: When the non root cache render surface layer is clipped and there
   // is a copy request layer beneath it.
   copy_layer->test_properties()->copy_requests.push_back(
-      viz::CopyOutputRequest::CreateRequest(
-          base::Bind(&EmptyCopyOutputCallback)));
+      viz::CopyOutputRequest::CreateStubForTesting());
   root->layer_tree_impl()->property_trees()->needs_rebuild = true;
   DCHECK(!copy_layer->test_properties()->copy_requests.empty());
   ExecuteCalculateDrawProperties(root);
@@ -10384,8 +10369,7 @@
   // request layer.
   cache_surface->test_properties()->cache_render_surface = true;
   copy_layer->test_properties()->copy_requests.push_back(
-      viz::CopyOutputRequest::CreateRequest(
-          base::Bind(&EmptyCopyOutputCallback)));
+      viz::CopyOutputRequest::CreateStubForTesting());
   root->layer_tree_impl()->property_trees()->needs_rebuild = true;
   DCHECK(!copy_layer->test_properties()->copy_requests.empty());
   ExecuteCalculateDrawProperties(root);
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index ef0edc0..0becf135 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -4127,11 +4127,9 @@
     did_draw_called_ = false;
   }
 
-  static void IgnoreResult(std::unique_ptr<viz::CopyOutputResult> result) {}
-
   void AddCopyRequest() {
     test_properties()->copy_requests.push_back(
-        viz::CopyOutputRequest::CreateRequest(base::BindOnce(&IgnoreResult)));
+        viz::CopyOutputRequest::CreateStubForTesting());
   }
 
  protected:
@@ -9188,9 +9186,6 @@
   EXPECT_NE(0u, id1);
 }
 
-void ShutdownReleasesContext_Callback(
-    std::unique_ptr<viz::CopyOutputResult> result) {}
-
 class FrameSinkClient : public viz::TestLayerTreeFrameSinkClient {
  public:
   explicit FrameSinkClient(
@@ -9233,9 +9228,16 @@
   SetupRootLayerImpl(LayerImpl::Create(host_impl_->active_tree(), 1));
 
   LayerImpl* root = host_impl_->active_tree()->root_layer_for_testing();
+  struct Helper {
+    std::unique_ptr<viz::CopyOutputResult> unprocessed_result;
+    void OnResult(std::unique_ptr<viz::CopyOutputResult> result) {
+      unprocessed_result = std::move(result);
+    }
+  } helper;
   root->test_properties()->copy_requests.push_back(
-      viz::CopyOutputRequest::CreateRequest(
-          base::BindOnce(&ShutdownReleasesContext_Callback)));
+      std::make_unique<viz::CopyOutputRequest>(
+          viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
+          base::BindOnce(&Helper::OnResult, base::Unretained(&helper))));
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
   TestFrameData frame;
@@ -9243,18 +9245,23 @@
   host_impl_->DrawLayers(&frame);
   host_impl_->DidDrawAllLayers(frame);
 
-  // The CopyOutputResult's callback has a ref on the viz::ContextProvider and a
-  // texture in a texture mailbox.
+  // The CopyOutputResult has a ref on the viz::ContextProvider and a texture in
+  // a texture mailbox.
+  ASSERT_TRUE(helper.unprocessed_result);
   EXPECT_FALSE(context_provider->HasOneRef());
   EXPECT_EQ(1u, context_provider->TestContext3d()->NumTextures());
 
   host_impl_->ReleaseLayerTreeFrameSink();
   host_impl_ = nullptr;
 
-  // The CopyOutputResult's callback was cancelled, the CopyOutputResult
-  // released, and the texture deleted.
+  // The texture release callback that was given to the CopyOutputResult has
+  // been canceled, and the texture deleted.
   EXPECT_TRUE(context_provider->HasOneRef());
   EXPECT_EQ(0u, context_provider->TestContext3d()->NumTextures());
+
+  // When resetting the CopyOutputResult, it will run its texture release
+  // callback. This should not cause a crash, etc.
+  helper.unprocessed_result.reset();
 }
 
 TEST_F(LayerTreeHostImplTest, TouchFlingShouldNotBubble) {
diff --git a/cc/trees/layer_tree_host_pixeltest_readback.cc b/cc/trees/layer_tree_host_pixeltest_readback.cc
index e2a5461e..b1a4058 100644
--- a/cc/trees/layer_tree_host_pixeltest_readback.cc
+++ b/cc/trees/layer_tree_host_pixeltest_readback.cc
@@ -64,20 +64,26 @@
     std::unique_ptr<viz::CopyOutputRequest> request;
 
     if (readback_type_ == READBACK_BITMAP) {
-      request = viz::CopyOutputRequest::CreateBitmapRequest(base::BindOnce(
-          &LayerTreeHostReadbackPixelTest::ReadbackResultAsBitmap,
-          base::Unretained(this)));
+      request = std::make_unique<viz::CopyOutputRequest>(
+          viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+          base::BindOnce(
+              &LayerTreeHostReadbackPixelTest::ReadbackResultAsBitmap,
+              base::Unretained(this)));
     } else {
       DCHECK_EQ(readback_type_, READBACK_DEFAULT);
       if (test_type_ == PIXEL_TEST_SOFTWARE) {
-        request = viz::CopyOutputRequest::CreateRequest(base::BindOnce(
-            &LayerTreeHostReadbackPixelTest::ReadbackResultAsBitmap,
-            base::Unretained(this)));
+        request = std::make_unique<viz::CopyOutputRequest>(
+            viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+            base::BindOnce(
+                &LayerTreeHostReadbackPixelTest::ReadbackResultAsBitmap,
+                base::Unretained(this)));
       } else {
         DCHECK_EQ(test_type_, PIXEL_TEST_GL);
-        request = viz::CopyOutputRequest::CreateRequest(base::BindOnce(
-            &LayerTreeHostReadbackPixelTest::ReadbackResultAsTexture,
-            base::Unretained(this)));
+        request = std::make_unique<viz::CopyOutputRequest>(
+            viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
+            base::BindOnce(
+                &LayerTreeHostReadbackPixelTest::ReadbackResultAsTexture,
+                base::Unretained(this)));
       }
     }
 
@@ -106,27 +112,30 @@
 
   void ReadbackResultAsBitmap(std::unique_ptr<viz::CopyOutputResult> result) {
     EXPECT_TRUE(task_runner_provider()->IsMainThread());
-    EXPECT_TRUE(result->HasBitmap());
-    result_bitmap_ = result->TakeBitmap();
+    EXPECT_FALSE(result->IsEmpty());
+    result_bitmap_ = std::make_unique<SkBitmap>(result->AsSkBitmap());
+    EXPECT_TRUE(result_bitmap_->readyToDraw());
     EndTest();
   }
 
   void ReadbackResultAsTexture(std::unique_ptr<viz::CopyOutputResult> result) {
     EXPECT_TRUE(task_runner_provider()->IsMainThread());
-    EXPECT_TRUE(result->HasTexture());
+    EXPECT_EQ(result->format(), viz::CopyOutputResult::Format::RGBA_TEXTURE);
 
     viz::TextureMailbox texture_mailbox;
     std::unique_ptr<viz::SingleReleaseCallback> release_callback;
-    result->TakeTexture(&texture_mailbox, &release_callback);
-    EXPECT_TRUE(texture_mailbox.IsValid());
-    EXPECT_TRUE(texture_mailbox.IsTexture());
+    if (auto* mailbox = result->GetTextureMailbox()) {
+      texture_mailbox = *mailbox;
+      release_callback = result->TakeTextureOwnership();
+    }
+    ASSERT_TRUE(texture_mailbox.IsTexture());
 
-    std::unique_ptr<SkBitmap> bitmap =
+    const SkBitmap bitmap =
         CopyTextureMailboxToBitmap(result->size(), texture_mailbox);
     release_callback->Run(gpu::SyncToken(), false);
 
-    ReadbackResultAsBitmap(
-        viz::CopyOutputResult::CreateBitmapResult(std::move(bitmap)));
+    ReadbackResultAsBitmap(std::make_unique<viz::CopyOutputSkBitmapResult>(
+        result->rect(), bitmap));
   }
 
   ReadbackType readback_type_;
@@ -134,8 +143,6 @@
   int insert_copy_request_after_frame_count_;
 };
 
-void IgnoreReadbackResult(std::unique_ptr<viz::CopyOutputResult> result) {}
-
 TEST_P(LayerTreeHostReadbackPixelTest, ReadbackRootLayer) {
   scoped_refptr<SolidColorLayer> background =
       CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
@@ -290,8 +297,7 @@
   hidden_target->AddChild(blue);
 
   hidden_target->RequestCopyOfOutput(
-      viz::CopyOutputRequest::CreateBitmapRequest(
-          base::BindOnce(&IgnoreReadbackResult)));
+      viz::CopyOutputRequest::CreateStubForTesting());
   RunReadbackTest(GetParam().pixel_test_type, GetParam().readback_type,
                   background, base::FilePath(FILE_PATH_LITERAL("black.png")));
 }
@@ -415,8 +421,7 @@
 
   scoped_refptr<SolidColorLayer> blue =
       CreateSolidColorLayer(gfx::Rect(150, 150, 50, 50), SK_ColorBLUE);
-  blue->RequestCopyOfOutput(viz::CopyOutputRequest::CreateBitmapRequest(
-      base::BindOnce(&IgnoreReadbackResult)));
+  blue->RequestCopyOfOutput(viz::CopyOutputRequest::CreateStubForTesting());
   background->AddChild(blue);
 
   RunReadbackTestWithReadbackTarget(
@@ -435,8 +440,8 @@
   scoped_refptr<SolidColorLayer> background =
       CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorGREEN);
 
-  background->RequestCopyOfOutput(viz::CopyOutputRequest::CreateBitmapRequest(
-      base::BindOnce(&IgnoreReadbackResult)));
+  background->RequestCopyOfOutput(
+      viz::CopyOutputRequest::CreateStubForTesting());
 
   RunReadbackTestWithReadbackTarget(
       GetParam().pixel_test_type, GetParam().readback_type, background,
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 990877d6..f5a7619 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -6924,9 +6924,6 @@
     LayerTreeHostTest::SetupTree();
   }
 
-  static void CopyOutputCallback(
-      std::unique_ptr<viz::CopyOutputResult> result) {}
-
   void BeginTest() override { PostSetNeedsCommitToMainThread(); }
 
   void WillCommit() override {
@@ -6941,8 +6938,8 @@
     gfx::Transform transform;
     switch (layer_tree_host()->SourceFrameNumber()) {
       case 1:
-        child->RequestCopyOfOutput(viz::CopyOutputRequest::CreateBitmapRequest(
-            base::BindOnce(CopyOutputCallback)));
+        child->RequestCopyOfOutput(
+            viz::CopyOutputRequest::CreateStubForTesting());
         transform.Scale(2.0, 2.0);
         child->SetTransform(transform);
         break;
diff --git a/cc/trees/layer_tree_host_unittest_checkerimaging.cc b/cc/trees/layer_tree_host_unittest_checkerimaging.cc
index f3f3ce2..3a96d3b3 100644
--- a/cc/trees/layer_tree_host_unittest_checkerimaging.cc
+++ b/cc/trees/layer_tree_host_unittest_checkerimaging.cc
@@ -112,6 +112,12 @@
             }
           }
         }
+
+        // Insetting of image is included in the update rect.
+        gfx::Rect expected_update_rect(-1, -1, 452, 452);
+        expected_update_rect.Union(gfx::Rect(600, 0, 50, 500));
+        EXPECT_EQ(sync_layer_impl->update_rect(), expected_update_rect);
+
         EndTest();
       } break;
       default:
@@ -165,6 +171,9 @@
         }
       }
     }
+
+    // Insetting of image is included in the update rect.
+    EXPECT_EQ(sync_layer_impl->update_rect(), gfx::Rect(-1, -1, 452, 452));
   }
 
   void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
diff --git a/cc/trees/layer_tree_host_unittest_copyrequest.cc b/cc/trees/layer_tree_host_unittest_copyrequest.cc
index 8c02666..be3cf6a 100644
--- a/cc/trees/layer_tree_host_unittest_copyrequest.cc
+++ b/cc/trees/layer_tree_host_unittest_copyrequest.cc
@@ -62,7 +62,8 @@
     int frame = layer_tree_host()->SourceFrameNumber();
     switch (frame) {
       case 1:
-        child->RequestCopyOfOutput(viz::CopyOutputRequest::CreateBitmapRequest(
+        child->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>(
+            viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
             base::BindOnce(&LayerTreeHostCopyRequestTestMultipleRequests::
                                CopyOutputCallback,
                            base::Unretained(this), 0)));
@@ -79,16 +80,19 @@
         EXPECT_EQ(1u, callbacks_.size());
         EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[0].ToString());
 
-        child->RequestCopyOfOutput(viz::CopyOutputRequest::CreateBitmapRequest(
+        child->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>(
+            viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
             base::BindOnce(&LayerTreeHostCopyRequestTestMultipleRequests::
                                CopyOutputCallback,
                            base::Unretained(this), 1)));
-        root->RequestCopyOfOutput(viz::CopyOutputRequest::CreateBitmapRequest(
+        root->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>(
+            viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
             base::BindOnce(&LayerTreeHostCopyRequestTestMultipleRequests::
                                CopyOutputCallback,
                            base::Unretained(this), 2)));
         grand_child->RequestCopyOfOutput(
-            viz::CopyOutputRequest::CreateBitmapRequest(
+            std::make_unique<viz::CopyOutputRequest>(
+                viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
                 base::BindOnce(&LayerTreeHostCopyRequestTestMultipleRequests::
                                    CopyOutputCallback,
                                base::Unretained(this), 3)));
@@ -121,10 +125,10 @@
   void CopyOutputCallback(size_t id,
                           std::unique_ptr<viz::CopyOutputResult> result) {
     EXPECT_TRUE(layer_tree_host()->GetTaskRunnerProvider()->IsMainThread());
-    EXPECT_TRUE(result->HasBitmap());
-    std::unique_ptr<SkBitmap> bitmap = result->TakeBitmap();
-    EXPECT_EQ(result->size().ToString(),
-              gfx::Size(bitmap->width(), bitmap->height()).ToString());
+    EXPECT_FALSE(result->IsEmpty());
+    const SkBitmap& bitmap = result->AsSkBitmap();
+    EXPECT_TRUE(bitmap.readyToDraw());
+    EXPECT_EQ(result->size(), gfx::Size(bitmap.width(), bitmap.height()));
     callbacks_[id] = result->size();
   }
 
@@ -220,7 +224,8 @@
     int frame = layer_tree_host()->SourceFrameNumber();
     switch (frame) {
       case 1:
-        layer_->RequestCopyOfOutput(viz::CopyOutputRequest::CreateBitmapRequest(
+        layer_->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>(
+            viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
             base::BindOnce(&LayerTreeHostCopyRequestCompletionCausesCommit::
                                CopyOutputCallback)));
         break;
@@ -277,12 +282,16 @@
     int frame = layer_tree_host()->SourceFrameNumber();
     switch (frame) {
       case 1:
-        main_destroyed_->RequestCopyOfOutput(
-            viz::CopyOutputRequest::CreateBitmapRequest(base::BindOnce(
+        main_destroyed_->RequestCopyOfOutput(std::make_unique<
+                                             viz::CopyOutputRequest>(
+            viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+            base::BindOnce(
                 &LayerTreeHostCopyRequestTestLayerDestroyed::CopyOutputCallback,
                 base::Unretained(this))));
-        impl_destroyed_->RequestCopyOfOutput(
-            viz::CopyOutputRequest::CreateBitmapRequest(base::BindOnce(
+        impl_destroyed_->RequestCopyOfOutput(std::make_unique<
+                                             viz::CopyOutputRequest>(
+            viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+            base::BindOnce(
                 &LayerTreeHostCopyRequestTestLayerDestroyed::CopyOutputCallback,
                 base::Unretained(this))));
         EXPECT_EQ(0, callback_count_);
@@ -368,8 +377,9 @@
   }
 
   void AddCopyRequest(Layer* layer) {
-    layer->RequestCopyOfOutput(
-        viz::CopyOutputRequest::CreateBitmapRequest(base::BindOnce(
+    layer->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>(
+        viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+        base::BindOnce(
             &LayerTreeHostCopyRequestTestInHiddenSubtree::CopyOutputCallback,
             base::Unretained(this))));
   }
@@ -482,8 +492,9 @@
   void BeginTest() override {
     PostSetNeedsCommitToMainThread();
 
-    copy_layer_->RequestCopyOfOutput(
-        viz::CopyOutputRequest::CreateBitmapRequest(base::BindOnce(
+    copy_layer_->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>(
+        viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+        base::BindOnce(
             &LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest::
                 CopyOutputCallback,
             base::Unretained(this))));
@@ -576,8 +587,9 @@
   void BeginTest() override {
     PostSetNeedsCommitToMainThread();
 
-    copy_layer_->RequestCopyOfOutput(
-        viz::CopyOutputRequest::CreateBitmapRequest(base::BindOnce(
+    copy_layer_->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>(
+        viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+        base::BindOnce(
             &LayerTreeHostCopyRequestTestClippedOut::CopyOutputCallback,
             base::Unretained(this))));
   }
@@ -632,9 +644,11 @@
     PostSetNeedsCommitToMainThread();
 
     std::unique_ptr<viz::CopyOutputRequest> request =
-        viz::CopyOutputRequest::CreateBitmapRequest(base::BindOnce(
-            &LayerTreeHostCopyRequestTestScaledLayer::CopyOutputCallback,
-            base::Unretained(this)));
+        std::make_unique<viz::CopyOutputRequest>(
+            viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+            base::BindOnce(
+                &LayerTreeHostCopyRequestTestScaledLayer::CopyOutputCallback,
+                base::Unretained(this)));
     request->set_area(gfx::Rect(5, 5));
     copy_layer_->RequestCopyOfOutput(std::move(request));
   }
@@ -673,8 +687,9 @@
   }
 
   void AddCopyRequest(Layer* layer) {
-    layer->RequestCopyOfOutput(
-        viz::CopyOutputRequest::CreateBitmapRequest(base::BindOnce(
+    layer->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>(
+        viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+        base::BindOnce(
             &LayerTreeHostTestAsyncTwoReadbacksWithoutDraw::CopyOutputCallback,
             base::Unretained(this))));
   }
@@ -769,7 +784,8 @@
       std::unique_ptr<viz::CopyOutputResult> result) {
     EXPECT_TRUE(layer_tree_host()->GetTaskRunnerProvider()->IsMainThread());
     EXPECT_EQ(gfx::Size(10, 10).ToString(), result->size().ToString());
-    EXPECT_TRUE(result->HasTexture());
+    EXPECT_EQ(result->format(), viz::CopyOutputResult::Format::RGBA_TEXTURE);
+    EXPECT_NE(result->GetTextureMailbox(), nullptr);
 
     // Save the result for later.
     EXPECT_FALSE(result_);
@@ -780,7 +796,8 @@
   }
 
   void InsertCopyRequest() {
-    copy_layer_->RequestCopyOfOutput(viz::CopyOutputRequest::CreateRequest(
+    copy_layer_->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>(
+        viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
         base::BindOnce(&LayerTreeHostCopyRequestTestDeleteTexture::
                            ReceiveCopyRequestOutputAndCommit,
                        base::Unretained(this))));
@@ -970,18 +987,22 @@
  protected:
   void RequestCopy(Layer* layer) override {
     // Request a normal texture copy. This should create a new texture.
-    copy_layer_->RequestCopyOfOutput(
-        viz::CopyOutputRequest::CreateRequest(base::BindOnce(
+    copy_layer_->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>(
+        viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
+        base::BindOnce(
             &LayerTreeHostCopyRequestTestCreatesTexture::CopyOutputCallback,
             base::Unretained(this))));
   }
 
   void CopyOutputCallback(std::unique_ptr<viz::CopyOutputResult> result) {
     EXPECT_FALSE(result->IsEmpty());
-    EXPECT_TRUE(result->HasTexture());
-
-    viz::TextureMailbox mailbox;
-    result->TakeTexture(&mailbox, &release_);
+    EXPECT_EQ(result->format(), viz::CopyOutputResult::Format::RGBA_TEXTURE);
+    viz::TextureMailbox texture_mailbox;
+    if (auto* mailbox = result->GetTextureMailbox()) {
+      texture_mailbox = *mailbox;
+      release_ = result->TakeTextureOwnership();
+    }
+    EXPECT_TRUE(texture_mailbox.IsTexture());
     EXPECT_TRUE(release_);
   }
 
@@ -1010,21 +1031,27 @@
 
   void CopyOutputCallback(std::unique_ptr<viz::CopyOutputResult> result) {
     EXPECT_FALSE(result->IsEmpty());
-    EXPECT_TRUE(result->HasTexture());
-
-    viz::TextureMailbox mailbox;
-    std::unique_ptr<viz::SingleReleaseCallback> release;
-    result->TakeTexture(&mailbox, &release);
-    EXPECT_FALSE(release);
+    EXPECT_EQ(result->format(), viz::CopyOutputResult::Format::RGBA_TEXTURE);
+    viz::TextureMailbox texture_mailbox;
+    std::unique_ptr<viz::SingleReleaseCallback> release_callback;
+    if (auto* mailbox = result->GetTextureMailbox()) {
+      texture_mailbox = *mailbox;
+      release_callback = result->TakeTextureOwnership();
+    }
+    EXPECT_TRUE(texture_mailbox.IsTexture());
+    ASSERT_TRUE(release_callback);
+    release_callback->Run(gpu::SyncToken(), false);
   }
 
   void RequestCopy(Layer* layer) override {
     // Request a copy to a provided texture. This should not create a new
     // texture.
     std::unique_ptr<viz::CopyOutputRequest> request =
-        viz::CopyOutputRequest::CreateRequest(base::BindOnce(
-            &LayerTreeHostCopyRequestTestProvideTexture::CopyOutputCallback,
-            base::Unretained(this)));
+        std::make_unique<viz::CopyOutputRequest>(
+            viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
+            base::BindOnce(
+                &LayerTreeHostCopyRequestTestProvideTexture::CopyOutputCallback,
+                base::Unretained(this)));
 
     gpu::gles2::GLES2Interface* gl = external_context_provider_->ContextGL();
     gpu::Mailbox mailbox;
@@ -1096,7 +1123,8 @@
         // Put a copy request on the layer, but then don't allow any
         // drawing to take place.
         std::unique_ptr<viz::CopyOutputRequest> request =
-            viz::CopyOutputRequest::CreateRequest(
+            std::make_unique<viz::CopyOutputRequest>(
+                viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
                 base::BindOnce(&LayerTreeHostCopyRequestTestDestroyBeforeCopy::
                                    CopyOutputCallback,
                                base::Unretained(this)));
@@ -1174,7 +1202,8 @@
         // Put a copy request on the layer, but then don't allow any
         // drawing to take place.
         std::unique_ptr<viz::CopyOutputRequest> request =
-            viz::CopyOutputRequest::CreateRequest(
+            std::make_unique<viz::CopyOutputRequest>(
+                viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
                 base::BindOnce(&LayerTreeHostCopyRequestTestShutdownBeforeCopy::
                                    CopyOutputCallback,
                                base::Unretained(this)));
@@ -1233,8 +1262,9 @@
   void DidCommit() override {
     // Send a copy request after the first commit.
     if (layer_tree_host()->SourceFrameNumber() == 1) {
-      child_->RequestCopyOfOutput(
-          viz::CopyOutputRequest::CreateBitmapRequest(base::BindOnce(
+      child_->RequestCopyOfOutput(std::make_unique<viz::CopyOutputRequest>(
+          viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+          base::BindOnce(
               &LayerTreeHostCopyRequestTestMultipleDrawsHiddenCopyRequest::
                   CopyOutputCallback,
               base::Unretained(this))));
diff --git a/cc/trees/occlusion_tracker_unittest.cc b/cc/trees/occlusion_tracker_unittest.cc
index c7c9ff6e..07fcd5e8 100644
--- a/cc/trees/occlusion_tracker_unittest.cc
+++ b/cc/trees/occlusion_tracker_unittest.cc
@@ -195,19 +195,13 @@
     layer_iterator_.reset();
   }
 
-  void CopyOutputCallback(std::unique_ptr<viz::CopyOutputResult> result) {}
-
   void AddCopyRequest(Layer* layer) {
-    layer->RequestCopyOfOutput(viz::CopyOutputRequest::CreateBitmapRequest(
-        base::BindOnce(&OcclusionTrackerTest::CopyOutputCallback,
-                       base::Unretained(this))));
+    layer->RequestCopyOfOutput(viz::CopyOutputRequest::CreateStubForTesting());
   }
 
   void AddCopyRequest(LayerImpl* layer) {
     layer->test_properties()->copy_requests.push_back(
-        viz::CopyOutputRequest::CreateBitmapRequest(
-            base::BindOnce(&OcclusionTrackerTest::CopyOutputCallback,
-                           base::Unretained(this))));
+        viz::CopyOutputRequest::CreateStubForTesting());
   }
 
   void CalcDrawEtc(TestContentLayerImpl* root) {
diff --git a/chrome/android/OWNERS b/chrome/android/OWNERS
index d798ab1..c857d41 100644
--- a/chrome/android/OWNERS
+++ b/chrome/android/OWNERS
@@ -8,6 +8,7 @@
 per-file static_initializers.gni=*
 per-file java_sources.gni=*
 per-file *.gn*=agrieve@chromium.org
+per-file android_chrome_strings.grd=twellington@chromium.org
 
 # COMPONENT: Mobile
 # OS: Android
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 53bf2e98..10e8c99e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -2209,10 +2209,7 @@
     @Override
     public boolean onActivityResultWithNative(int requestCode, int resultCode, Intent intent) {
         if (super.onActivityResultWithNative(requestCode, resultCode, intent)) return true;
-        if (requestCode == VrShellDelegate.EXIT_VR_RESULT) {
-            VrShellDelegate.onExitVrResult(resultCode);
-            return true;
-        }
+        if (VrShellDelegate.onActivityResultWithNative(requestCode, resultCode)) return true;
         return false;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 75b80f7..e0aa92f8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1702,6 +1702,11 @@
         if (!mUIInitialized) return false;
         final Tab currentTab = getActivityTab();
 
+        if (exitFullscreenIfShowing()) {
+            recordBackPressedUma("Exited fullscreen", BACK_PRESSED_EXITED_FULLSCREEN);
+            return true;
+        }
+
         if (getBottomSheet() != null && getBottomSheet().handleBackPress()) return true;
 
         if (currentTab == null) {
@@ -1717,11 +1722,6 @@
             return true;
         }
 
-        if (exitFullscreenIfShowing()) {
-            recordBackPressedUma("Exited fullscreen", BACK_PRESSED_EXITED_FULLSCREEN);
-            return true;
-        }
-
         if (getToolbarManager().back()) {
             recordBackPressedUma("Navigating backward", BACK_PRESSED_NAVIGATED_BACK);
             return true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
index f2335f3..00557dce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -55,6 +55,7 @@
 import org.chromium.chrome.browser.widget.ControlContainer;
 import org.chromium.content.browser.ContentView;
 import org.chromium.content.browser.ContentViewCore;
+import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.base.SPenSupport;
@@ -424,23 +425,42 @@
         return mCompositorView;
     }
 
-    private View getActiveView() {
+    private Tab getCurrentTab() {
         if (mLayoutManager == null || mTabModelSelector == null) return null;
-        Tab tab = mTabModelSelector.getCurrentTab();
+        return mTabModelSelector.getCurrentTab();
+    }
+
+    private View getActiveView() {
+        Tab tab = getCurrentTab();
         return tab != null ? tab.getContentView() : null;
     }
 
     private ContentViewCore getActiveContent() {
-        if (mLayoutManager == null || mTabModelSelector == null) return null;
-        Tab tab = mTabModelSelector.getCurrentTab();
+        Tab tab = getCurrentTab();
         return tab != null ? tab.getActiveContentViewCore() : null;
     }
 
+    private WebContents getActiveWebContents() {
+        Tab tab = getCurrentTab();
+        return tab != null ? tab.getWebContents() : null;
+    }
+
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
         View view = getActiveView();
-        if (view != null && setSizeOfUnattachedView(view)) requestRender();
+        if (view == null) return;
+        WebContents webContents = getActiveWebContents();
+        if (isAttachedToWindow(view)) {
+            webContents.setSize(w, h);
+        } else {
+            setSizeOfUnattachedView(view, webContents);
+            requestRender();
+        }
+    }
+
+    private static boolean isAttachedToWindow(View view) {
+        return view != null && view.getWindowToken() != null;
     }
 
     @Override
@@ -850,7 +870,7 @@
     public void onOverlayPanelContentViewCoreAdded(ContentViewCore content) {
         // TODO(dtrainor): Look into rolling this into onContentChanged().
         initializeContentViewCore(content);
-        setSizeOfUnattachedView(content.getContainerView());
+        setSizeOfUnattachedView(content.getContainerView(), content.getWebContents());
     }
 
     private void setTab(Tab tab) {
@@ -886,7 +906,12 @@
         if (content != null) initializeContentViewCore(content);
 
         View view = tab.getContentView();
-        if (view != tab.getView() || !tab.isNativePage()) setSizeOfUnattachedView(view);
+        if (view == null || (tab.isNativePage() && view == tab.getView())) return;
+        if (isAttachedToWindow(view)) {
+            tab.getWebContents().setSize(getWidth(), getHeight());
+        } else {
+            setSizeOfUnattachedView(view, tab.getWebContents());
+        }
     }
 
     /**
@@ -923,23 +948,22 @@
 
     /**
      * Resize {@code view} to match the size of this {@link FrameLayout}.  This will only happen if
-     * {@code view} is not {@code null} and if {@link View#getWindowToken()} returns {@code null}
-     * (the {@link View} is not part of the view hierarchy).
+     * the {@link View} is not part of the view hierarchy.
      * @param view The {@link View} to resize.
-     * @return     Whether or not {@code view} was resized.
+     * @param webContents {@link WebContents} associated with the view.
      */
-    private boolean setSizeOfUnattachedView(View view) {
+    private void setSizeOfUnattachedView(View view, WebContents webContents) {
         // Need to call layout() for the following View if it is not attached to the view hierarchy.
-        // Calling onSizeChanged() is dangerous because if the View has a different size than the
-        // ContentViewCore it might think a future size update is a NOOP and not call
+        // Calling {@code view.onSizeChanged()} is dangerous because if the View has a different
+        // size than the ContentViewCore it might think a future size update is a NOOP and not call
         // onSizeChanged() on the ContentViewCore.
-        if (view == null || view.getWindowToken() != null) return false;
+        if (isAttachedToWindow(view)) return;
         int width = getWidth();
         int height = getHeight();
         view.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
         view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
-        return true;
+        webContents.setSize(width, height);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
index 94c8e73..d889653 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
@@ -799,12 +799,8 @@
      */
     protected void resizePanelContentViewCore(float width, float height) {
         if (!isShowing()) return;
-        if (getContentViewCore() != null) {
-            getOverlayPanelContent().onSizeChanged(
-                    (int) (width / mPxToDp), (int) (height / mPxToDp));
-            getOverlayPanelContent().onPhysicalBackingSizeChanged(
-                    (int) (width / mPxToDp), (int) (height / mPxToDp));
-        }
+        getOverlayPanelContent().onPhysicalBackingSizeChanged(
+                (int) (width / mPxToDp), (int) (height / mPxToDp));
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
index 3bab95b..cf66c85 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
@@ -340,6 +340,9 @@
                 mNativeOverlayPanelContentPtr, mInterceptNavigationDelegate, panelWebContents);
 
         mContentDelegate.onContentViewCreated(mContentViewCore);
+        if (mContentViewWidth != 0 && mContentViewHeight != 0) {
+            onPhysicalBackingSizeChanged(mContentViewWidth, mContentViewHeight);
+        }
     }
 
     /**
@@ -479,15 +482,15 @@
         return mContentViewCore;
     }
 
-    void onSizeChanged(int width, int height) {
-        mContentViewCore.onSizeChanged(width, height, mContentViewCore.getViewportWidthPix(),
-                mContentViewCore.getViewportHeightPix());
+    private WebContents getWebContents() {
+        return mContentViewCore != null ? mContentViewCore.getWebContents() : null;
     }
 
     void onPhysicalBackingSizeChanged(int width, int height) {
-        if (mContentViewCore != null && mContentViewCore.getWebContents() != null) {
-            nativeOnPhysicalBackingSizeChanged(mNativeOverlayPanelContentPtr,
-                    mContentViewCore.getWebContents(), width, height);
+        WebContents webContents = getWebContents();
+        if (webContents != null) {
+            nativeOnPhysicalBackingSizeChanged(
+                    mNativeOverlayPanelContentPtr, webContents, width, height);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteState.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteState.java
index 97dfd00..7f5ddeb0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteState.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteState.java
@@ -143,10 +143,6 @@
         mAutocompleteText = "";
     }
 
-    public boolean equalsExceptAutocompleteText(AutocompleteState a) {
-        return mUserText.equals(a.mUserText) && mSelStart == a.mSelStart && mSelEnd == a.mSelEnd;
-    }
-
     @Override
     public boolean equals(Object o) {
         if (!(o instanceof AutocompleteState)) return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SpannableAutocompleteEditTextModel.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SpannableAutocompleteEditTextModel.java
index 8d2088c..4043096 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SpannableAutocompleteEditTextModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SpannableAutocompleteEditTextModel.java
@@ -182,11 +182,12 @@
         if (mBatchEditNestCount > 0) return;
         if (mCurrentState.equals(mPreviouslyNotifiedState)) return;
         notifyAccessibilityService();
-        // Nothing has changed except that autocomplete text has been set or modified.
-        if (mCurrentState.equalsExceptAutocompleteText(mPreviouslyNotifiedState)
-                && mCurrentState.hasAutocompleteText()) {
-            // Autocomplete text is set by the controller, we should not notify the controller with
-            // the same information.
+        if (mCurrentState.getUserText().equals(mPreviouslyNotifiedState.getUserText())
+                && (mCurrentState.hasAutocompleteText()
+                           || !mPreviouslyNotifiedState.hasAutocompleteText())) {
+            // Nothing has changed except that autocomplete text has been set or modified. Or
+            // selection change did not affect autocomplete text. Autocomplete text is set by the
+            // controller, so only text change or deletion of autocomplete text should be notified.
             mPreviouslyNotifiedState.copyFrom(mCurrentState);
             return;
         }
@@ -514,6 +515,11 @@
             Editable editable = mDelegate.getEditableText();
             editable.append(diff);
             decrementBatchEditCount();
+            if (mBatchEditNestCount == 0) { // only at the outermost batch edit
+                // crbug.com/758443: Japanese keyboard does not finish composition when we restore
+                // the deleted text.
+                super.finishComposingText();
+            }
         }
 
         private boolean setAutocompleteSpan() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java
index 00da1e1..809d72fa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java
@@ -264,7 +264,8 @@
                 };
 
                 new AlertDialog.Builder(getContext(), R.style.AlertDialogTheme)
-                        .setTitle(
+                        .setTitle(R.string.data_reduction_usage_reset_statistics_confirmation_title)
+                        .setMessage(
                                 R.string.data_reduction_usage_reset_statistics_confirmation_dialog)
                         .setPositiveButton(
                                 R.string.data_reduction_usage_reset_statistics_confirmation_button,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index 82e6608..91ac0a4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -2105,6 +2105,8 @@
                 mTabUma = new TabUma(TabCreationState.FROZEN_ON_RESTORE_FAILED);
                 mFailedToRestore = true;
             }
+            View compositorView = getActivity().getCompositorViewHolder();
+            webContents.setSize(compositorView.getWidth(), compositorView.getHeight());
 
             mFrozenContentsState = null;
             initContentViewCore(webContents);
@@ -2339,6 +2341,10 @@
         swapContentViewCore(cvc, false, didStartLoad, didFinishLoad);
     }
 
+    private static Rect getEstimatedContentSize(Context context) {
+        return ExternalPrerenderHandler.estimateContentSize((Application) context, false);
+    }
+
     /**
      * Called to swap out the current view with the one passed in.
      *
@@ -2351,7 +2357,7 @@
      * @param didFinishLoad Whether WebContentsObserver::DidFinishLoad() has
      *         already been called.
      */
-    public void swapContentViewCore(ContentViewCore newContentViewCore,
+    private void swapContentViewCore(ContentViewCore newContentViewCore,
             boolean deleteOldNativeWebContents, boolean didStartLoad, boolean didFinishLoad) {
         int originalWidth = 0;
         int originalHeight = 0;
@@ -2363,8 +2369,7 @@
 
         Rect bounds = new Rect();
         if (originalWidth == 0 && originalHeight == 0) {
-            bounds = ExternalPrerenderHandler.estimateContentSize(
-                    (Application) getApplicationContext(), false);
+            bounds = getEstimatedContentSize(getApplicationContext());
             originalWidth = bounds.right - bounds.left;
             originalHeight = bounds.bottom - bounds.top;
         }
@@ -2378,6 +2383,8 @@
         // However, this size fluttering may confuse Blink and rendered result can be broken
         // (see http://crbug.com/340987).
         newContentViewCore.onSizeChanged(originalWidth, originalHeight, 0, 0);
+        newContentViewCore.getWebContents().setSize(originalWidth, originalHeight);
+
         if (!bounds.isEmpty()) {
             nativeOnPhysicalBackingSizeChanged(mNativeTabAndroid,
                     newContentViewCore.getWebContents(), bounds.right, bounds.bottom);
@@ -2987,10 +2994,11 @@
         tab.initialize(null, null, delegateFactory, true, false);
 
         // Resize the webContent to avoid expensive post load resize when attaching the tab.
-        Rect bounds = ExternalPrerenderHandler.estimateContentSize((Application) context, false);
+        Rect bounds = getEstimatedContentSize(context);
         int width = bounds.right - bounds.left;
         int height = bounds.bottom - bounds.top;
         tab.getContentViewCore().onSizeChanged(width, height, 0, 0);
+        tab.getWebContents().setSize(width, height);
 
         tab.detach(null, null);
         return tab;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
index 98cb105..4baacdd1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
@@ -40,6 +40,7 @@
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ApplicationLifetime;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
@@ -69,8 +70,10 @@
                    ScreenOrientationDelegate {
     private static final String TAG = "VrShellDelegate";
 
-    // Pseudo-random number to avoid request id collisions.
-    public static final int EXIT_VR_RESULT = 721251;
+    // Pseudo-random number to avoid request id collisions. Result codes must fit in lower 16 bits
+    // when used with startActivityForResult...
+    public static final int EXIT_VR_RESULT = 7212;
+    public static final int VR_SERVICES_UPDATE_RESULT = 7213;
 
     private static final int ENTER_VR_NOT_NECESSARY = 0;
     private static final int ENTER_VR_CANCELLED = 1;
@@ -229,6 +232,27 @@
                 // Ignore this. This means our receiver was already unregistered somehow.
             }
         }
+
+        WeakReference<ChromeActivity> targetActivity() {
+            return mTargetActivity;
+        }
+    }
+
+    /**
+     * See {@link Activity#onActivityResult}.
+     */
+    public static boolean onActivityResultWithNative(int requestCode, int resultCode) {
+        // Handles the result of the exit VR flow (DOFF).
+        if (requestCode == EXIT_VR_RESULT) {
+            if (sInstance != null) sInstance.onExitVrResult(resultCode == Activity.RESULT_OK);
+            return true;
+        }
+        // Handles the result of requesting to update VR services.
+        if (requestCode == VR_SERVICES_UPDATE_RESULT) {
+            if (sInstance != null) sInstance.onVrServicesMaybeUpdated();
+            return true;
+        }
+        return false;
     }
 
     /**
@@ -285,14 +309,6 @@
     }
 
     /**
-     * Handles the result of the exit VR flow (DOFF).
-     */
-    public static void onExitVrResult(int resultCode) {
-        if (sInstance == null) return;
-        sInstance.onExitVrResult(resultCode == Activity.RESULT_OK);
-    }
-
-    /**
      * Returns the current {@VrSupportLevel}.
      */
     public static int getVrSupportLevel(VrDaydreamApi daydreamApi,
@@ -503,6 +519,13 @@
         return getVrClassesWrapper() != null;
     }
 
+    private static void onActivityDestroyed(Activity activity) {
+        if (sVrBroadcastReceiver == null) return;
+        if (sVrBroadcastReceiver.targetActivity().get() != activity) return;
+        sVrBroadcastReceiver.unregister();
+        sVrBroadcastReceiver = null;
+    }
+
     private VrShellDelegate(ChromeActivity activity, VrClassesWrapper wrapper) {
         mActivity = activity;
         mVrClassesWrapper = wrapper;
@@ -522,7 +545,10 @@
     public void onActivityStateChange(Activity activity, int newState) {
         switch (newState) {
             case ActivityState.DESTROYED:
-                if (activity == mActivity) destroy();
+                if (activity == mActivity) {
+                    destroy();
+                    onActivityDestroyed(activity);
+                }
                 break;
             case ActivityState.PAUSED:
                 if (activity == mActivity) onPause();
@@ -603,6 +629,12 @@
         mVrSupportLevel = supportLevel;
     }
 
+    private void onVrServicesMaybeUpdated() {
+        int vrCorePackageVersion = getVrCorePackageVersion();
+        if (mCachedVrCorePackageVersion == vrCorePackageVersion) return;
+        ApplicationLifetime.terminate(true);
+    }
+
     /**
      * Returns whether the device has support for Daydream.
      */
@@ -752,7 +784,7 @@
 
     private static void removeBlackOverlayView(ChromeActivity activity) {
         if (!sAddedBlackOverlayView) return;
-        View v = (View) activity.getWindow().findViewById(R.id.vr_overlay_view);
+        View v = activity.getWindow().findViewById(R.id.vr_overlay_view);
         assert v != null;
         UiUtils.removeViewFromParent(v);
         sAddedBlackOverlayView = false;
@@ -1373,8 +1405,9 @@
 
             @Override
             public boolean onInfoBarButtonClicked(boolean isPrimary) {
-                activity.startActivity(
-                        new Intent(Intent.ACTION_VIEW, Uri.parse(VR_CORE_MARKET_URI)));
+                activity.startActivityForResult(
+                        new Intent(Intent.ACTION_VIEW, Uri.parse(VR_CORE_MARKET_URI)),
+                        VR_SERVICES_UPDATE_RESULT);
                 return false;
             }
         };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
index d8ee5353..5d913be1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
@@ -28,6 +28,7 @@
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.NativePage;
 import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager.FullscreenListener;
 import org.chromium.chrome.browser.page_info.PageInfoPopup;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.InterceptNavigationDelegateImpl;
@@ -55,7 +56,8 @@
  * This view extends from GvrLayout which wraps a GLSurfaceView that renders VR shell.
  */
 @JNINamespace("vr_shell")
-public class VrShellImpl extends GvrLayout implements VrShell, SurfaceHolder.Callback {
+public class VrShellImpl
+        extends GvrLayout implements VrShell, SurfaceHolder.Callback, FullscreenListener {
     private static final String TAG = "VrShellImpl";
     private static final float INCHES_TO_METERS = 0.0254f;
 
@@ -95,6 +97,7 @@
     private float mLastContentHeight;
     private float mLastContentDpr;
     private Boolean mPaused;
+    private boolean mPendingVSyncPause;
 
     private AndroidUiGestureTarget mAndroidUiGestureTarget;
 
@@ -107,6 +110,8 @@
         mDelegate = delegate;
         mTabModelSelector = tabModelSelector;
 
+        mActivity.getFullscreenManager().addListener(this);
+
         // This overrides the default intent created by GVR to return to Chrome when the DON flow
         // is triggered by resuming the GvrLayout, which is the usual way Daydream apps enter VR.
         // See VrShellDelegate#getEnterVrPendingIntent for why we need to do this.
@@ -451,9 +456,8 @@
         Point size = new Point(surfaceWidth, surfaceHeight);
         mContentVirtualDisplay.update(size, dpr, null, null, null, null, null);
         if (mTab != null && mTab.getContentViewCore() != null) {
-            mTab.getContentViewCore().onSizeChanged(surfaceWidth, surfaceHeight, 0, 0);
-            nativeOnPhysicalBackingSizeChanged(mNativeVrShell,
-                    mTab.getContentViewCore().getWebContents(), surfaceWidth, surfaceHeight);
+            nativeOnPhysicalBackingSizeChanged(
+                    mNativeVrShell, mTab.getWebContents(), surfaceWidth, surfaceHeight);
         }
         mRenderToSurfaceLayout.setLayoutParams(
                 new FrameLayout.LayoutParams(surfaceWidth, surfaceHeight));
@@ -511,6 +515,7 @@
 
     @Override
     public void shutdown() {
+        mActivity.getFullscreenManager().removeListener(this);
         reparentAllTabs(mActivity.getWindowAndroid());
         if (mNativeVrShell != 0) {
             nativeDestroy(mNativeVrShell);
@@ -522,9 +527,7 @@
         mTab.removeObserver(mTabObserver);
         restoreTabFromVR();
 
-        if (mTab != null) {
-            mTab.updateBrowserControlsState(BrowserControlsState.SHOWN, true);
-        }
+        if (mTab != null) mTab.updateBrowserControlsState(BrowserControlsState.SHOWN, true);
 
         mContentVirtualDisplay.destroy();
         super.shutdown();
@@ -547,11 +550,35 @@
 
     @Override
     public void setWebVrModeEnabled(boolean enabled, boolean showToast) {
-        mContentVrWindowAndroid.setVSyncPaused(enabled);
+        if (!enabled && !mPendingVSyncPause) mContentVrWindowAndroid.setVSyncPaused(false);
+        // TODO(mthiesse, crbug.com/760970) We shouldn't have to wait for the controls to be hidden
+        // before pausing VSync. Something is going wrong in the controls code and should be fixed.
+        mPendingVSyncPause = enabled;
+
         nativeSetWebVrMode(mNativeVrShell, enabled, showToast);
     }
 
     @Override
+    public void onContentOffsetChanged(float offset) {}
+
+    @Override
+    public void onControlsOffsetChanged(float topOffset, float bottomOffset, boolean needsAnimate) {
+        if (!mPendingVSyncPause) return;
+        // As soon as the controls are starting to hide we can set vsync to paused.
+        // For some reason it seems onControlsOffsetChanged is sometimes called when the controls
+        // are partially hidden, but never called again when they're fully hidden.
+        if (mActivity.getFullscreenManager().getBrowserControlHiddenRatio() == 0.0) return;
+        mPendingVSyncPause = false;
+        mContentVrWindowAndroid.setVSyncPaused(true);
+    }
+
+    @Override
+    public void onToggleOverlayVideoMode(boolean enabled) {}
+
+    @Override
+    public void onBottomControlsHeightChanged(int bottomControlsHeight) {}
+
+    @Override
     public boolean getWebVrModeEnabled() {
         return nativeGetWebVrMode(mNativeVrShell);
     }
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 14f013d..c145a3ea 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -1080,8 +1080,11 @@
       <message name="IDS_DATA_REDUCTION_USAGE_RESET_STATISTICS_BUTTON" desc="Text to be displayed on the button to reset the Data Reduction statistics.">
         Reset statistics
       </message>
+      <message name="IDS_DATA_REDUCTION_USAGE_RESET_STATISTICS_CONFIRMATION_TITLE" desc="Text to be displayed on the confirmation dialog to reset the Data Reduction statistics.">
+        Reset Data Saver?
+      </message>
       <message name="IDS_DATA_REDUCTION_USAGE_RESET_STATISTICS_CONFIRMATION_DIALOG" desc="Text to be displayed on the confirmation dialog to reset the Data Reduction statistics.">
-        Reset Data Saver? Resetting erases Data Saver history, including the list of visited sites.
+        Resetting erases Data Saver history, including the list of visited sites.
       </message>
       <message name="IDS_DATA_REDUCTION_USAGE_RESET_STATISTICS_CONFIRMATION_BUTTON" desc="Text to be displayed on the confirmation button to proceed with resetting the Data Reduction statistics.">
         Reset
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/README.md b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/README.md
new file mode 100644
index 0000000..c1dfb04
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/README.md
@@ -0,0 +1,238 @@
+# VR Instrumentation Tests
+
+## Introduction
+
+This directory contains all the Java-side infrastructure for running
+instrumentation tests on the two virtual reality (VR) features currently
+in Chrome:
+
+* [WebVR](https://webvr.info/) - Experience VR content on the web
+* VR Browser - Browse the 2D web from a VR headset
+
+## Directories
+
+These are the files and directories that are relevant to VR instrumentation
+testing. Additional information on files in other directories can be found in
+those directories' README.md files.
+
+### Subdirectories
+
+* `mock/` - Contains all the classes for mock implementations of VR classes
+* `nfc_apk/` - Contains the code for the standalone APK for NFC simulation. Used
+  by Telemetry tests, not instrumentation tests, but kept here since it uses
+  code from util/
+* `rules/` - Contains all the VR-specific JUnit4 rules
+* `util/` - Contains utility classes with code that's used by multiple test
+  classes
+
+### Other Directories
+
+* `//chrome/android/shared_preference_files/test/` - Contains the VrCore settings
+  files for running VR instrumentation tests (see the "Building and Running"
+  section for more information).
+* `//chrome/test/data/android/webvr_instrumentation/` - Contains the JavaScript
+  and HTML files for VR instrumentation tests.
+* `//third_party/gvr-android-sdk/test-apks/` - Contains the VrCore and Daydream
+  Home APKs for testing. You must have `DOWNLOAD_VR_TEST_APKS` set as an
+  environment variable when you run gclient sync in order to actually download
+  these from storage.
+* `//third_party/gvr-android-sdk/test-libraries/` - Contains third party VR
+  testing libraries. Currently, only has the Daydream controller test library.
+
+## Building and Running
+
+The VR instrumentation tests can be built with the `chrome_public_test_vr_apk`
+target, which will also build `chrome_public_apk` to test with.
+
+Once both are built, the tests can be run by executing
+`run_chrome_public_test_vr_apk` in your output directory's `bin/` directory.
+However, unless you happen to have everything set up properly on the device, the
+tests will most likely fail. To fix this, you just need to pass a few extra
+arguments to `run_chrome_public_test_vr_apk`.
+
+**NOTE** These tests can only be run on rooted devices.
+
+### Standard Arguments
+
+These are the arguments that you'll pretty much always want to pass in order to
+ensure that your device is set up properly for testing.
+
+#### additional-apk
+
+`--additional-apk path/to/apk/to/install`
+
+All this does is install the specified APK before running tests. You'll
+generally want to install the current version of VrCore by passing it
+
+`--additional-apk
+third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk`
+
+Just make sure that the APK is up to date and on your system by running gclient
+sync with the `DOWNLOAD_VR_TEST_APKS` environment variable set. This argument can
+be ommitted if you're fine with using whatever version of VrCore is already
+installed on the device.
+
+**NOTE** This will fail on most Pixel devices, as VrCore is pre-installed as a
+system app. You can get around this in two ways.
+
+* Remove VrCore as a system app by following the instructions
+  [here](go/vrcore/building-and-running). This will permanently solve the issue
+  unless you reflash your device.
+* Use `--replace-system-package
+  com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk`
+  instead. This will take significantly longer, as it requires rebooting, and
+  must be done every time you run the tests.
+
+#### shared-prefs-file
+
+`--shared-prefs-file path/to/preference/json/file`
+
+This will configure VrCore according to the provided file, e.g. changing the
+paired headset. The two most common files to use are:
+
+* `//chrome/android/shared_preference_files/test/vr_cardboard_skipdon_setupcomplete.json`
+  This will pair the device with a Cardboard headset and disabled controller
+  emulation.
+* `//chrome/android/shared_preference_files/test/vr_ddview_skipdon_setupcomplete.json`
+  This will pair the device with a Daydream View headset, set the DON flow to be
+  skipped, and enable controller emulation. **NOTE** This will prevent you from
+  using a real controller outside of tests. To fix this, you can either
+  reinstall VrCore or apply the Cardboard settings file (there isn't currently a
+  way to manually re-enable real controller use from within the VrCore developer
+  settings)
+
+### Other Useful Arguments
+
+#### test-filter
+
+`--test-filter TestClass#TestCase`
+
+If you only have interest in a particular test case or class, such as when you
+add a new test or are trying to reproduce a failure, you can significantly cut
+down on the test runtime by limiting the tests that are run. You can either
+specify a specific test case to restrict to, or use \* to restrict to all test
+cases within a test class.
+
+## Creating New Tests
+
+### Adding A Test Case To An Existing Test Class
+
+If you're new to adding VR instrumentation tests or instrumentation tests in
+general, it's a good idea to take a look at existing tests to get a feel for
+what's going on. In general, these are the things that you need to be aware of
+when adding a new test:
+
+#### @Test
+
+Every test case must be annotated with the `@Test` annotation in order to be
+identified as a test.
+
+#### Test Length
+
+Every test case must also have a test length annotation, typically
+`@MediumTest`. Eventually, the test length annotations should imply the presence
+of `@Test` as well, but for now, both must be present.
+
+#### VR Test Framework
+
+Most test classes will define a `VrTestFramework` member as `mVrTestFramework`,
+which contains functions that are critical for WebVR testing, and still useful
+for VR Browser testing. Examples include:
+
+* Retrieving HTML test files
+* Accessing the WebContents and ContentViewCore of whatever tab the test started
+  on, which is where the vast majority of testing takes place
+* Waiting on JavaScript test steps and running arbitrary JavaScript code
+
+If you are writing a test that uses WebVR, you will likely also need to add an
+HTML file to `//chrome/test/data/android/webvr_instrumentation/html/` that
+handles the JavaScript portion of the test. In general, a WebVR test will have a
+number of steps as functions on the JavaScript side. The Java side of the test
+will load the file and start a JavaScript step, blocking until it finishes. The
+JavaScript code will execute and call `finishJavaScriptStep()` when done, which
+will signal the Java code to continue. This repeats until the test is done. For
+more specifics, see `vr_test_framework.md`.
+
+#### VR Test Rule
+
+Every test class will have a `mVrTestRule` member that inherits from
+`ChromeActivityTestRule`. It does some important setup under the hood, but can
+also be used to interact with Chrome during a test. If you need to interact with
+Chrome and `VrTestFramework` does not have a way of doing that, then there is
+likely a way to do it using `mVrTestRule`.
+
+#### Test Parameterization
+
+If a test class is annotated with `@RunWith(ParameterizedRunner.class)`, then it
+has support for test parameterization. While parameterization has many potential
+uses, the current use in VR is to allow a test case to be automatically run in
+multiple different activity types. For specifics, see
+`rules/vr_activity_restriction.md`.
+
+If a class has test parameterization enabled, you have three options when adding
+a new test:
+
+* Do nothing special. This will default to only running the test in
+  ChromeTabbedActivity, which is the normal Chrome browser.
+* Add the `@VrActivityRestriction` annotation with
+  `VrActivityRestriction.SupportedActivity.ALL` as its value, which will run the
+  test in all activities that support VR
+* Add the `@VrActivityRestriction` annotation, manually specifying the
+  activities you want the test to run in
+
+### Adding A New Test Class
+
+Adding a new test class is similar to adding a test case to an existing class,
+but with a few extra steps.
+
+#### Determine Whether To Enable Test Parameterization
+
+Adding test parameterization will allow tests in the class to be automatically
+run in multiple activities that support VR. However, enabling it on a test class
+adds a non-trivial amount of runtime overhead, so you should only enable it in
+classes where it actually makes sense. In general, this boils down to whether
+your test class will have WebVR tests in it or not - WebVR is supported in
+multiple activity types, but VR Browsing is only supported in
+ChromeTabbedActivity.
+
+Whether test parameterization is enabled or not only affects the boilerplate
+code in the test class - test cases themselves do not care whether the feature
+is enabled or not.
+
+##### Non-Parameterized
+
+See `VrShellNavigationTest.java` for an example of how to set up non-parameterized
+test classes. In general, you will need to:
+
+* Set `@RunWith` to `ChromeJUnit4ClassRunner.class`
+* Define `mVrTestRule` as a `ChromeTabbedActivityVrTestRule`, annotate it with
+  `@Rule`, and initialize it where it's defined
+* Define `mVrTestFramework` as a VrTestFramework and initialize it using
+  `mVrTestRule` in a setup function annotated with `@Before`
+
+  Initializing this in the setup function is necessary since the test framework
+  needs `mVrTestRule` to be applied, which is guaranteed to have occurred by the
+  time the setup function is run.
+
+##### Parameterized
+
+See `WebVrTransitionTest.java` for an example of how to set up parameterized
+test classes. In general, you will need to:
+
+* Set `@RunWith` to `ParameterizedRunner.class`
+* Add `@UseRunnerDelegate` and set it to `ChromeJUnit4RunnerDelegate.class`
+* Define `sClassParams`, annotate it with `@ClassParameter`, and set it to the
+  value returned by `VrTestRuleUtils.generateDefaultVrTestRuleParameters()`
+* Define `mRuleChain` as a `RuleChain` and annotate it with `@Rule`
+* Define `mVrTestRule` as a `ChromeActivityTestRule`
+* Define `mVrTestFramework` as a `VrTestFramework` and initialize it using
+  `mVrTestRule` in a setup function annotated with `@Before`
+* Define a constructor for your test class that takes a
+  `Callable<ChromeActivityTestRule>`. This constructor must set `mVrTestRule` to
+  the callable's `call()` return value and set `mRuleChain` to the return value
+  of `VrTestruleUtils.wrapRuleInVrActivityRestrictionRule(mVrTestRule)`
+
+#### Include New Class In Build
+
+Add the new test class to the `java_files` list of the `chrome_test_vr_java`
+target in `//chrome/android/BUILD.gn`
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTransitionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTransitionTest.java
index 318e030..138dec7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTransitionTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTransitionTest.java
@@ -7,6 +7,7 @@
 import static org.chromium.chrome.browser.vr_shell.VrTestFramework.PAGE_LOAD_TIMEOUT_S;
 import static org.chromium.chrome.browser.vr_shell.VrTestFramework.POLL_CHECK_INTERVAL_SHORT_MS;
 import static org.chromium.chrome.browser.vr_shell.VrTestFramework.POLL_TIMEOUT_LONG_MS;
+import static org.chromium.chrome.browser.vr_shell.VrTestFramework.POLL_TIMEOUT_SHORT_MS;
 import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_DON_ENABLED;
 import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM;
 
@@ -169,4 +170,28 @@
         mVrTestFramework.waitOnJavaScriptStep(wc);
         mVrTestFramework.endTest(wc);
     }
+
+    /**
+     * Tests that the omnibox reappears after exiting VR.
+     */
+    @Test
+    @MediumTest
+    public void testControlsVisibleAfterExitingVr() throws InterruptedException {
+        mVrTestFramework.loadUrlAndAwaitInitialization(
+                VrTestFramework.getHtmlTestFile("generic_webvr_page"), PAGE_LOAD_TIMEOUT_S);
+        VrTransitionUtils.enterPresentationAndWait(
+                mVrTestFramework.getFirstTabCvc(), mVrTestFramework.getFirstTabWebContents());
+        VrTransitionUtils.forceExitVr();
+        // The hiding of the controls may only propagate after VR has exited, so give it a chance
+        // to propagate. In the worst case this test will erroneously pass, but should never
+        // erroneously fail, and should only be flaky if omnibox showing is broken.
+        Thread.sleep(100);
+        CriteriaHelper.pollUiThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                ChromeActivity activity = mVrTestFramework.getRule().getActivity();
+                return activity.getFullscreenManager().getBrowserControlHiddenRatio() == 0.0;
+            }
+        }, POLL_TIMEOUT_SHORT_MS, POLL_CHECK_INTERVAL_SHORT_MS);
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java
index d96e7f7..57aa813f 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java
@@ -17,8 +17,10 @@
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
+import android.widget.LinearLayout;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -60,6 +62,8 @@
 
     private InOrder mInOrder;
     private TestAutocompleteEditText mAutocomplete;
+    private LinearLayout mFocusPlaceHolder;
+
     private Context mContext;
     private InputConnection mInputConnection;
     private Verifier mVerifier;
@@ -160,13 +164,16 @@
 
         mVerifier = spy(new Verifier());
         mAutocomplete = new TestAutocompleteEditText(mContext, null);
+        mFocusPlaceHolder = new LinearLayout(mContext);
+        mFocusPlaceHolder.setFocusable(true);
+        mFocusPlaceHolder.addView(mAutocomplete);
         assertNotNull(mAutocomplete);
 
         // Pretend that the view is shown in the activity hierarchy, which is for accessibility
         // testing.
         Activity activity = Robolectric.buildActivity(Activity.class).create().get();
-        activity.setContentView(mAutocomplete);
-        assertNotNull(mAutocomplete.getParent());
+        activity.setContentView(mFocusPlaceHolder);
+        assertNotNull(mFocusPlaceHolder.getParent());
         mIsShown = true;
         assertTrue(mAutocomplete.isShown());
 
@@ -181,7 +188,7 @@
         mInOrder = inOrder(mVerifier);
         assertTrue(mAutocomplete.requestFocus());
         verifyOnPopulateAccessibilityEvent(
-                AccessibilityEvent.TYPE_VIEW_FOCUSED, "", "", 1, -1, -1, -1, -1);
+                AccessibilityEvent.TYPE_VIEW_FOCUSED, "", "", 2, -1, -1, -1, -1);
         assertNotNull(mAutocomplete.onCreateInputConnection(new EditorInfo()));
         mInputConnection = mAutocomplete.getInputConnection();
         assertNotNull(mInputConnection);
@@ -738,7 +745,7 @@
         if (isUsingSpannableModel()) {
             // Pretend that we have deleted 'o' first.
             mInOrder.verify(mVerifier).onUpdateSelection(4, 4);
-            // We restore 'o', and clears autocomplete text instead.
+            // We restore 'o', and clear autocomplete text instead.
             mInOrder.verify(mVerifier).onUpdateSelection(5, 5);
             assertTrue(mAutocomplete.isCursorVisible());
             // Autocomplete removed.
@@ -772,12 +779,18 @@
         assertTexts("hello", "");
     }
 
+    private boolean isComposing() {
+        return BaseInputConnection.getComposingSpanStart(mAutocomplete.getText())
+                != BaseInputConnection.getComposingSpanEnd(mAutocomplete.getText());
+    }
+
     @Test
     @Features(@Features.Register(
             value = ChromeFeatureList.SPANNABLE_INLINE_AUTOCOMPLETE, enabled = true))
     public void testDelete_SetComposingTextWithSpannableModel() {
         // User types "hello".
         assertTrue(mInputConnection.setComposingText("hello", 1));
+        assertTrue(isComposing());
         mInOrder.verify(mVerifier).onUpdateSelection(5, 5);
         verifyOnPopulateAccessibilityEvent(
                 AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED, "hello", "", -1, 0, -1, 0, 5);
@@ -800,9 +813,10 @@
         assertTrue(mInputConnection.setComposingText("hell", 1));
         // Pretend that we have deleted 'o'.
         mInOrder.verify(mVerifier).onUpdateSelection(4, 4);
-        // We restore 'o', and clear autocomplete text instead.
+        // We restore 'o', finish composition, and clear autocomplete text instead.
         mInOrder.verify(mVerifier).onUpdateSelection(5, 5);
         assertTrue(mAutocomplete.isCursorVisible());
+        assertFalse(isComposing());
         // Remove autocomplete.
         verifyOnPopulateAccessibilityEvent(
                 AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED, "hello", "hello world", -1, 5, -1, 6, 0);
@@ -1104,4 +1118,61 @@
         // should not crash.
         new SpannableString(mAutocomplete.getText());
     }
-}
\ No newline at end of file
+
+    // crbug.com/759876
+    @Test
+    @Features(@Features.Register(
+            value = ChromeFeatureList.SPANNABLE_INLINE_AUTOCOMPLETE, enabled = true))
+    public void testFocusInAndSelectAllWithSpannableModel() {
+        internalTestFocusInAndSelectAll();
+    }
+
+    // crbug.com/759876
+    @Test
+    @Features(@Features.Register(
+            value = ChromeFeatureList.SPANNABLE_INLINE_AUTOCOMPLETE, enabled = false))
+    public void testFocusInAndSelectAllWithoutSpannableModel() {
+        internalTestFocusInAndSelectAll();
+    }
+
+    private void internalTestFocusInAndSelectAll() {
+        final String url = "https://google.com";
+        final int len = url.length();
+        mAutocomplete.setIgnoreTextChangesForAutocomplete(true);
+        mAutocomplete.setText(url);
+        mAutocomplete.setIgnoreTextChangesForAutocomplete(false);
+
+        mInOrder.verifyNoMoreInteractions();
+        assertVerifierCallCounts(0, 0);
+
+        assertTrue(mFocusPlaceHolder.requestFocus());
+
+        mInOrder.verifyNoMoreInteractions();
+        assertVerifierCallCounts(0, 0);
+
+        // LocationBarLayout does this.
+        mAutocomplete.setSelectAllOnFocus(true);
+
+        assertTrue(mAutocomplete.requestFocus());
+
+        if (isUsingSpannableModel()) {
+            verifyOnPopulateAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED,
+                    url, "", 18, 18, 18, -1, -1);
+        }
+        mInOrder.verify(mVerifier).onUpdateSelection(len, len);
+        verifyOnPopulateAccessibilityEvent(
+                AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED, url, "", 18, 18, 18, -1, -1);
+        mInOrder.verify(mVerifier).onUpdateSelection(0, len);
+        verifyOnPopulateAccessibilityEvent(
+                AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED, url, "", 18, 0, 18, -1, -1);
+        verifyOnPopulateAccessibilityEvent(
+                AccessibilityEvent.TYPE_VIEW_FOCUSED, url, "", 2, -1, -1, -1, -1);
+
+        if (isUsingSpannableModel()) {
+            assertVerifierCallCounts(2, 4);
+        } else {
+            assertVerifierCallCounts(2, 3);
+        }
+        mInOrder.verifyNoMoreInteractions();
+    }
+}
diff --git a/chrome/android/webapk/shell_apk/shell_apk_version.gni b/chrome/android/webapk/shell_apk/shell_apk_version.gni
index a719946..a474b51 100644
--- a/chrome/android/webapk/shell_apk/shell_apk_version.gni
+++ b/chrome/android/webapk/shell_apk/shell_apk_version.gni
@@ -6,7 +6,7 @@
 # (including AndroidManifest.xml) is updated. This version should be incremented
 # prior to uploading a new ShellAPK to the WebAPK Minting Server.
 # Does not affect Chrome.apk
-template_shell_apk_version = 22
+template_shell_apk_version = 23
 
 # The ShellAPK version expected by Chrome. Chrome will try to update the WebAPK
 # if the WebAPK's ShellAPK version is less than |expected_shell_apk_version|.
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index d3f73099..ecfb99ea 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2021,7 +2021,7 @@
   </message>
   <if expr="chromeos">
     <message name="IDS_SETTINGS_SEARCH_AND_ASSISTANT" desc="Name of the settings page which displays search engine and assistant preferences on Chrome OS.">
-      Search engine and Google Assistant
+      Search and Assistant
     </message>
   </if>
   <message name="IDS_SETTINGS_SEARCH_EXPLANATION" desc="Explanation for the search engine dropdown setting.">
@@ -3397,13 +3397,13 @@
     <message name="IDS_SETTINGS_POWER_IDLE_LABEL" desc="In Device Settings > Power, label for behavior when device is idle.">
       When idle
     </message>
-    <message name="IDS_SETTINGS_POWER_IDLE_DISPLAY_OFF_SLEEP" desc="In Device Settings > Power, menu item for idle behavior that turns the screen off and later puts the device to sleep.">
-      Turn off display and sleep
+    <message name="IDS_SETTINGS_POWER_IDLE_DISPLAY_OFF_SLEEP" desc="In Device Settings > Power, menu item for idle behavior that turns the screen off and later puts the device to sleep. String must be short enough to fit in a drop-down menu.">
+      Sleep
     </message>
-    <message name="IDS_SETTINGS_POWER_IDLE_DISPLAY_OFF" desc="In Device Settings > Power, menu item for idle behavior that turns the screen off but prevents the device from sleeping.">
+    <message name="IDS_SETTINGS_POWER_IDLE_DISPLAY_OFF" desc="In Device Settings > Power, menu item for idle behavior that turns the screen off but prevents the device from sleeping. String must be short enough to fit in a drop-down menu.">
       Turn off display
     </message>
-    <message name="IDS_SETTINGS_POWER_IDLE_DISPLAY_ON" desc="In Device Settings > Power, menu item for idle behavior that prevents the screen from turning off and prevents the device from sleeping.">
+    <message name="IDS_SETTINGS_POWER_IDLE_DISPLAY_ON" desc="In Device Settings > Power, menu item for idle behavior that prevents the screen from turning off and prevents the device from sleeping. String must be short enough to fit in a drop-down menu.">
       Keep display on
     </message>
     <message name="IDS_SETTINGS_POWER_IDLE_OTHER" desc="In Device Settings > Power, menu item for custom idle behavior.">
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index f22b101..20c3b001 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -442,6 +442,10 @@
     "file_select_helper_mac.mm",
     "flag_descriptions.cc",
     "flag_descriptions.h",
+    "font_pref_change_notifier.cc",
+    "font_pref_change_notifier.h",
+    "font_pref_change_notifier_factory.cc",
+    "font_pref_change_notifier_factory.h",
     "fullscreen.h",
     "fullscreen_chromeos.cc",
     "fullscreen_mac.mm",
@@ -1398,8 +1402,6 @@
     "tab_contents/tab_util.h",
     "task_manager/web_contents_tags.cc",
     "task_manager/web_contents_tags.h",
-    "task_profiler/task_profiler_data_serializer.cc",
-    "task_profiler/task_profiler_data_serializer.h",
     "themes/theme_service_win.cc",
     "themes/theme_service_win.h",
     "thumbnails/thumbnail_list_source.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 4c544b5c..1b5c44a 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2264,6 +2264,10 @@
      flag_descriptions::kOfflinePagesPrefetchingName,
      flag_descriptions::kOfflinePagesPrefetchingDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(offline_pages::kPrefetchingOfflinePagesFeature)},
+    {"offline-pages-prefetching-ui",
+     flag_descriptions::kOfflinePagesPrefetchingUIName,
+     flag_descriptions::kOfflinePagesPrefetchingUIDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(offline_pages::kOfflinePagesPrefetchingUIFeature)},
     {"background-loader-for-downloads",
      flag_descriptions::kBackgroundLoaderForDownloadsName,
      flag_descriptions::kBackgroundLoaderForDownloadsDescription, kOsAndroid,
@@ -2687,6 +2691,11 @@
      flag_descriptions::kWebPaymentsModifiersDescription,
      kOsAndroid | kOsDesktop,
      FEATURE_VALUE_TYPE(payments::features::kWebPaymentsModifiers)},
+    {"service-worker-payment-apps",
+     flag_descriptions::kServiceWorkerPaymentAppsName,
+     flag_descriptions::kServiceWorkerPaymentAppsDescription,
+     kOsAndroid | kOsDesktop,
+     FEATURE_VALUE_TYPE(features::kServiceWorkerPaymentApps)},
 #if defined(OS_ANDROID)
     {"enable-android-pay-integration-v1",
      flag_descriptions::kEnableAndroidPayIntegrationV1Name,
@@ -2712,10 +2721,6 @@
     {"pay-with-google-v1", flag_descriptions::kPayWithGoogleV1Name,
      flag_descriptions::kPayWithGoogleV1Description, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kPayWithGoogleV1)},
-    {"service-worker-payment-apps",
-     flag_descriptions::kServiceWorkerPaymentAppsName,
-     flag_descriptions::kServiceWorkerPaymentAppsDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(features::kServiceWorkerPaymentApps)},
 #endif  // OS_ANDROID
 #if defined(OS_CHROMEOS)
     {"disable-eol-notification", flag_descriptions::kEolNotificationName,
diff --git a/chrome/browser/android/bottombar/overlay_panel_content.cc b/chrome/browser/android/bottombar/overlay_panel_content.cc
index 9b35b4d..32977048 100644
--- a/chrome/browser/android/bottombar/overlay_panel_content.cc
+++ b/chrome/browser/android/bottombar/overlay_panel_content.cc
@@ -63,6 +63,7 @@
       content::WebContents::FromJavaWebContents(jweb_contents);
   gfx::Size size(width, height);
   web_contents->GetNativeView()->OnPhysicalBackingSizeChanged(size);
+  web_contents->GetNativeView()->OnSizeChanged(width, height);
 }
 
 void OverlayPanelContent::RemoveLastHistoryEntry(
diff --git a/chrome/browser/android/net/external_estimate_provider_android.cc b/chrome/browser/android/net/external_estimate_provider_android.cc
index 761a9fc2..689ac493 100644
--- a/chrome/browser/android/net/external_estimate_provider_android.cc
+++ b/chrome/browser/android/net/external_estimate_provider_android.cc
@@ -20,15 +20,18 @@
     : task_runner_(nullptr),
       delegate_(nullptr),
       weak_factory_(this) {
-  if (base::ThreadTaskRunnerHandle::IsSet())
-    task_runner_ = base::ThreadTaskRunnerHandle::Get();
-  JNIEnv* env = base::android::AttachCurrentThread();
-  j_external_estimate_provider_.Reset(
-      Java_ExternalEstimateProviderAndroid_create(
-          env, reinterpret_cast<intptr_t>(this)));
-  DCHECK(!j_external_estimate_provider_.is_null());
-  no_value_ = Java_ExternalEstimateProviderAndroid_getNoValue(env);
+  DCHECK(j_external_estimate_provider_.is_null());
   DCHECK_GE(-1, no_value_);
+
+  if (!base::ThreadTaskRunnerHandle::IsSet())
+    return;
+
+  task_runner_ = base::ThreadTaskRunnerHandle::Get();
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&ExternalEstimateProviderAndroid::CreateJavaObject,
+                 weak_factory_.GetWeakPtr()),
+      base::TimeDelta::FromSeconds(10));
 }
 
 ExternalEstimateProviderAndroid::~ExternalEstimateProviderAndroid() {
@@ -39,8 +42,23 @@
   }
 }
 
+void ExternalEstimateProviderAndroid::CreateJavaObject() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(j_external_estimate_provider_.is_null());
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  j_external_estimate_provider_.Reset(
+      Java_ExternalEstimateProviderAndroid_create(
+          env, reinterpret_cast<intptr_t>(this)));
+  DCHECK(!j_external_estimate_provider_.is_null());
+  DCHECK_EQ(no_value_, Java_ExternalEstimateProviderAndroid_getNoValue(env));
+  Update();
+}
+
 base::TimeDelta ExternalEstimateProviderAndroid::GetRTT() const {
   DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!j_external_estimate_provider_.is_null());
+
   JNIEnv* env = base::android::AttachCurrentThread();
   int32_t milliseconds =
       Java_ExternalEstimateProviderAndroid_getRTTMilliseconds(
@@ -51,6 +69,8 @@
 
 int32_t ExternalEstimateProviderAndroid::GetDownstreamThroughputKbps() const {
   DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!j_external_estimate_provider_.is_null());
+
   JNIEnv* env = base::android::AttachCurrentThread();
   int32_t kbps =
       Java_ExternalEstimateProviderAndroid_getDownstreamThroughputKbps(
@@ -97,7 +117,6 @@
 
 void ExternalEstimateProviderAndroid::NotifyUpdatedEstimateAvailable() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(!j_external_estimate_provider_.is_null());
 
   if (delegate_) {
     delegate_->OnUpdatedEstimateAvailable(GetRTT(),
diff --git a/chrome/browser/android/net/external_estimate_provider_android.h b/chrome/browser/android/net/external_estimate_provider_android.h
index 222443b..92d0ff279c 100644
--- a/chrome/browser/android/net/external_estimate_provider_android.h
+++ b/chrome/browser/android/net/external_estimate_provider_android.h
@@ -47,18 +47,21 @@
   // Protected for testing.
   void NotifyUpdatedEstimateAvailable() const;
 
- private:
   // Returns the estimated RTT value. If the estimate is unavailable, a negative
-  // value is returned.
-  base::TimeDelta GetRTT() const;
+  // value is returned. Protected for testing.
+  virtual base::TimeDelta GetRTT() const;
 
   // Returns the estimated downstream throughput (in Kbps -- Kilobits
   // per second) is available.  If the estimate is unavailable, a negative value
-  // is returned.
-  int32_t GetDownstreamThroughputKbps() const;
+  // is returned. Protected for testing.
+  virtual int32_t GetDownstreamThroughputKbps() const;
+
+ private:
+  // Creates the corresponding Java object.
+  void CreateJavaObject();
 
   // Value returned if valid value is unavailable.
-  int32_t no_value_ = -1;
+  const int32_t no_value_ = -1;
 
   base::android::ScopedJavaGlobalRef<jobject> j_external_estimate_provider_;
 
diff --git a/chrome/browser/android/net/external_estimate_provider_android_unittest.cc b/chrome/browser/android/net/external_estimate_provider_android_unittest.cc
index 205cee0..efa1f9f 100644
--- a/chrome/browser/android/net/external_estimate_provider_android_unittest.cc
+++ b/chrome/browser/android/net/external_estimate_provider_android_unittest.cc
@@ -46,8 +46,8 @@
 
   void OnUpdatedEstimateAvailable(const base::TimeDelta& rtt,
                                   int32_t downstream_throughput_kbps) override {
-    EXPECT_GT(base::TimeDelta(), rtt);
-    EXPECT_EQ(-1, downstream_throughput_kbps);
+    EXPECT_EQ(base::TimeDelta::FromSeconds(1), rtt);
+    EXPECT_EQ(200, downstream_throughput_kbps);
     notified_ = true;
     net::NetworkQualityEstimator::OnUpdatedEstimateAvailable(
         rtt, downstream_throughput_kbps);
@@ -66,6 +66,13 @@
       : chrome::android::ExternalEstimateProviderAndroid() {}
   ~TestExternalEstimateProviderAndroid() override {}
   using ExternalEstimateProviderAndroid::NotifyUpdatedEstimateAvailable;
+
+ private:
+  base::TimeDelta GetRTT() const override {
+    return base::TimeDelta::FromSeconds(1);
+  }
+
+  int32_t GetDownstreamThroughputKbps() const override { return 200; }
 };
 
 // Tests if the |ExternalEstimateProviderAndroid| notifies
@@ -88,7 +95,7 @@
   ptr->NotifyUpdatedEstimateAvailable();
   EXPECT_TRUE(network_quality_estimator.notified());
 
-  histogram_tester.ExpectTotalCount("NQE.ExternalEstimateProviderStatus", 2);
+  histogram_tester.ExpectTotalCount("NQE.ExternalEstimateProviderStatus", 4);
 
   // EXTERNAL_ESTIMATE_PROVIDER_STATUS_AVAILABLE
   histogram_tester.ExpectBucketCount("NQE.ExternalEstimateProviderStatus", 1,
diff --git a/chrome/browser/android/vr_shell/vr_shell.cc b/chrome/browser/android/vr_shell/vr_shell.cc
index 62a20494..a1902ca 100644
--- a/chrome/browser/android/vr_shell/vr_shell.cc
+++ b/chrome/browser/android/vr_shell/vr_shell.cc
@@ -529,6 +529,7 @@
     jint height) {
   content::WebContents* web_contents =
       content::WebContents::FromJavaWebContents(jweb_contents);
+  web_contents->GetNativeView()->OnSizeChanged(width, height);
   gfx::Size size(width, height);
   web_contents->GetNativeView()->OnPhysicalBackingSizeChanged(size);
 }
diff --git a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
index ba0a7245..ffc216b 100644
--- a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
@@ -617,6 +617,17 @@
 
 // The tests below aren't needed in --use-cross-process-frames-for-guests.
 class WebViewContextMenuInteractiveTest : public WebViewInteractiveTestBase {};
+class WebViewBrowserPluginInteractiveTest : public WebViewInteractiveTestBase {
+ public:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    WebViewInteractiveTestBase::SetUpCommandLine(command_line);
+    scoped_feature_list_.InitAndDisableFeature(
+        features::kGuestViewCrossProcessFrames);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
 
 // The following class of tests do not work for OOPIF <webview>.
 // TODO(ekaramad): Make this tests work with OOPIF and replace the test classes
@@ -1195,6 +1206,57 @@
   }
 }
 
+// https://crbug.com/754890: The embedder could become out of sync and think
+// that the guest is not focused when the guest actually was.
+IN_PROC_BROWSER_TEST_F(WebViewBrowserPluginInteractiveTest, EnsureFocusSynced) {
+  LoadAndLaunchPlatformApp("web_view/focus_sync", "WebViewTest.LAUNCHED");
+
+  content::WebContents* embedder_web_contents = GetFirstAppWindowWebContents();
+  content::WebContents* guest_web_contents =
+      GetGuestViewManager()->WaitForSingleGuestCreated();
+
+  content::MainThreadFrameObserver embedder_observer(
+      embedder_web_contents->GetMainFrame()->GetView()->GetRenderWidgetHost());
+  content::MainThreadFrameObserver guest_observer(
+      guest_web_contents->GetMainFrame()->GetView()->GetRenderWidgetHost());
+  embedder_observer.Wait();
+  guest_observer.Wait();
+
+  ExtensionTestMessageListener listener{"WebViewTest.WEBVIEW_LOADED", false};
+  EXPECT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(GetPlatformAppWindow()));
+  EXPECT_TRUE(content::ExecuteScript(embedder_web_contents,
+                                     "chrome.app.window.getAll()[0].focus()"));
+
+  // Embedder should be focused.
+  EXPECT_EQ(guest_web_contents,
+            content::GetFocusedWebContents(guest_web_contents));
+  listener.WaitUntilSatisfied();
+
+  // Check that the inner contents is correctly focused.
+  bool result;
+  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+      guest_web_contents,
+      "window.requestAnimationFrame(function() {"
+      "  window.domAutomationController.send(checkValid());"
+      "});",
+      &result));
+  EXPECT_TRUE(result);
+
+  listener.Reset();
+  EXPECT_TRUE(
+      content::ExecuteScript(embedder_web_contents, "reloadWebview();"));
+  listener.WaitUntilSatisfied();
+
+  // Check that the inner contents is correctly focused after a reload.
+  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+      guest_web_contents,
+      "window.requestAnimationFrame(function() {"
+      "  window.domAutomationController.send(checkValid());"
+      "});",
+      &result));
+  EXPECT_TRUE(result);
+}
+
 IN_PROC_BROWSER_TEST_P(WebViewInteractiveTest, ExecuteCode) {
   ASSERT_TRUE(RunPlatformAppTestWithArg(
       "platform_apps/web_view/common", "execute_code")) << message_;
diff --git a/chrome/browser/background/background_contents.cc b/chrome/browser/background/background_contents.cc
index a419a54..49a8ac07 100644
--- a/chrome/browser/background/background_contents.cc
+++ b/chrome/browser/background/background_contents.cc
@@ -6,7 +6,6 @@
 
 #include <utility>
 
-#include "base/profiler/scoped_tracker.h"
 #include "chrome/browser/background/background_contents_service.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/data_use_measurement/data_use_web_contents_observer.h"
@@ -212,10 +211,6 @@
 }
 
 void BackgroundContents::CreateRenderViewNow() {
-  // TODO(robliao): Remove ScopedTracker below once crbug.com/464206 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "464206 BackgroundContents::CreateRenderViewNow"));
   web_contents()->GetController().LoadURL(initial_url_, content::Referrer(),
                                           ui::PAGE_TRANSITION_LINK,
                                           std::string());
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index b4bc9fb3..ce3717d 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -452,8 +452,6 @@
         <include name="IDR_PRINT_PREVIEW_IMAGES_MOBILE_SHARED"
                  file="resources\print_preview\images\mobile_shared.png" type="BINDATA" />
       </if>
-      <include name="IDR_PROFILER_HTML" file="resources\profiler\profiler.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
-      <include name="IDR_PROFILER_JS" file="resources\profiler\profiler.js" flattenhtml="true" type="BINDATA" compress="gzip" />
       <include name="IDR_SITE_ENGAGEMENT_HTML" file="resources\engagement\site_engagement.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
       <include name="IDR_SITE_ENGAGEMENT_JS" file="resources\engagement\site_engagement.js" flattenhtml="true" type="BINDATA" compress="gzip" />
       <include name="IDR_SITE_ENGAGEMENT_MOJO_JS" file="${root_gen_dir}\chrome\browser\engagement\site_engagement_details.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index e41b16f5..9d4d86a 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -27,7 +27,6 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/path_service.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/profiler/stack_sampling_profiler.h"
 #include "base/run_loop.h"
 #include "base/sequenced_task_runner.h"
@@ -726,22 +725,6 @@
       variations::SyntheticTrialsActiveGroupIdProvider::GetInstance());
   // Now that field trials have been created, initializes metrics recording.
   metrics->InitializeMetricsRecordingState();
-
-  const version_info::Channel channel = chrome::GetChannel();
-
-  // Enable profiler instrumentation depending on the channel.
-  switch (channel) {
-    case version_info::Channel::UNKNOWN:
-    case version_info::Channel::CANARY:
-      tracked_objects::ScopedTracker::Enable();
-      break;
-
-    case version_info::Channel::DEV:
-    case version_info::Channel::BETA:
-    case version_info::Channel::STABLE:
-      // Don't enable instrumentation.
-      break;
-  }
 }
 
 void ChromeBrowserMainParts::StartMetricsRecording() {
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index c977afc..e8361e5 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -501,14 +501,8 @@
     "display/output_protection_controller_mus.h",
     "display/output_protection_delegate.cc",
     "display/output_protection_delegate.h",
-    "display/overscan_calibrator.cc",
-    "display/overscan_calibrator.h",
     "display/quirks_manager_delegate_impl.cc",
     "display/quirks_manager_delegate_impl.h",
-    "display/touch_calibrator/touch_calibrator_controller.cc",
-    "display/touch_calibrator/touch_calibrator_controller.h",
-    "display/touch_calibrator/touch_calibrator_view.cc",
-    "display/touch_calibrator/touch_calibrator_view.h",
     "drive/debug_info_collector.cc",
     "drive/debug_info_collector.h",
     "drive/download_handler.cc",
@@ -1354,8 +1348,6 @@
     "preferences.h",
     "prefs/pref_connector_service.cc",
     "prefs/pref_connector_service.h",
-    "printing/combining_printer_detector.cc",
-    "printing/combining_printer_detector.h",
     "printing/cups_print_job.cc",
     "printing/cups_print_job.h",
     "printing/cups_print_job_manager.cc",
@@ -1722,7 +1714,6 @@
     "certificate_provider/certificate_provider_service_unittest.cc",
     "customization/customization_document_unittest.cc",
     "display/display_preferences_unittest.cc",
-    "display/touch_calibrator/touch_calibrator_controller_unittest.cc",
     "drive/download_handler_unittest.cc",
     "drive/drive_file_stream_reader_unittest.cc",
     "drive/drive_integration_service_unittest.cc",
@@ -1884,7 +1875,6 @@
     "power/power_prefs_unittest.cc",
     "power/renderer_freezer_unittest.cc",
     "preferences_unittest.cc",
-    "printing/combining_printer_detector_unittest.cc",
     "printing/cups_printers_manager_unittest.cc",
     "printing/printer_event_tracker_unittest.cc",
     "printing/printers_sync_bridge_unittest.cc",
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_external_loader.cc b/chrome/browser/chromeos/app_mode/kiosk_app_external_loader.cc
index 1895edd3..18c0a12 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_external_loader.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_external_loader.cc
@@ -25,7 +25,7 @@
 
 void KioskAppExternalLoader::StartLoading() {
   if (prefs_)
-    LoadFinished();
+    LoadFinished(std::move(prefs_));
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_external_loader.h b/chrome/browser/chromeos/app_mode/kiosk_app_external_loader.h
index ac1a6819..f268980d 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_external_loader.h
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_external_loader.h
@@ -28,6 +28,8 @@
  private:
   ~KioskAppExternalLoader() override;
 
+  std::unique_ptr<base::DictionaryValue> prefs_;
+
   DISALLOW_COPY_AND_ASSIGN(KioskAppExternalLoader);
 };
 
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
index cbc1b54..5e6a77b 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.cc
@@ -144,8 +144,68 @@
   return true;
 }
 
+bool HasCoveringSpan(arc::mojom::AccessibilityNodeInfoData* data,
+                     arc::mojom::AccessibilityStringProperty prop,
+                     arc::mojom::SpanType span_type) {
+  if (!data->spannable_string_properties)
+    return false;
+
+  std::string text;
+  GetStringProperty(data, prop, &text);
+  if (text.empty())
+    return false;
+
+  auto span_entries_it = data->spannable_string_properties->find(prop);
+  if (span_entries_it == data->spannable_string_properties->end())
+    return false;
+
+  for (size_t i = 0; i < span_entries_it->second.size(); ++i) {
+    if (span_entries_it->second[i]->span_type != span_type)
+      continue;
+
+    size_t span_size =
+        span_entries_it->second[i]->end - span_entries_it->second[i]->start;
+    if (span_size == text.size())
+      return true;
+  }
+  return false;
+}
+
 void PopulateAXRole(arc::mojom::AccessibilityNodeInfoData* node,
                     ui::AXNodeData* out_data) {
+  if (HasCoveringSpan(node, arc::mojom::AccessibilityStringProperty::TEXT,
+                      arc::mojom::SpanType::URL) ||
+      HasCoveringSpan(
+          node, arc::mojom::AccessibilityStringProperty::CONTENT_DESCRIPTION,
+          arc::mojom::SpanType::URL)) {
+    out_data->role = ui::AX_ROLE_LINK;
+    return;
+  }
+
+  arc::mojom::AccessibilityCollectionItemInfoData* collection_item_info =
+      node->collection_item_info.get();
+  if (collection_item_info) {
+    if (collection_item_info->is_heading) {
+      out_data->role = ui::AX_ROLE_HEADING;
+    } else {
+      out_data->role = ui::AX_ROLE_LIST_ITEM;
+      out_data->AddIntAttribute(ui::AX_ATTR_POS_IN_SET,
+                                collection_item_info->row_index);
+    }
+    return;
+  }
+
+  std::string chrome_role;
+  if (GetStringProperty(node,
+                        arc::mojom::AccessibilityStringProperty::CHROME_ROLE,
+                        &chrome_role)) {
+    ui::AXRole role_value = ui::ParseAXRole(chrome_role);
+    if (role_value != ui::AX_ROLE_NONE) {
+      out_data->role = role_value;
+      return;
+    }
+  }
+
   std::string class_name;
   GetStringProperty(node, arc::mojom::AccessibilityStringProperty::CLASS_NAME,
                     &class_name);
@@ -431,6 +491,12 @@
   else if (GetStringProperty(node, AXStringProperty::CONTENT_DESCRIPTION,
                              &text))
     out_data->SetName(text);
+  std::string role_description;
+  if (GetStringProperty(node, AXStringProperty::ROLE_DESCRIPTION,
+                        &role_description)) {
+    out_data->AddStringAttribute(ui::AX_ATTR_ROLE_DESCRIPTION,
+                                 role_description);
+  }
 
   // Boolean properties.
   PopulateAXState(node, out_data);
diff --git a/chrome/browser/chromeos/customization/customization_document.cc b/chrome/browser/chromeos/customization/customization_document.cc
index ada7398..427efc51 100644
--- a/chrome/browser/chromeos/customization/customization_document.cc
+++ b/chrome/browser/chromeos/customization/customization_document.cc
@@ -201,10 +201,9 @@
         return;
     }
 
-    prefs_.reset(apps_.DeepCopy());
     VLOG(1) << "ServicesCustomization extension loader publishing "
             << apps_.size() << " apps.";
-    LoadFinished();
+    LoadFinished(apps_.CreateDeepCopy());
   }
 
  protected:
diff --git a/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.cc b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.cc
index 820211e..71be9b2 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.cc
+++ b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.cc
@@ -58,8 +58,13 @@
 }
 
 void DeviceLocalAccountExternalPolicyLoader::StartLoading() {
+  DCHECK(has_owner());
+
+  // Through OnExtensionListsUpdated(), |prefs_| might have already loaded but
+  // not consumed because we didn't have an owner then. Pass |prefs_| in that
+  // case.
   if (prefs_)
-    LoadFinished();
+    LoadFinished(std::move(prefs_));
 }
 
 void DeviceLocalAccountExternalPolicyLoader::OnStoreLoaded(
@@ -78,8 +83,10 @@
 void DeviceLocalAccountExternalPolicyLoader::OnExtensionListsUpdated(
     const base::DictionaryValue* prefs) {
   DCHECK(external_cache_ || prefs->empty());
-  prefs_.reset(prefs->DeepCopy());
-  LoadFinished();
+  prefs_ = prefs->CreateDeepCopy();
+  // Only call LoadFinished() when there is an owner to consume |prefs_|.
+  if (has_owner())
+    LoadFinished(std::move(prefs_));
 }
 
 ExternalCache*
diff --git a/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h
index d65d5fe..342bc704 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h
+++ b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h
@@ -71,6 +71,7 @@
   policy::CloudPolicyStore* store_;
   const base::FilePath cache_dir_;
   std::unique_ptr<ExternalCache> external_cache_;
+  std::unique_ptr<base::DictionaryValue> prefs_;
 
   DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountExternalPolicyLoader);
 };
diff --git a/chrome/browser/chromeos/lock_screen_apps/app_manager_impl_unittest.cc b/chrome/browser/chromeos/lock_screen_apps/app_manager_impl_unittest.cc
index 2ed311c..e8ef17e 100644
--- a/chrome/browser/chromeos/lock_screen_apps/app_manager_impl_unittest.cc
+++ b/chrome/browser/chromeos/lock_screen_apps/app_manager_impl_unittest.cc
@@ -32,11 +32,10 @@
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/arc_session.h"
 #include "content/public/test/test_browser_thread_bundle.h"
-#include "extensions/browser/event_router.h"
-#include "extensions/browser/event_router_factory.h"
 #include "extensions/browser/extension_file_task_runner.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
+#include "extensions/browser/test_event_router.h"
 #include "extensions/common/api/app_runtime.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
@@ -55,34 +54,42 @@
   return nullptr;
 }
 
-class TestEventRouter : public extensions::EventRouter {
+class LockScreenEventRouter : public extensions::TestEventRouter {
  public:
-  explicit TestEventRouter(content::BrowserContext* context)
-      : extensions::EventRouter(context,
-                                extensions::ExtensionPrefs::Get(context)),
-        context_(context) {}
-  ~TestEventRouter() override = default;
+  explicit LockScreenEventRouter(content::BrowserContext* context)
+      : extensions::TestEventRouter(context) {}
+  ~LockScreenEventRouter() override = default;
 
+  // extensions::EventRouter:
   bool ExtensionHasEventListener(const std::string& extension_id,
                                  const std::string& event_name) const override {
     return event_name == extensions::api::app_runtime::OnLaunched::kEventName;
   }
 
-  void BroadcastEvent(std::unique_ptr<extensions::Event> event) override {}
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LockScreenEventRouter);
+};
 
-  void DispatchEventToExtension(
-      const std::string& extension_id,
-      std::unique_ptr<extensions::Event> event) override {
-    if (event->event_name !=
+class LockScreenEventObserver
+    : public extensions::TestEventRouter::EventObserver {
+ public:
+  explicit LockScreenEventObserver(content::BrowserContext* context)
+      : context_(context) {}
+  ~LockScreenEventObserver() override = default;
+
+  // extensions::TestEventRouter::EventObserver:
+  void OnDispatchEventToExtension(const std::string& extension_id,
+                                  const extensions::Event& event) override {
+    if (event.event_name !=
         extensions::api::app_runtime::OnLaunched::kEventName) {
       return;
     }
-    ASSERT_TRUE(event->event_args);
+    ASSERT_TRUE(event.event_args);
     const base::Value* arg_value = nullptr;
-    ASSERT_TRUE(event->event_args->Get(0, &arg_value));
+    ASSERT_TRUE(event.event_args->Get(0, &arg_value));
     ASSERT_TRUE(arg_value);
-    if (event->restrict_to_browser_context)
-      EXPECT_EQ(context_, event->restrict_to_browser_context);
+    if (event.restrict_to_browser_context)
+      EXPECT_EQ(context_, event.restrict_to_browser_context);
 
     std::unique_ptr<extensions::api::app_runtime::LaunchData> launch_data =
         extensions::api::app_runtime::LaunchData::FromValue(*arg_value);
@@ -116,14 +123,9 @@
   content::BrowserContext* context_;
   bool expect_restore_action_state_ = true;
 
-  DISALLOW_COPY_AND_ASSIGN(TestEventRouter);
+  DISALLOW_COPY_AND_ASSIGN(LockScreenEventObserver);
 };
 
-std::unique_ptr<KeyedService> TestEventRouterFactoryFunction(
-    content::BrowserContext* profile) {
-  return base::MakeUnique<TestEventRouter>(profile);
-}
-
 enum class TestAppLocation { kUnpacked, kInternal };
 
 class LockScreenAppManagerImplTest
@@ -184,6 +186,15 @@
         false /* autoupdate_enabled */);
   }
 
+  void SetUpTestEventRouter() {
+    LockScreenEventRouter* event_router =
+        extensions::CreateAndUseTestEventRouter<LockScreenEventRouter>(
+            lock_screen_profile()->GetOriginalProfile());
+    event_observer_ = std::make_unique<LockScreenEventObserver>(
+        lock_screen_profile()->GetOriginalProfile());
+    event_router->AddEventObserver(event_observer_.get());
+  }
+
   base::FilePath GetTestAppSourcePath(TestAppLocation location,
                                       Profile* profile,
                                       const std::string& id,
@@ -382,6 +393,8 @@
 
   int NoteTakingChangedCountOnStart() { return IsInstallAsync() ? 1 : 0; }
 
+  LockScreenEventObserver* event_observer() { return event_observer_.get(); }
+
  protected:
   base::SimpleTestTickClock tick_clock_;
 
@@ -399,6 +412,8 @@
   TestingProfile* profile_ = nullptr;
   TestingProfile* lock_screen_profile_ = nullptr;
 
+  std::unique_ptr<LockScreenEventObserver> event_observer_;
+
   std::unique_ptr<arc::ArcServiceManager> arc_service_manager_;
   std::unique_ptr<arc::ArcSessionManager> arc_session_manager_;
 
@@ -1007,11 +1022,7 @@
 }
 
 TEST_P(LockScreenAppManagerImplTest, LaunchAppWhenEnabled) {
-  TestEventRouter* event_router = static_cast<TestEventRouter*>(
-      extensions::EventRouterFactory::GetInstance()->SetTestingFactoryAndUse(
-          lock_screen_profile()->GetOriginalProfile(),
-          &TestEventRouterFactoryFunction));
-  ASSERT_TRUE(event_router);
+  SetUpTestEventRouter();
 
   scoped_refptr<const extensions::Extension> note_taking_app =
       AddTestAppWithLockScreenSupport(
@@ -1026,25 +1037,21 @@
 
   EXPECT_TRUE(app_manager()->LaunchNoteTaking());
 
-  ASSERT_EQ(1u, event_router->launched_apps().size());
+  ASSERT_EQ(1u, event_observer()->launched_apps().size());
   EXPECT_EQ(chromeos::NoteTakingHelper::kProdKeepExtensionId,
-            event_router->launched_apps()[0]);
-  event_router->ClearLaunchedApps();
+            event_observer()->launched_apps()[0]);
+  event_observer()->ClearLaunchedApps();
 
   app_manager()->Stop();
 
   EXPECT_FALSE(app_manager()->LaunchNoteTaking());
-  EXPECT_TRUE(event_router->launched_apps().empty());
+  EXPECT_TRUE(event_observer()->launched_apps().empty());
 }
 
 TEST_P(LockScreenAppManagerImplTest, LaunchAppWithFalseRestoreLastActionState) {
-  TestEventRouter* event_router = static_cast<TestEventRouter*>(
-      extensions::EventRouterFactory::GetInstance()->SetTestingFactoryAndUse(
-          lock_screen_profile()->GetOriginalProfile(),
-          &TestEventRouterFactoryFunction));
-  ASSERT_TRUE(event_router);
+  SetUpTestEventRouter();
 
-  event_router->set_expect_restore_action_state(false);
+  event_observer()->set_expect_restore_action_state(false);
   profile()->GetPrefs()->SetBoolean(prefs::kRestoreLastLockScreenNote, false);
 
   scoped_refptr<const extensions::Extension> note_taking_app =
@@ -1060,23 +1067,19 @@
 
   EXPECT_TRUE(app_manager()->LaunchNoteTaking());
 
-  ASSERT_EQ(1u, event_router->launched_apps().size());
+  ASSERT_EQ(1u, event_observer()->launched_apps().size());
   EXPECT_EQ(chromeos::NoteTakingHelper::kProdKeepExtensionId,
-            event_router->launched_apps()[0]);
-  event_router->ClearLaunchedApps();
+            event_observer()->launched_apps()[0]);
+  event_observer()->ClearLaunchedApps();
 
   app_manager()->Stop();
 
   EXPECT_FALSE(app_manager()->LaunchNoteTaking());
-  EXPECT_TRUE(event_router->launched_apps().empty());
+  EXPECT_TRUE(event_observer()->launched_apps().empty());
 }
 
 TEST_P(LockScreenAppManagerImplTest, LaunchAppWhenNoLockScreenApp) {
-  TestEventRouter* event_router = static_cast<TestEventRouter*>(
-      extensions::EventRouterFactory::GetInstance()->SetTestingFactoryAndUse(
-          lock_screen_profile()->GetOriginalProfile(),
-          &TestEventRouterFactoryFunction));
-  ASSERT_TRUE(event_router);
+  SetUpTestEventRouter();
 
   scoped_refptr<const extensions::Extension> note_taking_app =
       AddTestAppWithLockScreenSupport(
@@ -1087,11 +1090,11 @@
   RunExtensionServiceTaskRunner(lock_screen_profile());
 
   EXPECT_FALSE(app_manager()->LaunchNoteTaking());
-  EXPECT_TRUE(event_router->launched_apps().empty());
+  EXPECT_TRUE(event_observer()->launched_apps().empty());
 
   app_manager()->Stop();
   EXPECT_FALSE(app_manager()->LaunchNoteTaking());
-  EXPECT_TRUE(event_router->launched_apps().empty());
+  EXPECT_TRUE(event_observer()->launched_apps().empty());
 }
 
 }  // namespace lock_screen_apps
diff --git a/chrome/browser/chromeos/policy/power_policy_browsertest.cc b/chrome/browser/chromeos/policy/power_policy_browsertest.cc
index 4dfb14a..4d65cc1 100644
--- a/chrome/browser/chromeos/policy/power_policy_browsertest.cc
+++ b/chrome/browser/chromeos/policy/power_policy_browsertest.cc
@@ -480,12 +480,11 @@
   pm::PowerManagementPolicy baseline_policy =
       power_manager_client_->policy();
 
-  // Default settings should have delays.
+  // Default settings should not report any wake locks.
   pm::PowerManagementPolicy power_management_policy = baseline_policy;
-  EXPECT_NE(0, baseline_policy.ac_delays().screen_dim_ms());
-  EXPECT_NE(0, baseline_policy.ac_delays().screen_off_ms());
-  EXPECT_NE(0, baseline_policy.battery_delays().screen_dim_ms());
-  EXPECT_NE(0, baseline_policy.battery_delays().screen_off_ms());
+  EXPECT_FALSE(baseline_policy.screen_wake_lock());
+  EXPECT_FALSE(baseline_policy.dim_wake_lock());
+  EXPECT_FALSE(baseline_policy.system_wake_lock());
 
   // Pretend an extension grabs a screen wake lock.
   const char kExtensionId[] = "abcdefghijklmnopabcdefghijlkmnop";
@@ -500,10 +499,7 @@
   // Check that the lock is in effect (ignoring ac_idle_action,
   // battery_idle_action and reason).
   pm::PowerManagementPolicy policy = baseline_policy;
-  policy.mutable_ac_delays()->set_screen_dim_ms(0);
-  policy.mutable_ac_delays()->set_screen_off_ms(0);
-  policy.mutable_battery_delays()->set_screen_dim_ms(0);
-  policy.mutable_battery_delays()->set_screen_off_ms(0);
+  policy.set_screen_wake_lock(true);
   policy.set_ac_idle_action(
       power_manager_client_->policy().ac_idle_action());
   policy.set_battery_idle_action(
@@ -512,10 +508,12 @@
   EXPECT_EQ(GetDebugString(policy),
             GetDebugString(power_manager_client_->policy()));
 
-  // Engage the user policy and verify that the defaults take effect again.
+  // Engage the user policy and verify that the screen wake lock is downgraded
+  // to be a system wake lock.
   user_policy_.payload().mutable_allowscreenwakelocks()->set_value(false);
   StoreAndReloadUserPolicy();
   policy = baseline_policy;
+  policy.set_system_wake_lock(true);
   policy.set_ac_idle_action(power_manager_client_->policy().ac_idle_action());
   policy.set_battery_idle_action(
       power_manager_client_->policy().battery_idle_action());
diff --git a/chrome/browser/chromeos/printing/combining_printer_detector.cc b/chrome/browser/chromeos/printing/combining_printer_detector.cc
deleted file mode 100644
index 0e160857..0000000
--- a/chrome/browser/chromeos/printing/combining_printer_detector.cc
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/printing/combining_printer_detector.h"
-
-#include <map>
-#include <utility>
-#include <vector>
-
-#include "base/memory/ptr_util.h"
-#include "base/observer_list.h"
-#include "base/scoped_observer.h"
-#include "chrome/browser/chromeos/printing/printer_detector.h"
-#include "chromeos/printing/printer_configuration.h"
-
-namespace chromeos {
-namespace {
-
-class CombiningPrinterDetectorImpl;
-
-// We can't just implement PrinterDetector::Observer in
-// CombiningPrinterDetectorImpl because we need to be able to determing *which*
-// underlying detector is giving us new information.  Thus we use delegate
-// instantions to allow for a disambiguation about the source of a
-// PrinterDetector callback from one of the detectors being combined.
-class ObserverDelegate : public PrinterDetector::Observer {
- public:
-  ObserverDelegate(CombiningPrinterDetectorImpl* parent,
-                   PrinterDetector* detector)
-      : parent_(parent), observer_(this) {
-    observer_.Add(detector);
-  }
-  void OnPrintersFound(
-      const std::vector<PrinterDetector::DetectedPrinter>& printers) override;
-  void OnPrinterScanComplete() override;
-
- private:
-  CombiningPrinterDetectorImpl* parent_;
-  ScopedObserver<PrinterDetector, PrinterDetector::Observer> observer_;
-};
-
-class CombiningPrinterDetectorImpl : public CombiningPrinterDetector {
- public:
-  ~CombiningPrinterDetectorImpl() override = default;
-
-  void AddDetector(PrinterDetector* detector) override {
-    detectors_.push_back(detector);
-    delegates_.push_back(base::MakeUnique<ObserverDelegate>(this, detector));
-    printers_.insert({delegates_.back().get(), detector->GetPrinters()});
-    scan_done_[delegates_.back().get()] = false;
-  }
-
-  void AddOwnedDetector(std::unique_ptr<PrinterDetector> detector) override {
-    AddDetector(detector.get());
-    owned_detectors_.emplace_back(std::move(detector));
-  }
-
-  // PrinterDetector implementations.
-  void AddObserver(PrinterDetector::Observer* observer) override {
-    observer_list_.AddObserver(observer);
-  }
-
-  void RemoveObserver(PrinterDetector::Observer* observer) override {
-    observer_list_.RemoveObserver(observer);
-  }
-
-  // Note this is *not* the PrinterDetectorObserver interface directly,
-  // it's the entry point for ObserverDelegates to pass along those messages
-  // into this class.
-  void OnPrintersFound(const ObserverDelegate* source,
-                       const std::vector<DetectedPrinter>& printers) {
-    // If this object doesn't know about the delegate trying to update its
-    // printers, we missed delegate registration somehow, which shouldn't
-    // happen.
-    DCHECK(base::ContainsKey(printers_, source));
-    printers_[source] = printers;
-    const std::vector<DetectedPrinter> all_printers = GetPrinters();
-    for (PrinterDetector::Observer& observer : observer_list_) {
-      observer.OnPrintersFound(all_printers);
-    }
-  }
-
-  void OnPrinterScanComplete(const ObserverDelegate* source) {
-    DCHECK(base::ContainsKey(printers_, source));
-    bool& scan_done = scan_done_[source];
-    if (!scan_done) {
-      scan_done = true;
-      for (const auto& entry : scan_done_) {
-        if (!entry.second) {
-          // Not all done yet.
-          return;
-        }
-      }
-      // Final outstanding scan just finished, notify observers.
-      for (PrinterDetector::Observer& observer : observer_list_) {
-        observer.OnPrinterScanComplete();
-      }
-    }
-  }
-
-  // Aggregate the printers from all underlying sources and return them.
-  std::vector<DetectedPrinter> GetPrinters() override {
-    std::vector<DetectedPrinter> ret;
-    for (const auto& entry : printers_) {
-      ret.insert(ret.end(), entry.second.begin(), entry.second.end());
-    }
-    return ret;
-  }
-
- private:
-  // Map from observer delegate to the most recent list of printers from that
-  // observer.
-  std::map<const ObserverDelegate*, std::vector<DetectedPrinter>> printers_;
-
-  // Map from observer delegate to whether or not that observer has completed
-  // its scan.
-  std::map<const ObserverDelegate*, bool> scan_done_;
-
-  // Observers of this object.
-  base::ObserverList<PrinterDetector::Observer> observer_list_;
-
-  std::vector<std::unique_ptr<PrinterDetector>> owned_detectors_;
-
-  // Memory management -- this just exists to ensure that the held
-  // elements get cleaned up.
-  std::vector<std::unique_ptr<ObserverDelegate>> delegates_;
-
-  // All detectors used by the combining detector, owned or unowned.
-  std::vector<PrinterDetector*> detectors_;
-};
-
-void ObserverDelegate::OnPrintersFound(
-    const std::vector<PrinterDetector::DetectedPrinter>& printers) {
-  parent_->OnPrintersFound(this, printers);
-}
-
-void ObserverDelegate::OnPrinterScanComplete() {
-  parent_->OnPrinterScanComplete(this);
-}
-
-}  // namespace
-
-// static
-std::unique_ptr<CombiningPrinterDetector> CombiningPrinterDetector::Create() {
-  return base::MakeUnique<CombiningPrinterDetectorImpl>();
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/combining_printer_detector.h b/chrome/browser/chromeos/printing/combining_printer_detector.h
deleted file mode 100644
index fcba36f..0000000
--- a/chrome/browser/chromeos/printing/combining_printer_detector.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_PRINTING_COMBINING_PRINTER_DETECTOR_H_
-#define CHROME_BROWSER_CHROMEOS_PRINTING_COMBINING_PRINTER_DETECTOR_H_
-
-#include <memory>
-
-#include "chrome/browser/chromeos/printing/printer_detector.h"
-
-namespace chromeos {
-
-// CombiningPrinterDetector implements PrinterDetector and exports the combined
-// results of one or more underlying PrinterDetectors.
-class CHROMEOS_EXPORT CombiningPrinterDetector : public PrinterDetector {
- public:
-  // Static factory
-  static std::unique_ptr<CombiningPrinterDetector> Create();
-
-  ~CombiningPrinterDetector() override = default;
-
-  // Add an underlying detector to be used.  Ownership is not taken.  All
-  // detectors should be added on the same sequence.  Calling AddDetector after
-  // observers have been added is disallowed.
-  virtual void AddDetector(PrinterDetector* detector) = 0;
-
-  // Same as AddDetector, but ownership of the detector is taken.
-  virtual void AddOwnedDetector(std::unique_ptr<PrinterDetector> detector) = 0;
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_COMBINING_PRINTER_DETECTOR_H_
diff --git a/chrome/browser/chromeos/printing/combining_printer_detector_unittest.cc b/chrome/browser/chromeos/printing/combining_printer_detector_unittest.cc
deleted file mode 100644
index 20335a3..0000000
--- a/chrome/browser/chromeos/printing/combining_printer_detector_unittest.cc
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/printing/combining_printer_detector.h"
-
-#include <algorithm>
-#include <memory>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/memory/ptr_util.h"
-#include "base/observer_list.h"
-#include "base/strings/string_util.h"
-#include "chromeos/printing/printer_configuration.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace chromeos {
-
-namespace {
-// Fake printer detectors used as the source of detections to be combined.
-class FakePrinterDetector : public PrinterDetector {
- public:
-  explicit FakePrinterDetector(
-      const std::vector<std::string>& starting_printers)
-      : printer_ids_(starting_printers.begin(), starting_printers.end()) {}
-
-  ~FakePrinterDetector() override = default;
-
-  // Add these printers to the list of detected printers, and notify
-  // downstream observers.
-  void AddPrinters(const std::vector<std::string>& ids) {
-    for (const std::string& id : ids) {
-      CHECK(printer_ids_.insert(id).second)
-          << "Cowardly refusing to add printer with duplicate id " << id;
-    }
-    Notify();
-  }
-
-  // Remove these printers from the list of detected printers, and notify
-  // downstream observers.
-  void RemovePrinters(const std::vector<std::string>& ids) {
-    for (const std::string id : ids) {
-      CHECK_EQ(printer_ids_.erase(id), static_cast<size_t>(1))
-          << "Can't remove printer with nonexistant id " << id;
-    }
-    Notify();
-  }
-
-  // Tell downstream observers that we're done finding printers.
-  void PrinterScanComplete() {
-    for (PrinterDetector::Observer& observer : observer_list_) {
-      observer.OnPrinterScanComplete();
-    }
-  }
-
-  // PrinterDetector implementations.
-  void AddObserver(PrinterDetector::Observer* observer) override {
-    observer_list_.AddObserver(observer);
-  }
-
-  void RemoveObserver(PrinterDetector::Observer* observer) override {
-    observer_list_.RemoveObserver(observer);
-  }
-
-  std::vector<DetectedPrinter> GetPrinters() override {
-    std::vector<DetectedPrinter> printers;
-    for (const std::string& id : printer_ids_) {
-      DetectedPrinter detected;
-      detected.printer.set_id(id);
-      printers.push_back(detected);
-    }
-    return printers;
-  }
-
- private:
-  void Notify() {
-    const std::vector<DetectedPrinter> printers = GetPrinters();
-    for (PrinterDetector::Observer& observer : observer_list_) {
-      observer.OnPrintersFound(printers);
-    }
-  }
-
-  base::ObserverList<PrinterDetector::Observer> observer_list_;
-
-  // In the fake, we only use printer ids to figure out if the right printers
-  // are being propagated.  So internally we just track ids and construct
-  // printers on the fly when asked for them.
-  std::set<std::string> printer_ids_;
-};
-
-class PrintingCombiningPrinterDetectorTest : public testing::Test,
-                                             public PrinterDetector::Observer {
- public:
-  void Init(const std::vector<std::vector<std::string>>& id_lists) {
-    combining_detector_ = CombiningPrinterDetector::Create();
-
-    bool test_owned = false;
-    for (const std::vector<std::string>& id_list : id_lists) {
-      // Divide up fake detectors between owned and unowned to exercise both
-      // paths.  It shouldn't really matter in the test below where ownership of
-      // a specific fake detector lies.
-      auto fake_detector = base::MakeUnique<FakePrinterDetector>(id_list);
-      fake_detectors_.push_back(fake_detector.get());
-      if (test_owned) {
-        combining_detector_->AddDetector(fake_detector.get());
-        owned_fake_detectors_.emplace_back(std::move(fake_detector));
-      } else {
-        combining_detector_->AddOwnedDetector(std::move(fake_detector));
-      }
-      test_owned = !test_owned;
-    }
-    combining_detector_->AddObserver(this);
-    printers_ = combining_detector_->GetPrinters();
-  }
-
-  void OnPrintersFound(
-      const std::vector<PrinterDetector::DetectedPrinter>& printers) override {
-    printers_ = printers;
-  }
-
-  void OnPrinterScanComplete() override { scan_complete_ = true; }
-
- protected:
-  // Testing utility function -- return true if the printers we got in the most
-  // recent OnPrintersFound call are the same as printers (without considering
-  // order).
-  void ExpectFoundPrintersAre(const std::vector<std::string>& expected_ids) {
-    std::vector<std::string> sorted_expected(expected_ids.begin(),
-                                             expected_ids.end());
-    std::vector<std::string> sorted_actual;
-    for (const PrinterDetector::DetectedPrinter& printer : printers_) {
-      sorted_actual.push_back(printer.printer.id());
-    }
-    std::sort(sorted_expected.begin(), sorted_expected.end());
-    std::sort(sorted_actual.begin(), sorted_actual.end());
-    if (sorted_expected != sorted_actual) {
-      ADD_FAILURE() << "Printer ids mismatch.  Expected: {"
-                    << base::JoinString(sorted_expected, ", ") << "}; Found: {"
-                    << base::JoinString(sorted_actual, ", ") << "}";
-    }
-  }
-
-  // Have we been notified that the scan is complete?
-  bool scan_complete_ = false;
-
-  // The printers in the most recent OnPrintersFound call.
-  std::vector<PrinterDetector::DetectedPrinter> printers_;
-
-  // The fake detectors plugged into the combining detector.
-  std::vector<FakePrinterDetector*> fake_detectors_;
-
-  // Cleanup pointers for fake detectors owned by the test.  (The
-  // other fake detectors are owned by the combining_detector.
-  std::vector<std::unique_ptr<FakePrinterDetector>> owned_fake_detectors_;
-
-  std::unique_ptr<CombiningPrinterDetector> combining_detector_;
-};
-
-TEST_F(PrintingCombiningPrinterDetectorTest, BasicUsage) {
-  Init({{"1a"}, std::vector<std::string>(), {"3a"}});
-  ExpectFoundPrintersAre({"1a", "3a"});
-  EXPECT_FALSE(scan_complete_);
-
-  // Find some printers in the second detector.
-  fake_detectors_[1]->AddPrinters({"2a", "2b", "2c"});
-  ExpectFoundPrintersAre({"1a", "2a", "2b", "2c", "3a"});
-  EXPECT_FALSE(scan_complete_);
-
-  // Find some printers on the first detector.
-  fake_detectors_[0]->AddPrinters({"1b"});
-  ExpectFoundPrintersAre({"1a", "1b", "2a", "2b", "2c", "3a"});
-  EXPECT_FALSE(scan_complete_);
-
-  // Have the first detector signal completion, the combined detector should
-  // not be complete until all 3 fake detectors have completed.
-  fake_detectors_[0]->PrinterScanComplete();
-  EXPECT_FALSE(scan_complete_);
-  ExpectFoundPrintersAre({"1a", "1b", "2a", "2b", "2c", "3a"});
-
-  // Exercise removal of a printer.
-  fake_detectors_[2]->RemovePrinters({"3a"});
-  EXPECT_FALSE(scan_complete_);
-  ExpectFoundPrintersAre({"1a", "1b", "2a", "2b", "2c"});
-
-  // Mark the 3rd detector as complete.  The 2nd detector has still not declared
-  // itself complete.
-  fake_detectors_[2]->PrinterScanComplete();
-  EXPECT_FALSE(scan_complete_);
-  ExpectFoundPrintersAre({"1a", "1b", "2a", "2b", "2c"});
-
-  // Multiple scan complete calls from the same detector shouldn't happen, but
-  // if they do happen, it should be a nop.
-  fake_detectors_[2]->PrinterScanComplete();
-  EXPECT_FALSE(scan_complete_);
-  ExpectFoundPrintersAre({"1a", "1b", "2a", "2b", "2c"});
-
-  // We can still add printers from a detector that has declared itself
-  // complete.
-  fake_detectors_[0]->AddPrinters({"1c"});
-  ExpectFoundPrintersAre({"1a", "1b", "1c", "2a", "2b", "2c"});
-  EXPECT_FALSE(scan_complete_);
-
-  // Finally the 2nd detector declares itself complete, which should mean the
-  // combined detector is now complete.
-  fake_detectors_[1]->PrinterScanComplete();
-  EXPECT_TRUE(scan_complete_);
-  ExpectFoundPrintersAre({"1a", "1b", "1c", "2a", "2b", "2c"});
-
-  // Should still be able to add printers even after completion.
-  fake_detectors_[2]->AddPrinters({"3g", "3h", "3i"});
-  EXPECT_TRUE(scan_complete_);
-  ExpectFoundPrintersAre(
-      {"1a", "1b", "1c", "2a", "2b", "2c", "3g", "3h", "3i"});
-}
-
-}  // namespace
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/system/timezone_util.cc b/chrome/browser/chromeos/system/timezone_util.cc
index 31f6e890..4a0a20c 100644
--- a/chrome/browser/chromeos/system/timezone_util.cc
+++ b/chrome/browser/chromeos/system/timezone_util.cc
@@ -242,6 +242,12 @@
 
     if (primary_user) {
       Profile* profile = ProfileHelper::Get()->GetProfileByUser(primary_user);
+      // profile can be NULL only if user has logged in, but profile has not
+      // been initialized yet. Ignore delayed time zone update until user
+      // preferences are initialized.
+      if (!profile)
+        return;
+
       profile->GetPrefs()->SetString(prefs::kUserTimezone,
                                      timezone->timeZoneId);
       // chromeos::Preferences::ApplyPreferences() will automatically change
diff --git a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc
index 7a1ad1de..39858304 100644
--- a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc
+++ b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc
@@ -17,7 +17,6 @@
 #include "chrome/browser/extensions/extension_api_unittest.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
 #include "chrome/browser/extensions/extension_system_factory.h"
-#include "chrome/browser/extensions/test_extension_prefs.h"
 #include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/easy_unlock_app_manager.h"
@@ -31,9 +30,7 @@
 #include "components/proximity_auth/switches.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
 #include "extensions/browser/api_test_utils.h"
-#include "extensions/browser/event_router.h"
-#include "extensions/browser/event_router_factory.h"
-#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/test_event_router.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
@@ -482,48 +479,10 @@
   return std::move(service);
 }
 
-// A fake EventRouter that logs event it dispatches for testing.
-class FakeEventRouter : public extensions::EventRouter {
- public:
-  FakeEventRouter(
-      Profile* profile,
-      std::unique_ptr<extensions::TestExtensionPrefs> extension_prefs)
-      : EventRouter(profile, extension_prefs->prefs()),
-        extension_prefs_(std::move(extension_prefs)),
-        event_count_(0) {}
-
-  void DispatchEventToExtension(
-      const std::string& extension_id,
-      std::unique_ptr<extensions::Event> event) override {
-    ++event_count_;
-    last_extension_id_ = extension_id;
-    last_event_name_ = event ? event->event_name : std::string();
-  }
-
-  int event_count() const { return event_count_; }
-  const std::string& last_extension_id() const { return last_extension_id_; }
-  const std::string& last_event_name() const { return last_event_name_; }
-
- private:
-  std::unique_ptr<extensions::TestExtensionPrefs> extension_prefs_;
-  int event_count_;
-  std::string last_extension_id_;
-  std::string last_event_name_;
-};
-
-// FakeEventRouter factory function
-std::unique_ptr<KeyedService> FakeEventRouterFactoryFunction(
-    content::BrowserContext* profile) {
-  std::unique_ptr<extensions::TestExtensionPrefs> extension_prefs(
-      new extensions::TestExtensionPrefs(base::ThreadTaskRunnerHandle::Get()));
-  return base::MakeUnique<FakeEventRouter>(static_cast<Profile*>(profile),
-                                           std::move(extension_prefs));
-}
-
 TEST_F(EasyUnlockPrivateApiTest, AutoPairing) {
-  FakeEventRouter* event_router = static_cast<FakeEventRouter*>(
-      extensions::EventRouterFactory::GetInstance()->SetTestingFactoryAndUse(
-          profile(), &FakeEventRouterFactoryFunction));
+  extensions::TestEventRouter* event_router =
+      extensions::CreateAndUseTestEventRouter(profile());
+  event_router->set_expected_extension_id(extension_misc::kEasyUnlockAppId);
 
   EasyUnlockServiceFactory::GetInstance()->SetTestingFactoryAndUse(
       profile(), &BuildTestEasyUnlockService);
@@ -534,12 +493,9 @@
   EasyUnlockService* service = EasyUnlockService::Get(profile());
   service->StartAutoPairing(base::Bind(&AutoPairingResult::SetResult,
                                        base::Unretained(&result)));
-  EXPECT_EQ(1, event_router->event_count());
-  EXPECT_EQ(extension_misc::kEasyUnlockAppId,
-            event_router->last_extension_id());
-  EXPECT_EQ(
-      extensions::api::easy_unlock_private::OnStartAutoPairing::kEventName,
-      event_router->last_event_name());
+  EXPECT_EQ(1,
+            event_router->GetEventCount(extensions::api::easy_unlock_private::
+                                            OnStartAutoPairing::kEventName));
 
   // Test SetAutoPairingResult call with failure.
   scoped_refptr<EasyUnlockPrivateSetAutoPairingResultFunction> function(
diff --git a/chrome/browser/extensions/api/font_settings/font_settings_api.cc b/chrome/browser/extensions/api/font_settings/font_settings_api.cc
index d5f5b63..f348fda5d 100644
--- a/chrome/browser/extensions/api/font_settings/font_settings_api.cc
+++ b/chrome/browser/extensions/api/font_settings/font_settings_api.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/extensions/api/preference/preference_api.h"
 #include "chrome/browser/extensions/api/preference/preference_helpers.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/font_pref_change_notifier_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/settings_utils.h"
 #include "chrome/common/extensions/api/font_settings.h"
@@ -54,9 +55,6 @@
 const char kSetFromIncognitoError[] =
     "Can't modify regular settings from an incognito context.";
 
-// Format for font name preference paths.
-const char kWebKitFontPrefFormat[] = "webkit.webprefs.fonts.%s.%s";
-
 // Gets the font name preference path for |generic_family| and |script|. If
 // |script| is NULL, uses prefs::kWebKitCommonScript.
 std::string GetFontNamePrefPath(fonts::GenericFamily generic_family_enum,
@@ -65,31 +63,34 @@
   if (script.empty())
     script = prefs::kWebKitCommonScript;
   std::string generic_family = fonts::ToString(generic_family_enum);
-  return base::StringPrintf(kWebKitFontPrefFormat,
-                            generic_family.c_str(),
-                            script.c_str());
-}
 
-// Registers |obs| to observe per-script font prefs under the path |map_name|.
-void RegisterFontFamilyMapObserver(
-    PrefChangeRegistrar* registrar,
-    const char* map_name,
-    const PrefChangeRegistrar::NamedChangeCallback& callback) {
-  for (size_t i = 0; i < prefs::kWebKitScriptsForFontFamilyMapsLength; ++i) {
-    const char* script = prefs::kWebKitScriptsForFontFamilyMaps[i];
-    registrar->Add(base::StringPrintf("%s.%s", map_name, script), callback);
-  }
+  size_t prefix_len = strlen(pref_names_util::kWebKitFontPrefPrefix);
+
+  // Format is <prefix-(includes-dot)><family>.<script>
+  std::string result;
+  result.reserve(prefix_len + generic_family.size() + script.size() + 1);
+  result.append(pref_names_util::kWebKitFontPrefPrefix, prefix_len);
+  result.append(generic_family);
+  result.push_back('.');
+  result.append(script);
+  return result;
 }
 
 }  // namespace
 
-FontSettingsEventRouter::FontSettingsEventRouter(
-    Profile* profile) : profile_(profile) {
+FontSettingsEventRouter::FontSettingsEventRouter(Profile* profile)
+    : profile_(profile) {
   TRACE_EVENT0("browser,startup", "FontSettingsEventRouter::ctor")
   SCOPED_UMA_HISTOGRAM_TIMER("Extensions.FontSettingsEventRouterCtorTime");
 
   registrar_.Init(profile_->GetPrefs());
 
+  // Unretained is safe here because the registrar is owned by this class.
+  font_change_registrar_.Register(
+      FontPrefChangeNotifierFactory::GetForProfile(profile),
+      base::Bind(&FontSettingsEventRouter::OnFontFamilyMapPrefChanged,
+                 base::Unretained(this)));
+
   AddPrefToObserve(prefs::kWebKitDefaultFixedFontSize,
                    events::FONT_SETTINGS_ON_DEFAULT_FIXED_FONT_SIZE_CHANGED,
                    fonts::OnDefaultFixedFontSizeChanged::kEventName,
@@ -100,25 +101,6 @@
   AddPrefToObserve(prefs::kWebKitMinimumFontSize,
                    events::FONT_SETTINGS_ON_MINIMUM_FONT_SIZE_CHANGED,
                    fonts::OnMinimumFontSizeChanged::kEventName, kPixelSizeKey);
-
-  PrefChangeRegistrar::NamedChangeCallback callback =
-      base::Bind(&FontSettingsEventRouter::OnFontFamilyMapPrefChanged,
-                 base::Unretained(this));
-  RegisterFontFamilyMapObserver(&registrar_,
-                                prefs::kWebKitStandardFontFamilyMap, callback);
-  RegisterFontFamilyMapObserver(&registrar_,
-                                prefs::kWebKitSerifFontFamilyMap, callback);
-  RegisterFontFamilyMapObserver(&registrar_,
-                                prefs::kWebKitSansSerifFontFamilyMap, callback);
-  RegisterFontFamilyMapObserver(&registrar_,
-                                prefs::kWebKitFixedFontFamilyMap, callback);
-  RegisterFontFamilyMapObserver(&registrar_,
-                                prefs::kWebKitCursiveFontFamilyMap, callback);
-  RegisterFontFamilyMapObserver(&registrar_,
-                                prefs::kWebKitFantasyFontFamilyMap, callback);
-  RegisterFontFamilyMapObserver(&registrar_,
-                                prefs::kWebKitPictographFontFamilyMap,
-                                callback);
 }
 
 FontSettingsEventRouter::~FontSettingsEventRouter() {}
diff --git a/chrome/browser/extensions/api/font_settings/font_settings_api.h b/chrome/browser/extensions/api/font_settings/font_settings_api.h
index 964535c8..880218a 100644
--- a/chrome/browser/extensions/api/font_settings/font_settings_api.h
+++ b/chrome/browser/extensions/api/font_settings/font_settings_api.h
@@ -13,6 +13,7 @@
 
 #include "base/macros.h"
 #include "chrome/browser/extensions/chrome_extension_function.h"
+#include "chrome/browser/font_pref_change_notifier.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
 #include "extensions/browser/browser_context_keyed_api_factory.h"
@@ -68,6 +69,7 @@
 
   // Manages pref observation registration.
   PrefChangeRegistrar registrar_;
+  FontPrefChangeNotifier::Registrar font_change_registrar_;
 
   // Weak, owns us (transitively via ExtensionService).
   Profile* profile_;
diff --git a/chrome/browser/extensions/api/image_writer_private/operation_manager_unittest.cc b/chrome/browser/extensions/api/image_writer_private/operation_manager_unittest.cc
index d020f25..d83d6180 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation_manager_unittest.cc
+++ b/chrome/browser/extensions/api/image_writer_private/operation_manager_unittest.cc
@@ -11,35 +11,12 @@
 #include "build/build_config.h"
 #include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
 #include "chrome/browser/extensions/api/image_writer_private/test_utils.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_system_factory.h"
-#include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/test/base/testing_profile.h"
-#include "extensions/browser/event_router.h"
-#include "extensions/browser/event_router_factory.h"
+#include "extensions/browser/test_event_router.h"
 
 namespace extensions {
 namespace image_writer {
 
-// A fake for the EventRouter. If tests require monitoring of interaction with
-// the event router put the logic here.
-class FakeEventRouter : public extensions::EventRouter {
- public:
-  explicit FakeEventRouter(Profile* profile) : EventRouter(profile, NULL) {}
-
-  void DispatchEventToExtension(
-      const std::string& extension_id,
-      std::unique_ptr<extensions::Event> event) override {
-    // Do nothing with the event as no tests currently care.
-  }
-};
-
-// FakeEventRouter factory function
-std::unique_ptr<KeyedService> FakeEventRouterFactoryFunction(
-    content::BrowserContext* context) {
-  return base::MakeUnique<FakeEventRouter>(static_cast<Profile*>(context));
-}
-
 namespace {
 
 class ImageWriterOperationManagerTest : public ImageWriterUnitTestBase {
@@ -64,9 +41,7 @@
 
   void SetUp() override {
     ImageWriterUnitTestBase::SetUp();
-    event_router_ = static_cast<FakeEventRouter*>(
-        extensions::EventRouterFactory::GetInstance()->SetTestingFactoryAndUse(
-            &test_profile_, &FakeEventRouterFactoryFunction));
+    CreateAndUseTestEventRouter(&test_profile_);
   }
 
   bool started_;
@@ -78,7 +53,6 @@
   std::string cancel_error_;
 
   TestingProfile test_profile_;
-  FakeEventRouter* event_router_;
 };
 
 TEST_F(ImageWriterOperationManagerTest, WriteFromFile) {
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_apitest.cc b/chrome/browser/extensions/api/networking_private/networking_private_apitest.cc
index 2ec7a54..53b8300 100644
--- a/chrome/browser/extensions/api/networking_private/networking_private_apitest.cc
+++ b/chrome/browser/extensions/api/networking_private/networking_private_apitest.cc
@@ -206,15 +206,15 @@
     return !fail_;
   }
 
-  bool RequestScan() override {
-    scan_requested_.push_back(true);
+  bool RequestScan(const std::string& type) override {
+    scan_requested_.push_back(type);
     return !fail_;
   }
 
   void set_fail(bool fail) { fail_ = fail; }
   bool GetEnabled(const std::string& type) { return enabled_[type]; }
   bool GetDisabled(const std::string& type) { return disabled_[type]; }
-  size_t GetScanRequested() { return scan_requested_.size(); }
+  const std::vector<std::string>& GetScanRequested() { return scan_requested_; }
 
   void DictionaryResult(const std::string& guid,
                         const DictionaryCallback& success_callback,
@@ -261,7 +261,7 @@
   bool fail_;
   std::map<std::string, bool> enabled_;
   std::map<std::string, bool> disabled_;
-  std::vector<bool> scan_requested_;
+  std::vector<std::string> scan_requested_;
 
   DISALLOW_COPY_AND_ASSIGN(TestNetworkingPrivateDelegate);
 };
@@ -373,7 +373,7 @@
     return networking_private_delegate_->GetDisabled(type);
   }
 
-  size_t GetScanRequested() {
+  const std::vector<std::string>& GetScanRequested() {
     return networking_private_delegate_->GetScanRequested();
   }
 
@@ -488,7 +488,10 @@
 
 IN_PROC_BROWSER_TEST_F(NetworkingPrivateApiTest, RequestNetworkScan) {
   EXPECT_TRUE(RunNetworkingSubtest("requestNetworkScan")) << message_;
-  EXPECT_EQ(1u, GetScanRequested());
+  const std::vector<std::string>& scan_requested = GetScanRequested();
+  ASSERT_EQ(2u, scan_requested.size());
+  EXPECT_EQ("", scan_requested[0]);
+  EXPECT_EQ("Cellular", scan_requested[1]);
 }
 
 IN_PROC_BROWSER_TEST_F(NetworkingPrivateApiTest, StartConnect) {
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc b/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
index 10c2cab..e499306 100644
--- a/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
+++ b/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
@@ -609,6 +609,12 @@
   EXPECT_TRUE(RunNetworkingSubtest("requestNetworkScan")) << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(NetworkingPrivateChromeOSApiTest,
+                       RequestNetworkScanCellular) {
+  SetupCellular();
+  EXPECT_TRUE(RunNetworkingSubtest("requestNetworkScanCellular")) << message_;
+}
+
 // Properties are filtered and translated through
 // ShillToONCTranslator::TranslateWiFiWithState
 IN_PROC_BROWSER_TEST_F(NetworkingPrivateChromeOSApiTest, GetProperties) {
diff --git a/chrome/browser/extensions/display_info_provider_chromeos.cc b/chrome/browser/extensions/display_info_provider_chromeos.cc
index 03506ca..7bbe7b35 100644
--- a/chrome/browser/extensions/display_info_provider_chromeos.cc
+++ b/chrome/browser/extensions/display_info_provider_chromeos.cc
@@ -7,15 +7,15 @@
 #include <stdint.h>
 
 #include "ash/display/display_configuration_controller.h"
+#include "ash/display/overscan_calibrator.h"
 #include "ash/display/resolution_notification_controller.h"
 #include "ash/display/screen_orientation_controller_chromeos.h"
+#include "ash/display/touch_calibrator_controller.h"
 #include "ash/shell.h"
 #include "ash/touch/ash_touch_transform_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/chromeos/display/display_preferences.h"
-#include "chrome/browser/chromeos/display/overscan_calibrator.h"
-#include "chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.h"
 #include "chrome/browser/ui/ash/ash_util.h"
 #include "extensions/common/api/system_display.h"
 #include "ui/display/display.h"
@@ -399,7 +399,7 @@
 bool ValidateParamsForTouchCalibration(
     const std::string& id,
     const display::Display& display,
-    chromeos::TouchCalibratorController* const touch_calibrator_controller,
+    ash::TouchCalibratorController* const touch_calibrator_controller,
     std::string* error) {
   if (display.id() == display::kInvalidDisplayId) {
     *error = "Display Id(" + id + ") is an invalid display ID";
@@ -723,8 +723,7 @@
   auto insets =
       ash::Shell::Get()->window_tree_host_manager()->GetOverscanInsets(
           display.id());
-  overscan_calibrators_[id].reset(
-      new chromeos::OverscanCalibrator(display, insets));
+  overscan_calibrators_[id].reset(new ash::OverscanCalibrator(display, insets));
   return true;
 }
 
@@ -732,7 +731,7 @@
     const std::string& id,
     const system_display::Insets& delta) {
   VLOG(1) << "OverscanCalibrationAdjust: " << id;
-  chromeos::OverscanCalibrator* calibrator = GetOverscanCalibrator(id);
+  ash::OverscanCalibrator* calibrator = GetOverscanCalibrator(id);
   if (!calibrator)
     return false;
   gfx::Insets insets = calibrator->insets();
@@ -744,7 +743,7 @@
 bool DisplayInfoProviderChromeOS::OverscanCalibrationReset(
     const std::string& id) {
   VLOG(1) << "OverscanCalibrationReset: " << id;
-  chromeos::OverscanCalibrator* calibrator = GetOverscanCalibrator(id);
+  ash::OverscanCalibrator* calibrator = GetOverscanCalibrator(id);
   if (!calibrator)
     return false;
   calibrator->Reset();
@@ -754,7 +753,7 @@
 bool DisplayInfoProviderChromeOS::OverscanCalibrationComplete(
     const std::string& id) {
   VLOG(1) << "OverscanCalibrationComplete: " << id;
-  chromeos::OverscanCalibrator* calibrator = GetOverscanCalibrator(id);
+  ash::OverscanCalibrator* calibrator = GetOverscanCalibrator(id);
   if (!calibrator)
     return false;
   calibrator->Commit();
@@ -905,18 +904,18 @@
   return false;
 }
 
-chromeos::OverscanCalibrator*
-DisplayInfoProviderChromeOS::GetOverscanCalibrator(const std::string& id) {
+ash::OverscanCalibrator* DisplayInfoProviderChromeOS::GetOverscanCalibrator(
+    const std::string& id) {
   auto iter = overscan_calibrators_.find(id);
   if (iter == overscan_calibrators_.end())
     return nullptr;
   return iter->second.get();
 }
 
-chromeos::TouchCalibratorController*
+ash::TouchCalibratorController*
 DisplayInfoProviderChromeOS::GetTouchCalibrator() {
   if (!touch_calibrator_)
-    touch_calibrator_.reset(new chromeos::TouchCalibratorController);
+    touch_calibrator_.reset(new ash::TouchCalibratorController);
   return touch_calibrator_.get();
 }
 
diff --git a/chrome/browser/extensions/display_info_provider_chromeos.h b/chrome/browser/extensions/display_info_provider_chromeos.h
index 8b6b327e..b27e98ab 100644
--- a/chrome/browser/extensions/display_info_provider_chromeos.h
+++ b/chrome/browser/extensions/display_info_provider_chromeos.h
@@ -11,7 +11,7 @@
 #include "base/macros.h"
 #include "extensions/browser/api/system_display/display_info_provider.h"
 
-namespace chromeos {
+namespace ash {
 class OverscanCalibrator;
 class TouchCalibratorController;
 }
@@ -61,14 +61,14 @@
   bool IsNativeTouchCalibrationActive(std::string* error) override;
 
  private:
-  chromeos::TouchCalibratorController* GetTouchCalibrator();
+  ash::TouchCalibratorController* GetTouchCalibrator();
 
-  chromeos::OverscanCalibrator* GetOverscanCalibrator(const std::string& id);
+  ash::OverscanCalibrator* GetOverscanCalibrator(const std::string& id);
 
-  std::map<std::string, std::unique_ptr<chromeos::OverscanCalibrator>>
+  std::map<std::string, std::unique_ptr<ash::OverscanCalibrator>>
       overscan_calibrators_;
 
-  std::unique_ptr<chromeos::TouchCalibratorController> touch_calibrator_;
+  std::unique_ptr<ash::TouchCalibratorController> touch_calibrator_;
 
   std::string touch_calibration_target_id_;
   bool custom_touch_calibration_active_ = false;
diff --git a/chrome/browser/extensions/extension_message_bubble_controller.cc b/chrome/browser/extensions/extension_message_bubble_controller.cc
index e7efdd1..cd8767a 100644
--- a/chrome/browser/extensions/extension_message_bubble_controller.cc
+++ b/chrome/browser/extensions/extension_message_bubble_controller.cc
@@ -280,10 +280,7 @@
   g_should_ignore_learn_more_for_testing = should_ignore;
 }
 
-void ExtensionMessageBubbleController::OnExtensionUnloaded(
-    content::BrowserContext* browser_context,
-    const Extension* extension,
-    UnloadedExtensionReason reason) {
+void ExtensionMessageBubbleController::HandleExtensionUnloadOrUninstall() {
   UpdateExtensionIdList();
   // If the callback is set, then that means that OnShown() was called, and the
   // bubble is displayed.
@@ -293,6 +290,20 @@
   // If the bubble refers to multiple extensions, we do not close the bubble.
 }
 
+void ExtensionMessageBubbleController::OnExtensionUnloaded(
+    content::BrowserContext* browser_context,
+    const Extension* extension,
+    UnloadedExtensionReason reason) {
+  HandleExtensionUnloadOrUninstall();
+}
+
+void ExtensionMessageBubbleController::OnExtensionUninstalled(
+    content::BrowserContext* browser_context,
+    const Extension* extension,
+    UninstallReason reason) {
+  HandleExtensionUnloadOrUninstall();
+}
+
 void ExtensionMessageBubbleController::OnShutdown(ExtensionRegistry* registry) {
   // It is possible that the extension registry is destroyed before the
   // controller. In such case, the controller should no longer observe the
diff --git a/chrome/browser/extensions/extension_message_bubble_controller.h b/chrome/browser/extensions/extension_message_bubble_controller.h
index e23f7e8b..701742f9 100644
--- a/chrome/browser/extensions/extension_message_bubble_controller.h
+++ b/chrome/browser/extensions/extension_message_bubble_controller.h
@@ -186,11 +186,17 @@
       bool should_ignore_learn_more);
 
  private:
+  void HandleExtensionUnloadOrUninstall();
+
   // ExtensionRegistryObserver:
   void OnExtensionUnloaded(content::BrowserContext* browser_context,
                            const Extension* extension,
                            UnloadedExtensionReason reason) override;
+  void OnExtensionUninstalled(content::BrowserContext* browser_context,
+                              const Extension* extension,
+                              UninstallReason reason) override;
   void OnShutdown(ExtensionRegistry* registry) override;
+
   // BrowserListObserver:
   void OnBrowserRemoved(Browser* browser) override;
 
diff --git a/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc b/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
index e858478..a7e0531 100644
--- a/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
+++ b/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
@@ -819,8 +819,9 @@
 }
 
 // Tests that a displayed extension bubble will be closed after its associated
-// extension is uninstalled.
-TEST_F(ExtensionMessageBubbleTest, TestBubbleClosedAfterExtensionUninstall) {
+// enabled extension is uninstalled.
+TEST_F(ExtensionMessageBubbleTest,
+       TestBubbleClosedAfterEnabledExtensionUninstall) {
   Init();
   ASSERT_TRUE(LoadExtensionOverridingNtp("1", kId1, Manifest::UNPACKED));
 
@@ -850,6 +851,60 @@
   controller.reset();
 }
 
+// Tests that a displayed extension bubble will be closed after its associated
+// disabled extension is uninstalled. Here a suspicious bubble controller is
+// tested, which can display bubbles for disabled extensions.
+TEST_F(ExtensionMessageBubbleTest,
+       TestBubbleClosedAfterDisabledExtensionUninstall) {
+  Init();
+  ASSERT_TRUE(LoadExtensionOverridingNtp("1", kId1, Manifest::COMMAND_LINE));
+
+  auto controller = base::MakeUnique<TestExtensionMessageBubbleController>(
+      new SuspiciousExtensionBubbleDelegate(browser()->profile()), browser());
+  controller->SetIsActiveBubble();
+  FakeExtensionMessageBubble bubble;
+  bubble.set_action_on_show(
+      FakeExtensionMessageBubble::BUBBLE_ACTION_CLICK_DISMISS_BUTTON);
+
+  // Validate that we don't have a suppress value for the extensions.
+  EXPECT_FALSE(controller->delegate()->HasBubbleInfoBeenAcknowledged(kId1));
+  EXPECT_FALSE(controller->delegate()->HasBubbleInfoBeenAcknowledged(kId1));
+
+  EXPECT_FALSE(controller->ShouldShow());
+  std::vector<base::string16> suspicious_extensions =
+      controller->GetExtensionList();
+  EXPECT_EQ(0U, suspicious_extensions.size());
+
+  // Now disable an extension, specifying the wipeout flag.
+  service_->DisableExtension(kId1, disable_reason::DISABLE_NOT_VERIFIED);
+
+  EXPECT_FALSE(controller->delegate()->HasBubbleInfoBeenAcknowledged(kId1));
+  EXPECT_FALSE(controller->delegate()->HasBubbleInfoBeenAcknowledged(kId2));
+  controller.reset(new TestExtensionMessageBubbleController(
+      new SuspiciousExtensionBubbleDelegate(browser()->profile()), browser()));
+  controller->SetIsActiveBubble();
+  controller->ClearProfileListForTesting();
+  EXPECT_TRUE(controller->ShouldShow());
+  suspicious_extensions = controller->GetExtensionList();
+  ASSERT_EQ(1U, suspicious_extensions.size());
+  EXPECT_TRUE(base::ASCIIToUTF16("Extension 1") == suspicious_extensions[0]);
+  bubble.set_controller(controller.get());
+  bubble.set_action_on_show(FakeExtensionMessageBubble::BUBBLE_ACTION_IGNORE);
+  bubble.Show();  // Simulate showing the bubble.
+
+  EXPECT_FALSE(bubble.is_closed());
+
+  // Uninstall the extension.
+  service_->UninstallExtension(kId1, UNINSTALL_REASON_FOR_TESTING,
+                               base::Bind(&base::DoNothing), nullptr);
+  ASSERT_EQ(0U, controller->GetExtensionList().size());
+
+  // The bubble should be closed after the extension is uninstalled.
+  EXPECT_TRUE(bubble.is_closed());
+
+  controller.reset();
+}
+
 // Tests that a bubble associated with multiple extensions remains shown after
 // one of its associated extensions is uninstalled. Also tests that the bubble
 // closes when all of its associated extensions are uninstalled.
@@ -1210,9 +1265,31 @@
   controller.reset();
 }
 
-// TODO(catmullings): Test disabling an extenion rather than uninstalling. Also,
-// test a suspicious bubble controller + uninstalling, which has disabled
-// extensions.
+// Tests that when an extension -- associated with a bubble controller -- is
+// disabling after the browser is destroyed, the controller does not access
+// the associated browser object and therefore, no use-after-free occurs.
+TEST_F(ExtensionMessageBubbleTest,
+       TestDisablingExtensionAfterBrowserDestroyed) {
+  FeatureSwitch::ScopedOverride force_dev_mode_highlighting(
+      FeatureSwitch::force_dev_mode_highlighting(), true);
+  Init();
+  ToolbarActionsModel* model = ToolbarActionsModel::Get(profile());
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_TRUE(LoadExtensionWithAction("1", kId1, Manifest::UNPACKED));
+
+  auto controller = std::make_unique<TestExtensionMessageBubbleController>(
+      new DevModeBubbleDelegate(browser()->profile()), browser());
+  controller->SetIsActiveBubble();
+  EXPECT_TRUE(controller->ShouldShow());
+  EXPECT_EQ(1u, model->toolbar_items().size());
+  controller->HighlightExtensionsIfNecessary();
+  EXPECT_TRUE(model->is_highlighting());
+  set_browser(nullptr);
+  service_->DisableExtension(kId1, disable_reason::DISABLE_NONE);
+  EXPECT_FALSE(model->is_highlighting());
+  controller.reset();
+}
 
 // Tests if that ShouldShow() returns false if the bubble's associated extension
 // has been removed.
diff --git a/chrome/browser/extensions/extension_migrator.cc b/chrome/browser/extensions/extension_migrator.cc
index e46e8ab..cb39532 100644
--- a/chrome/browser/extensions/extension_migrator.cc
+++ b/chrome/browser/extensions/extension_migrator.cc
@@ -24,7 +24,7 @@
 }
 
 void ExtensionMigrator::StartLoading() {
-  prefs_.reset(new base::DictionaryValue);
+  auto prefs = std::make_unique<base::DictionaryValue>();
 
   const bool should_have_extension =
       IsAppPresent(old_id_) || IsAppPresent(new_id_);
@@ -33,10 +33,10 @@
     entry->SetKey(ExternalProviderImpl::kExternalUpdateUrl,
                   base::Value(extension_urls::GetWebstoreUpdateUrl().spec()));
 
-    prefs_->SetWithoutPathExpansion(new_id_, std::move(entry));
+    prefs->SetWithoutPathExpansion(new_id_, std::move(entry));
   }
 
-  LoadFinished();
+  LoadFinished(std::move(prefs));
 }
 
 bool ExtensionMigrator::IsAppPresent(const std::string& app_id) {
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 00000913..542b63a 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -303,8 +303,7 @@
 
   void StartLoading() override {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    prefs_.reset(testing_prefs_->DeepCopy());
-    LoadFinished();
+    LoadFinished(testing_prefs_->CreateDeepCopy());
   }
 
  private:
diff --git a/chrome/browser/extensions/external_component_loader.cc b/chrome/browser/extensions/external_component_loader.cc
index 1686fd3..c3ad803 100644
--- a/chrome/browser/extensions/external_component_loader.cc
+++ b/chrome/browser/extensions/external_component_loader.cc
@@ -37,44 +37,47 @@
 ExternalComponentLoader::~ExternalComponentLoader() {}
 
 void ExternalComponentLoader::StartLoading() {
-  prefs_.reset(new base::DictionaryValue());
+  auto prefs = std::make_unique<base::DictionaryValue>();
 #if defined(GOOGLE_CHROME_BUILD)
-  AddExternalExtension(extension_misc::kInAppPaymentsSupportAppId);
+  AddExternalExtension(extension_misc::kInAppPaymentsSupportAppId, prefs.get());
 #endif  // defined(GOOGLE_CHROME_BUILD)
 
   if (HotwordServiceFactory::IsHotwordAllowed(profile_))
-    AddExternalExtension(extension_misc::kHotwordSharedModuleId);
+    AddExternalExtension(extension_misc::kHotwordSharedModuleId, prefs.get());
 
 #if defined(OS_CHROMEOS)
   {
     base::CommandLine* const command_line =
         base::CommandLine::ForCurrentProcess();
     if (!command_line->HasSwitch(chromeos::switches::kDisableNewZIPUnpacker))
-      AddExternalExtension(extension_misc::kZIPUnpackerExtensionId);
+      AddExternalExtension(extension_misc::kZIPUnpackerExtensionId,
+                           prefs.get());
   }
 #endif
 
   if (media_router::MediaRouterEnabled(profile_) &&
       FeatureSwitch::load_media_router_component_extension()->IsEnabled()) {
-    AddExternalExtension(extension_misc::kMediaRouterStableExtensionId);
+    AddExternalExtension(extension_misc::kMediaRouterStableExtensionId,
+                         prefs.get());
   }
 
 #if BUILDFLAG(ENABLE_APP_LIST) && defined(OS_CHROMEOS)
   std::string google_now_extension_id;
   if (GetGoogleNowExtensionId(&google_now_extension_id))
-    AddExternalExtension(google_now_extension_id);
+    AddExternalExtension(google_now_extension_id, prefs.get());
 #endif
 
-  LoadFinished();
+  LoadFinished(std::move(prefs));
 }
 
 void ExternalComponentLoader::AddExternalExtension(
-    const std::string& extension_id) {
+    const std::string& extension_id,
+    base::DictionaryValue* prefs) {
   if (!IsComponentExtensionWhitelisted(extension_id))
     return;
 
-  prefs_->SetString(extension_id + ".external_update_url",
-                    extension_urls::GetWebstoreUpdateUrl().spec());
+  prefs->SetString(extension_id + ".external_update_url",
+                   extension_urls::GetWebstoreUpdateUrl().spec());
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/external_component_loader.h b/chrome/browser/extensions/external_component_loader.h
index 3d026cef..c2497ed 100644
--- a/chrome/browser/extensions/external_component_loader.h
+++ b/chrome/browser/extensions/external_component_loader.h
@@ -12,6 +12,10 @@
 #include "chrome/browser/extensions/external_loader.h"
 #include "chrome/browser/profiles/profile.h"
 
+namespace base {
+class DictionaryValue;
+}
+
 namespace extensions {
 
 // A specialization of the ExternalLoader that loads a hard-coded list of
@@ -31,7 +35,8 @@
   friend class base::RefCountedThreadSafe<ExternalLoader>;
   ~ExternalComponentLoader() override;
 
-  void AddExternalExtension(const std::string& extension_id);
+  void AddExternalExtension(const std::string& extension_id,
+                            base::DictionaryValue* prefs);
 
   // The profile that this loader is associated with. It listens for
   // preference changes for that profile.
diff --git a/chrome/browser/extensions/external_loader.cc b/chrome/browser/extensions/external_loader.cc
index 065095c1..16deebe 100644
--- a/chrome/browser/extensions/external_loader.cc
+++ b/chrome/browser/extensions/external_loader.cc
@@ -37,10 +37,11 @@
 
 ExternalLoader::~ExternalLoader() {}
 
-void ExternalLoader::LoadFinished() {
+void ExternalLoader::LoadFinished(
+    std::unique_ptr<base::DictionaryValue> prefs) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   if (owner_) {
-    owner_->SetPrefs(std::move(prefs_));
+    owner_->SetPrefs(std::move(prefs));
   }
 }
 
diff --git a/chrome/browser/extensions/external_loader.h b/chrome/browser/extensions/external_loader.h
index 83948db..685fce55 100644
--- a/chrome/browser/extensions/external_loader.h
+++ b/chrome/browser/extensions/external_loader.h
@@ -40,10 +40,7 @@
   void OwnerShutdown();
 
   // Initiates the possibly asynchronous loading of extension list.
-  // It is the responsibility of the caller to ensure that calls to
-  // this method do not overlap with each other.
-  // Implementations of this method should save the loaded results
-  // in prefs_ and then call LoadFinished.
+  // Implementations of this method should call LoadFinished with results.
   virtual void StartLoading() = 0;
 
   // Some external providers allow relative file paths to local CRX files.
@@ -57,21 +54,15 @@
   virtual ~ExternalLoader();
 
   // Notifies the provider that the list of extensions has been loaded.
-  virtual void LoadFinished();
+  virtual void LoadFinished(std::unique_ptr<base::DictionaryValue> prefs);
 
   // Notifies the provider that the list of extensions has been updated.
   virtual void OnUpdated(std::unique_ptr<base::DictionaryValue> updated_prefs);
 
-  // Used for passing the list of extensions from the method that loads them
-  // to |LoadFinished|. To ensure thread safety, the rules are the following:
-  // if this value is written on another thread than the UI, then it should
-  // only be written in a task that was posted from |StartLoading|. After that,
-  // this task should invoke |LoadFinished| with a PostTask. This scheme of
-  // posting tasks will avoid concurrent access and imply the necessary memory
-  // barriers.
-  // TODO(lazyboy): To avoid |prefs_| getting unexpectedly overwritten before it
-  // is consumed, consider passing the prefs directly in LoadFinished().
-  std::unique_ptr<base::DictionaryValue> prefs_;
+  // Returns true if this loader has an owner.
+  // This is useful to know if calling LoadFinished/OnUpdated will propagate
+  // prefs to our owner.
+  bool has_owner() const { return owner_ != nullptr; }
 
  private:
   friend class base::RefCountedThreadSafe<ExternalLoader>;
diff --git a/chrome/browser/extensions/external_policy_loader.cc b/chrome/browser/extensions/external_policy_loader.cc
index 5a401d5..4536f4d 100644
--- a/chrome/browser/extensions/external_policy_loader.cc
+++ b/chrome/browser/extensions/external_policy_loader.cc
@@ -35,15 +35,16 @@
 }
 
 void ExternalPolicyLoader::StartLoading() {
+  std::unique_ptr<base::DictionaryValue> prefs;
   switch (type_) {
     case FORCED:
-      prefs_ = settings_->GetForceInstallList();
+      prefs = settings_->GetForceInstallList();
       break;
     case RECOMMENDED:
-      prefs_ = settings_->GetRecommendedInstallList();
+      prefs = settings_->GetRecommendedInstallList();
       break;
   }
-  LoadFinished();
+  LoadFinished(std::move(prefs));
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/external_pref_loader.cc b/chrome/browser/extensions/external_pref_loader.cc
index aba8d99..a0669f79 100644
--- a/chrome/browser/extensions/external_pref_loader.cc
+++ b/chrome/browser/extensions/external_pref_loader.cc
@@ -16,6 +16,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/path_service.h"
+#include "base/scoped_observer.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task_scheduler/lazy_task_runner.h"
@@ -25,7 +26,10 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/common/chrome_paths.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/sync/driver/sync_service_observer.h"
 #include "components/sync_preferences/pref_service_syncable.h"
+#include "components/sync_preferences/pref_service_syncable_observer.h"
 #include "content/public/browser/browser_thread.h"
 #include "extensions/browser/extension_file_task_runner.h"
 
@@ -77,13 +81,95 @@
 
 namespace extensions {
 
+// Helper class to wait for priority sync to be ready.
+class ExternalPrefLoader::PrioritySyncReadyWaiter
+    : public sync_preferences::PrefServiceSyncableObserver,
+      public syncer::SyncServiceObserver {
+ public:
+  explicit PrioritySyncReadyWaiter(Profile* profile)
+      : profile_(profile),
+        syncable_pref_observer_(this),
+        sync_service_observer_(this) {}
+
+  ~PrioritySyncReadyWaiter() override {}
+
+  void Start(base::OnceClosure done_closure) {
+    if (IsPrioritySyncing()) {
+      std::move(done_closure).Run();
+      // Note: |this| is deleted here.
+      return;
+    }
+    // Start observing sync changes.
+    DCHECK(profile_);
+    browser_sync::ProfileSyncService* service =
+        ProfileSyncServiceFactory::GetForProfile(profile_);
+    DCHECK(service);
+    if (service->CanSyncStart() && (service->IsFirstSetupComplete() ||
+                                    browser_defaults::kSyncAutoStarts)) {
+      done_closure_ = std::move(done_closure);
+      AddObservers();
+    } else {
+      std::move(done_closure).Run();
+      // Note: |this| is deleted here.
+    }
+  }
+
+  // sync_preferences::PrefServiceSyncableObserver:
+  void OnIsSyncingChanged() override {
+    DCHECK(profile_);
+    if (!IsPrioritySyncing())
+      return;
+
+    Finish();
+    // Note: |this| is deleted here.
+  }
+
+  // syncer::SyncServiceObserver
+  void OnStateChanged(syncer::SyncService* sync) override {
+    if (!sync->CanSyncStart())
+      Finish();
+  }
+
+ private:
+  bool IsPrioritySyncing() {
+    DCHECK(profile_);
+    sync_preferences::PrefServiceSyncable* prefs =
+        PrefServiceSyncableFromProfile(profile_);
+    DCHECK(prefs);
+    return prefs->IsPrioritySyncing();
+  }
+
+  void AddObservers() {
+    sync_preferences::PrefServiceSyncable* prefs =
+        PrefServiceSyncableFromProfile(profile_);
+    DCHECK(prefs);
+    syncable_pref_observer_.Add(prefs);
+
+    browser_sync::ProfileSyncService* service =
+        ProfileSyncServiceFactory::GetForProfile(profile_);
+    sync_service_observer_.Add(service);
+  }
+
+  void Finish() { std::move(done_closure_).Run(); }
+
+  Profile* profile_;
+
+  base::OnceClosure done_closure_;
+
+  // Used for registering observer for sync_preferences::PrefServiceSyncable.
+  ScopedObserver<sync_preferences::PrefServiceSyncable,
+                 sync_preferences::PrefServiceSyncableObserver>
+      syncable_pref_observer_;
+  ScopedObserver<browser_sync::ProfileSyncService, syncer::SyncServiceObserver>
+      sync_service_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrioritySyncReadyWaiter);
+};
+
 ExternalPrefLoader::ExternalPrefLoader(int base_path_id,
                                        Options options,
                                        Profile* profile)
-    : base_path_id_(base_path_id),
-      options_(options),
-      profile_(profile),
-      syncable_pref_observer_(this) {
+    : base_path_id_(base_path_id), options_(options), profile_(profile) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 }
 
@@ -101,28 +187,30 @@
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   if ((options_ & DELAY_LOAD_UNTIL_PRIORITY_SYNC) &&
       (profile_ && profile_->IsSyncAllowed())) {
-    if (!PostLoadIfPrioritySyncReady()) {
-      DCHECK(profile_);
-      sync_preferences::PrefServiceSyncable* prefs =
-          PrefServiceSyncableFromProfile(profile_);
-      DCHECK(prefs);
-      syncable_pref_observer_.Add(prefs);
-      browser_sync::ProfileSyncService* service =
-          ProfileSyncServiceFactory::GetForProfile(profile_);
-      DCHECK(service);
-      if (service->CanSyncStart() && (service->IsFirstSetupComplete() ||
-                                      browser_defaults::kSyncAutoStarts)) {
-        service->AddObserver(this);
-      } else {
-        PostLoadAndRemoveObservers();
-      }
-    }
+    pending_waiter_list_.push_back(
+        std::make_unique<PrioritySyncReadyWaiter>(profile_));
+    PrioritySyncReadyWaiter* waiter_ptr = pending_waiter_list_.back().get();
+    waiter_ptr->Start(base::BindOnce(&ExternalPrefLoader::OnPrioritySyncReady,
+                                     this, waiter_ptr));
   } else {
     GetExtensionFileTaskRunner()->PostTask(
         FROM_HERE, base::BindOnce(&ExternalPrefLoader::LoadOnFileThread, this));
   }
 }
 
+void ExternalPrefLoader::OnPrioritySyncReady(
+    ExternalPrefLoader::PrioritySyncReadyWaiter* waiter) {
+  // Delete |waiter| from |pending_waiter_list_|.
+  pending_waiter_list_.erase(
+      std::find_if(pending_waiter_list_.begin(), pending_waiter_list_.end(),
+                   [waiter](const std::unique_ptr<PrioritySyncReadyWaiter>& w) {
+                     return w.get() == waiter;
+                   }));
+  // Continue loading.
+  GetExtensionFileTaskRunner()->PostTask(
+      FROM_HERE, base::BindOnce(&ExternalPrefLoader::LoadOnFileThread, this));
+}
+
 // static.
 std::unique_ptr<base::DictionaryValue>
 ExternalPrefLoader::ExtractExtensionPrefs(base::ValueDeserializer* deserializer,
@@ -145,45 +233,6 @@
   return base::MakeUnique<base::DictionaryValue>();
 }
 
-void ExternalPrefLoader::OnIsSyncingChanged() {
-  PostLoadIfPrioritySyncReady();
-}
-
-void ExternalPrefLoader::OnStateChanged(syncer::SyncService* sync) {
-  if (!sync->CanSyncStart())
-    PostLoadAndRemoveObservers();
-}
-
-bool ExternalPrefLoader::PostLoadIfPrioritySyncReady() {
-  DCHECK(options_ & DELAY_LOAD_UNTIL_PRIORITY_SYNC);
-  DCHECK(profile_);
-
-  sync_preferences::PrefServiceSyncable* prefs =
-      PrefServiceSyncableFromProfile(profile_);
-  DCHECK(prefs);
-  if (prefs->IsPrioritySyncing()) {
-    PostLoadAndRemoveObservers();
-    return true;
-  }
-
-  return false;
-}
-
-void ExternalPrefLoader::PostLoadAndRemoveObservers() {
-  sync_preferences::PrefServiceSyncable* prefs =
-      PrefServiceSyncableFromProfile(profile_);
-  DCHECK(prefs);
-  syncable_pref_observer_.Remove(prefs);
-
-  browser_sync::ProfileSyncService* service =
-      ProfileSyncServiceFactory::GetForProfile(profile_);
-  DCHECK(service);
-  service->RemoveObserver(this);
-
-  GetExtensionFileTaskRunner()->PostTask(
-      FROM_HERE, base::BindOnce(&ExternalPrefLoader::LoadOnFileThread, this));
-}
-
 void ExternalPrefLoader::LoadOnFileThread() {
   base::ThreadRestrictions::AssertIOAllowed();
 
@@ -207,22 +256,18 @@
     ReadStandaloneExtensionPrefFiles(prefs.get());
   }
 
-  prefs_.swap(prefs);
-
-  if (base_path_id_ == chrome::DIR_EXTERNAL_EXTENSIONS) {
-    UMA_HISTOGRAM_COUNTS_100("Extensions.ExternalJsonCount",
-                             prefs_->size());
-  }
+  if (base_path_id_ == chrome::DIR_EXTERNAL_EXTENSIONS)
+    UMA_HISTOGRAM_COUNTS_100("Extensions.ExternalJsonCount", prefs->size());
 
   // If we have any records to process, then we must have
   // read at least one .json file.  If so, then we should have
   // set |base_path_|.
-  if (!prefs_->empty())
+  if (!prefs->empty())
     CHECK(!base_path_.empty());
 
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::BindOnce(&ExternalPrefLoader::LoadFinished, this));
+  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+                          base::BindOnce(&ExternalPrefLoader::LoadFinished,
+                                         this, std::move(prefs)));
 }
 
 void ExternalPrefLoader::ReadExternalExtensionPrefFile(
diff --git a/chrome/browser/extensions/external_pref_loader.h b/chrome/browser/extensions/external_pref_loader.h
index 82ed4d3a..21778534 100644
--- a/chrome/browser/extensions/external_pref_loader.h
+++ b/chrome/browser/extensions/external_pref_loader.h
@@ -10,28 +10,18 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "base/scoped_observer.h"
 #include "base/values.h"
 #include "chrome/browser/extensions/external_loader.h"
-#include "components/browser_sync/profile_sync_service.h"
-#include "components/sync/driver/sync_service_observer.h"
-#include "components/sync_preferences/pref_service_syncable_observer.h"
 
 class Profile;
 
-namespace sync_preferences {
-class PrefServiceSyncable;
-}
-
 namespace extensions {
 
 // A specialization of the ExternalLoader that uses a json file to
 // look up which external extensions are registered.
 // Instances of this class are expected to be created and destroyed on the UI
 // thread and they are expecting public method calls from the UI thread.
-class ExternalPrefLoader : public ExternalLoader,
-                           public sync_preferences::PrefServiceSyncableObserver,
-                           public syncer::SyncServiceObserver {
+class ExternalPrefLoader : public ExternalLoader {
  public:
   enum Options {
     NONE = 0,
@@ -71,6 +61,9 @@
  private:
   friend class base::RefCountedThreadSafe<ExternalLoader>;
   friend class ExternalTestingLoader;
+  friend class TestExternalPrefLoader;
+
+  class PrioritySyncReadyWaiter;
 
   // Extracts extension information from a json file serialized by |serializer|.
   // |path| is only used for informational purposes (outputted when an error
@@ -80,12 +73,6 @@
       base::ValueDeserializer* deserializer,
       const base::FilePath& path);
 
-  // sync_preferences::PrefServiceSyncableObserver:
-  void OnIsSyncingChanged() override;
-
-  // syncer::SyncServiceObserver
-  void OnStateChanged(syncer::SyncService* sync) override;
-
   // If priority sync ready posts LoadOnFileThread and return true.
   bool PostLoadIfPrioritySyncReady();
 
@@ -95,7 +82,8 @@
   // Actually searches for and loads candidate standalone extension preference
   // files in the path corresponding to |base_path_id|.
   // Must be called on the file thread.
-  void LoadOnFileThread();
+  // Note: Overridden in tests.
+  virtual void LoadOnFileThread();
 
   // Extracts the information contained in an external_extension.json file
   // regarding which extensions to install. |prefs| will be modified to
@@ -110,6 +98,8 @@
   // Must be called from the File thread.
   void ReadStandaloneExtensionPrefFiles(base::DictionaryValue* prefs);
 
+  void OnPrioritySyncReady(PrioritySyncReadyWaiter* waiter);
+
   // The path (coresponding to |base_path_id_| containing the json files
   // describing which extensions to load.
   base::FilePath base_path_;
@@ -118,10 +108,7 @@
   // Needed for waiting for waiting priority sync.
   Profile* profile_;
 
-  // Used for registering observer for sync_preferences::PrefServiceSyncable.
-  ScopedObserver<sync_preferences::PrefServiceSyncable,
-                 sync_preferences::PrefServiceSyncableObserver>
-      syncable_pref_observer_;
+  std::vector<std::unique_ptr<PrioritySyncReadyWaiter>> pending_waiter_list_;
 
   DISALLOW_COPY_AND_ASSIGN(ExternalPrefLoader);
 };
diff --git a/chrome/browser/extensions/external_pref_loader_unittest.cc b/chrome/browser/extensions/external_pref_loader_unittest.cc
new file mode 100644
index 0000000..97de478
--- /dev/null
+++ b/chrome/browser/extensions/external_pref_loader_unittest.cc
@@ -0,0 +1,142 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/external_pref_loader.h"
+#include "base/macros.h"
+#include "chrome/browser/extensions/external_provider_impl.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/sync/profile_sync_test_util.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "extensions/common/extension.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+namespace {
+
+class TestSyncService : public browser_sync::ProfileSyncServiceMock {
+ public:
+  enum class SyncedTypes { ALL, NONE };
+
+  explicit TestSyncService(Profile* profile)
+      : browser_sync::ProfileSyncServiceMock(
+            CreateProfileSyncServiceParamsForTest(profile)),
+        synced_types_(SyncedTypes::NONE) {}
+  ~TestSyncService() override {}
+
+  // FakeSyncService:
+  bool IsFirstSetupComplete() const override { return true; }
+  bool IsSyncAllowed() const override { return true; }
+  bool IsSyncActive() const override { return true; }
+  syncer::ModelTypeSet GetActiveDataTypes() const override {
+    switch (synced_types_) {
+      case SyncedTypes::ALL:
+        return syncer::ModelTypeSet::All();
+      case SyncedTypes::NONE:
+        return syncer::ModelTypeSet();
+    }
+    NOTREACHED();
+    return syncer::ModelTypeSet();
+  }
+  bool CanSyncStart() const override { return can_sync_start_; }
+  void AddObserver(syncer::SyncServiceObserver* observer) override {
+    ASSERT_FALSE(observer_);
+    observer_ = observer;
+  }
+  void RemoveObserver(syncer::SyncServiceObserver* observer) override {
+    EXPECT_EQ(observer_, observer);
+  }
+
+  void set_can_sync_start(bool value) { can_sync_start_ = value; }
+
+  void FireOnStateChanged(browser_sync::ProfileSyncService* service) {
+    ASSERT_TRUE(observer_);
+    observer_->OnStateChanged(service);
+  }
+
+ private:
+  syncer::SyncServiceObserver* observer_ = nullptr;
+  bool can_sync_start_ = true;
+
+  SyncedTypes synced_types_;
+  DISALLOW_COPY_AND_ASSIGN(TestSyncService);
+};
+
+std::unique_ptr<KeyedService> TestingSyncFactoryFunction(
+    content::BrowserContext* context) {
+  return std::make_unique<TestSyncService>(static_cast<Profile*>(context));
+}
+
+}  // namespace
+
+// Test version of ExternalPrefLoader that doesn't do any IO.
+class TestExternalPrefLoader : public ExternalPrefLoader {
+ public:
+  TestExternalPrefLoader(Profile* profile, base::OnceClosure load_callback)
+      : ExternalPrefLoader(
+            // Invalid value, doesn't matter since it's not used.
+            -1,
+            // Make sure ExternalPrefLoader waits for priority sync.
+            ExternalPrefLoader::DELAY_LOAD_UNTIL_PRIORITY_SYNC,
+            profile),
+        load_callback_(std::move(load_callback)) {}
+
+  void LoadOnFileThread() override {
+    content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
+                                     std::move(load_callback_));
+  }
+
+ private:
+  ~TestExternalPrefLoader() override {}
+  base::OnceClosure load_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestExternalPrefLoader);
+};
+
+class ExternalPrefLoaderTest : public testing::Test {
+ public:
+  ExternalPrefLoaderTest() {}
+  ~ExternalPrefLoaderTest() override {}
+
+  void SetUp() override { profile_ = std::make_unique<TestingProfile>(); }
+
+  void TearDown() override { profile_.reset(); }
+
+  Profile* profile() { return profile_.get(); }
+
+ private:
+  content::TestBrowserThreadBundle thread_bundle_;
+  std::unique_ptr<TestingProfile> profile_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExternalPrefLoaderTest);
+};
+
+// TODO(lazyboy): Add a test to cover
+// PrioritySyncReadyWaiter::OnIsSyncingChanged().
+
+// Tests that we fire pref reading correctly after priority sync state
+// is resolved by ExternalPrefLoader.
+TEST_F(ExternalPrefLoaderTest, PrefReadInitiatesCorrectly) {
+  TestSyncService* test_service = static_cast<TestSyncService*>(
+      ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
+          profile(), &TestingSyncFactoryFunction));
+
+  base::RunLoop run_loop;
+  scoped_refptr<ExternalPrefLoader> loader(
+      new TestExternalPrefLoader(profile(), run_loop.QuitWhenIdleClosure()));
+  ExternalProviderImpl provider(
+      nullptr, loader, profile(), Manifest::INVALID_LOCATION,
+      Manifest::INVALID_LOCATION, Extension::NO_FLAGS);
+  provider.VisitRegisteredExtension();
+
+  // Initially CanSyncStart() returns true, returning false will let |loader|
+  // proceed.
+  test_service->set_can_sync_start(false);
+  test_service->FireOnStateChanged(test_service);
+  run_loop.Run();
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/external_registry_loader_win.cc b/chrome/browser/extensions/external_registry_loader_win.cc
index 5fc75b3b..c2e1bdf 100644
--- a/chrome/browser/extensions/external_registry_loader_win.cc
+++ b/chrome/browser/extensions/external_registry_loader_win.cc
@@ -215,8 +215,7 @@
     std::unique_ptr<base::DictionaryValue> prefs) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(prefs);
-  prefs_ = std::move(prefs);
-  LoadFinished();
+  LoadFinished(std::move(prefs));
 
   // Attempt to watch registry if we haven't already.
   if (attempted_watching_registry_)
diff --git a/chrome/browser/extensions/external_registry_loader_win_unittest.cc b/chrome/browser/extensions/external_registry_loader_win_unittest.cc
index 9ce0417..bf9ba21d 100644
--- a/chrome/browser/extensions/external_registry_loader_win_unittest.cc
+++ b/chrome/browser/extensions/external_registry_loader_win_unittest.cc
@@ -40,16 +40,17 @@
   std::unique_ptr<base::DictionaryValue> LoadPrefsOnBlockingThread() override {
     return DictionaryBuilder().Set(kDummyRegistryKey, id_++).Build();
   }
-  void LoadFinished() override {
-    ExternalRegistryLoader::LoadFinished();
+  void LoadFinished(std::unique_ptr<base::DictionaryValue> prefs) override {
     ++load_finished_count_;
     ASSERT_LE(load_finished_count_, 2);
 
-    ASSERT_TRUE(prefs_);
+    ASSERT_TRUE(prefs);
     int prefs_test_id = -1;
-    EXPECT_TRUE(prefs_->GetInteger(kDummyRegistryKey, &prefs_test_id));
+    EXPECT_TRUE(prefs->GetInteger(kDummyRegistryKey, &prefs_test_id));
     prefs_test_ids_.push_back(prefs_test_id);
 
+    ExternalRegistryLoader::LoadFinished(std::move(prefs));
+
     if (load_finished_count_ == 2)
       run_loop_.Quit();
   }
diff --git a/chrome/browser/feature_engagement/bookmark/bookmark_tracker.cc b/chrome/browser/feature_engagement/bookmark/bookmark_tracker.cc
index 3cce2c6..d6f9daf 100644
--- a/chrome/browser/feature_engagement/bookmark/bookmark_tracker.cc
+++ b/chrome/browser/feature_engagement/bookmark/bookmark_tracker.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/feature_engagement/bookmark/bookmark_tracker.h"
 
-#include "base/feature_list.h"
 #include "base/time/time.h"
 #include "chrome/browser/feature_engagement/session_duration_updater.h"
 #include "chrome/browser/feature_engagement/session_duration_updater_factory.h"
@@ -18,7 +17,7 @@
 
 namespace {
 
-const int kFiveHoursInMinutes = 300;
+const int kDefaultPromoShowTimeInHours = 5;
 
 }  // namespace
 
@@ -27,7 +26,11 @@
 BookmarkTracker::BookmarkTracker(
     Profile* profile,
     SessionDurationUpdater* session_duration_updater)
-    : FeatureTracker(profile, session_duration_updater) {}
+    : FeatureTracker(profile,
+                     session_duration_updater,
+                     &kIPHBookmarkFeature,
+                     base::TimeDelta::FromHours(kDefaultPromoShowTimeInHours)) {
+}
 
 BookmarkTracker::BookmarkTracker(
     SessionDurationUpdater* session_duration_updater)
@@ -48,18 +51,10 @@
     ShowPromo();
 }
 
-bool BookmarkTracker::ShouldShowPromo() {
-  return GetTracker()->ShouldTriggerHelpUI(kIPHBookmarkFeature);
-}
-
 void BookmarkTracker::OnSessionTimeMet() {
   GetTracker()->NotifyEvent(events::kBookmarkSessionTimeMet);
 }
 
-int BookmarkTracker::GetSessionTimeRequiredToShowInMinutes() {
-  return kFiveHoursInMinutes;
-}
-
 void BookmarkTracker::ShowPromo() {
   // TODO: Call the promo.
 
diff --git a/chrome/browser/feature_engagement/bookmark/bookmark_tracker.h b/chrome/browser/feature_engagement/bookmark/bookmark_tracker.h
index 1adca30..fbd4cb6 100644
--- a/chrome/browser/feature_engagement/bookmark/bookmark_tracker.h
+++ b/chrome/browser/feature_engagement/bookmark/bookmark_tracker.h
@@ -35,8 +35,6 @@
   void OnVisitedKnownURL();
   // Clears the flag for whether there is any in-product help being displayed.
   void OnPromoClosed();
-  // Returns whether or not the promo should be displayed.
-  bool ShouldShowPromo();
 
  protected:
   // Alternate constructor to support unit testing.
@@ -49,7 +47,6 @@
   FRIEND_TEST_ALL_PREFIXES(BookmarkTrackerTest, TestShouldShowPromo);
 
   // FeatureTracker:
-  int GetSessionTimeRequiredToShowInMinutes() override;
   void OnSessionTimeMet() override;
 
   // Sets the BookmarkInProductHelp pref to true and calls the Bookmark Promo.
diff --git a/chrome/browser/feature_engagement/feature_tracker.cc b/chrome/browser/feature_engagement/feature_tracker.cc
index f98a607..b451f39 100644
--- a/chrome/browser/feature_engagement/feature_tracker.cc
+++ b/chrome/browser/feature_engagement/feature_tracker.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/feature_engagement/feature_tracker.h"
 
+#include "base/metrics/field_trial_params.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "chrome/browser/feature_engagement/tracker_factory.h"
 #include "components/feature_engagement/public/event_constants.h"
@@ -12,11 +14,16 @@
 
 namespace feature_engagement {
 
-FeatureTracker::FeatureTracker(Profile* profile,
-                               SessionDurationUpdater* session_duration_updater)
+FeatureTracker::FeatureTracker(
+    Profile* profile,
+    SessionDurationUpdater* session_duration_updater,
+    const base::Feature* feature,
+    base::TimeDelta default_time_required_to_show_promo)
     : profile_(profile),
       session_duration_updater_(session_duration_updater),
-      session_duration_observer_(this) {
+      session_duration_observer_(this),
+      feature_(feature),
+      field_trial_time_delta_(default_time_required_to_show_promo) {
   AddSessionDurationObserver();
 }
 
@@ -34,6 +41,10 @@
   return session_duration_observer_.IsObserving(session_duration_updater_);
 }
 
+bool FeatureTracker::ShouldShowPromo() {
+  return GetTracker()->ShouldTriggerHelpUI(*feature_);
+}
+
 Tracker* FeatureTracker::GetTracker() const {
   return TrackerFactory::GetForBrowserContext(profile_);
 }
@@ -45,10 +56,24 @@
   }
 }
 
+base::TimeDelta FeatureTracker::GetSessionTimeRequiredToShow() {
+  if (!has_retrieved_field_trial_minutes_) {
+    has_retrieved_field_trial_minutes_ = true;
+    std::string field_trial_string_value =
+        base::GetFieldTrialParamValueByFeature(*feature_, "x_minutes");
+    int field_trial_int_value;
+    if (base::StringToInt(field_trial_string_value, &field_trial_int_value)) {
+      field_trial_time_delta_ =
+          base::TimeDelta::FromMinutes(field_trial_int_value);
+    }
+  }
+  return field_trial_time_delta_;
+}
+
 bool FeatureTracker::HasEnoughSessionTimeElapsed(
     base::TimeDelta total_session_time) {
   return total_session_time.InMinutes() >=
-         GetSessionTimeRequiredToShowInMinutes();
+         GetSessionTimeRequiredToShow().InMinutes();
 }
 
 }  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/feature_tracker.h b/chrome/browser/feature_engagement/feature_tracker.h
index 295f8c36..34b9bd93 100644
--- a/chrome/browser/feature_engagement/feature_tracker.h
+++ b/chrome/browser/feature_engagement/feature_tracker.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_FEATURE_ENGAGEMENT_FEATURE_TRACKER_H_
 #define CHROME_BROWSER_FEATURE_ENGAGEMENT_FEATURE_TRACKER_H_
 
+#include "base/feature_list.h"
 #include "base/scoped_observer.h"
 #include "chrome/browser/feature_engagement/session_duration_updater.h"
 #include "chrome/browser/profiles/profile.h"
@@ -24,7 +25,7 @@
 // SessionDurationUpdater keeps track of the observed session time and, upon
 // each session ending, updates all of the FeatureTrackers with the new total
 // observed session time. Once the observed session time exceeds the time
-// requirement provided by GetSessionTimeRequiredToShowInMinutes(), the
+// requirement provided by GetSessionTimeRequiredToShow(), the
 // FeatureTracker unregisters itself as an obsever of SessionDurationUpdater.
 // SessionDurationUpdater stops updating the observed session time if no
 // features are observing it, and will start tracking the observed sesion time
@@ -33,7 +34,9 @@
                        public KeyedService {
  public:
   FeatureTracker(Profile* profile,
-                 SessionDurationUpdater* session_duration_updater);
+                 SessionDurationUpdater* session_duration_updater,
+                 const base::Feature* feature,
+                 base::TimeDelta defaultTimeRequiredToShowPromo);
 
   // Adds the SessionDurationUpdater observer.
   void AddSessionDurationObserver();
@@ -47,16 +50,21 @@
   // SessionDurationUpdater::Observer:
   void OnSessionEnded(base::TimeDelta total_session_time) override;
 
+  // Returns whether or not the promo should be displayed.
+  bool ShouldShowPromo();
+
  protected:
   ~FeatureTracker() override;
-  // Returns the required session time in minutes for the FeatureTracker's
-  // subclass to show its promo.
-  virtual int GetSessionTimeRequiredToShowInMinutes() = 0;
+
   // Alerts the feature tracker that the session time is up.
   virtual void OnSessionTimeMet() = 0;
   // Returns the Tracker associated with this FeatureTracker.
   virtual Tracker* GetTracker() const;
 
+  // Returns the required session time in minutes for the FeatureTracker's
+  // subclass to show its promo.
+  base::TimeDelta GetSessionTimeRequiredToShow();
+
  private:
   // Returns whether the active session time of a user has elapsed more than the
   // required active session time for the feature.
@@ -73,6 +81,16 @@
   ScopedObserver<SessionDurationUpdater, SessionDurationUpdater::Observer>
       session_duration_observer_;
 
+  // IPH Feature that the tracker is tracking.
+  const base::Feature* const feature_;
+
+  // "x_minutes" param value from the field trial.
+  base::TimeDelta field_trial_time_delta_;
+
+  // Whether the "x_minutes" param value has already been retrieved to prevent
+  // reading from the field trial multiple times for the same param.
+  bool has_retrieved_field_trial_minutes_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(FeatureTracker);
 };
 
diff --git a/chrome/browser/feature_engagement/feature_tracker_unittest.cc b/chrome/browser/feature_engagement/feature_tracker_unittest.cc
index 85ef03d..c93f3c1 100644
--- a/chrome/browser/feature_engagement/feature_tracker_unittest.cc
+++ b/chrome/browser/feature_engagement/feature_tracker_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/run_loop.h"
 #include "base/sequenced_task_runner.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/time/time.h"
 #include "chrome/browser/feature_engagement/session_duration_updater.h"
 #include "chrome/browser/feature_engagement/session_duration_updater_factory.h"
 #include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
@@ -29,9 +30,11 @@
 
 namespace {
 
-const int kTestTimeInMinutes = 100;
+const int kTestTimeDeltaInMinutes = 100;
 const int kTestTimeSufficentInMinutes = 110;
 const int kTestTimeInsufficientInMinutes = 90;
+const char kGroupName[] = "Enabled";
+const char kNewTabFieldTrialName[] = "NewTabFieldTrial";
 const char kTestProfileName[] = "test-profile";
 
 class TestFeatureTracker : public FeatureTracker {
@@ -40,14 +43,16 @@
       : FeatureTracker(
             profile,
             feature_engagement::SessionDurationUpdaterFactory::GetInstance()
-                ->GetForProfile(profile)),
+                ->GetForProfile(profile),
+            &kIPHNewTabFeature,
+            base::TimeDelta::FromMinutes(kTestTimeDeltaInMinutes)),
         pref_service_(
             base::MakeUnique<sync_preferences::TestingPrefServiceSyncable>()) {
     SessionDurationUpdater::RegisterProfilePrefs(pref_service_->registry());
   }
 
-  int GetSessionTimeRequiredToShowInMinutes() override {
-    return kTestTimeInMinutes;
+  base::TimeDelta GetSessionTimeRequiredToShowWrapper() {
+    return GetSessionTimeRequiredToShow();
   }
 
   void OnSessionTimeMet() override {}
@@ -83,6 +88,7 @@
 
   void TearDown() override {
     metrics::DesktopSessionDurationTracker::CleanupForTesting();
+    // Need to invoke the reset method as TearDown is on the UI thread.
     testing_profile_manager_.reset();
   }
 
@@ -126,6 +132,108 @@
   EXPECT_FALSE(mock_feature_tracker_->IsObserving());
 }
 
+class FeatureTrackerMinutesTest : public testing::Test {
+ public:
+  FeatureTrackerMinutesTest() = default;
+  ~FeatureTrackerMinutesTest() override = default;
+
+  // testing::Test:
+  void SetUp() override {
+    // Start the DesktopSessionDurationTracker to track active session time.
+    metrics::DesktopSessionDurationTracker::Initialize();
+    testing_profile_manager_ = base::MakeUnique<TestingProfileManager>(
+        TestingBrowserProcess::GetGlobal());
+    ASSERT_TRUE(testing_profile_manager_->SetUp());
+    mock_feature_tracker_ =
+        base::MakeUnique<testing::StrictMock<MockTestFeatureTracker>>(
+            testing_profile_manager_->CreateTestingProfile(kTestProfileName));
+    // Set up the NewTabInProductHelp field trial.
+    base::FieldTrial* new_tab_trial = base::FieldTrialList::CreateFieldTrial(
+        kNewTabFieldTrialName, kGroupName);
+    trials_[kIPHNewTabFeature.name] = new_tab_trial;
+
+    std::unique_ptr<base::FeatureList> feature_list =
+        base::MakeUnique<base::FeatureList>();
+    feature_list->RegisterFieldTrialOverride(
+        kIPHNewTabFeature.name, base::FeatureList::OVERRIDE_ENABLE_FEATURE,
+        new_tab_trial);
+
+    scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
+    ASSERT_EQ(new_tab_trial,
+              base::FeatureList::GetFieldTrial(kIPHNewTabFeature));
+  }
+
+  void TearDown() override {
+    mock_feature_tracker_.get()->RemoveSessionDurationObserver();
+    metrics::DesktopSessionDurationTracker::CleanupForTesting();
+    // Need to invoke the rest method as TearDown is on the UI thread.
+    testing_profile_manager_.reset();
+  }
+
+  void SetFeatureParams(const base::Feature& feature,
+                        std::map<std::string, std::string> params) {
+    ASSERT_TRUE(
+        base::FieldTrialParamAssociator::GetInstance()
+            ->AssociateFieldTrialParams(trials_[feature.name]->trial_name(),
+                                        kGroupName, params));
+
+    std::map<std::string, std::string> actualParams;
+    EXPECT_TRUE(base::GetFieldTrialParamsByFeature(feature, &actualParams));
+    EXPECT_EQ(params, actualParams);
+  }
+
+ protected:
+  std::unique_ptr<TestingProfileManager> testing_profile_manager_;
+  std::unique_ptr<MockTestFeatureTracker> mock_feature_tracker_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+  std::map<std::string, base::FieldTrial*> trials_;
+  variations::testing::VariationParamsManager params_manager_;
+
+ private:
+  content::TestBrowserThreadBundle thread_bundle_;
+
+  DISALLOW_COPY_AND_ASSIGN(FeatureTrackerMinutesTest);
+};
+
+// Test that session time defaults to the time in the constructor if there is no
+// field param value.
+TEST_F(FeatureTrackerMinutesTest, TestSessionTimeWithNoFieldTrialValue) {
+  EXPECT_EQ(mock_feature_tracker_->GetSessionTimeRequiredToShowWrapper(),
+            base::TimeDelta::FromMinutes(kTestTimeDeltaInMinutes));
+}
+
+// Test that session time defaults to the valid time from the field param value.
+TEST_F(FeatureTrackerMinutesTest, TestSessionTimeWithValidFieldTrialValue) {
+  std::map<std::string, std::string> new_tab_params;
+  new_tab_params["x_minutes"] = "1";
+  SetFeatureParams(kIPHNewTabFeature, new_tab_params);
+
+  EXPECT_EQ(mock_feature_tracker_->GetSessionTimeRequiredToShowWrapper(),
+            base::TimeDelta::FromMinutes(1));
+}
+
+// Test that session time defaults to the time in the constructor if the field
+// param value is empty string.
+TEST_F(FeatureTrackerMinutesTest, TestSessionTimeWithEmptyFieldTrialValue) {
+  std::map<std::string, std::string> new_tab_params;
+  new_tab_params["x_minutes"] = "";
+  SetFeatureParams(kIPHNewTabFeature, new_tab_params);
+
+  EXPECT_EQ(mock_feature_tracker_->GetSessionTimeRequiredToShowWrapper(),
+            base::TimeDelta::FromMinutes(kTestTimeDeltaInMinutes));
+}
+
+// Test that session time defaults to the time in the constructor if the field
+// param value is invalid.
+TEST_F(FeatureTrackerMinutesTest, TestSessionTimeWithInvalidFieldTrialValue) {
+  std::map<std::string, std::string> new_tab_params;
+  new_tab_params["x_minutes"] = "12g4";
+  SetFeatureParams(kIPHNewTabFeature, new_tab_params);
+
+  EXPECT_EQ(mock_feature_tracker_->GetSessionTimeRequiredToShowWrapper(),
+            base::TimeDelta::FromMinutes(kTestTimeDeltaInMinutes));
+}
+
 }  // namespace
 
 }  // namespace feature_engagement
diff --git a/chrome/browser/feature_engagement/new_tab/new_tab_tracker.cc b/chrome/browser/feature_engagement/new_tab/new_tab_tracker.cc
index fc57d4f0..d07cab3 100644
--- a/chrome/browser/feature_engagement/new_tab/new_tab_tracker.cc
+++ b/chrome/browser/feature_engagement/new_tab/new_tab_tracker.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/feature_engagement/new_tab/new_tab_tracker.h"
 
-#include "base/feature_list.h"
 #include "base/time/time.h"
 #include "chrome/browser/feature_engagement/tracker_factory.h"
 #include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
@@ -17,7 +16,7 @@
 
 namespace {
 
-const int kTwoHoursInMinutes = 120;
+const int kDefaultPromoShowTimeInHours = 2;
 
 }  // namespace
 
@@ -25,7 +24,11 @@
 
 NewTabTracker::NewTabTracker(Profile* profile,
                              SessionDurationUpdater* session_duration_updater)
-    : FeatureTracker(profile, session_duration_updater) {}
+    : FeatureTracker(profile,
+                     session_duration_updater,
+                     &kIPHNewTabFeature,
+                     base::TimeDelta::FromHours(kDefaultPromoShowTimeInHours)) {
+}
 
 NewTabTracker::NewTabTracker(SessionDurationUpdater* session_duration_updater)
     : NewTabTracker(nullptr, session_duration_updater) {}
@@ -49,18 +52,10 @@
   GetTracker()->Dismissed(kIPHNewTabFeature);
 }
 
-bool NewTabTracker::ShouldShowPromo() {
-  return GetTracker()->ShouldTriggerHelpUI(kIPHNewTabFeature);
-}
-
 void NewTabTracker::OnSessionTimeMet() {
   GetTracker()->NotifyEvent(events::kNewTabSessionTimeMet);
 }
 
-int NewTabTracker::GetSessionTimeRequiredToShowInMinutes() {
-  return kTwoHoursInMinutes;
-}
-
 void NewTabTracker::ShowPromo() {
   NewTabButton::ShowPromoForLastActiveBrowser();
 }
diff --git a/chrome/browser/feature_engagement/new_tab/new_tab_tracker.h b/chrome/browser/feature_engagement/new_tab/new_tab_tracker.h
index 19d8f7cc..bfe48e9 100644
--- a/chrome/browser/feature_engagement/new_tab/new_tab_tracker.h
+++ b/chrome/browser/feature_engagement/new_tab/new_tab_tracker.h
@@ -37,8 +37,6 @@
   void OnOmniboxFocused();
   // Clears the flag for whether there is any in-product help being displayed.
   void OnPromoClosed();
-  // Returns whether or not the promo should be displayed.
-  bool ShouldShowPromo();
 
  protected:
   // Alternate constructor to support unit testing.
@@ -51,8 +49,8 @@
   FRIEND_TEST_ALL_PREFIXES(NewTabTrackerTest, TestShouldShowPromo);
   FRIEND_TEST_ALL_PREFIXES(NewTabTrackerBrowserTest, TestShowPromo);
 
+  base::TimeTicks last_promo_seen_;
   // FeatureTracker:
-  int GetSessionTimeRequiredToShowInMinutes() override;
   void OnSessionTimeMet() override;
 
   // Sets the NewTabInProductHelp pref to true and calls the New Tab Promo.
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 79533ad..2833633 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1113,6 +1113,10 @@
     "Enable web pages to use the experimental service worker navigation "
     "preload API.";
 
+const char kServiceWorkerPaymentAppsName[] = "Service Worker payment apps";
+const char kServiceWorkerPaymentAppsDescription[] =
+    "Enable Service Worker applications to integrate as payment apps";
+
 const char kServiceWorkerScriptStreamingName[] =
     "Service worker script streaming.";
 const char kServiceWorkerScriptStreamingDescription[] =
@@ -1849,6 +1853,12 @@
     "Enables suggested offline pages to be prefetched, so useful content is "
     "available while offline.";
 
+const char kOfflinePagesPrefetchingUIName[] =
+    "Enables prefetched offline pages to be shown in UI.";
+const char kOfflinePagesPrefetchingUIDescription[] =
+    "Enables prefetched offline pages to raise notifications and be shown in "
+    "download home UI.";
+
 const char kOfflinePagesRenovationsName[] = "Enables offline page renovations.";
 const char kOfflinePagesRenovationsDescription[] =
     "Enables offline page renovations which correct issues with dynamic "
@@ -1919,10 +1929,6 @@
 const char kReaderModeInCCTDescription[] =
     "Open Reader Mode in Chrome Custom Tabs.";
 
-const char kServiceWorkerPaymentAppsName[] = "Service Worker payment apps";
-const char kServiceWorkerPaymentAppsDescription[] =
-    "Enable Service Worker applications to integrate as payment apps";
-
 const char kSetMarketUrlForTestingName[] = "Set market URL for testing";
 const char kSetMarketUrlForTestingDescription[] =
     "When enabled, sets the market URL for use in testing the update menu "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index a5df45fa..98a4b0e 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -682,6 +682,9 @@
 extern const char kServiceWorkerNavigationPreloadName[];
 extern const char kServiceWorkerNavigationPreloadDescription[];
 
+extern const char kServiceWorkerPaymentAppsName[];
+extern const char kServiceWorkerPaymentAppsDescription[];
+
 extern const char kServiceWorkerScriptStreamingName[];
 extern const char kServiceWorkerScriptStreamingDescription[];
 
@@ -1126,6 +1129,9 @@
 extern const char kOfflinePagesPrefetchingName[];
 extern const char kOfflinePagesPrefetchingDescription[];
 
+extern const char kOfflinePagesPrefetchingUIName[];
+extern const char kOfflinePagesPrefetchingUIDescription[];
+
 extern const char kOfflinePagesRenovationsName[];
 extern const char kOfflinePagesRenovationsDescription[];
 
@@ -1168,9 +1174,6 @@
 extern const char kReaderModeInCCTName[];
 extern const char kReaderModeInCCTDescription[];
 
-extern const char kServiceWorkerPaymentAppsName[];
-extern const char kServiceWorkerPaymentAppsDescription[];
-
 extern const char kSetMarketUrlForTestingName[];
 extern const char kSetMarketUrlForTestingDescription[];
 
diff --git a/chrome/browser/font_family_cache.cc b/chrome/browser/font_family_cache.cc
index 94611c66..e273b0e 100644
--- a/chrome/browser/font_family_cache.cc
+++ b/chrome/browser/font_family_cache.cc
@@ -12,6 +12,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/font_pref_change_notifier_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_font_webkit_names.h"
 #include "chrome/common/pref_names.h"
@@ -23,10 +24,14 @@
 
 FontFamilyCache::FontFamilyCache(Profile* profile)
     : prefs_(profile->GetPrefs()) {
-  profile_pref_registrar_.Init(profile->GetPrefs());
   notification_registrar_.Add(this,
                               chrome::NOTIFICATION_PROFILE_DESTROYED,
                               content::Source<Profile>(profile));
+
+  // Safe to use Unretained here since the registrar is scoped to this class.
+  font_change_registrar_.Register(
+      FontPrefChangeNotifierFactory::GetForProfile(profile),
+      base::Bind(&FontFamilyCache::OnPrefsChanged, base::Unretained(this)));
 }
 
 FontFamilyCache::~FontFamilyCache() {
@@ -66,11 +71,6 @@
   // Lazily constructs the map if it doesn't already exist.
   ScriptFontMap& map = font_family_map_[map_name];
   map[script] = font16;
-
-  // Register for profile preference changes.
-  profile_pref_registrar_.Add(
-      pref_name.c_str(),
-      base::Bind(&FontFamilyCache::OnPrefsChanged, base::Unretained(this)));
   return font16;
 }
 
@@ -118,9 +118,8 @@
       if (pref_name[map_name_length] != delimiter)
         continue;
 
-      // Clear the cache and the observer.
+      // Clear the cache.
       map.erase(it2);
-      profile_pref_registrar_.Remove(pref_name.c_str());
       break;
     }
   }
@@ -130,5 +129,5 @@
                               const content::NotificationSource& source,
                               const content::NotificationDetails& details) {
   DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
-  profile_pref_registrar_.RemoveAll();
+  font_change_registrar_.Unregister();
 }
diff --git a/chrome/browser/font_family_cache.h b/chrome/browser/font_family_cache.h
index 9366134..36577f32 100644
--- a/chrome/browser/font_family_cache.h
+++ b/chrome/browser/font_family_cache.h
@@ -11,7 +11,7 @@
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "base/supports_user_data.h"
-#include "components/prefs/pref_change_registrar.h"
+#include "chrome/browser/font_pref_change_notifier.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/common/web_preferences.h"
@@ -90,8 +90,8 @@
   // PrefService, so there is no worry about an invalid pointer.
   const PrefService* prefs_;
 
-  // Reacts to profile changes.
-  PrefChangeRegistrar profile_pref_registrar_;
+  // Reacts to profile font changes.
+  FontPrefChangeNotifier::Registrar font_change_registrar_;
 
   // Listens for profile destruction.
   content::NotificationRegistrar notification_registrar_;
diff --git a/chrome/browser/font_pref_change_notifier.cc b/chrome/browser/font_pref_change_notifier.cc
new file mode 100644
index 0000000..919d451c
--- /dev/null
+++ b/chrome/browser/font_pref_change_notifier.cc
@@ -0,0 +1,67 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/font_pref_change_notifier.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "chrome/common/pref_names_util.h"
+#include "components/prefs/pref_service.h"
+
+FontPrefChangeNotifier::Registrar::Registrar() {}
+FontPrefChangeNotifier::Registrar::~Registrar() {
+  if (is_registered())
+    Unregister();
+}
+
+void FontPrefChangeNotifier::Registrar::Register(
+    FontPrefChangeNotifier* notifier,
+    FontPrefChangeNotifier::Callback cb) {
+  DCHECK(!is_registered());
+  notifier_ = notifier;
+  callback_ = std::move(cb);
+
+  notifier_->AddRegistrar(this);
+}
+
+void FontPrefChangeNotifier::Registrar::Unregister() {
+  DCHECK(is_registered());
+  notifier_->RemoveRegistrar(this);
+
+  notifier_ = nullptr;
+  callback_ = FontPrefChangeNotifier::Callback();
+}
+
+FontPrefChangeNotifier::FontPrefChangeNotifier(PrefService* pref_service)
+    : pref_service_(pref_service) {
+  pref_service_->AddPrefObserverAllPrefs(this);
+}
+
+FontPrefChangeNotifier::~FontPrefChangeNotifier() {
+  pref_service_->RemovePrefObserverAllPrefs(this);
+
+  // There could be a shutdown race between this class and the objects
+  // registered with it. We don't want the registrars to call back into us
+  // when we're deleted, so tell them to unregister now.
+  for (auto& reg : registrars_)
+    reg.Unregister();
+}
+
+void FontPrefChangeNotifier::AddRegistrar(Registrar* registrar) {
+  registrars_.AddObserver(registrar);
+}
+
+void FontPrefChangeNotifier::RemoveRegistrar(Registrar* registrar) {
+  registrars_.RemoveObserver(registrar);
+}
+
+void FontPrefChangeNotifier::OnPreferenceChanged(PrefService* pref_service,
+                                                 const std::string& pref_name) {
+  if (base::StartsWith(pref_name, pref_names_util::kWebKitFontPrefPrefix,
+                       base::CompareCase::SENSITIVE)) {
+    for (auto& reg : registrars_)
+      reg.callback_.Run(pref_name);
+  }
+}
diff --git a/chrome/browser/font_pref_change_notifier.h b/chrome/browser/font_pref_change_notifier.h
new file mode 100644
index 0000000..e6fbc8f
--- /dev/null
+++ b/chrome/browser/font_pref_change_notifier.h
@@ -0,0 +1,82 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_FONT_PREF_CHANGE_NOTIFIER_H_
+#define CHROME_BROWSER_FONT_PREF_CHANGE_NOTIFIER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/prefs/pref_observer.h"
+
+class PrefService;
+
+// There are more than 1000 font prefs and several subsystems need to be
+// notified when a font changes. Registering for 1000 font change notifications
+// is inefficient and slows down other pref notifications, and having each font
+// subsystem registering "any preference changed" observers is also
+// inefficient.
+//
+// This class registers for "any preference changed" notifications, filters
+// for font pref changes only, and then issues notifications to registered
+// observers.
+//
+// There is one FontPrefChangeNotifier per Profile. Construct one with the
+// FontPrefChangeNotifierFactory.
+class FontPrefChangeNotifier : public PrefObserver, public KeyedService {
+ public:
+  // The parameter is the full name of the font pref that changed.
+  using Callback = base::RepeatingCallback<void(const std::string&)>;
+
+  // Instantiate this subclass to scope an observer notification. The registrar
+  // can have only one callback.
+  class Registrar {
+   public:
+    Registrar();
+    ~Registrar();
+
+    bool is_registered() const { return !!notifier_; }
+
+    // Start watching for changes.
+    void Register(FontPrefChangeNotifier* notifier,
+                  FontPrefChangeNotifier::Callback cb);
+
+    // Optional way to unregister before the Registrar object goes out of
+    // scope. The object must currently be registered.
+    void Unregister();
+
+   private:
+    friend FontPrefChangeNotifier;
+
+    FontPrefChangeNotifier* notifier_ = nullptr;
+    FontPrefChangeNotifier::Callback callback_;
+
+    DISALLOW_COPY_AND_ASSIGN(Registrar);
+  };
+
+  // The pref service must outlive this class.
+  explicit FontPrefChangeNotifier(PrefService* pref_service);
+  ~FontPrefChangeNotifier() override;
+
+ private:
+  friend Registrar;
+
+  void AddRegistrar(Registrar* registrar);
+  void RemoveRegistrar(Registrar* registrar);
+
+  // PrefObserver implementation.
+  void OnPreferenceChanged(PrefService* service,
+                           const std::string& pref_name) override;
+
+  PrefService* pref_service_;  // Non-owning.
+
+  // Non-owning pointers to the Registrars that have registered themselves
+  // with us. We expect few registrars.
+  base::ObserverList<Registrar> registrars_;
+
+  DISALLOW_COPY_AND_ASSIGN(FontPrefChangeNotifier);
+};
+
+#endif  // CHROME_BROWSER_FONT_PREF_CHANGE_NOTIFIER_H_
diff --git a/chrome/browser/font_pref_change_notifier_factory.cc b/chrome/browser/font_pref_change_notifier_factory.cc
new file mode 100644
index 0000000..70b0ef0
--- /dev/null
+++ b/chrome/browser/font_pref_change_notifier_factory.cc
@@ -0,0 +1,40 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/font_pref_change_notifier_factory.h"
+
+#include "chrome/browser/font_pref_change_notifier.h"
+#include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+
+FontPrefChangeNotifierFactory::FontPrefChangeNotifierFactory()
+    : BrowserContextKeyedServiceFactory(
+          "FontPrefChangeNotifier",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+FontPrefChangeNotifierFactory::~FontPrefChangeNotifierFactory() = default;
+
+// static
+FontPrefChangeNotifier* FontPrefChangeNotifierFactory::GetForProfile(
+    Profile* profile) {
+  return static_cast<FontPrefChangeNotifier*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+// static
+FontPrefChangeNotifierFactory* FontPrefChangeNotifierFactory::GetInstance() {
+  return base::Singleton<FontPrefChangeNotifierFactory>::get();
+}
+
+KeyedService* FontPrefChangeNotifierFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  return new FontPrefChangeNotifier(
+      Profile::FromBrowserContext(context)->GetPrefs());
+}
+
+content::BrowserContext* FontPrefChangeNotifierFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  return chrome::GetBrowserContextRedirectedInIncognito(context);
+}
diff --git a/chrome/browser/font_pref_change_notifier_factory.h b/chrome/browser/font_pref_change_notifier_factory.h
new file mode 100644
index 0000000..be6d4b82
--- /dev/null
+++ b/chrome/browser/font_pref_change_notifier_factory.h
@@ -0,0 +1,34 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_FONT_PREF_CHANGE_NOTIFIER_FACTORY_H_
+#define CHROME_BROWSER_FONT_PREF_CHANGE_NOTIFIER_FACTORY_H_
+
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class FontPrefChangeNotifier;
+class Profile;
+
+// Keyed service factory for a FontPrefChangeNotifier.
+class FontPrefChangeNotifierFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  static FontPrefChangeNotifier* GetForProfile(Profile* profile);
+
+  static FontPrefChangeNotifierFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<FontPrefChangeNotifierFactory>;
+
+  FontPrefChangeNotifierFactory();
+  ~FontPrefChangeNotifierFactory() override;
+
+  // BrowserContextKeyedServiceFactory overrides:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+};
+
+#endif  // CHROME_BROWSER_FONT_PREF_CHANGE_NOTIFIER_FACTORY_H_
diff --git a/chrome/browser/font_pref_change_notifier_unittest.cc b/chrome/browser/font_pref_change_notifier_unittest.cc
new file mode 100644
index 0000000..a1664e8
--- /dev/null
+++ b/chrome/browser/font_pref_change_notifier_unittest.cc
@@ -0,0 +1,92 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/font_pref_change_notifier.h"
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/common/pref_names_util.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void AppendString(std::vector<std::string>* vect, const std::string& str) {
+  vect->push_back(str);
+}
+
+}  // namespace
+
+TEST(FontPrefChangeNotifier, Registrars) {
+  std::string prefix((pref_names_util::kWebKitFontPrefPrefix));
+
+  // Registrar 0 will outlive the notifier.
+  std::vector<std::string> fonts0;
+  FontPrefChangeNotifier::Registrar reg0;
+
+  std::unique_ptr<TestingPrefServiceSimple> service =
+      base::MakeUnique<TestingPrefServiceSimple>();
+  PrefRegistrySimple* pref_registry = service->registry();
+
+  std::string font1 = prefix + "font1";
+  pref_registry->RegisterStringPref(font1, std::string());
+  std::string font2 = prefix + "font2";
+  pref_registry->RegisterStringPref(font2, std::string());
+  std::string font3 = prefix + "font3";
+  pref_registry->RegisterStringPref(font3, std::string());
+
+  std::unique_ptr<FontPrefChangeNotifier> notifier =
+      base::MakeUnique<FontPrefChangeNotifier>(service.get());
+  reg0.Register(notifier.get(),
+                base::BindRepeating(&AppendString, base::Unretained(&fonts0)));
+
+  // Registrar 1 will be manually unregistered.
+  std::vector<std::string> fonts1;
+  FontPrefChangeNotifier::Registrar reg1;
+  reg1.Register(notifier.get(),
+                base::BindRepeating(&AppendString, base::Unretained(&fonts1)));
+
+  // Registrar 2 will automatically unregister itself when it goes out of scope.
+  std::vector<std::string> fonts2;
+  {
+    FontPrefChangeNotifier::Registrar reg2;
+    reg2.Register(
+        notifier.get(),
+        base::BindRepeating(&AppendString, base::Unretained(&fonts2)));
+
+    // All lists should get the font.
+    service->SetString(font1, "1");
+    EXPECT_EQ(1u, fonts0.size());
+    EXPECT_EQ(font1, fonts0.back());
+    EXPECT_EQ(1u, fonts1.size());
+    EXPECT_EQ(font1, fonts1.back());
+    EXPECT_EQ(1u, fonts2.size());
+    EXPECT_EQ(font1, fonts2.back());
+  }
+
+  // Now that Regsitrar 2 is gone, only 0 and 1 should get changes.
+  service->SetString(font2, "2");
+  EXPECT_EQ(2u, fonts0.size());
+  EXPECT_EQ(font2, fonts0.back());
+  EXPECT_EQ(2u, fonts1.size());
+  EXPECT_EQ(font2, fonts1.back());
+  EXPECT_EQ(1u, fonts2.size());
+
+  // Manually unregister Registrar 1.
+  reg1.Unregister();
+  EXPECT_FALSE(reg1.is_registered());
+
+  // Only Registrar 0 should see changes now.
+  service->SetString(font3, "3");
+  EXPECT_EQ(3u, fonts0.size());
+  EXPECT_EQ(font3, fonts0.back());
+  EXPECT_EQ(2u, fonts1.size());
+  EXPECT_EQ(1u, fonts2.size());
+  EXPECT_EQ(font3, fonts0.back());
+
+  notifier.reset();
+
+  // Registrar 0 should have been automatically unregistered.
+  EXPECT_FALSE(reg0.is_registered());
+}
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
index c65ce977..c8b224f 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
@@ -53,8 +53,6 @@
 #include "components/policy/core/common/cloud/policy_header_io_helper.h"
 #include "components/previews/core/previews_experiments.h"
 #include "components/previews/core/previews_io_data.h"
-#include "components/rappor/public/rappor_utils.h"
-#include "components/rappor/rappor_service_impl.h"
 #include "components/variations/net/variations_http_headers.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_data.h"
@@ -975,23 +973,3 @@
   return ProfileIOData::FromResourceContext(resource_context)->
       CreateClientCertStore();
 }
-
-// Record RAPPOR for aborted main frame loads. Separate into a fast and
-// slow bucket because a shocking number of aborts happen under 100ms.
-void ChromeResourceDispatcherHostDelegate::OnAbortedFrameLoad(
-    const GURL& url,
-    base::TimeDelta request_loading_time) {
-  if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
-    BrowserThread::PostTask(
-        BrowserThread::UI, FROM_HERE,
-        base::BindOnce(
-            &ChromeResourceDispatcherHostDelegate::OnAbortedFrameLoad,
-            base::Unretained(this), url, request_loading_time));
-    return;
-  }
-
-  std::string metric_name = (request_loading_time.InMilliseconds() < 100 ?
-      "Net.ErrAborted.Fast" : "Net.ErrAborted.Slow");
-  rappor::SampleDomainAndRegistryFromGURL(
-      g_browser_process->rappor_service(), metric_name, url);
-}
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h
index dfe215f..166db17 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.h
@@ -93,8 +93,6 @@
       net::URLRequest* request) const override;
   std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
       content::ResourceContext* resource_context) override;
-  void OnAbortedFrameLoad(const GURL& url,
-                          base::TimeDelta request_loading_time) override;
 
   // Called on the UI thread. Allows switching out the
   // ExternalProtocolHandler::Delegate for testing code.
diff --git a/chrome/browser/net/chrome_network_delegate.cc b/chrome/browser/net/chrome_network_delegate.cc
index 949d0709..02931346 100644
--- a/chrome/browser/net/chrome_network_delegate.cc
+++ b/chrome/browser/net/chrome_network_delegate.cc
@@ -20,7 +20,6 @@
 #include "base/metrics/sparse_histogram.h"
 #include "base/metrics/user_metrics.h"
 #include "base/path_service.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
@@ -216,11 +215,6 @@
     net::URLRequest* request,
     const net::CompletionCallback& callback,
     GURL* new_url) {
-  // TODO(mmenke): Remove ScopedTracker below once crbug.com/456327 is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456327 URLRequest::ChromeNetworkDelegate::OnBeforeURLRequest"));
-
   // TODO(joaodasilva): This prevents extensions from seeing URLs that are
   // blocked. However, an extension might redirect the request to another URL,
   // which is not blocked.
@@ -239,11 +233,6 @@
     return error;
   }
 
-  // TODO(mmenke): Remove ScopedTracker below once crbug.com/456327 is fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456327 URLRequest::ChromeNetworkDelegate::OnBeforeURLRequest 2"));
-
   extensions_delegate_->ForwardStartRequestStatus(request);
 
   if (!enable_referrers_->GetValue())
@@ -251,11 +240,6 @@
   if (enable_do_not_track_ && enable_do_not_track_->GetValue())
     request->SetExtraRequestHeaderByName(kDNTHeader, "1", true /* override */);
 
-  // TODO(mmenke): Remove ScopedTracker below once crbug.com/456327 is fixed.
-  tracked_objects::ScopedTracker tracking_profile3(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456327 URLRequest::ChromeNetworkDelegate::OnBeforeURLRequest 3"));
-
   bool force_safe_search =
       (force_google_safe_search_ && force_google_safe_search_->GetValue());
 
@@ -270,19 +254,9 @@
   int rv = extensions_delegate_->OnBeforeURLRequest(
       request, wrapped_callback, new_url);
 
-  // TODO(mmenke): Remove ScopedTracker below once crbug.com/456327 is fixed.
-  tracked_objects::ScopedTracker tracking_profile4(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456327 URLRequest::ChromeNetworkDelegate::OnBeforeURLRequest 4"));
-
   if (force_safe_search && rv == net::OK && new_url->is_empty())
     safe_search_util::ForceGoogleSafeSearch(request, new_url);
 
-  // TODO(mmenke): Remove ScopedTracker below once crbug.com/456327 is fixed.
-  tracked_objects::ScopedTracker tracking_profile5(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456327 URLRequest::ChromeNetworkDelegate::OnBeforeURLRequest 5"));
-
   if (allowed_domains_for_apps_ &&
       !allowed_domains_for_apps_->GetValue().empty() &&
       request->url().DomainIs("google.com")) {
diff --git a/chrome/browser/notifications/login_state_notification_blocker_chromeos_browsertest.cc b/chrome/browser/notifications/login_state_notification_blocker_chromeos_browsertest.cc
index 3e0043b..5dfb1f5c 100644
--- a/chrome/browser/notifications/login_state_notification_blocker_chromeos_browsertest.cc
+++ b/chrome/browser/notifications/login_state_notification_blocker_chromeos_browsertest.cc
@@ -130,7 +130,8 @@
   LoginUser(kTestUsers[0]);
   // Two session state changes for login:
   //   LOGIN_PRIMARY -> LOGGED_IN_NOT_ACTIVE -> ACTIVE.
-  EXPECT_EQ(2, GetStateChangedCountAndReset());
+  // Plus one state change for the InactiveUserNotificationBlocker.
+  EXPECT_EQ(3, GetStateChangedCountAndReset());
   EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
 
   // Multi-login user switch.
@@ -167,7 +168,8 @@
   LoginUser(kTestUsers[0]);
   // Two session state changes for login:
   //   LOGIN_PRIMARY -> LOGGED_IN_NOT_ACTIVE -> ACTIVE.
-  EXPECT_EQ(2, GetStateChangedCountAndReset());
+  // Plus one state change for the InactiveUserNotificationBlocker.
+  EXPECT_EQ(3, GetStateChangedCountAndReset());
   EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
 
   // Multi-login user switch.
diff --git a/chrome/browser/notifications/message_center_notifications_unittest.cc b/chrome/browser/notifications/message_center_notifications_unittest.cc
index 43794bf9..0df6493 100644
--- a/chrome/browser/notifications/message_center_notifications_unittest.cc
+++ b/chrome/browser/notifications/message_center_notifications_unittest.cc
@@ -26,14 +26,6 @@
 #include "ui/message_center/notification_types.h"
 #include "ui/message_center/notifier_settings.h"
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h"
-#include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
-#include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
-#include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h"
-#include "components/signin/core/account_id/account_id.h"
-#endif
-
 namespace message_center {
 
 class MessageCenterNotificationManagerTest : public BrowserWithTestWindowTest {
@@ -126,25 +118,4 @@
   EXPECT_TRUE(message_center()->NotificationCount() == 1);
 }
 
-#if defined(OS_CHROMEOS)
-TEST_F(MessageCenterNotificationManagerTest, MultiUserUpdates) {
-  TestingProfile profile;
-  const AccountId active_user_id(
-      multi_user_util::GetAccountIdFromProfile(&profile));
-  chrome::MultiUserWindowManagerChromeOS* multi_user_window_manager =
-      new chrome::MultiUserWindowManagerChromeOS(active_user_id);
-  multi_user_window_manager->Init();
-  chrome::MultiUserWindowManager::SetInstanceForTest(multi_user_window_manager);
-  std::unique_ptr<MultiUserNotificationBlockerChromeOS> blocker(
-      new MultiUserNotificationBlockerChromeOS(
-          message_center::MessageCenter::Get(), active_user_id));
-  EXPECT_EQ(0u, message_center()->NotificationCount());
-  notification_manager()->Add(GetANotification("test"), &profile);
-  EXPECT_EQ(1u, message_center()->NotificationCount());
-  notification_manager()->Update(GetANotification("test"), &profile);
-  EXPECT_EQ(1u, message_center()->NotificationCount());
-  chrome::MultiUserWindowManager::DeleteInstance();
-}
-#endif
-
 }  // namespace message_center
diff --git a/chrome/browser/password_manager/password_store_win.cc b/chrome/browser/password_manager/password_store_win.cc
index 58f482b..f6c97e8 100644
--- a/chrome/browser/password_manager/password_store_win.cc
+++ b/chrome/browser/password_manager/password_store_win.cc
@@ -16,7 +16,6 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/os_crypt/ie7_password_win.h"
@@ -150,12 +149,6 @@
 void PasswordStoreWin::DBHandler::OnWebDataServiceRequestDone(
     PasswordWebDataService::Handle handle,
     std::unique_ptr<WDTypedResult> result) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 PasswordStoreWin::DBHandler::OnWebDataServiceRequestDone"));
-
   DCHECK(
       password_store_->background_task_runner()->RunsTasksInCurrentSequence());
 
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service.cc b/chrome/browser/policy/cloud/user_policy_signin_service.cc
index d3e9c258..565c51c 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service.cc
+++ b/chrome/browser/policy/cloud/user_policy_signin_service.cc
@@ -9,7 +9,6 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback.h"
-#include "base/profiler/scoped_tracker.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile.h"
@@ -150,12 +149,6 @@
 
 void UserPolicySigninService::OnRefreshTokenAvailable(
     const std::string& account_id) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 UserPolicySigninService::OnRefreshTokenAvailable"));
-
   // Ignore OAuth tokens for any account but the primary one.
   if (account_id != signin_manager()->GetAuthenticatedAccountId())
     return;
diff --git a/chrome/browser/profiles/avatar_menu.cc b/chrome/browser/profiles/avatar_menu.cc
index a02c35b..b50892b1 100644
--- a/chrome/browser/profiles/avatar_menu.cc
+++ b/chrome/browser/profiles/avatar_menu.cc
@@ -7,7 +7,6 @@
 #include "base/bind.h"
 #include "base/i18n/case_conversion.h"
 #include "base/metrics/field_trial.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
@@ -232,11 +231,6 @@
 
 void AvatarMenu::OnProfileHighResAvatarLoaded(
     const base::FilePath& profile_path) {
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "461175 AvatarMenu::OnProfileHighResAvatarLoaded"));
   Update();
 }
 
diff --git a/chrome/browser/profiles/profile_info_cache.cc b/chrome/browser/profiles/profile_info_cache.cc
index aed2447..cae630c9 100644
--- a/chrome/browser/profiles/profile_info_cache.cc
+++ b/chrome/browser/profiles/profile_info_cache.cc
@@ -13,7 +13,6 @@
 #include "base/i18n/case_conversion.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/task_scheduler/post_task.h"
@@ -827,11 +826,6 @@
 #if defined(OS_ANDROID) || defined(OS_CHROMEOS)
   return;
 #endif
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "461175 ProfileInfoCache::DownloadHighResAvatar::GetFileName"));
   const char* file_name =
       profiles::GetDefaultAvatarIconFileNameAtIndex(icon_index);
   DCHECK(file_name);
@@ -839,11 +833,6 @@
   if (avatar_images_downloads_in_progress_.count(file_name))
     return;
 
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "461175 ProfileInfoCache::DownloadHighResAvatar::MakeDownloader"));
   // Start the download for this file. The cache takes ownership of the
   // avatar downloader, which will be deleted when the download completes, or
   // if that never happens, when the ProfileInfoCache is destroyed.
@@ -856,11 +845,6 @@
                      base::Unretained(this),
                      profile_path)));
 
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile3(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "461175 ProfileInfoCache::DownloadHighResAvatar::StartDownload"));
   current_downloader->Start();
 }
 
@@ -897,35 +881,14 @@
                                              const std::string& key,
                                              gfx::Image** image) const {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "461175 ProfileInfoCache::OnAvatarPictureLoaded::Start"));
-
   cached_avatar_images_loading_[key] = false;
 
   if (*image) {
-    // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile2(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "461175 ProfileInfoCache::OnAvatarPictureLoaded::SetImage"));
     cached_avatar_images_[key].reset(*image);
   } else {
-    // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile3(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "461175 ProfileInfoCache::OnAvatarPictureLoaded::MakeEmptyImage"));
     // Place an empty image in the cache to avoid reloading it again.
     cached_avatar_images_[key].reset(new gfx::Image());
   }
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile4(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "461175 ProfileInfoCache::OnAvatarPictureLoaded::DeleteImage"));
   delete image;
 
   for (auto& observer : observer_list_)
diff --git a/chrome/browser/profiling_host/profiling_process_host.cc b/chrome/browser/profiling_host/profiling_process_host.cc
index d708bcd..86dcf1e1 100644
--- a/chrome/browser/profiling_host/profiling_process_host.cc
+++ b/chrome/browser/profiling_host/profiling_process_host.cc
@@ -12,6 +12,7 @@
 #include "base/sys_info.h"
 #include "base/task_scheduler/post_task.h"
 #include "base/trace_event/memory_dump_manager.h"
+#include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/tracing/crash_service_uploader.h"
@@ -71,11 +72,19 @@
     return;
   }
 
+  std::unique_ptr<base::DictionaryValue> metadata =
+      base::MakeUnique<base::DictionaryValue>();
+  base::Value configs(base::Value::Type::DICTIONARY);
+  configs.SetKey("mode", base::Value("REACTIVE_TRACING_MODE"));
+  configs.SetKey("category", base::Value("MEMLOG"));
+  metadata->SetKey("config", std::move(configs));
+
   TraceCrashServiceUploader* uploader = new TraceCrashServiceUploader(
       g_browser_process->system_request_context());
 
   uploader->DoUpload(file_contents, content::TraceUploader::UNCOMPRESSED_UPLOAD,
-                     nullptr, content::TraceUploader::UploadProgressCallback(),
+                     std::move(metadata),
+                     content::TraceUploader::UploadProgressCallback(),
                      base::Bind(&OnTraceUploadComplete, base::Owned(uploader)));
 }
 
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_predicate.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_predicate.js
index 395bb9e..db72fa8 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_predicate.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_predicate.js
@@ -334,10 +334,6 @@
   if (AutomationPredicate.structuralContainer(node))
     return true;
 
-  // Ignore list markers since we already announce listitem role.
-  if (node.role == Role.LIST_MARKER)
-    return true;
-
   // Don't ignore nodes with names or name-like attribute.
   if (node.name || node.value || node.description || node.url)
     return false;
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 b78b3f4..e6a9956 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
@@ -865,8 +865,12 @@
     mockFeedback.call(listItem.focus.bind(listItem))
         .expectSpeech('List item')
         .call(this.doCmd('nextObject'))
+        .expectSpeech('\u2022') // bullet
+        .call(this.doCmd('nextObject'))
         .expectSpeech('Button')
         .call(this.doCmd('previousObject'))
+        .expectSpeech('\u2022') // bullet
+        .call(this.doCmd('previousObject'))
         .expectSpeech('List item')
         .call(this.doCmd('previousObject'))
         .expectSpeech('Edit text')
@@ -1247,3 +1251,34 @@
         .replay();
   });
 });
+
+TEST_F('BackgroundTest', 'ListItem', function() {
+  this.forceContextualFirstOutput();
+  var mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree(function(root) {/*!
+    <p>start</p>
+    <ul><li>apple<li>grape<li>banana</ul>
+    <ol><li>pork<li>beef<li>chicken</ol>
+  */}, function(root) {
+    mockFeedback.call(doCmd('nextLine'))
+        .expectSpeech('List item', '\u2022', 'apple')
+        .expectBraille('lstitm list +3 \u2022 apple')
+        .call(doCmd('nextLine'))
+        .expectSpeech('List item', '\u2022', 'grape')
+        .expectBraille('lstitm \u2022 grape')
+        .call(doCmd('nextLine'))
+        .expectSpeech('List item', '\u2022', 'banana')
+        .expectBraille('lstitm \u2022 banana')
+
+        .call(doCmd('nextLine'))
+        .expectSpeech('List item', '1', 'pork')
+        .expectBraille('lstitm list +3 1 pork')
+        .call(doCmd('nextLine'))
+        .expectSpeech('List item', '2', 'beef')
+        .expectBraille('lstitm 2 beef')
+        .call(doCmd('nextLine'))
+        .expectSpeech('List item', '3', 'chicken')
+        .expectBraille('lstitm 3 chicken')
+        .replay();
+  });
+});
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
index 3b5ddf1..3871afd 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
@@ -148,7 +148,8 @@
   link: {msgId: 'role_link', earconId: 'LINK'},
   listBox: {msgId: 'role_listbox', earconId: 'LISTBOX'},
   listBoxOption: {msgId: 'role_listitem', earconId: 'LIST_ITEM'},
-  listItem: {msgId: 'role_listitem', earconId: 'LIST_ITEM'},
+  listItem:
+      {msgId: 'role_listitem', earconId: 'LIST_ITEM', outputContextFirst: true},
   log: {
     msgId: 'role_log',
   },
diff --git a/chrome/browser/resources/chromeos/chromevox/testing/chromevox_next_e2e_test_base.js b/chrome/browser/resources/chromeos/chromevox/testing/chromevox_next_e2e_test_base.js
index 3b46331..ef795d9 100644
--- a/chrome/browser/resources/chromeos/chromevox/testing/chromevox_next_e2e_test_base.js
+++ b/chrome/browser/resources/chromeos/chromevox/testing/chromevox_next_e2e_test_base.js
@@ -86,5 +86,13 @@
   forceContextualLastOutput: function() {
     for (var role in Output.ROLE_INFO_)
       Output.ROLE_INFO_[role]['outputContextFirst'] = undefined;
+  },
+
+  /**
+   * Forces output to place context utterances at the beginning of output.
+   */
+  forceContextualFirstOutput: function() {
+    for (var role in Output.ROLE_INFO_)
+      Output.ROLE_INFO_[role]['outputContextFirst'] = true;
   }
 };
diff --git a/chrome/browser/resources/profiler/OWNERS b/chrome/browser/resources/profiler/OWNERS
deleted file mode 100644
index 6015001..0000000
--- a/chrome/browser/resources/profiler/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-eroman@chromium.org
-jar@chromium.org
-
-# COMPONENT: Internals>Metrics
diff --git a/chrome/browser/resources/profiler/profiler.html b/chrome/browser/resources/profiler/profiler.html
deleted file mode 100644
index cc5638f..0000000
--- a/chrome/browser/resources/profiler/profiler.html
+++ /dev/null
@@ -1,164 +0,0 @@
-<!doctype html>
-<html>
-<head>
-<meta charset="utf-8">
-<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
-<script src="chrome://resources/js/load_time_data.js"></script>
-<script src="chrome://resources/js/util.js"></script>
-<script src="chrome://profiler/strings.js"></script>
-<script src="chrome://profiler/profiler.js"></script>
-
-<style>
-
-body {
-  font-size: 80%;
-}
-
-/*
- * The following styles are for a TABLE that uses a thin collapsed border, and
- * has a blue heading. When you hover over rows, they turn yellow.
- */
-table.results-table {
-  border-collapse: collapse;
-}
-
-table.results-table,
-.results-table th,
-.results-table td {
-  border: 1px solid #777;
-  padding-left: 4px;
-  padding-right: 4px;
-}
-
-.results-table th {
-  background: rgb(224,236,255);
-}
-
-.results-table tbody tr:hover {
-  background-color: rgb(255, 255, 187);
-}
-
-/*
- * Make the column headers change the mouse to a "hand" cursor, sine they are
- * clickable.
- */
-.results-table th {
-  cursor: pointer;
-}
-
-/*
- * This is row which displays aggregate totals for each column.
- */
-.results-table .aggregator-row {
-  background: rgb(255, 204, 153);
-}
-
-/*
- * This is the row at the end of tables which explains how many rows were shown,
- * and how many were hidden.
- */
-.results-table .truncation-row {
-  background: #eee;
-}
-
-/*---------------------------------------------------------------------------*/
-
-/*
- * When grouping data, the table for each bucket is wrapped in a DIV with this
- * class. Used to add a bit of spacing between groups.
- */
-.group-container {
-  margin-bottom: 2ex;
-  margin-top: 2ex;
-}
-
-/*
- * The title for each group is enclosed in a DIV of the following class.
- */
-.group-title-container {
-  font-size: 140%;
-  margin-bottom: 1ex;
-}
-
-/* Styling to make a span look like a clickable link */
-.pseudo-link {
-  color: blue;
-  cursor: pointer;
-  text-decoration: underline;
-}
-
-.selected-snapshot {
-  color: purple;
-  font-weight: bold;
-}
-
-#snapshot-selection-summary {
-  color: green;
-  font-style: italic;
-  font-weight: bold;
-  margin-top: 1ex;
-}
-
-.errormsg {
-  color: red;
-}
-
-</style>
-</head>
-<body>
-  <table width=100%>
-    <tr>
-      <td>
-        <b>Save:</b><button id=save-snapshots-button>Save</button>
-        <b>Restore:</b> <input type=file id=snapshot-file-loader>
-        <span id=file-load-error hidden class=errormsg></span>
-      </td>
-      <td align=right>
-        <a target="_blank"
-          href="https://sites.google.com/a/chromium.org/dev/developers/threaded-task-tracking">
-          Profiler Documentation
-        </a>
-      </td>
-    </tr>
-  </table>
-  <hr>
-  <table width=100%>
-    <tr>
-      <td>
-        <b>Group by: </b> <span id=group-by-container></span>
-        <b>Sort by: </b> <span id=sort-by-container></span>
-      </td>
-      <td align=right>
-        <span id=snapshots-link class=pseudo-link>[snapshots]</span>
-        <span id=edit-columns-link class=pseudo-link>[columns]</span>
-        <input type='search' incremental id='filter-search'>
-      </td>
-    </tr>
-    <tr id=edit-columns-row style='display:none'>
-      <td colspan=2>
-        <div>
-          <b>Merge: </b><span id=column-merge-toggles-container></span>
-            <label><input type=checkbox id='merge-similar-threads-checkbox' checked>
-               Merge similar threads.</label>
-        </div>
-        <div>
-          <b>Show: </b><span id=column-toggles-container></span>
-        </div>
-      </td>
-    </tr>
-    <tr id=snapshots-row style='display:none'>
-      <td colspan=2>
-        <button id=take-snapshot-button>Add snapshot</button>
-        <table><tbody id=snapshots-tbody></tbody></table>
-        <div id=snapshot-selection-summary></div>
-      </td>
-    </tr>
-  </table>
-
-  <hr>
-
-  <div id='results-div'></div>
-
-  <a style="display: none" id="download-anchor" download="profile.json"></a>
-</body>
-</html>
diff --git a/chrome/browser/resources/profiler/profiler.js b/chrome/browser/resources/profiler/profiler.js
deleted file mode 100644
index 81b1250b8..0000000
--- a/chrome/browser/resources/profiler/profiler.js
+++ /dev/null
@@ -1,2371 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var g_browserBridge;
-var g_mainView;
-
-// TODO(eroman): The handling of "max" across snapshots is not correct.
-// For starters the browser needs to be aware to generate new maximums.
-// Secondly, we need to take into account the "max" of intermediary snapshots,
-// not just the terminal ones.
-
-/**
- * Main entry point called once the page has loaded.
- */
-function onLoad() {
-  g_browserBridge = new BrowserBridge();
-  g_mainView = new MainView();
-}
-
-document.addEventListener('DOMContentLoaded', onLoad);
-
-/**
- * This class provides a "bridge" for communicating between the javascript and
- * the browser. Used as a singleton.
- */
-var BrowserBridge = (function() {
-  'use strict';
-
-  /**
-   * @constructor
-   */
-  function BrowserBridge() {}
-
-  BrowserBridge.prototype = {
-    //--------------------------------------------------------------------------
-    // Messages sent to the browser
-    //--------------------------------------------------------------------------
-
-    sendGetData: function() {
-      chrome.send('getData');
-    },
-
-    //--------------------------------------------------------------------------
-    // Messages received from the browser.
-    //--------------------------------------------------------------------------
-
-    receivedData: function(data) {
-      // TODO(eroman): The browser should give an indication of which snapshot
-      // this data belongs to. For now we always assume it is for the latest.
-      g_mainView.addDataToSnapshot(data);
-    },
-  };
-
-  return BrowserBridge;
-})();
-
-/**
- * This class handles the presentation of our profiler view. Used as a
- * singleton.
- */
-var MainView = (function() {
-  'use strict';
-
-  // --------------------------------------------------------------------------
-  // Important IDs in the HTML document
-  // --------------------------------------------------------------------------
-
-  // The search box to filter results.
-  var FILTER_SEARCH_ID = 'filter-search';
-
-  // The container node to put all the "Group by" dropdowns into.
-  var GROUP_BY_CONTAINER_ID = 'group-by-container';
-
-  // The container node to put all the "Sort by" dropdowns into.
-  var SORT_BY_CONTAINER_ID = 'sort-by-container';
-
-  // The DIV to put all the tables into.
-  var RESULTS_DIV_ID = 'results-div';
-
-  // The container node to put all the column (visibility) checkboxes into.
-  var COLUMN_TOGGLES_CONTAINER_ID = 'column-toggles-container';
-
-  // The container node to put all the column (merge) checkboxes into.
-  var COLUMN_MERGE_TOGGLES_CONTAINER_ID = 'column-merge-toggles-container';
-
-  // The anchor which toggles visibility of column checkboxes.
-  var EDIT_COLUMNS_LINK_ID = 'edit-columns-link';
-
-  // The container node to show/hide when toggling the column checkboxes.
-  var EDIT_COLUMNS_ROW = 'edit-columns-row';
-
-  // The checkbox which controls whether things like "Worker Threads" and
-  // "PAC threads" will be merged together.
-  var MERGE_SIMILAR_THREADS_CHECKBOX_ID = 'merge-similar-threads-checkbox';
-
-  var TOGGLE_SNAPSHOTS_LINK_ID = 'snapshots-link';
-  var SNAPSHOTS_ROW = 'snapshots-row';
-  var SNAPSHOT_SELECTION_SUMMARY_ID = 'snapshot-selection-summary';
-  var TAKE_SNAPSHOT_BUTTON_ID = 'take-snapshot-button';
-
-  var SAVE_SNAPSHOTS_BUTTON_ID = 'save-snapshots-button';
-  var SNAPSHOT_FILE_LOADER_ID = 'snapshot-file-loader';
-  var LOAD_ERROR_ID = 'file-load-error';
-
-  var DOWNLOAD_ANCHOR_ID = 'download-anchor';
-
-  // --------------------------------------------------------------------------
-  // Row keys
-  // --------------------------------------------------------------------------
-
-  // Each row of our data is an array of values rather than a dictionary. This
-  // avoids some overhead from repeating the key string multiple times, and
-  // speeds up the property accesses a bit. The following keys are well-known
-  // indexes into the array for various properties.
-  //
-  // Note that the declaration order will also define the default display order.
-
-  var BEGIN_KEY = 1;  // Start at 1 rather than 0 to simplify sorting code.
-  var END_KEY = BEGIN_KEY;
-
-  var KEY_COUNT = END_KEY++;
-  var KEY_RUN_TIME = END_KEY++;
-  var KEY_AVG_RUN_TIME = END_KEY++;
-  var KEY_MAX_RUN_TIME = END_KEY++;
-  var KEY_QUEUE_TIME = END_KEY++;
-  var KEY_AVG_QUEUE_TIME = END_KEY++;
-  var KEY_MAX_QUEUE_TIME = END_KEY++;
-  if (loadTimeData.getBoolean('enableMemoryTaskProfiler')) {
-    var KEY_MEMORY_AVG_ALLOC_OPS = END_KEY++;
-    var KEY_MEMORY_AVG_FREE_OPS = END_KEY++;
-    var KEY_MEMORY_AVG_NET_BYTES = END_KEY++;
-    var KEY_MEMORY_MAX_ALLOCATED_BYTES = END_KEY++;
-    var KEY_MEMORY_ALLOC_OPS = END_KEY++;
-    var KEY_MEMORY_FREE_OPS = END_KEY++;
-    var KEY_MEMORY_ALLOCATED_BYTES = END_KEY++;
-    var KEY_MEMORY_FREED_BYTES = END_KEY++;
-    var KEY_MEMORY_ALLOC_OVERHEAD_BYTES = END_KEY++;
-  }
-  var KEY_BIRTH_THREAD = END_KEY++;
-  var KEY_DEATH_THREAD = END_KEY++;
-  var KEY_PROCESS_TYPE = END_KEY++;
-  var KEY_PROCESS_ID = END_KEY++;
-  var KEY_FUNCTION_NAME = END_KEY++;
-  var KEY_SOURCE_LOCATION = END_KEY++;
-  var KEY_FILE_NAME = END_KEY++;
-  var KEY_LINE_NUMBER = END_KEY++;
-
-  var NUM_KEYS = END_KEY - BEGIN_KEY;
-
-  // --------------------------------------------------------------------------
-  // Aggregators
-  // --------------------------------------------------------------------------
-
-  // To generalize computing/displaying the aggregate "counts" for each column,
-  // we specify an optional "Aggregator" class to use with each property.
-
-  // The following are actually "Aggregator factories". They create an
-  // aggregator instance by calling 'create()'. The instance is then fed
-  // each row one at a time via the 'consume()' method. After all rows have
-  // been consumed, the 'getValueAsText()' method will return the aggregated
-  // value.
-
-  /**
-   * This aggregator counts the number of unique values that were fed to it.
-   */
-  var UniquifyAggregator = (function() {
-    function Aggregator(key) {
-      this.key_ = key;
-      this.valuesSet_ = {};
-    }
-
-    Aggregator.prototype = {
-      consume: function(e) {
-        this.valuesSet_[e[this.key_]] = true;
-      },
-
-      getValueAsText: function() {
-        return getDictionaryKeys(this.valuesSet_).length + ' unique';
-      },
-    };
-
-    return {
-      create: function(key) {
-        return new Aggregator(key);
-      }
-    };
-  })();
-
-  /**
-   * This aggregator sums a numeric field.
-   */
-  var SumAggregator = (function() {
-    function Aggregator(key) {
-      this.key_ = key;
-      this.sum_ = 0;
-    }
-
-    Aggregator.prototype = {
-      consume: function(e) {
-        this.sum_ += e[this.key_];
-      },
-
-      getValue: function() {
-        return this.sum_;
-      },
-
-      getValueAsText: function() {
-        return formatNumberAsText(this.getValue());
-      },
-    };
-
-    return {
-      create: function(key) {
-        return new Aggregator(key);
-      }
-    };
-  })();
-
-  /**
-   * This aggregator computes an average by summing two
-   * numeric fields, and then dividing the totals.
-   */
-  var AvgAggregator = (function() {
-    function Aggregator(numeratorKey, divisorKey) {
-      this.numeratorKey_ = numeratorKey;
-      this.divisorKey_ = divisorKey;
-
-      this.numeratorSum_ = 0;
-      this.divisorSum_ = 0;
-    }
-
-    Aggregator.prototype = {
-      consume: function(e) {
-        this.numeratorSum_ += e[this.numeratorKey_];
-        this.divisorSum_ += e[this.divisorKey_];
-      },
-
-      getValue: function() {
-        return this.numeratorSum_ / this.divisorSum_;
-      },
-
-      getValueAsText: function() {
-        return formatNumberAsText(this.getValue());
-      },
-    };
-
-    return {
-      create: function(numeratorKey, divisorKey) {
-        return {
-          create: function(key) {
-            return new Aggregator(numeratorKey, divisorKey);
-          },
-        };
-      }
-    };
-  })();
-
-  /**
-   * This aggregator computes an average by summing the difference of two
-   * numeric fields, summing a count, and then dividing the totals.
-   */
-  var AvgDiffAggregator = (function() {
-    function Aggregator(numeratorPosKey, numeratorNegKey, divisorKey) {
-      this.numeratorPosKey_ = numeratorPosKey;
-      this.numeratorNegKey_ = numeratorNegKey;
-      this.divisorKey_ = divisorKey;
-
-      this.numeratorSum_ = 0;
-      this.divisorSum_ = 0;
-    }
-
-    Aggregator.prototype = {
-      consume: function(e) {
-        this.numeratorSum_ +=
-            e[this.numeratorPosKey_] - e[this.numeratorNegKey_];
-        this.divisorSum_ += e[this.divisorKey_];
-      },
-
-      getValue: function() {
-        return this.numeratorSum_ / this.divisorSum_;
-      },
-
-      getValueAsText: function() {
-        return formatNumberAsText(this.getValue());
-      },
-    };
-
-    return {
-      create: function(numeratorPosKey, numeratorNegKey, divisorKey) {
-        return {
-          create: function(key) {
-            return new Aggregator(numeratorPosKey, numeratorNegKey, divisorKey);
-          },
-        };
-      }
-    };
-  })();
-
-  /**
-   * This aggregator finds the maximum for a numeric field.
-   */
-  var MaxAggregator = (function() {
-    function Aggregator(key) {
-      this.key_ = key;
-      this.max_ = -Infinity;
-    }
-
-    Aggregator.prototype = {
-      consume: function(e) {
-        this.max_ = Math.max(this.max_, e[this.key_]);
-      },
-
-      getValue: function() {
-        return this.max_;
-      },
-
-      getValueAsText: function() {
-        return formatNumberAsText(this.getValue());
-      },
-    };
-
-    return {
-      create: function(key) {
-        return new Aggregator(key);
-      }
-    };
-  })();
-
-  // --------------------------------------------------------------------------
-  // Key properties
-  // --------------------------------------------------------------------------
-
-  // Custom comparator for thread names (sorts main thread and IO thread
-  // higher than would happen lexicographically.)
-  var threadNameComparator = createLexicographicComparatorWithExceptions([
-    'CrBrowserMain',
-    'Chrome_IOThread',
-    'Chrome_FileThread',
-    'Chrome_HistoryThread',
-    'Chrome_DBThread',
-    'Still_Alive',
-  ]);
-
-  function diffFuncForCount(a, b) {
-    return b - a;
-  }
-
-  function diffFuncForMax(a, b) {
-    return b;
-  }
-
-  /**
-   * Enumerates information about various keys. Such as whether their data is
-   * expected to be numeric or is a string, a descriptive name (title) for the
-   * property, and what function should be used to aggregate the property when
-   * displayed in a column.
-   *
-   * --------------------------------------
-   * The following properties are required:
-   * --------------------------------------
-   *
-   *   [name]: This is displayed as the column's label.
-   *   [aggregator]: Aggregator factory that is used to compute an aggregate
-   *                 value for this column.
-   *
-   * --------------------------------------
-   * The following properties are optional:
-   * --------------------------------------
-   *
-   *   [inputJsonKey]: The corresponding key for this property in the original
-   *                   JSON dictionary received from the browser. If this is
-   *                   present, values for this key will be automatically
-   *                   populated during import.
-   *   [comparator]: A comparator function for sorting this column.
-   *   [textPrinter]: A function that transforms values into the user-displayed
-   *                  text shown in the UI. If unspecified, will default to the
-   *                  "toString()" function.
-   *   [cellAlignment]: The horizonal alignment to use for columns of this
-   *                    property (for instance 'right'). If unspecified will
-   *                    default to left alignment.
-   *   [sortDescending]: When first clicking on this column, we will default to
-   *                     sorting by |comparator| in ascending order. If this
-   *                     property is true, we will reverse that to descending.
-   *   [diff]: Function to call to compute a "difference" value between
-   *           parameters (a, b). This is used when calculating the difference
-   *           between two snapshots. Diffing numeric quantities generally
-   *           involves subtracting, but some fields like max may need to do
-   *           something different.
-   */
-  var KEY_PROPERTIES = [];
-
-  KEY_PROPERTIES[KEY_PROCESS_ID] = {
-    name: 'PID',
-    cellAlignment: 'right',
-    aggregator: UniquifyAggregator,
-  };
-
-  KEY_PROPERTIES[KEY_PROCESS_TYPE] = {
-    name: 'Process type',
-    aggregator: UniquifyAggregator,
-  };
-
-  KEY_PROPERTIES[KEY_BIRTH_THREAD] = {
-    name: 'Birth thread',
-    inputJsonKey: 'birth_thread',
-    aggregator: UniquifyAggregator,
-    comparator: threadNameComparator,
-  };
-
-  KEY_PROPERTIES[KEY_DEATH_THREAD] = {
-    name: 'Exec thread',
-    inputJsonKey: 'death_thread',
-    aggregator: UniquifyAggregator,
-    comparator: threadNameComparator,
-  };
-
-  KEY_PROPERTIES[KEY_FUNCTION_NAME] = {
-    name: 'Function name',
-    inputJsonKey: 'birth_location.function_name',
-    aggregator: UniquifyAggregator,
-  };
-
-  KEY_PROPERTIES[KEY_FILE_NAME] = {
-    name: 'File name',
-    inputJsonKey: 'birth_location.file_name',
-    aggregator: UniquifyAggregator,
-  };
-
-  KEY_PROPERTIES[KEY_LINE_NUMBER] = {
-    name: 'Line number',
-    cellAlignment: 'right',
-    inputJsonKey: 'birth_location.line_number',
-    aggregator: UniquifyAggregator,
-  };
-
-  KEY_PROPERTIES[KEY_COUNT] = {
-    name: 'Count',
-    cellAlignment: 'right',
-    sortDescending: true,
-    textPrinter: formatNumberAsText,
-    inputJsonKey: 'death_data.count',
-    aggregator: SumAggregator,
-    diff: diffFuncForCount,
-  };
-
-  KEY_PROPERTIES[KEY_QUEUE_TIME] = {
-    name: 'Total queue time',
-    cellAlignment: 'right',
-    sortDescending: true,
-    textPrinter: formatNumberAsText,
-    inputJsonKey: 'death_data.queue_ms',
-    aggregator: SumAggregator,
-    diff: diffFuncForCount,
-  };
-
-  KEY_PROPERTIES[KEY_MAX_QUEUE_TIME] = {
-    name: 'Max queue time',
-    cellAlignment: 'right',
-    sortDescending: true,
-    textPrinter: formatNumberAsText,
-    inputJsonKey: 'death_data.queue_ms_max',
-    aggregator: MaxAggregator,
-    diff: diffFuncForMax,
-  };
-
-  KEY_PROPERTIES[KEY_RUN_TIME] = {
-    name: 'Total run time',
-    cellAlignment: 'right',
-    sortDescending: true,
-    textPrinter: formatNumberAsText,
-    inputJsonKey: 'death_data.run_ms',
-    aggregator: SumAggregator,
-    diff: diffFuncForCount,
-  };
-
-  if (loadTimeData.getBoolean('enableMemoryTaskProfiler')) {
-    KEY_PROPERTIES[KEY_MEMORY_AVG_ALLOC_OPS] = {
-      name: 'Avg Allocations',
-      cellAlignment: 'right',
-      sortDescending: true,
-      textPrinter: formatNumberAsText,
-      aggregator: AvgAggregator.create(KEY_MEMORY_ALLOC_OPS, KEY_COUNT),
-    };
-
-    KEY_PROPERTIES[KEY_MEMORY_AVG_FREE_OPS] = {
-      name: 'Avg Frees',
-      cellAlignment: 'right',
-      sortDescending: true,
-      textPrinter: formatNumberAsText,
-      aggregator: AvgAggregator.create(KEY_MEMORY_FREE_OPS, KEY_COUNT),
-    };
-
-    KEY_PROPERTIES[KEY_MEMORY_AVG_NET_BYTES] = {
-      name: 'Avg Net Bytes',
-      cellAlignment: 'right',
-      sortDescending: true,
-      textPrinter: formatNumberAsText,
-      aggregator: AvgDiffAggregator.create(
-          KEY_MEMORY_ALLOCATED_BYTES, KEY_MEMORY_FREED_BYTES, KEY_COUNT),
-    };
-
-    KEY_PROPERTIES[KEY_MEMORY_ALLOC_OPS] = {
-      name: 'Allocation count',
-      cellAlignment: 'right',
-      sortDescending: true,
-      textPrinter: formatNumberAsText,
-      inputJsonKey: 'death_data.alloc_ops',
-      aggregator: SumAggregator,
-      diff: diffFuncForCount,
-    };
-
-    KEY_PROPERTIES[KEY_MEMORY_FREE_OPS] = {
-      name: 'Free Count',
-      cellAlignment: 'right',
-      sortDescending: true,
-      textPrinter: formatNumberAsText,
-      inputJsonKey: 'death_data.free_ops',
-      aggregator: SumAggregator,
-      diff: diffFuncForCount,
-    };
-
-    KEY_PROPERTIES[KEY_MEMORY_ALLOCATED_BYTES] = {
-      name: 'Allocated bytes',
-      cellAlignment: 'right',
-      sortDescending: true,
-      textPrinter: formatNumberAsText,
-      inputJsonKey: 'death_data.allocated_bytes',
-      aggregator: SumAggregator,
-      diff: diffFuncForCount,
-    };
-
-    KEY_PROPERTIES[KEY_MEMORY_FREED_BYTES] = {
-      name: 'Freed bytes',
-      cellAlignment: 'right',
-      sortDescending: true,
-      textPrinter: formatNumberAsText,
-      inputJsonKey: 'death_data.freed_bytes',
-      aggregator: SumAggregator,
-      diff: diffFuncForCount,
-    };
-
-    KEY_PROPERTIES[KEY_MEMORY_ALLOC_OVERHEAD_BYTES] = {
-      name: 'Overhead bytes',
-      cellAlignment: 'right',
-      sortDescending: true,
-      textPrinter: formatNumberAsText,
-      inputJsonKey: 'death_data.alloc_overhead_bytes',
-      aggregator: SumAggregator,
-      diff: diffFuncForCount,
-    };
-
-    KEY_PROPERTIES[KEY_MEMORY_MAX_ALLOCATED_BYTES] = {
-      name: 'Max allocated (outstanding) bytes',
-      cellAlignment: 'right',
-      sortDescending: true,
-      textPrinter: formatNumberAsText,
-      inputJsonKey: 'death_data.max_allocated_bytes',
-      aggregator: MaxAggregator,
-      diff: diffFuncForMax,
-    };
-  }
-
-  KEY_PROPERTIES[KEY_AVG_RUN_TIME] = {
-    name: 'Avg run time',
-    cellAlignment: 'right',
-    sortDescending: true,
-    textPrinter: formatNumberAsText,
-    aggregator: AvgAggregator.create(KEY_RUN_TIME, KEY_COUNT),
-  };
-
-  KEY_PROPERTIES[KEY_MAX_RUN_TIME] = {
-    name: 'Max run time',
-    cellAlignment: 'right',
-    sortDescending: true,
-    textPrinter: formatNumberAsText,
-    inputJsonKey: 'death_data.run_ms_max',
-    aggregator: MaxAggregator,
-    diff: diffFuncForMax,
-  };
-
-  KEY_PROPERTIES[KEY_AVG_QUEUE_TIME] = {
-    name: 'Avg queue time',
-    cellAlignment: 'right',
-    sortDescending: true,
-    textPrinter: formatNumberAsText,
-    aggregator: AvgAggregator.create(KEY_QUEUE_TIME, KEY_COUNT),
-  };
-
-  KEY_PROPERTIES[KEY_SOURCE_LOCATION] = {
-    name: 'Source location',
-    type: 'string',
-    aggregator: UniquifyAggregator,
-  };
-
-  /**
-   * Returns the string name for |key|.
-   */
-  function getNameForKey(key) {
-    var props = KEY_PROPERTIES[key];
-    if (props == undefined)
-      throw 'Did not define properties for key: ' + key;
-    return props.name;
-  }
-
-  /**
-   * Ordered list of all keys. This is the order we generally want
-   * to display the properties in. Default to declaration order.
-   */
-  var ALL_KEYS = [];
-  for (var k = BEGIN_KEY; k < END_KEY; ++k)
-    ALL_KEYS.push(k);
-
-  // --------------------------------------------------------------------------
-  // Default settings
-  // --------------------------------------------------------------------------
-
-  /**
-   * List of keys for those properties which we want to initially omit
-   * from the table. (They can be re-enabled by clicking [Edit columns]).
-   */
-  var INITIALLY_HIDDEN_KEYS = [
-    KEY_FILE_NAME,
-    KEY_LINE_NUMBER,
-    KEY_QUEUE_TIME,
-  ];
-
-  if (loadTimeData.getBoolean('enableMemoryTaskProfiler')) {
-    INITIALLY_HIDDEN_KEYS = INITIALLY_HIDDEN_KEYS.concat([
-      KEY_MEMORY_ALLOC_OPS,
-      KEY_MEMORY_FREE_OPS,
-      KEY_MEMORY_ALLOCATED_BYTES,
-      KEY_MEMORY_FREED_BYTES,
-      KEY_MEMORY_ALLOC_OVERHEAD_BYTES,
-    ]);
-  }
-
-  /**
-   * The ordered list of grouping choices to expose in the "Group by"
-   * dropdowns. We don't include the numeric properties, since they
-   * leads to awkward bucketing.
-   */
-  var GROUPING_DROPDOWN_CHOICES = [
-    KEY_PROCESS_TYPE,
-    KEY_PROCESS_ID,
-    KEY_BIRTH_THREAD,
-    KEY_DEATH_THREAD,
-    KEY_FUNCTION_NAME,
-    KEY_SOURCE_LOCATION,
-    KEY_FILE_NAME,
-    KEY_LINE_NUMBER,
-  ];
-
-  /**
-   * The ordered list of sorting choices to expose in the "Sort by"
-   * dropdowns.
-   */
-  var SORT_DROPDOWN_CHOICES = ALL_KEYS;
-
-  /**
-   * The ordered list of all columns that can be displayed in the tables (not
-   * including whatever has been hidden via [Edit Columns]).
-   */
-  var ALL_TABLE_COLUMNS = ALL_KEYS;
-
-  /**
-   * The initial keys to sort by when loading the page (can be changed later).
-   */
-  var INITIAL_SORT_KEYS = [-KEY_COUNT];
-
-  /**
-   * The default sort keys to use when nothing has been specified.
-   */
-  var DEFAULT_SORT_KEYS = [-KEY_COUNT];
-
-  /**
-   * The initial keys to group by when loading the page (can be changed later).
-   */
-  var INITIAL_GROUP_KEYS = [];
-
-  /**
-   * The columns to give the option to merge on.
-   */
-  var MERGEABLE_KEYS = [
-    KEY_PROCESS_ID,
-    KEY_PROCESS_TYPE,
-    KEY_BIRTH_THREAD,
-    KEY_DEATH_THREAD,
-  ];
-
-  /**
-   * The columns to merge by default.
-   */
-  var INITIALLY_MERGED_KEYS = [];
-
-  /**
-   * The full set of columns which define the "identity" for a row. A row is
-   * considered equivalent to another row if it matches on all of these
-   * fields. This list is used when merging the data, to determine which rows
-   * should be merged together. The remaining columns not listed in
-   * IDENTITY_KEYS will be aggregated.
-   */
-  var IDENTITY_KEYS = [
-    KEY_BIRTH_THREAD,
-    KEY_DEATH_THREAD,
-    KEY_PROCESS_TYPE,
-    KEY_PROCESS_ID,
-    KEY_FUNCTION_NAME,
-    KEY_SOURCE_LOCATION,
-    KEY_FILE_NAME,
-    KEY_LINE_NUMBER,
-  ];
-
-  /**
-   * The time (in milliseconds) to wait after receiving new data before
-   * re-drawing it to the screen. The reason we wait a bit is to avoid
-   * repainting repeatedly during the loading phase (which can slow things
-   * down). Note that this only slows down the addition of new data. It does
-   * not impact the  latency of user-initiated operations like sorting or
-   * merging.
-   */
-  var PROCESS_DATA_DELAY_MS = 500;
-
-  /**
-   * The initial number of rows to display (the rest are hidden) when no
-   * grouping is selected. We use a higher limit than when grouping is used
-   * since there is a lot of vertical real estate.
-   */
-  var INITIAL_UNGROUPED_ROW_LIMIT = 30;
-
-  /**
-   * The initial number of rows to display (rest are hidden) for each group.
-   */
-  var INITIAL_GROUP_ROW_LIMIT = 10;
-
-  /**
-   * The number of extra rows to show/hide when clicking the "Show more" or
-   * "Show less" buttons.
-   */
-  var LIMIT_INCREMENT = 10;
-
-  // --------------------------------------------------------------------------
-  // General utility functions
-  // --------------------------------------------------------------------------
-
-  /**
-   * Returns a list of all the keys in |dict|.
-   */
-  function getDictionaryKeys(dict) {
-    var keys = [];
-    for (var key in dict) {
-      keys.push(key);
-    }
-    return keys;
-  }
-
-  /**
-   * Formats the number |x| as a decimal integer. Strips off any decimal parts,
-   * and comma separates the number every 3 characters.
-   */
-  function formatNumberAsText(x) {
-    var orig = x.toFixed(0);
-
-    var parts = [];
-    for (var end = orig.length; end > 0;) {
-      var chunk = Math.min(end, 3);
-      parts.push(orig.substr(end - chunk, chunk));
-      end -= chunk;
-    }
-    return parts.reverse().join(',');
-  }
-
-  /**
-   * Simple comparator function which works for both strings and numbers.
-   */
-  function simpleCompare(a, b) {
-    if (a == b)
-      return 0;
-    if (a < b)
-      return -1;
-    return 1;
-  }
-
-  /**
-   * Returns a comparator function that compares values lexicographically,
-   * but special-cases the values in |orderedList| to have a higher
-   * rank.
-   */
-  function createLexicographicComparatorWithExceptions(orderedList) {
-    var valueToRankMap = {};
-    for (var i = 0; i < orderedList.length; ++i)
-      valueToRankMap[orderedList[i]] = i;
-
-    function getCustomRank(x) {
-      var rank = valueToRankMap[x];
-      if (rank == undefined)
-        rank = Infinity;  // Unmatched.
-      return rank;
-    }
-
-    return function(a, b) {
-      var aRank = getCustomRank(a);
-      var bRank = getCustomRank(b);
-
-      // Not matched by any of our exceptions.
-      if (aRank == bRank)
-        return simpleCompare(a, b);
-
-      if (aRank < bRank)
-        return -1;
-      return 1;
-    };
-  }
-
-  /**
-   * Returns dict[key]. Note that if |key| contains periods (.), they will be
-   * interpreted as meaning a sub-property.
-   */
-  function getPropertyByPath(dict, key) {
-    var cur = dict;
-    var parts = key.split('.');
-    for (var i = 0; i < parts.length; ++i) {
-      if (cur == undefined)
-        return undefined;
-      cur = cur[parts[i]];
-    }
-    return cur;
-  }
-
-  /**
-   * Creates and appends a DOM node of type |tagName| to |parent|. Optionally,
-   * sets the new node's text to |opt_text|. Returns the newly created node.
-   */
-  function addNode(parent, tagName, opt_text) {
-    var n = parent.ownerDocument.createElement(tagName);
-    parent.appendChild(n);
-    if (opt_text != undefined) {
-      addText(n, opt_text);
-    }
-    return n;
-  }
-
-  /**
-   * Adds |text| to |parent|.
-   */
-  function addText(parent, text) {
-    var textNode = parent.ownerDocument.createTextNode(text);
-    parent.appendChild(textNode);
-    return textNode;
-  }
-
-  /**
-   * Deletes all the strings in |array| which appear in |valuesToDelete|.
-   */
-  function deleteValuesFromArray(array, valuesToDelete) {
-    var valueSet = arrayToSet(valuesToDelete);
-    for (var i = 0; i < array.length;) {
-      if (valueSet[array[i]]) {
-        array.splice(i, 1);
-      } else {
-        i++;
-      }
-    }
-  }
-
-  /**
-   * Deletes all the repeated ocurrences of strings in |array|.
-   */
-  function deleteDuplicateStringsFromArray(array) {
-    // Build up set of each entry in array.
-    var seenSoFar = {};
-
-    for (var i = 0; i < array.length;) {
-      var value = array[i];
-      if (seenSoFar[value]) {
-        array.splice(i, 1);
-      } else {
-        seenSoFar[value] = true;
-        i++;
-      }
-    }
-  }
-
-  /**
-   * Builds a map out of the array |list|.
-   */
-  function arrayToSet(list) {
-    var set = {};
-    for (var i = 0; i < list.length; ++i)
-      set[list[i]] = true;
-    return set;
-  }
-
-  function trimWhitespace(text) {
-    var m = /^\s*(.*)\s*$/.exec(text);
-    return m[1];
-  }
-
-  /**
-   * Selects the option in |select| which has a value of |value|.
-   */
-  function setSelectedOptionByValue(select, value) {
-    for (var i = 0; i < select.options.length; ++i) {
-      if (select.options[i].value == value) {
-        select.options[i].selected = true;
-        return true;
-      }
-    }
-    return false;
-  }
-
-  /**
-   * Adds a checkbox to |parent|. The checkbox will have a label on its right
-   * with text |label|. Returns the checkbox input node.
-   */
-  function addLabeledCheckbox(parent, label) {
-    var labelNode = addNode(parent, 'label');
-    var checkbox = addNode(labelNode, 'input');
-    checkbox.type = 'checkbox';
-    addText(labelNode, label);
-    return checkbox;
-  }
-
-  /**
-   * Return the last component in a path which is separated by either forward
-   * slashes or backslashes.
-   */
-  function getFilenameFromPath(path) {
-    var lastSlash = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
-    if (lastSlash == -1)
-      return path;
-
-    return path.substr(lastSlash + 1);
-  }
-
-  /**
-   * Returns the current time in milliseconds since unix epoch.
-   */
-  function getTimeMillis() {
-    return (new Date()).getTime();
-  }
-
-  /**
-   * Toggle a node between hidden/invisible.
-   */
-  function toggleNodeDisplay(n) {
-    if (n.style.display == '') {
-      n.style.display = 'none';
-    } else {
-      n.style.display = '';
-    }
-  }
-
-  /**
-   * Set the visibility state of a node.
-   */
-  function setNodeDisplay(n, visible) {
-    if (visible) {
-      n.style.display = '';
-    } else {
-      n.style.display = 'none';
-    }
-  }
-
-  // --------------------------------------------------------------------------
-  // Functions that augment, bucket, and compute aggregates for the input data.
-  // --------------------------------------------------------------------------
-
-  /**
-   * Adds new derived properties to row. Mutates the provided dictionary |e|.
-   */
-  function augmentDataRow(e) {
-    computeDataRowAverages(e);
-    e[KEY_SOURCE_LOCATION] = e[KEY_FILE_NAME] + ' [' + e[KEY_LINE_NUMBER] + ']';
-  }
-
-  function computeDataRowAverages(e) {
-    e[KEY_AVG_QUEUE_TIME] = e[KEY_QUEUE_TIME] / e[KEY_COUNT];
-    e[KEY_AVG_RUN_TIME] = e[KEY_RUN_TIME] / e[KEY_COUNT];
-
-    if (loadTimeData.getBoolean('enableMemoryTaskProfiler')) {
-      e[KEY_MEMORY_AVG_ALLOC_OPS] = e[KEY_MEMORY_ALLOC_OPS] / e[KEY_COUNT];
-      e[KEY_MEMORY_AVG_FREE_OPS] = e[KEY_MEMORY_FREE_OPS] / e[KEY_COUNT];
-      e[KEY_MEMORY_AVG_NET_BYTES] =
-          (e[KEY_MEMORY_ALLOCATED_BYTES] - e[KEY_MEMORY_FREED_BYTES]) /
-          e[KEY_COUNT];
-    }
-  }
-
-  /**
-   * Creates and initializes an aggregator object for each key in |columns|.
-   * Returns an array whose keys are values from |columns|, and whose
-   * values are Aggregator instances.
-   */
-  function initializeAggregates(columns) {
-    var aggregates = [];
-
-    for (var i = 0; i < columns.length; ++i) {
-      var key = columns[i];
-      var aggregatorFactory = KEY_PROPERTIES[key].aggregator;
-      aggregates[key] = aggregatorFactory.create(key);
-    }
-
-    return aggregates;
-  }
-
-  function consumeAggregates(aggregates, row) {
-    for (var key in aggregates)
-      aggregates[key].consume(row);
-  }
-
-  function bucketIdenticalRows(rows, identityKeys, propertyGetterFunc) {
-    var identicalRows = {};
-    for (var i = 0; i < rows.length; ++i) {
-      var r = rows[i];
-
-      var rowIdentity = [];
-      for (var j = 0; j < identityKeys.length; ++j)
-        rowIdentity.push(propertyGetterFunc(r, identityKeys[j]));
-      rowIdentity = rowIdentity.join('\n');
-
-      var l = identicalRows[rowIdentity];
-      if (!l) {
-        l = [];
-        identicalRows[rowIdentity] = l;
-      }
-      l.push(r);
-    }
-    return identicalRows;
-  }
-
-  /**
-   * Merges the rows in |origRows|, by collapsing the columns listed in
-   * |mergeKeys|. Returns an array with the merged rows (in no particular
-   * order).
-   *
-   * If |mergeSimilarThreads| is true, then threads with a similar name will be
-   * considered equivalent. For instance, "WorkerThread-1" and "WorkerThread-2"
-   * will be remapped to "WorkerThread-*".
-   *
-   * If |outputAsDictionary| is false then the merged rows will be returned as a
-   * flat list. Otherwise the result will be a dictionary, where each row
-   * has a unique key.
-   */
-  function mergeRows(
-      origRows, mergeKeys, mergeSimilarThreads, outputAsDictionary) {
-    // Define a translation function for each property. Normally we copy over
-    // properties as-is, but if we have been asked to "merge similar threads" we
-    // we will remap the thread names that end in a numeric suffix.
-    var propertyGetterFunc;
-
-    if (mergeSimilarThreads) {
-      propertyGetterFunc = function(row, key) {
-        var value = row[key];
-        // If the property is a thread name, try to remap it.
-        if (key == KEY_BIRTH_THREAD || key == KEY_DEATH_THREAD) {
-          var m = /^(.*[^\d])(\d+)$/.exec(value);
-          if (m)
-            value = m[1] + '*';
-        }
-        return value;
-      };
-    } else {
-      propertyGetterFunc = function(row, key) {
-        return row[key];
-      };
-    }
-
-    // Determine which sets of properties a row needs to match on to be
-    // considered identical to another row.
-    var identityKeys = IDENTITY_KEYS.slice(0);
-    deleteValuesFromArray(identityKeys, mergeKeys);
-
-    // Set |aggregateKeys| to everything else, since we will be aggregating
-    // their value as part of the merge.
-    var aggregateKeys = ALL_KEYS.slice(0);
-    deleteValuesFromArray(aggregateKeys, IDENTITY_KEYS);
-    deleteValuesFromArray(aggregateKeys, mergeKeys);
-
-    // Group all the identical rows together, bucketed into |identicalRows|.
-    var identicalRows =
-        bucketIdenticalRows(origRows, identityKeys, propertyGetterFunc);
-
-    var mergedRows = outputAsDictionary ? {} : [];
-
-    // Merge the rows and save the results to |mergedRows|.
-    for (var k in identicalRows) {
-      // We need to smash the list |l| down to a single row...
-      var l = identicalRows[k];
-
-      var newRow = [];
-
-      if (outputAsDictionary) {
-        mergedRows[k] = newRow;
-      } else {
-        mergedRows.push(newRow);
-      }
-
-      // Copy over all the identity columns to the new row (since they
-      // were the same for each row matched).
-      for (var i = 0; i < identityKeys.length; ++i)
-        newRow[identityKeys[i]] = propertyGetterFunc(l[0], identityKeys[i]);
-
-      // Compute aggregates for the other columns.
-      var aggregates = initializeAggregates(aggregateKeys);
-
-      // Feed the rows to the aggregators.
-      for (var i = 0; i < l.length; ++i)
-        consumeAggregates(aggregates, l[i]);
-
-      // Suck out the data generated by the aggregators.
-      for (var aggregateKey in aggregates)
-        newRow[aggregateKey] = aggregates[aggregateKey].getValue();
-    }
-
-    return mergedRows;
-  }
-
-  /**
-   * Takes two dictionaries data1 and data2, and returns a new flat list which
-   * represents the difference between them. The exact meaning of "difference"
-   * is column specific, but for most numeric fields (like the count, or total
-   * time), it is found by subtracting.
-   *
-   * Rows in data1 and data2 are expected to use the same scheme for the keys.
-   * In other words, data1[k] is considered the analagous row to data2[k].
-   */
-  function subtractSnapshots(data1, data2, columnsToExclude) {
-    // These columns are computed from the other columns. We won't bother
-    // diffing/aggregating these, but rather will derive them again from the
-    // final row.
-    var COMPUTED_AGGREGATE_KEYS = [KEY_AVG_QUEUE_TIME, KEY_AVG_RUN_TIME];
-    if (loadTimeData.getBoolean('enableMemoryTaskProfiler')) {
-      COMPUTED_AGGREGATE_KEYS = COMPUTED_AGGREGATE_KEYS.concat([
-        KEY_MEMORY_AVG_ALLOC_OPS, KEY_MEMORY_AVG_FREE_OPS,
-        KEY_MEMORY_AVG_NET_BYTES
-      ]);
-    }
-
-    // These are the keys which determine row equality. Since we are not doing
-    // any merging yet at this point, it is simply the list of all identity
-    // columns.
-    var identityKeys = IDENTITY_KEYS.slice(0);
-    deleteValuesFromArray(identityKeys, columnsToExclude);
-
-    // The columns to compute via aggregation is everything else.
-    var aggregateKeys = ALL_KEYS.slice(0);
-    deleteValuesFromArray(aggregateKeys, IDENTITY_KEYS);
-    deleteValuesFromArray(aggregateKeys, COMPUTED_AGGREGATE_KEYS);
-    deleteValuesFromArray(aggregateKeys, columnsToExclude);
-
-    var diffedRows = [];
-
-    for (var rowId in data2) {
-      var row1 = data1[rowId];
-      var row2 = data2[rowId];
-
-      var newRow = [];
-
-      // Copy over all the identity columns to the new row (since they
-      // were the same for each row matched).
-      for (var i = 0; i < identityKeys.length; ++i)
-        newRow[identityKeys[i]] = row2[identityKeys[i]];
-
-      // Diff the two rows.
-      if (row1) {
-        for (var i = 0; i < aggregateKeys.length; ++i) {
-          var aggregateKey = aggregateKeys[i];
-          var a = row1[aggregateKey];
-          var b = row2[aggregateKey];
-
-          var diffFunc = KEY_PROPERTIES[aggregateKey].diff;
-          newRow[aggregateKey] = diffFunc(a, b);
-        }
-      } else {
-        // If the the row doesn't appear in snapshot1, then there is nothing to
-        // diff, so just copy row2 as is.
-        for (var i = 0; i < aggregateKeys.length; ++i) {
-          var aggregateKey = aggregateKeys[i];
-          newRow[aggregateKey] = row2[aggregateKey];
-        }
-      }
-
-      if (newRow[KEY_COUNT] == 0) {
-        // If a row's count has gone to zero, it means there were no new
-        // occurrences of it in the second snapshot, so remove it.
-        continue;
-      }
-
-      // Since we excluded the averages during the diffing phase, re-compute
-      // them using the diffed totals.
-      computeDataRowAverages(newRow);
-      diffedRows.push(newRow);
-    }
-
-    return diffedRows;
-  }
-
-  // --------------------------------------------------------------------------
-  // HTML drawing code
-  // --------------------------------------------------------------------------
-
-  function getTextValueForProperty(key, value) {
-    if (value == undefined) {
-      // A value may be undefined as a result of having merging rows. We
-      // won't actually draw it, but this might be called by the filter.
-      return '';
-    }
-
-    var textPrinter = KEY_PROPERTIES[key].textPrinter;
-    if (textPrinter)
-      return textPrinter(value);
-    return value.toString();
-  }
-
-  /**
-   * Renders the property value |value| into cell |td|. The name of this
-   * property is |key|.
-   */
-  function drawValueToCell(td, key, value) {
-    // Get a text representation of the value.
-    var text = getTextValueForProperty(key, value);
-
-    // Apply the desired cell alignment.
-    var cellAlignment = KEY_PROPERTIES[key].cellAlignment;
-    if (cellAlignment)
-      td.align = cellAlignment;
-
-    if (key == KEY_SOURCE_LOCATION) {
-      // Linkify the source column so it jumps to the source code. This doesn't
-      // take into account the particular code this build was compiled from, or
-      // local edits to source. It should however work correctly for top of tree
-      // builds.
-      var m = /^(.*) \[(\d+)\]$/.exec(text);
-      if (m) {
-        var filepath = m[1];
-        var filename = getFilenameFromPath(filepath);
-        var linenumber = m[2];
-
-        var link = addNode(td, 'a', filename + ' [' + linenumber + ']');
-
-        link.href = 'https://code.google.com/p/chromium/codesearch#search/&q=' +
-            encodeURIComponent(filename) + ':' + linenumber +
-            '&sq=package:chromium&type=cs';
-        link.target = '_blank';
-        return;
-      }
-    }
-
-    // String values can get pretty long. If the string contains no spaces, then
-    // CSS fails to wrap it, and it overflows the cell causing the table to get
-    // really big. We solve this using a hack: insert a <wbr> element after
-    // every single character. This will allow the rendering engine to wrap the
-    // value, and hence avoid it overflowing!
-    var kMinLengthBeforeWrap = 20;
-
-    addText(td, text.substr(0, kMinLengthBeforeWrap));
-    for (var i = kMinLengthBeforeWrap; i < text.length; ++i) {
-      addNode(td, 'wbr');
-      addText(td, text.substr(i, 1));
-    }
-  }
-
-  // --------------------------------------------------------------------------
-  // Helper code for handling the sort and grouping dropdowns.
-  // --------------------------------------------------------------------------
-
-  function addOptionsForGroupingSelect(select) {
-    // Add "no group" choice.
-    addNode(select, 'option', '---').value = '';
-
-    for (var i = 0; i < GROUPING_DROPDOWN_CHOICES.length; ++i) {
-      var key = GROUPING_DROPDOWN_CHOICES[i];
-      var option = addNode(select, 'option', getNameForKey(key));
-      option.value = key;
-    }
-  }
-
-  function addOptionsForSortingSelect(select) {
-    // Add "no sort" choice.
-    addNode(select, 'option', '---').value = '';
-
-    // Add a divider.
-    addNode(select, 'optgroup').label = '';
-
-    for (var i = 0; i < SORT_DROPDOWN_CHOICES.length; ++i) {
-      var key = SORT_DROPDOWN_CHOICES[i];
-      addNode(select, 'option', getNameForKey(key)).value = key;
-    }
-
-    // Add a divider.
-    addNode(select, 'optgroup').label = '';
-
-    // Add the same options, but for descending.
-    for (var i = 0; i < SORT_DROPDOWN_CHOICES.length; ++i) {
-      var key = SORT_DROPDOWN_CHOICES[i];
-      var n = addNode(select, 'option', getNameForKey(key) + ' (DESC)');
-      n.value = reverseSortKey(key);
-    }
-  }
-
-  /**
-   * Helper function used to update the sorting and grouping lists after a
-   * dropdown changes.
-   */
-  function updateKeyListFromDropdown(list, i, select) {
-    // Update the list.
-    if (i < list.length) {
-      list[i] = select.value;
-    } else {
-      list.push(select.value);
-    }
-
-    // Normalize the list, so setting 'none' as primary zeros out everything
-    // else.
-    for (var i = 0; i < list.length; ++i) {
-      if (list[i] == '') {
-        list.splice(i, list.length - i);
-        break;
-      }
-    }
-  }
-
-  /**
-   * Comparator for property |key|, having values |value1| and |value2|.
-   * If the key has defined a custom comparator use it. Otherwise use a
-   * default "less than" comparison.
-   */
-  function compareValuesForKey(key, value1, value2) {
-    var comparator = KEY_PROPERTIES[key].comparator;
-    if (comparator)
-      return comparator(value1, value2);
-    return simpleCompare(value1, value2);
-  }
-
-  function reverseSortKey(key) {
-    return -key;
-  }
-
-  function sortKeyIsReversed(key) {
-    return key < 0;
-  }
-
-  function sortKeysMatch(key1, key2) {
-    return Math.abs(key1) == Math.abs(key2);
-  }
-
-  function getKeysForCheckedBoxes(checkboxes) {
-    var keys = [];
-    for (var k in checkboxes) {
-      if (checkboxes[k].checked)
-        keys.push(k);
-    }
-    return keys;
-  }
-
-  // --------------------------------------------------------------------------
-
-  /**
-   * @constructor
-   */
-  function MainView() {
-    // Make sure we have a definition for each key.
-    for (var k = BEGIN_KEY; k < END_KEY; ++k) {
-      if (!KEY_PROPERTIES[k])
-        throw 'KEY_PROPERTIES[] not defined for key: ' + k;
-    }
-
-    this.init_();
-  }
-
-  MainView.prototype = {
-    addDataToSnapshot: function(data) {
-      // TODO(eroman): We need to know which snapshot this data belongs to!
-      // For now we assume it is the most recent snapshot.
-      var snapshotIndex = this.snapshots_.length - 1;
-
-      var snapshot = this.snapshots_[snapshotIndex];
-
-      var pid = data.process_id;
-      var ptype = data.process_type;
-
-      // Save the browser's representation of the data
-      snapshot.origData.push(data);
-
-      // Augment each data row with the process information.
-      var rows = data.list;
-      for (var i = 0; i < rows.length; ++i) {
-        // Transform the data from a dictionary to an array. This internal
-        // representation is more compact and faster to access.
-        var origRow = rows[i];
-        var newRow = [];
-
-        newRow[KEY_PROCESS_ID] = pid;
-        newRow[KEY_PROCESS_TYPE] = ptype;
-
-        // Copy over the known properties which have a 1:1 mapping with JSON.
-        for (var k = BEGIN_KEY; k < END_KEY; ++k) {
-          var inputJsonKey = KEY_PROPERTIES[k].inputJsonKey;
-          if (inputJsonKey != undefined) {
-            newRow[k] = getPropertyByPath(origRow, inputJsonKey);
-          }
-        }
-
-        if (newRow[KEY_COUNT] == 0) {
-          // When resetting the data, it is possible for the backend to give us
-          // counts of "0". There is no point adding these rows (in fact they
-          // will cause us to do divide by zeros when calculating averages and
-          // stuff), so we skip past them.
-          continue;
-        }
-
-        // Add our computed properties.
-        augmentDataRow(newRow);
-
-        snapshot.flatData.push(newRow);
-      }
-
-      if (!arrayToSet(this.getSelectedSnapshotIndexes_())[snapshotIndex]) {
-        // Optimization: If this snapshot is not a data dependency for the
-        // current display, then don't bother updating anything.
-        return;
-      }
-
-      // We may end up calling addDataToSnapshot_() repeatedly (once for each
-      // process). To avoid this from slowing us down we do bulk updates on a
-      // timer.
-      this.updateMergedDataSoon_();
-    },
-
-    updateMergedDataSoon_: function() {
-      if (this.updateMergedDataPending_) {
-        // If a delayed task has already been posted to re-merge the data,
-        // then we don't need to do anything extra.
-        return;
-      }
-
-      // Otherwise schedule updateMergedData_() to be called later. We want it
-      // to be called no more than once every PROCESS_DATA_DELAY_MS
-      // milliseconds.
-
-      if (this.lastUpdateMergedDataTime_ == undefined)
-        this.lastUpdateMergedDataTime_ = 0;
-
-      var timeSinceLastMerge = getTimeMillis() - this.lastUpdateMergedDataTime_;
-      var timeToWait = Math.max(0, PROCESS_DATA_DELAY_MS - timeSinceLastMerge);
-
-      var functionToRun = function() {
-        // Do the actual update.
-        this.updateMergedData_();
-        // Keep track of when we last ran.
-        this.lastUpdateMergedDataTime_ = getTimeMillis();
-        this.updateMergedDataPending_ = false;
-      }.bind(this);
-
-      this.updateMergedDataPending_ = true;
-      window.setTimeout(functionToRun, timeToWait);
-    },
-
-    /**
-     * Returns a list of the currently selected snapshots. This list is
-     * guaranteed to be of length 1 or 2.
-     */
-    getSelectedSnapshotIndexes_: function() {
-      var indexes = this.getSelectedSnapshotBoxes_();
-      for (var i = 0; i < indexes.length; ++i)
-        indexes[i] = indexes[i].__index;
-      return indexes;
-    },
-
-    /**
-     * Same as getSelectedSnapshotIndexes_(), only it returns the actual
-     * checkbox input DOM nodes rather than the snapshot ID.
-     */
-    getSelectedSnapshotBoxes_: function() {
-      // Figure out which snapshots to use for our data.
-      var boxes = [];
-      for (var i = 0; i < this.snapshots_.length; ++i) {
-        var box = this.getSnapshotCheckbox_(i);
-        if (box.checked)
-          boxes.push(box);
-      }
-      return boxes;
-    },
-
-    /**
-     * Re-draw the description that explains which snapshots are currently
-     * selected (if two snapshots were selected we explain that the *difference*
-     * between them is being displayed).
-     */
-    updateSnapshotSelectionSummaryDiv_: function() {
-      var summaryDiv = $(SNAPSHOT_SELECTION_SUMMARY_ID);
-
-      var selectedSnapshots = this.getSelectedSnapshotIndexes_();
-      if (selectedSnapshots.length == 0) {
-        // This can occur during an attempt to load a file or following file
-        // load failure.  We just ignore it and move on.
-      } else if (selectedSnapshots.length == 1) {
-        // If only one snapshot is chosen then we will display that snapshot's
-        // data in its entirety.
-        this.flatData_ = this.snapshots_[selectedSnapshots[0]].flatData;
-
-        // Don't bother displaying any text when just 1 snapshot is selected,
-        // since it is obvious what this should do.
-        summaryDiv.innerText = '';
-      } else if (selectedSnapshots.length == 2) {
-        // Otherwise if two snapshots were chosen, show the difference between
-        // them.
-        var snapshot1 = this.snapshots_[selectedSnapshots[0]];
-        var snapshot2 = this.snapshots_[selectedSnapshots[1]];
-
-        var timeDeltaInSeconds =
-            ((snapshot2.time - snapshot1.time) / 1000).toFixed(0);
-
-        // Explain that what is being shown is the difference between two
-        // snapshots.
-        summaryDiv.innerText = 'Showing the difference between snapshots #' +
-            selectedSnapshots[0] + ' and #' + selectedSnapshots[1] + ' (' +
-            timeDeltaInSeconds + ' seconds worth of data)';
-      } else {
-        // This shouldn't be possible...
-        throw 'Unexpected number of selected snapshots';
-      }
-    },
-
-    updateMergedData_: function() {
-      // Retrieve the merge options.
-      var mergeColumns = this.getMergeColumns_();
-      var shouldMergeSimilarThreads = this.shouldMergeSimilarThreads_();
-
-      var selectedSnapshots = this.getSelectedSnapshotIndexes_();
-
-      // We do merges a bit differently depending if we are displaying the diffs
-      // between two snapshots, or just displaying a single snapshot.
-      if (selectedSnapshots.length == 1) {
-        var snapshot = this.snapshots_[selectedSnapshots[0]];
-        this.mergedData_ = mergeRows(
-            snapshot.flatData, mergeColumns, shouldMergeSimilarThreads, false);
-
-      } else if (selectedSnapshots.length == 2) {
-        var snapshot1 = this.snapshots_[selectedSnapshots[0]];
-        var snapshot2 = this.snapshots_[selectedSnapshots[1]];
-
-        // Merge the data for snapshot1.
-        var mergedRows1 = mergeRows(
-            snapshot1.flatData, mergeColumns, shouldMergeSimilarThreads, true);
-
-        // Merge the data for snapshot2.
-        var mergedRows2 = mergeRows(
-            snapshot2.flatData, mergeColumns, shouldMergeSimilarThreads, true);
-
-        // Do a diff between the two snapshots.
-        this.mergedData_ =
-            subtractSnapshots(mergedRows1, mergedRows2, mergeColumns);
-      } else {
-        throw 'Unexpected number of selected snapshots';
-      }
-
-      // Recompute filteredData_ (since it is derived from mergedData_)
-      this.updateFilteredData_();
-    },
-
-    updateFilteredData_: function() {
-      // Recompute filteredData_.
-      this.filteredData_ = [];
-      var filterFunc = this.getFilterFunction_();
-      for (var i = 0; i < this.mergedData_.length; ++i) {
-        var r = this.mergedData_[i];
-        if (!filterFunc(r)) {
-          // Not matched by our filter, discard.
-          continue;
-        }
-        this.filteredData_.push(r);
-      }
-
-      // Recompute groupedData_ (since it is derived from filteredData_)
-      this.updateGroupedData_();
-    },
-
-    updateGroupedData_: function() {
-      // Recompute groupedData_.
-      var groupKeyToData = {};
-      var entryToGroupKeyFunc = this.getGroupingFunction_();
-      for (var i = 0; i < this.filteredData_.length; ++i) {
-        var r = this.filteredData_[i];
-
-        var groupKey = entryToGroupKeyFunc(r);
-
-        var groupData = groupKeyToData[groupKey];
-        if (!groupData) {
-          groupData = {
-            key: JSON.parse(groupKey),
-            aggregates: initializeAggregates(ALL_KEYS),
-            rows: [],
-          };
-          groupKeyToData[groupKey] = groupData;
-        }
-
-        // Add the row to our list.
-        groupData.rows.push(r);
-
-        // Update aggregates for each column.
-        consumeAggregates(groupData.aggregates, r);
-      }
-      this.groupedData_ = groupKeyToData;
-
-      // Figure out a display order for the groups themselves.
-      this.sortedGroupKeys_ = getDictionaryKeys(groupKeyToData);
-      this.sortedGroupKeys_.sort(this.getGroupSortingFunction_());
-
-      // Sort the group data.
-      this.sortGroupedData_();
-    },
-
-    sortGroupedData_: function() {
-      var sortingFunc = this.getSortingFunction_();
-      for (var k in this.groupedData_)
-        this.groupedData_[k].rows.sort(sortingFunc);
-
-      // Every cached data dependency is now up to date, all that is left is
-      // to actually draw the result.
-      this.redrawData_();
-    },
-
-    getVisibleColumnKeys_: function() {
-      // Figure out what columns to include, based on the selected checkboxes.
-      var columns = this.getSelectionColumns_();
-      columns = columns.slice(0);
-
-      // Eliminate columns which we are merging on.
-      deleteValuesFromArray(columns, this.getMergeColumns_());
-
-      // Eliminate columns which we are grouped on.
-      if (this.sortedGroupKeys_.length > 0) {
-        // The grouping will be the the same for each so just pick the first.
-        var randomGroupKey = this.groupedData_[this.sortedGroupKeys_[0]].key;
-
-        // The grouped properties are going to be the same for each row in our,
-        // table, so avoid drawing them in our table!
-        var keysToExclude = [];
-
-        for (var i = 0; i < randomGroupKey.length; ++i)
-          keysToExclude.push(randomGroupKey[i].key);
-        deleteValuesFromArray(columns, keysToExclude);
-      }
-
-      // If we are currently showing a "diff", hide the max columns, since we
-      // are not populating it correctly. See the TODO at the top of this file.
-      if (this.getSelectedSnapshotIndexes_().length > 1)
-        deleteValuesFromArray(columns, [KEY_MAX_RUN_TIME, KEY_MAX_QUEUE_TIME]);
-
-      return columns;
-    },
-
-    redrawData_: function() {
-      // Clear the results div, sine we may be overwriting older data.
-      var parent = $(RESULTS_DIV_ID);
-      parent.innerHTML = '';
-
-      var columns = this.getVisibleColumnKeys_();
-
-      // Draw each group.
-      for (var i = 0; i < this.sortedGroupKeys_.length; ++i) {
-        var k = this.sortedGroupKeys_[i];
-        this.drawGroup_(parent, k, columns);
-      }
-    },
-
-    /**
-     * Renders the information for a particular group.
-     */
-    drawGroup_: function(parent, groupKey, columns) {
-      var groupData = this.groupedData_[groupKey];
-
-      var div = addNode(parent, 'div');
-      div.className = 'group-container';
-
-      this.drawGroupTitle_(div, groupData.key);
-
-      var table = addNode(div, 'table');
-
-      this.drawDataTable_(table, groupData, columns, groupKey);
-    },
-
-    /**
-     * Draws a title into |parent| that describes |groupKey|.
-     */
-    drawGroupTitle_: function(parent, groupKey) {
-      if (groupKey.length == 0) {
-        // Empty group key means there was no grouping.
-        return;
-      }
-
-      var parent = addNode(parent, 'div');
-      parent.className = 'group-title-container';
-
-      // Each component of the group key represents the "key=value" constraint
-      // for this group. Show these as an AND separated list.
-      for (var i = 0; i < groupKey.length; ++i) {
-        if (i > 0)
-          addNode(parent, 'i', ' and ');
-        var e = groupKey[i];
-        addNode(parent, 'b', getNameForKey(e.key) + ' = ');
-        addNode(parent, 'span', e.value);
-      }
-    },
-
-    /**
-     * Renders a table which summarizes all |column| fields for |data|.
-     */
-    drawDataTable_: function(table, data, columns, groupKey) {
-      table.className = 'results-table';
-      var thead = addNode(table, 'thead');
-      var tbody = addNode(table, 'tbody');
-
-      var displaySettings = this.getGroupDisplaySettings_(groupKey);
-      var limit = displaySettings.limit;
-
-      this.drawAggregateRow_(thead, data.aggregates, columns);
-      this.drawTableHeader_(thead, columns);
-      this.drawTableBody_(tbody, data.rows, columns, limit);
-      this.drawTruncationRow_(
-          tbody, data.rows.length, limit, columns.length, groupKey);
-    },
-
-    drawTableHeader_: function(thead, columns) {
-      var tr = addNode(thead, 'tr');
-      for (var i = 0; i < columns.length; ++i) {
-        var key = columns[i];
-        var th = addNode(tr, 'th', getNameForKey(key));
-        th.onclick = this.onClickColumn_.bind(this, key);
-
-        // Draw an indicator if we are currently sorted on this column.
-        // TODO(eroman): Should use an icon instead of asterisk!
-        for (var j = 0; j < this.currentSortKeys_.length; ++j) {
-          if (sortKeysMatch(this.currentSortKeys_[j], key)) {
-            var sortIndicator = addNode(th, 'span', '*');
-            sortIndicator.style.color = 'red';
-            if (sortKeyIsReversed(this.currentSortKeys_[j])) {
-              // Use double-asterisk for descending columns.
-              addText(sortIndicator, '*');
-            }
-            break;
-          }
-        }
-      }
-    },
-
-    drawTableBody_: function(tbody, rows, columns, limit) {
-      for (var i = 0; i < rows.length && i < limit; ++i) {
-        var e = rows[i];
-
-        var tr = addNode(tbody, 'tr');
-
-        for (var c = 0; c < columns.length; ++c) {
-          var key = columns[c];
-          var value = e[key];
-
-          var td = addNode(tr, 'td');
-          drawValueToCell(td, key, value);
-        }
-      }
-    },
-
-    /**
-     * Renders a row that describes all the aggregate values for |columns|.
-     */
-    drawAggregateRow_: function(tbody, aggregates, columns) {
-      var tr = addNode(tbody, 'tr');
-      tr.className = 'aggregator-row';
-
-      for (var i = 0; i < columns.length; ++i) {
-        var key = columns[i];
-        var td = addNode(tr, 'td');
-
-        // Most of our outputs are numeric, so we want to align them to the
-        // right. However for the  unique counts we will center.
-        if (KEY_PROPERTIES[key].aggregator == UniquifyAggregator) {
-          td.align = 'center';
-        } else {
-          td.align = 'right';
-        }
-
-        var aggregator = aggregates[key];
-        if (aggregator)
-          td.innerText = aggregator.getValueAsText();
-      }
-    },
-
-    /**
-     * Renders a row which describes how many rows the table has, how many are
-     * currently hidden, and a set of buttons to show more.
-     */
-    drawTruncationRow_: function(tbody, numRows, limit, numColumns, groupKey) {
-      var numHiddenRows = Math.max(numRows - limit, 0);
-      var numVisibleRows = numRows - numHiddenRows;
-
-      var tr = addNode(tbody, 'tr');
-      tr.className = 'truncation-row';
-      var td = addNode(tr, 'td');
-      td.colSpan = numColumns;
-
-      addText(td, numRows + ' rows');
-      if (numHiddenRows > 0) {
-        var s = addNode(td, 'span', ' (' + numHiddenRows + ' hidden) ');
-        s.style.color = 'red';
-      }
-
-      if (numVisibleRows > LIMIT_INCREMENT) {
-        addNode(td, 'button', 'Show less').onclick =
-            this.changeGroupDisplayLimit_.bind(
-                this, groupKey, -LIMIT_INCREMENT);
-      }
-      if (numVisibleRows > 0) {
-        addNode(td, 'button', 'Show none').onclick =
-            this.changeGroupDisplayLimit_.bind(this, groupKey, -Infinity);
-      }
-
-      if (numHiddenRows > 0) {
-        addNode(td, 'button', 'Show more').onclick =
-            this.changeGroupDisplayLimit_.bind(this, groupKey, LIMIT_INCREMENT);
-        addNode(td, 'button', 'Show all').onclick =
-            this.changeGroupDisplayLimit_.bind(this, groupKey, Infinity);
-      }
-    },
-
-    /**
-     * Adjusts the row limit for group |groupKey| by |delta|.
-     */
-    changeGroupDisplayLimit_: function(groupKey, delta) {
-      // Get the current settings for this group.
-      var settings = this.getGroupDisplaySettings_(groupKey, true);
-
-      // Compute the adjusted limit.
-      var newLimit = settings.limit;
-      var totalNumRows = this.groupedData_[groupKey].rows.length;
-      newLimit = Math.min(totalNumRows, newLimit);
-      newLimit += delta;
-      newLimit = Math.max(0, newLimit);
-
-      // Update the settings with the new limit.
-      settings.limit = newLimit;
-
-      // TODO(eroman): It isn't necessary to redraw *all* the data. Really we
-      // just need to insert the missing rows (everything else stays the same)!
-      this.redrawData_();
-    },
-
-    /**
-     * Returns the rendering settings for group |groupKey|. This includes things
-     * like how many rows to display in the table.
-     */
-    getGroupDisplaySettings_: function(groupKey, opt_create) {
-      var settings = this.groupDisplaySettings_[groupKey];
-      if (!settings) {
-        // If we don't have any settings for this group yet, create some
-        // default ones.
-        if (groupKey == '[]') {
-          // (groupKey of '[]' is what we use for ungrouped data).
-          settings = {limit: INITIAL_UNGROUPED_ROW_LIMIT};
-        } else {
-          settings = {limit: INITIAL_GROUP_ROW_LIMIT};
-        }
-        if (opt_create)
-          this.groupDisplaySettings_[groupKey] = settings;
-      }
-      return settings;
-    },
-
-    init_: function() {
-      this.snapshots_ = [];
-
-      // Start fetching the data from the browser; this will be our snapshot #0.
-      this.takeSnapshot_();
-
-      // Data goes through the following pipeline:
-      // (1) Raw data received from browser, and transformed into our own
-      //     internal row format (where properties are indexed by KEY_*
-      //     constants.)
-      // (2) We "augment" each row by adding some extra computed columns
-      //     (like averages).
-      // (3) The rows are merged using current merge settings.
-      // (4) The rows that don't match current search expression are
-      //     tossed out.
-      // (5) The rows are organized into "groups" based on current settings,
-      //     and aggregate values are computed for each resulting group.
-      // (6) The rows within each group are sorted using current settings.
-      // (7) The grouped rows are drawn to the screen.
-      this.mergedData_ = [];
-      this.filteredData_ = [];
-      this.groupedData_ = {};
-      this.sortedGroupKeys_ = [];
-
-      this.groupDisplaySettings_ = {};
-
-      this.fillSelectionCheckboxes_($(COLUMN_TOGGLES_CONTAINER_ID));
-      this.fillMergeCheckboxes_($(COLUMN_MERGE_TOGGLES_CONTAINER_ID));
-
-      $(FILTER_SEARCH_ID).onsearch = this.onChangedFilter_.bind(this);
-
-      this.currentSortKeys_ = INITIAL_SORT_KEYS.slice(0);
-      this.currentGroupingKeys_ = INITIAL_GROUP_KEYS.slice(0);
-
-      this.fillGroupingDropdowns_();
-      this.fillSortingDropdowns_();
-
-      $(EDIT_COLUMNS_LINK_ID).onclick =
-          toggleNodeDisplay.bind(null, $(EDIT_COLUMNS_ROW));
-
-      $(TOGGLE_SNAPSHOTS_LINK_ID).onclick =
-          toggleNodeDisplay.bind(null, $(SNAPSHOTS_ROW));
-
-      $(MERGE_SIMILAR_THREADS_CHECKBOX_ID).onchange =
-          this.onMergeSimilarThreadsCheckboxChanged_.bind(this);
-
-      $(TAKE_SNAPSHOT_BUTTON_ID).onclick = this.takeSnapshot_.bind(this);
-
-      $(SAVE_SNAPSHOTS_BUTTON_ID).onclick = this.saveSnapshots_.bind(this);
-      $(SNAPSHOT_FILE_LOADER_ID).onchange = this.loadFileChanged_.bind(this);
-    },
-
-    takeSnapshot_: function() {
-      // Start a new empty snapshot. Make note of the current time, so we know
-      // when the snapshot was taken.
-      this.snapshots_.push({flatData: [], origData: [], time: getTimeMillis()});
-
-      // Update the UI to reflect the new snapshot.
-      this.addSnapshotToList_(this.snapshots_.length - 1);
-
-      // Ask the browser for the profiling data. We will receive the data
-      // later through a callback to addDataToSnapshot_().
-      g_browserBridge.sendGetData();
-    },
-
-    saveSnapshots_: function() {
-      var snapshots = [];
-      for (var i = 0; i < this.snapshots_.length; ++i) {
-        snapshots.push({
-          data: this.snapshots_[i].origData,
-          timestamp: Math.floor(this.snapshots_[i].time / 1000)
-        });
-      }
-
-      var dump = {
-        'userAgent': navigator.userAgent,
-        'version': 1,
-        'snapshots': snapshots
-      };
-
-      var dumpText = JSON.stringify(dump, null, ' ');
-      var textBlob =
-          new Blob([dumpText], {type: 'octet/stream', endings: 'native'});
-      var blobUrl = window.URL.createObjectURL(textBlob);
-      $(DOWNLOAD_ANCHOR_ID).href = blobUrl;
-      $(DOWNLOAD_ANCHOR_ID).click();
-    },
-
-    loadFileChanged_: function() {
-      this.loadSnapshots_($(SNAPSHOT_FILE_LOADER_ID).files[0]);
-    },
-
-    loadSnapshots_: function(file) {
-      if (file) {
-        var fileReader = new FileReader();
-
-        fileReader.onload = this.onLoadSnapshotsFile_.bind(this, file);
-        fileReader.onerror = this.onLoadSnapshotsFileError_.bind(this, file);
-
-        fileReader.readAsText(file);
-      }
-    },
-
-    onLoadSnapshotsFile_: function(file, event) {
-      try {
-        var parsed = null;
-        parsed = JSON.parse(event.target.result);
-
-        if (parsed.version != 1) {
-          throw new Error('Unrecognized version: ' + parsed.version);
-        }
-
-        if (parsed.snapshots.length < 1) {
-          throw new Error('File contains no data');
-        }
-
-        this.displayLoadedFile_(file, parsed);
-        this.hideFileLoadError_();
-      } catch (error) {
-        this.displayFileLoadError_('File load failure: ' + error.message);
-      }
-    },
-
-    clearExistingSnapshots_: function() {
-      var tbody = $('snapshots-tbody');
-      this.snapshots_ = [];
-      tbody.innerHTML = '';
-      this.updateMergedDataSoon_();
-    },
-
-    displayLoadedFile_: function(file, content) {
-      this.clearExistingSnapshots_();
-      $(TAKE_SNAPSHOT_BUTTON_ID).disabled = true;
-      $(SAVE_SNAPSHOTS_BUTTON_ID).disabled = true;
-
-      if (content.snapshots.length > 1) {
-        setNodeDisplay($(SNAPSHOTS_ROW), true);
-      }
-
-      for (var i = 0; i < content.snapshots.length; ++i) {
-        var snapshot = content.snapshots[i];
-        this.snapshots_.push(
-            {flatData: [], origData: [], time: snapshot.timestamp * 1000});
-        this.addSnapshotToList_(this.snapshots_.length - 1);
-        var snapshotData = snapshot.data;
-        for (var j = 0; j < snapshotData.length; ++j) {
-          this.addDataToSnapshot(snapshotData[j]);
-        }
-      }
-      this.redrawData_();
-    },
-
-    onLoadSnapshotsFileError_: function(file, filedata) {
-      this.displayFileLoadError_('Error loading ' + file.name);
-    },
-
-    displayFileLoadError_: function(message) {
-      $(LOAD_ERROR_ID).textContent = message;
-      $(LOAD_ERROR_ID).hidden = false;
-    },
-
-    hideFileLoadError_: function() {
-      $(LOAD_ERROR_ID).textContent = '';
-      $(LOAD_ERROR_ID).hidden = true;
-    },
-
-    getSnapshotCheckbox_: function(i) {
-      return $(this.getSnapshotCheckboxId_(i));
-    },
-
-    getSnapshotCheckboxId_: function(i) {
-      return 'snapshotCheckbox-' + i;
-    },
-
-    addSnapshotToList_: function(i) {
-      var tbody = $('snapshots-tbody');
-
-      var tr = addNode(tbody, 'tr');
-
-      var id = this.getSnapshotCheckboxId_(i);
-
-      var checkboxCell = addNode(tr, 'td');
-      var checkbox = addNode(checkboxCell, 'input');
-      checkbox.type = 'checkbox';
-      checkbox.id = id;
-      checkbox.__index = i;
-      checkbox.onclick = this.onSnapshotCheckboxChanged_.bind(this);
-
-      addNode(tr, 'td', '#' + i);
-
-      var labelCell = addNode(tr, 'td');
-      var l = addNode(labelCell, 'label');
-
-      var dateString = new Date(this.snapshots_[i].time).toLocaleString();
-      addText(l, dateString);
-      l.htmlFor = id;
-
-      // If we are on snapshot 0, make it the default.
-      if (i == 0) {
-        checkbox.checked = true;
-        checkbox.__time = getTimeMillis();
-        this.updateSnapshotCheckboxStyling_();
-      }
-    },
-
-    updateSnapshotCheckboxStyling_: function() {
-      for (var i = 0; i < this.snapshots_.length; ++i) {
-        var checkbox = this.getSnapshotCheckbox_(i);
-        checkbox.parentNode.parentNode.className =
-            checkbox.checked ? 'selected-snapshot' : '';
-      }
-    },
-
-    onSnapshotCheckboxChanged_: function(event) {
-      // Keep track of when we clicked this box (for when we need to uncheck
-      // older boxes).
-      event.target.__time = getTimeMillis();
-
-      // Find all the checked boxes. Either 1 or 2 can be checked. If a third
-      // was just checked, then uncheck one of the earlier ones so we only have
-      // 2.
-      var checked = this.getSelectedSnapshotBoxes_();
-      checked.sort(function(a, b) {
-        return b.__time - a.__time;
-      });
-      if (checked.length > 2) {
-        for (var i = 2; i < checked.length; ++i)
-          checked[i].checked = false;
-        checked.length = 2;
-      }
-
-      // We should always have at least 1 selection. Prevent the user from
-      // unselecting the final box.
-      if (checked.length == 0)
-        event.target.checked = true;
-
-      this.updateSnapshotCheckboxStyling_();
-      this.updateSnapshotSelectionSummaryDiv_();
-
-      // Recompute mergedData_ (since it is derived from selected snapshots).
-      this.updateMergedData_();
-    },
-
-    fillSelectionCheckboxes_: function(parent) {
-      this.selectionCheckboxes_ = {};
-
-      var onChangeFunc = this.onSelectCheckboxChanged_.bind(this);
-
-      for (var i = 0; i < ALL_TABLE_COLUMNS.length; ++i) {
-        var key = ALL_TABLE_COLUMNS[i];
-        var checkbox = addLabeledCheckbox(parent, getNameForKey(key));
-        checkbox.checked = true;
-        checkbox.onchange = onChangeFunc;
-        addText(parent, ' ');
-        this.selectionCheckboxes_[key] = checkbox;
-      }
-
-      for (var i = 0; i < INITIALLY_HIDDEN_KEYS.length; ++i) {
-        this.selectionCheckboxes_[INITIALLY_HIDDEN_KEYS[i]].checked = false;
-      }
-    },
-
-    getSelectionColumns_: function() {
-      return getKeysForCheckedBoxes(this.selectionCheckboxes_);
-    },
-
-    getMergeColumns_: function() {
-      return getKeysForCheckedBoxes(this.mergeCheckboxes_);
-    },
-
-    shouldMergeSimilarThreads_: function() {
-      return $(MERGE_SIMILAR_THREADS_CHECKBOX_ID).checked;
-    },
-
-    fillMergeCheckboxes_: function(parent) {
-      this.mergeCheckboxes_ = {};
-
-      var onChangeFunc = this.onMergeCheckboxChanged_.bind(this);
-
-      for (var i = 0; i < MERGEABLE_KEYS.length; ++i) {
-        var key = MERGEABLE_KEYS[i];
-        var checkbox = addLabeledCheckbox(parent, getNameForKey(key));
-        checkbox.onchange = onChangeFunc;
-        addText(parent, ' ');
-        this.mergeCheckboxes_[key] = checkbox;
-      }
-
-      for (var i = 0; i < INITIALLY_MERGED_KEYS.length; ++i) {
-        this.mergeCheckboxes_[INITIALLY_MERGED_KEYS[i]].checked = true;
-      }
-    },
-
-    fillGroupingDropdowns_: function() {
-      var parent = $(GROUP_BY_CONTAINER_ID);
-      parent.innerHTML = '';
-
-      for (var i = 0; i <= this.currentGroupingKeys_.length; ++i) {
-        // Add a dropdown.
-        var select = addNode(parent, 'select');
-        select.onchange = this.onChangedGrouping_.bind(this, select, i);
-
-        addOptionsForGroupingSelect(select);
-
-        if (i < this.currentGroupingKeys_.length) {
-          var key = this.currentGroupingKeys_[i];
-          setSelectedOptionByValue(select, key);
-        }
-      }
-    },
-
-    fillSortingDropdowns_: function() {
-      var parent = $(SORT_BY_CONTAINER_ID);
-      parent.innerHTML = '';
-
-      for (var i = 0; i <= this.currentSortKeys_.length; ++i) {
-        // Add a dropdown.
-        var select = addNode(parent, 'select');
-        select.onchange = this.onChangedSorting_.bind(this, select, i);
-
-        addOptionsForSortingSelect(select);
-
-        if (i < this.currentSortKeys_.length) {
-          var key = this.currentSortKeys_[i];
-          setSelectedOptionByValue(select, key);
-        }
-      }
-    },
-
-    onChangedGrouping_: function(select, i) {
-      updateKeyListFromDropdown(this.currentGroupingKeys_, i, select);
-      this.fillGroupingDropdowns_();
-      this.updateGroupedData_();
-    },
-
-    onChangedSorting_: function(select, i) {
-      updateKeyListFromDropdown(this.currentSortKeys_, i, select);
-      this.fillSortingDropdowns_();
-      this.sortGroupedData_();
-    },
-
-    onSelectCheckboxChanged_: function() {
-      this.redrawData_();
-    },
-
-    onMergeCheckboxChanged_: function() {
-      this.updateMergedData_();
-    },
-
-    onMergeSimilarThreadsCheckboxChanged_: function() {
-      this.updateMergedData_();
-    },
-
-    onChangedFilter_: function() {
-      this.updateFilteredData_();
-    },
-
-    /**
-     * When left-clicking a column, change the primary sort order to that
-     * column. If we were already sorted on that column then reverse the order.
-     *
-     * When alt-clicking, add a secondary sort column. Similarly, if
-     * alt-clicking a column which was already being sorted on, reverse its
-     * order.
-     */
-    onClickColumn_: function(key, event) {
-      // If this property wants to start off in descending order rather then
-      // ascending, flip it.
-      if (KEY_PROPERTIES[key].sortDescending)
-        key = reverseSortKey(key);
-
-      // Scan through our sort order and see if we are already sorted on this
-      // key. If so, reverse that sort ordering.
-      var foundIndex = -1;
-      for (var i = 0; i < this.currentSortKeys_.length; ++i) {
-        var curKey = this.currentSortKeys_[i];
-        if (sortKeysMatch(curKey, key)) {
-          this.currentSortKeys_[i] = reverseSortKey(curKey);
-          foundIndex = i;
-          break;
-        }
-      }
-
-      if (event.altKey) {
-        if (foundIndex == -1) {
-          // If we weren't already sorted on the column that was alt-clicked,
-          // then add it to our sort.
-          this.currentSortKeys_.push(key);
-        }
-      } else {
-        if (foundIndex != 0 ||
-            !sortKeysMatch(this.currentSortKeys_[foundIndex], key)) {
-          // If the column we left-clicked wasn't already our primary column,
-          // make it so.
-          this.currentSortKeys_ = [key];
-        } else {
-          // If the column we left-clicked was already our primary column (and
-          // we just reversed it), remove any secondary sorts.
-          this.currentSortKeys_.length = 1;
-        }
-      }
-
-      this.fillSortingDropdowns_();
-      this.sortGroupedData_();
-    },
-
-    getSortingFunction_: function() {
-      var sortKeys = this.currentSortKeys_.slice(0);
-
-      // Eliminate the empty string keys (which means they were unspecified).
-      deleteValuesFromArray(sortKeys, ['']);
-
-      // If no sort is specified, use our default sort.
-      if (sortKeys.length == 0)
-        sortKeys = [DEFAULT_SORT_KEYS];
-
-      return function(a, b) {
-        for (var i = 0; i < sortKeys.length; ++i) {
-          var key = Math.abs(sortKeys[i]);
-          var factor = sortKeys[i] < 0 ? -1 : 1;
-
-          var propA = a[key];
-          var propB = b[key];
-
-          var comparison = compareValuesForKey(key, propA, propB);
-          comparison *= factor;  // Possibly reverse the ordering.
-
-          if (comparison != 0)
-            return comparison;
-        }
-
-        // Tie breaker.
-        return simpleCompare(JSON.stringify(a), JSON.stringify(b));
-      };
-    },
-
-    getGroupSortingFunction_: function() {
-      return function(a, b) {
-        var groupKey1 = JSON.parse(a);
-        var groupKey2 = JSON.parse(b);
-
-        for (var i = 0; i < groupKey1.length; ++i) {
-          var comparison = compareValuesForKey(
-              groupKey1[i].key, groupKey1[i].value, groupKey2[i].value);
-
-          if (comparison != 0)
-            return comparison;
-        }
-
-        // Tie breaker.
-        return simpleCompare(a, b);
-      };
-    },
-
-    getFilterFunction_: function() {
-      var searchStr = $(FILTER_SEARCH_ID).value;
-
-      // Normalize the search expression.
-      searchStr = trimWhitespace(searchStr);
-      searchStr = searchStr.toLowerCase();
-
-      return function(x) {
-        // Match everything when there was no filter.
-        if (searchStr == '')
-          return true;
-
-        // Treat the search text as a LOWERCASE substring search.
-        for (var k = BEGIN_KEY; k < END_KEY; ++k) {
-          var propertyText = getTextValueForProperty(k, x[k]);
-          if (propertyText.toLowerCase().indexOf(searchStr) != -1)
-            return true;
-        }
-
-        return false;
-      };
-    },
-
-    getGroupingFunction_: function() {
-      var groupings = this.currentGroupingKeys_.slice(0);
-
-      // Eliminate the empty string groupings (which means they were
-      // unspecified).
-      deleteValuesFromArray(groupings, ['']);
-
-      // Eliminate duplicate primary/secondary group by directives, since they
-      // are redundant.
-      deleteDuplicateStringsFromArray(groupings);
-
-      return function(e) {
-        var groupKey = [];
-
-        for (var i = 0; i < groupings.length; ++i) {
-          var entry = {key: groupings[i], value: e[groupings[i]]};
-          groupKey.push(entry);
-        }
-
-        return JSON.stringify(groupKey);
-      };
-    },
-  };
-
-  return MainView;
-})();
diff --git a/chrome/browser/resources/settings/people_page/lock_screen.js b/chrome/browser/resources/settings/people_page/lock_screen.js
index b8d3d02..aaac86af 100644
--- a/chrome/browser/resources/settings/people_page/lock_screen.js
+++ b/chrome/browser/resources/settings/people_page/lock_screen.js
@@ -208,12 +208,14 @@
    * @protected
    */
   currentRouteChanged: function(newRoute, oldRoute) {
-    if (newRoute == settings.routes.LOCK_SCREEN &&
-        this.fingerprintUnlockEnabled_ && this.fingerprintBrowserProxy_) {
-      this.fingerprintBrowserProxy_.getNumFingerprints().then(
-          numFingerprints => {
-            this.numFingerprints_ = numFingerprints;
-          });
+    if (newRoute == settings.routes.LOCK_SCREEN) {
+      this.updateUnlockType();
+      if (this.fingerprintUnlockEnabled_ && this.fingerprintBrowserProxy_) {
+        this.fingerprintBrowserProxy_.getNumFingerprints().then(
+            numFingerprints => {
+              this.numFingerprints_ = numFingerprints;
+            });
+      }
     }
 
     if (this.shouldAskForPassword_(newRoute)) {
diff --git a/chrome/browser/resources/settings/people_page/lock_state_behavior.js b/chrome/browser/resources/settings/people_page/lock_state_behavior.js
index d40f58e5..b42fbee 100644
--- a/chrome/browser/resources/settings/people_page/lock_state_behavior.js
+++ b/chrome/browser/resources/settings/people_page/lock_state_behavior.js
@@ -42,11 +42,11 @@
 
   /** @override */
   attached: function() {
-    this.boundOnActiveModesChanged_ = this.updateUnlockType_.bind(this);
+    this.boundOnActiveModesChanged_ = this.updateUnlockType.bind(this);
     this.quickUnlockPrivate_.onActiveModesChanged.addListener(
         this.boundOnActiveModesChanged_);
 
-    this.updateUnlockType_();
+    this.updateUnlockType();
   },
 
   /** @override */
@@ -59,10 +59,8 @@
    * Updates the selected unlock type radio group. This function will get called
    * after preferences are initialized, after the quick unlock mode has been
    * changed, and after the lockscreen preference has changed.
-   *
-   * @private
    */
-  updateUnlockType_: function() {
+  updateUnlockType: function() {
     this.quickUnlockPrivate_.getActiveModes(modes => {
       if (modes.includes(chrome.quickUnlockPrivate.QuickUnlockMode.PIN)) {
         this.hasPin = true;
diff --git a/chrome/browser/resources/ssl/ssl_error_assistant/ssl_error_assistant.asciipb b/chrome/browser/resources/ssl/ssl_error_assistant/ssl_error_assistant.asciipb
index a02509a..67ac283 100644
--- a/chrome/browser/resources/ssl/ssl_error_assistant/ssl_error_assistant.asciipb
+++ b/chrome/browser/resources/ssl/ssl_error_assistant/ssl_error_assistant.asciipb
@@ -151,6 +151,17 @@
   sha256_hash: "sha256/LKtpdq9q7F7msGK0w1+b/gKoDHaQcZKTHIf9PTz2u+U="
 }
 
+# https://mitm-software.badssl.com leaf.
+# This is a test certificate, keep it at the top of the MITM software list.
+mitm_software {
+  name: "BadSSL Antivirus",
+  issuer_common_name_regex: "BadSSL MITM Software Test",
+  issuer_organization_regex: "BadSSL"
+}
+
+################################################################################
+# The rest of the MITM software certificates are sorted alphabetically by name.
+
 mitm_software {
   name: "Avast Antivirus",
   issuer_common_name_regex: "avast! Web/Mail Shield Root",
diff --git a/chrome/browser/signin/easy_unlock_app_manager_unittest.cc b/chrome/browser/signin/easy_unlock_app_manager_unittest.cc
index 0f78e02..c5ef5b7 100644
--- a/chrome/browser/signin/easy_unlock_app_manager_unittest.cc
+++ b/chrome/browser/signin/easy_unlock_app_manager_unittest.cc
@@ -10,6 +10,7 @@
 #include <string>
 
 #include "base/command_line.h"
+#include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
@@ -25,13 +26,12 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/proximity_auth/switches.h"
 #include "content/public/test/test_browser_thread_bundle.h"
-#include "extensions/browser/event_router.h"
-#include "extensions/browser/event_router_factory.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_registry_observer.h"
 #include "extensions/browser/process_manager.h"
 #include "extensions/browser/process_manager_factory.h"
+#include "extensions/browser/test_event_router.h"
 #include "extensions/common/api/app_runtime.h"
 #include "extensions/common/extension.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -132,36 +132,32 @@
 };
 
 // Consumes events dispatched from test event router.
-class EasyUnlockAppEventConsumer {
+class EasyUnlockAppEventConsumer
+    : public extensions::TestEventRouter::EventObserver {
  public:
-  explicit EasyUnlockAppEventConsumer(Profile* profile)
-      : user_updated_count_(0u),
-        auth_attempted_count_(0u),
-        app_launched_count_(0u) {}
+  EasyUnlockAppEventConsumer() = default;
+  ~EasyUnlockAppEventConsumer() override = default;
 
-  ~EasyUnlockAppEventConsumer() {}
-
-  // Processes event for test event router.
-  // It returns whether the event is expected to be dispatched during tests and
-  // whether it's well formed.
-  bool ConsumeEvent(const std::string& event_name, base::ListValue* args) {
-    if (event_name == easy_unlock_private_api::OnUserInfoUpdated::kEventName)
-      return ConsumeUserInfoUpdated(args);
-
-    if (event_name == screenlock_private_api::OnAuthAttempted::kEventName)
-      return ConsumeAuthAttempted(args);
-
-    if (event_name == app_runtime_api::OnLaunched::kEventName)
-      return ConsumeLaunched(args);
-
-    LOG(ERROR) << "Unexpected event: " << event_name;
-    return false;
+  // extensions::TestEventRouter::EventObserver:
+  void OnDispatchEventToExtension(const std::string& extension_id,
+                                  const extensions::Event& event) override {
+    if (event.event_name ==
+        easy_unlock_private_api::OnUserInfoUpdated::kEventName) {
+      ConsumeUserInfoUpdated(event.event_args.get());
+    } else if (event.event_name ==
+               screenlock_private_api::OnAuthAttempted::kEventName) {
+      ConsumeAuthAttempted(event.event_args.get());
+    } else {
+      ASSERT_EQ(app_runtime_api::OnLaunched::kEventName, event.event_name)
+          << "Unexpected event: " << event.event_name;
+    }
   }
 
-  // Information about encountered events:
-  size_t user_updated_count() const { return user_updated_count_; }
-  size_t auth_attempted_count() const { return auth_attempted_count_; }
-  size_t app_launched_count() const { return app_launched_count_; }
+  void OnBroadcastEvent(const extensions::Event& event) override {
+    ASSERT_EQ(screenlock_private_api::OnAuthAttempted::kEventName,
+              event.event_name);
+    ConsumeAuthAttempted(event.event_args.get());
+  }
 
   // The data carried by the last UserInfoUpdated event:
   std::string user_id() const { return user_id_; }
@@ -170,68 +166,41 @@
 
  private:
   // Processes easyUnlockPrivate.onUserInfoUpdated event.
-  bool ConsumeUserInfoUpdated(base::ListValue* args) {
+  void ConsumeUserInfoUpdated(base::ListValue* args) {
     if (!args) {
-      LOG(ERROR) << "No argument list for onUserInfoUpdated event.";
-      return false;
+      ADD_FAILURE() << "No argument list for onUserInfoUpdated event.";
+      return;
     }
 
     if (args->GetSize() != 1u) {
-      LOG(ERROR) << "Invalid argument list size for onUserInfoUpdated event: "
-                 << args->GetSize() << " expected: " << 1u;
-      return false;
+      ADD_FAILURE()
+          << "Invalid argument list size for onUserInfoUpdated event: "
+          << args->GetSize() << " expected: " << 1u;
+      return;
     }
 
     base::DictionaryValue* user_info;
     if (!args->GetDictionary(0u, &user_info) || !user_info) {
-      LOG(ERROR) << "Unabled to get event argument as dictionary for "
-                 << "onUserInfoUpdated event.";
-      return false;
+      ADD_FAILURE() << "Unabled to get event argument as dictionary for "
+                    << "onUserInfoUpdated event.";
+      return;
     }
 
     EXPECT_TRUE(user_info->GetString("userId", &user_id_));
     EXPECT_TRUE(user_info->GetBoolean("loggedIn", &user_logged_in_));
     EXPECT_TRUE(user_info->GetBoolean("dataReady", &user_data_ready_));
-
-    ++user_updated_count_;
-    return true;
   }
 
   // Processes screenlockPrivate.onAuthAttempted event.
-  bool ConsumeAuthAttempted(base::ListValue* args) {
-    if (!args) {
-      LOG(ERROR) << "No argument list for onAuthAttempted event";
-      return false;
-    }
-
-    if (args->GetSize() != 2u) {
-      LOG(ERROR) << "Invalid argument list size for onAuthAttempted event: "
-                 << args->GetSize() << " expected: " << 2u;
-      return false;
-    }
+  void ConsumeAuthAttempted(base::ListValue* args) {
+    ASSERT_TRUE(args);
+    ASSERT_EQ(2u, args->GetSize());
 
     std::string auth_type;
-    if (!args->GetString(0u, &auth_type)) {
-      LOG(ERROR) << "Unable to get first argument as string for "
-                 << "onAuthAttempted event.";
-      return false;
-    }
-
+    ASSERT_TRUE(args->GetString(0u, &auth_type));
     EXPECT_EQ("userClick", auth_type);
-    ++auth_attempted_count_;
-    return true;
   }
 
-  // Processes app.runtime.onLaunched event.
-  bool ConsumeLaunched(base::ListValue* args) {
-    ++app_launched_count_;
-    return true;
-  }
-
-  size_t user_updated_count_;
-  size_t auth_attempted_count_;
-  size_t app_launched_count_;
-
   std::string user_id_;
   bool user_logged_in_;
   bool user_data_ready_;
@@ -239,53 +208,9 @@
   DISALLOW_COPY_AND_ASSIGN(EasyUnlockAppEventConsumer);
 };
 
-// Event router injected into extension system for the tests. It redirects
-// events to EasyUnlockAppEventConsumer.
-class TestEventRouter : public extensions::EventRouter {
- public:
-  TestEventRouter(Profile* profile, extensions::ExtensionPrefs* extension_prefs)
-      : extensions::EventRouter(profile, extension_prefs) {}
-
-  ~TestEventRouter() override {}
-
-  // extensions::EventRouter implementation:
-  void BroadcastEvent(std::unique_ptr<extensions::Event> event) override {
-    ASSERT_EQ(screenlock_private_api::OnAuthAttempted::kEventName,
-              event->event_name);
-    EXPECT_TRUE(event_consumer_->ConsumeEvent(event->event_name,
-                                              event->event_args.get()));
-  }
-
-  void DispatchEventToExtension(
-      const std::string& extension_id,
-      std::unique_ptr<extensions::Event> event) override {
-    ASSERT_EQ(extension_misc::kEasyUnlockAppId, extension_id);
-    EXPECT_TRUE(event_consumer_->ConsumeEvent(event->event_name,
-                                              event->event_args.get()));
-  }
-
-  void set_event_consumer(EasyUnlockAppEventConsumer* event_consumer) {
-    event_consumer_ = event_consumer;
-  }
-
- private:
-  EasyUnlockAppEventConsumer* event_consumer_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestEventRouter);
-};
-
-// TestEventRouter factory function
-std::unique_ptr<KeyedService> TestEventRouterFactoryFunction(
-    content::BrowserContext* context) {
-  return base::MakeUnique<TestEventRouter>(
-      static_cast<Profile*>(context), extensions::ExtensionPrefs::Get(context));
-}
-
 class EasyUnlockAppManagerTest : public testing::Test {
  public:
-  EasyUnlockAppManagerTest()
-      : event_consumer_(&profile_),
-        command_line_(base::CommandLine::NO_PROGRAM) {}
+  EasyUnlockAppManagerTest() : command_line_(base::CommandLine::NO_PROGRAM) {}
   ~EasyUnlockAppManagerTest() override {}
 
   void SetUp() override {
@@ -311,6 +236,21 @@
         .AppendASCII("easy_unlock");
   }
 
+  int UserUpdatedCount() const {
+    return event_router_->GetEventCount(
+        easy_unlock_private_api::OnUserInfoUpdated::kEventName);
+  }
+
+  int AuthAttemptedCount() const {
+    return event_router_->GetEventCount(
+        screenlock_private_api::OnAuthAttempted::kEventName);
+  }
+
+  int AppLaunchedCount() const {
+    return event_router_->GetEventCount(
+        app_runtime_api::OnLaunched::kEventName);
+  }
+
  private:
   // Initializes test extension system.
   extensions::ExtensionSystem* SetUpExtensionSystem() {
@@ -326,10 +266,9 @@
     extensions::ScreenlockPrivateEventRouter::GetFactoryInstance()
         ->SetTestingFactory(&profile_, &CreateScreenlockPrivateEventRouter);
 
-    event_router_ = static_cast<TestEventRouter*>(
-        extensions::EventRouterFactory::GetInstance()->SetTestingFactoryAndUse(
-            &profile_, &TestEventRouterFactoryFunction));
-    event_router_->set_event_consumer(&event_consumer_);
+    event_router_ = extensions::CreateAndUseTestEventRouter(&profile_);
+    event_router_->AddEventObserver(&event_consumer_);
+    event_router_->set_expected_extension_id(extension_misc::kEasyUnlockAppId);
 
     extension_service_->component_loader()->
         set_ignore_whitelist_for_testing(true);
@@ -355,7 +294,7 @@
 
   EasyUnlockAppEventConsumer event_consumer_;
   ExtensionService* extension_service_;
-  TestEventRouter* event_router_;
+  extensions::TestEventRouter* event_router_;
 
   base::CommandLine command_line_;
 
@@ -506,35 +445,35 @@
 TEST_F(EasyUnlockAppManagerTest, LaunchSetup) {
   SetExtensionSystemReady();
 
-  ASSERT_EQ(0u, event_consumer_.app_launched_count());
+  ASSERT_EQ(0, AppLaunchedCount());
 
   app_manager_->LoadApp();
   app_manager_->LaunchSetup();
 
-  EXPECT_EQ(1u, event_consumer_.app_launched_count());
+  EXPECT_EQ(1, AppLaunchedCount());
 }
 
 TEST_F(EasyUnlockAppManagerTest, LaunchSetupWhenDisabled) {
   SetExtensionSystemReady();
 
-  ASSERT_EQ(0u, event_consumer_.app_launched_count());
+  ASSERT_EQ(0, AppLaunchedCount());
 
   app_manager_->LoadApp();
   app_manager_->DisableAppIfLoaded();
 
   app_manager_->LaunchSetup();
 
-  EXPECT_EQ(0u, event_consumer_.app_launched_count());
+  EXPECT_EQ(0, AppLaunchedCount());
 }
 
 TEST_F(EasyUnlockAppManagerTest, LaunchSetupWhenNotLoaded) {
   SetExtensionSystemReady();
 
-  ASSERT_EQ(0u, event_consumer_.app_launched_count());
+  ASSERT_EQ(0, AppLaunchedCount());
 
   app_manager_->LaunchSetup();
 
-  EXPECT_EQ(0u, event_consumer_.app_launched_count());
+  EXPECT_EQ(0, AppLaunchedCount());
 }
 
 TEST_F(EasyUnlockAppManagerTest, SendUserUpdated) {
@@ -545,12 +484,12 @@
       easy_unlock_private_api::OnUserInfoUpdated::kEventName,
       extension_misc::kEasyUnlockAppId);
 
-  ASSERT_EQ(0u, event_consumer_.user_updated_count());
+  ASSERT_EQ(0, UserUpdatedCount());
 
   EXPECT_TRUE(app_manager_->SendUserUpdatedEvent("user", true /* logged_in */,
                                                  false /* data_ready */));
 
-  EXPECT_EQ(1u, event_consumer_.user_updated_count());
+  EXPECT_EQ(1, UserUpdatedCount());
 
   EXPECT_EQ("user", event_consumer_.user_id());
   EXPECT_TRUE(event_consumer_.user_logged_in());
@@ -565,12 +504,12 @@
       easy_unlock_private_api::OnUserInfoUpdated::kEventName,
       extension_misc::kEasyUnlockAppId);
 
-  ASSERT_EQ(0u, event_consumer_.user_updated_count());
+  ASSERT_EQ(0, UserUpdatedCount());
 
   EXPECT_TRUE(app_manager_->SendUserUpdatedEvent("user", false /* logged_in */,
                                                  true /* data_ready */));
 
-  EXPECT_EQ(1u, event_consumer_.user_updated_count());
+  EXPECT_EQ(1, UserUpdatedCount());
 
   EXPECT_EQ("user", event_consumer_.user_id());
   EXPECT_FALSE(event_consumer_.user_logged_in());
@@ -582,10 +521,10 @@
 
   app_manager_->LoadApp();
 
-  ASSERT_EQ(0u, event_consumer_.user_updated_count());
+  ASSERT_EQ(0, UserUpdatedCount());
 
   EXPECT_FALSE(app_manager_->SendUserUpdatedEvent("user", true, true));
-  EXPECT_EQ(0u, event_consumer_.user_updated_count());
+  EXPECT_EQ(0, UserUpdatedCount());
 }
 
 TEST_F(EasyUnlockAppManagerTest, SendUserUpdatedAppDisabled) {
@@ -597,10 +536,10 @@
       extension_misc::kEasyUnlockAppId);
   app_manager_->DisableAppIfLoaded();
 
-  ASSERT_EQ(0u, event_consumer_.user_updated_count());
+  ASSERT_EQ(0, UserUpdatedCount());
 
   EXPECT_FALSE(app_manager_->SendUserUpdatedEvent("user", true, true));
-  EXPECT_EQ(0u, event_consumer_.user_updated_count());
+  EXPECT_EQ(0, UserUpdatedCount());
 }
 
 TEST_F(EasyUnlockAppManagerTest, SendAuthAttempted) {
@@ -611,10 +550,10 @@
       screenlock_private_api::OnAuthAttempted::kEventName,
       extension_misc::kEasyUnlockAppId);
 
-  ASSERT_EQ(0u, event_consumer_.user_updated_count());
+  ASSERT_EQ(0, UserUpdatedCount());
 
   EXPECT_TRUE(app_manager_->SendAuthAttemptEvent());
-  EXPECT_EQ(1u, event_consumer_.auth_attempted_count());
+  EXPECT_EQ(1, AuthAttemptedCount());
 }
 
 TEST_F(EasyUnlockAppManagerTest, SendAuthAttemptedNoRegisteredListeners) {
@@ -622,10 +561,10 @@
 
   app_manager_->LoadApp();
 
-  ASSERT_EQ(0u, event_consumer_.auth_attempted_count());
+  ASSERT_EQ(0, AuthAttemptedCount());
 
   EXPECT_FALSE(app_manager_->SendAuthAttemptEvent());
-  EXPECT_EQ(0u, event_consumer_.auth_attempted_count());
+  EXPECT_EQ(0, AuthAttemptedCount());
 }
 
 TEST_F(EasyUnlockAppManagerTest, SendAuthAttemptedAppDisabled) {
@@ -637,10 +576,10 @@
       extension_misc::kEasyUnlockAppId);
   app_manager_->DisableAppIfLoaded();
 
-  ASSERT_EQ(0u, event_consumer_.auth_attempted_count());
+  ASSERT_EQ(0, AuthAttemptedCount());
 
   EXPECT_FALSE(app_manager_->SendAuthAttemptEvent());
-  EXPECT_EQ(0u, event_consumer_.auth_attempted_count());
+  EXPECT_EQ(0, AuthAttemptedCount());
 }
 
 }  // namespace
diff --git a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.cc b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.cc
index b5d2a8a..29e314d 100644
--- a/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.cc
+++ b/chrome/browser/signin/mutable_profile_oauth2_token_service_delegate.cc
@@ -11,7 +11,6 @@
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "base/profiler/scoped_tracker.h"
 #include "components/signin/core/browser/signin_client.h"
 #include "components/signin/core/browser/signin_metrics.h"
 #include "components/signin/core/browser/webdata/token_web_data.h"
@@ -327,12 +326,6 @@
           << (result.get() == nullptr ? -1
                                       : static_cast<int>(result->GetType()));
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 MutableProfileOAuth2Token...::OnWebDataServiceRequestDone"));
-
   DCHECK_EQ(web_data_service_request_, handle);
   web_data_service_request_ = 0;
 
diff --git a/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc b/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
index 488d0835..a6dd60a 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
@@ -2052,9 +2052,18 @@
 }
 
 // Tests that the security level of a HTTP page is NEUTRAL when MarkHttpAs is
-// not set.
+// not set to either kMarkHttpAsNonSecureWhileIncognito or
+// kMarkHttpAsNonSecureWhileIncognitoOrEditing.
 IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperIncognitoTest,
                        SecurityLevelNeutralByDefaultForHTTP) {
+  // We must explicitly specify a configuration using the command-line
+  // argument or this test can fail based on the values inside the
+  // fieldtrial_testing_config.json file.
+  base::test::ScopedCommandLine scoped_command_line;
+  scoped_command_line.GetProcessCommandLine()->AppendSwitchASCII(
+      security_state::switches::kMarkHttpAs,
+      security_state::switches::kMarkHttpAsNonSecureAfterEditing);
+
   content::WebContents* contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   ASSERT_TRUE(contents);
diff --git a/chrome/browser/task_profiler/OWNERS b/chrome/browser/task_profiler/OWNERS
deleted file mode 100644
index 8b39a540..0000000
--- a/chrome/browser/task_profiler/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-jar@chromium.org
diff --git a/chrome/browser/task_profiler/task_profiler_data_serializer.cc b/chrome/browser/task_profiler/task_profiler_data_serializer.cc
deleted file mode 100644
index 56f36e60..0000000
--- a/chrome/browser/task_profiler/task_profiler_data_serializer.cc
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/task_profiler/task_profiler_data_serializer.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/json/json_string_value_serializer.h"
-#include "base/time/time.h"
-#include "base/tracked_objects.h"
-#include "base/values.h"
-#include "chrome/common/chrome_content_client.h"
-#include "components/nacl/common/nacl_process_type.h"
-#include "content/public/common/process_type.h"
-#include "url/gurl.h"
-
-using base::DictionaryValue;
-using base::ListValue;
-using base::Value;
-using tracked_objects::BirthOnThreadSnapshot;
-using tracked_objects::DeathDataSnapshot;
-using tracked_objects::LocationSnapshot;
-using tracked_objects::TaskSnapshot;
-using tracked_objects::ProcessDataPhaseSnapshot;
-
-namespace {
-
-// Re-serializes the |location| into |dictionary|.
-void LocationSnapshotToValue(const LocationSnapshot& location,
-                             base::DictionaryValue* dictionary) {
-  dictionary->SetString("file_name", location.file_name);
-  // Note: This function name is not escaped, and templates have less-than
-  // characters, which means this is not suitable for display as HTML unless
-  // properly escaped.
-  dictionary->SetString("function_name", location.function_name);
-  dictionary->SetInteger("line_number", location.line_number);
-}
-
-// Re-serializes the |birth| into |dictionary|.  Prepends the |prefix| to the
-// "thread" and "location" key names in the dictionary.
-void BirthOnThreadSnapshotToValue(const BirthOnThreadSnapshot& birth,
-                                  const std::string& prefix,
-                                  base::DictionaryValue* dictionary) {
-  DCHECK(!prefix.empty());
-
-  std::unique_ptr<base::DictionaryValue> location_value(
-      new base::DictionaryValue);
-  LocationSnapshotToValue(birth.location, location_value.get());
-  dictionary->Set(prefix + "_location", std::move(location_value));
-
-  dictionary->SetString(prefix + "_thread", birth.sanitized_thread_name);
-}
-
-// Re-serializes the |death_data| into |dictionary|.
-void DeathDataSnapshotToValue(const DeathDataSnapshot& death_data,
-                              base::DictionaryValue* dictionary) {
-  dictionary->SetInteger("count", death_data.count);
-  dictionary->SetInteger("run_ms", death_data.run_duration_sum);
-  dictionary->SetInteger("run_ms_max", death_data.run_duration_max);
-  dictionary->SetInteger("run_ms_sample", death_data.run_duration_sample);
-  dictionary->SetInteger("queue_ms", death_data.queue_duration_sum);
-  dictionary->SetInteger("queue_ms_max", death_data.queue_duration_max);
-  dictionary->SetInteger("queue_ms_sample", death_data.queue_duration_sample);
-
-  dictionary->SetInteger("alloc_ops", death_data.alloc_ops);
-  dictionary->SetInteger("free_ops", death_data.free_ops);
-  // The byte counts are 64 bit integers, pass them through as doubles, as
-  // integer values truncate to 32 bits.
-  dictionary->SetDouble("allocated_bytes", death_data.allocated_bytes);
-  dictionary->SetDouble("freed_bytes", death_data.freed_bytes);
-  dictionary->SetDouble("alloc_overhead_bytes",
-                        death_data.alloc_overhead_bytes);
-  dictionary->SetInteger("max_allocated_bytes", death_data.max_allocated_bytes);
-}
-
-// Re-serializes the |snapshot| into |dictionary|.
-void TaskSnapshotToValue(const TaskSnapshot& snapshot,
-                         base::DictionaryValue* dictionary) {
-  BirthOnThreadSnapshotToValue(snapshot.birth, "birth", dictionary);
-
-  std::unique_ptr<base::DictionaryValue> death_data(new base::DictionaryValue);
-  DeathDataSnapshotToValue(snapshot.death_data, death_data.get());
-  dictionary->Set("death_data", std::move(death_data));
-
-  dictionary->SetString("death_thread", snapshot.death_sanitized_thread_name);
-}
-
-int AsChromeProcessType(
-    metrics::ProfilerEventProto::TrackedObject::ProcessType process_type) {
-  switch (process_type) {
-    case metrics::ProfilerEventProto::TrackedObject::UNKNOWN:
-    case metrics::ProfilerEventProto::TrackedObject::PLUGIN:
-      return content::PROCESS_TYPE_UNKNOWN;
-    case metrics::ProfilerEventProto::TrackedObject::BROWSER:
-      return content::PROCESS_TYPE_BROWSER;
-    case metrics::ProfilerEventProto::TrackedObject::RENDERER:
-      return content::PROCESS_TYPE_RENDERER;
-    case metrics::ProfilerEventProto::TrackedObject::WORKER:
-      return content::PROCESS_TYPE_WORKER_DEPRECATED;
-    case metrics::ProfilerEventProto::TrackedObject::NACL_LOADER:
-      return PROCESS_TYPE_NACL_LOADER;
-    case metrics::ProfilerEventProto::TrackedObject::UTILITY:
-      return content::PROCESS_TYPE_UTILITY;
-    case metrics::ProfilerEventProto::TrackedObject::PROFILE_IMPORT:
-      return content::PROCESS_TYPE_UNKNOWN;
-    case metrics::ProfilerEventProto::TrackedObject::ZYGOTE:
-      return content::PROCESS_TYPE_ZYGOTE;
-    case metrics::ProfilerEventProto::TrackedObject::SANDBOX_HELPER:
-      return content::PROCESS_TYPE_SANDBOX_HELPER;
-    case metrics::ProfilerEventProto::TrackedObject::NACL_BROKER:
-      return PROCESS_TYPE_NACL_BROKER;
-    case metrics::ProfilerEventProto::TrackedObject::GPU:
-      return content::PROCESS_TYPE_GPU;
-    case metrics::ProfilerEventProto::TrackedObject::PPAPI_PLUGIN:
-      return content::PROCESS_TYPE_PPAPI_PLUGIN;
-    case metrics::ProfilerEventProto::TrackedObject::PPAPI_BROKER:
-      return content::PROCESS_TYPE_PPAPI_BROKER;
-  }
-  NOTREACHED();
-  return content::PROCESS_TYPE_UNKNOWN;
-}
-
-}  // anonymous namespace
-
-namespace task_profiler {
-
-// static
-void TaskProfilerDataSerializer::ToValue(
-    const ProcessDataPhaseSnapshot& process_data_phase,
-    base::ProcessId process_id,
-    metrics::ProfilerEventProto::TrackedObject::ProcessType process_type,
-    base::DictionaryValue* dictionary) {
-  std::unique_ptr<base::ListValue> tasks_list(new base::ListValue);
-  for (const auto& task : process_data_phase.tasks) {
-    std::unique_ptr<base::DictionaryValue> snapshot(new base::DictionaryValue);
-    TaskSnapshotToValue(task, snapshot.get());
-    tasks_list->Append(std::move(snapshot));
-  }
-  dictionary->Set("list", std::move(tasks_list));
-
-  dictionary->SetInteger("process_id", process_id);
-  dictionary->SetString("process_type", content::GetProcessTypeNameInEnglish(
-                                            AsChromeProcessType(process_type)));
-}
-
-}  // namespace task_profiler
diff --git a/chrome/browser/task_profiler/task_profiler_data_serializer.h b/chrome/browser/task_profiler/task_profiler_data_serializer.h
deleted file mode 100644
index 458c2bc..0000000
--- a/chrome/browser/task_profiler/task_profiler_data_serializer.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_TASK_PROFILER_TASK_PROFILER_DATA_SERIALIZER_H_
-#define CHROME_BROWSER_TASK_PROFILER_TASK_PROFILER_DATA_SERIALIZER_H_
-
-#include "base/macros.h"
-#include "base/process/process_handle.h"
-#include "components/metrics/proto/profiler_event.pb.h"
-
-namespace base {
-class DictionaryValue;
-}
-
-namespace tracked_objects {
-struct ProcessDataPhaseSnapshot;
-}
-
-namespace task_profiler {
-
-// This class collects task profiler data and serializes it to a file.  The file
-// format is compatible with the about:profiler UI.
-class TaskProfilerDataSerializer {
- public:
-  TaskProfilerDataSerializer() {}
-
-  // Writes the contents of |process_data_phase|, |process_id| and
-  // |process_type| into |dictionary|.
-  static void ToValue(
-      const tracked_objects::ProcessDataPhaseSnapshot& process_data_phase,
-      base::ProcessId process_id,
-      metrics::ProfilerEventProto::TrackedObject::ProcessType process_type,
-      base::DictionaryValue* dictionary);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TaskProfilerDataSerializer);
-};
-
-}  // namespace task_profiler
-
-#endif  // CHROME_BROWSER_TASK_PROFILER_TASK_PROFILER_DATA_SERIALIZER_H_
diff --git a/chrome/browser/task_profiler/task_profiler_data_serializer_unittest.cc b/chrome/browser/task_profiler/task_profiler_data_serializer_unittest.cc
deleted file mode 100644
index c97ff6b..0000000
--- a/chrome/browser/task_profiler/task_profiler_data_serializer_unittest.cc
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string>
-
-#include "base/json/json_writer.h"
-#include "base/process/process_handle.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/tracked_objects.h"
-#include "base/values.h"
-#include "chrome/browser/task_profiler/task_profiler_data_serializer.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-void ExpectSerialization(
-    const tracked_objects::ProcessDataPhaseSnapshot& process_data_phase,
-    base::ProcessId process_id,
-    metrics::ProfilerEventProto::TrackedObject::ProcessType process_type,
-    const std::string& expected_json) {
-  base::DictionaryValue serialized_value;
-  task_profiler::TaskProfilerDataSerializer::ToValue(
-      process_data_phase, process_id, process_type, &serialized_value);
-
-  std::string serialized_json;
-  base::JSONWriter::Write(serialized_value, &serialized_json);
-
-  EXPECT_EQ(expected_json, serialized_json);
-}
-
-}  // anonymous namespace
-
-// Tests the JSON serialization format for profiled process data.
-TEST(TaskProfilerDataSerializerTest, SerializeProcessDataToJson) {
-  {
-    // Empty data.
-    tracked_objects::ProcessDataPhaseSnapshot process_data_phase;
-    ExpectSerialization(process_data_phase, 239,
-                        metrics::ProfilerEventProto::TrackedObject::BROWSER,
-                        "{"
-                        "\"list\":["
-                        "],"
-                        "\"process_id\":239,"
-                        "\"process_type\":\"Browser\""
-                        "}");
-  }
-
-  {
-    // Non-empty data.
-    tracked_objects::ProcessDataPhaseSnapshot process_data_phase;
-
-    tracked_objects::BirthOnThreadSnapshot parent;
-    parent.location.file_name = "path/to/foo.cc";
-    parent.location.function_name = "WhizBang";
-    parent.location.line_number = 101;
-    parent.sanitized_thread_name = "CrBrowserMain";
-
-    tracked_objects::BirthOnThreadSnapshot child;
-    child.location.file_name = "path/to/bar.cc";
-    child.location.function_name = "FizzBoom";
-    child.location.line_number = 433;
-    child.sanitized_thread_name = "Chrome_IOThread";
-
-    // Add a snapshot.
-    process_data_phase.tasks.push_back(tracked_objects::TaskSnapshot());
-    process_data_phase.tasks.back().birth = parent;
-    process_data_phase.tasks.back().death_data.count = 37;
-    process_data_phase.tasks.back().death_data.run_duration_max = 5;
-    process_data_phase.tasks.back().death_data.run_duration_sample = 3;
-    process_data_phase.tasks.back().death_data.run_duration_sum = 17;
-    process_data_phase.tasks.back().death_data.queue_duration_max = 53;
-    process_data_phase.tasks.back().death_data.queue_duration_sample = 13;
-    process_data_phase.tasks.back().death_data.queue_duration_sum = 79;
-    process_data_phase.tasks.back().death_data.alloc_ops = 127;
-    process_data_phase.tasks.back().death_data.free_ops = 120;
-    process_data_phase.tasks.back().death_data.allocated_bytes = 2013;
-    process_data_phase.tasks.back().death_data.freed_bytes = 1092;
-    process_data_phase.tasks.back().death_data.alloc_overhead_bytes = 201;
-    process_data_phase.tasks.back().death_data.max_allocated_bytes = 1500;
-    process_data_phase.tasks.back().death_sanitized_thread_name =
-        "WorkerPool/-1340960768";
-
-    // Add a second snapshot.
-    process_data_phase.tasks.push_back(tracked_objects::TaskSnapshot());
-    process_data_phase.tasks.back().birth = child;
-    process_data_phase.tasks.back().death_data.count = 41;
-    process_data_phase.tasks.back().death_data.run_duration_max = 205;
-    process_data_phase.tasks.back().death_data.run_duration_sample = 203;
-    process_data_phase.tasks.back().death_data.run_duration_sum = 2017;
-    process_data_phase.tasks.back().death_data.queue_duration_max = 2053;
-    process_data_phase.tasks.back().death_data.queue_duration_sample = 2013;
-    process_data_phase.tasks.back().death_data.queue_duration_sum = 2079;
-    process_data_phase.tasks.back().death_data.alloc_ops = 1207;
-    process_data_phase.tasks.back().death_data.free_ops = 1200;
-    process_data_phase.tasks.back().death_data.allocated_bytes = 20013;
-    process_data_phase.tasks.back().death_data.freed_bytes = 10092;
-    process_data_phase.tasks.back().death_data.alloc_overhead_bytes = 2001;
-    process_data_phase.tasks.back().death_data.max_allocated_bytes = 15000;
-    process_data_phase.tasks.back().death_sanitized_thread_name =
-        "PAC thread #3";
-
-    ExpectSerialization(process_data_phase, 239,
-                        metrics::ProfilerEventProto::TrackedObject::RENDERER,
-                        "{"
-                        "\"list\":[{"
-                        "\"birth_location\":{"
-                        "\"file_name\":\"path/to/foo.cc\","
-                        "\"function_name\":\"WhizBang\","
-                        "\"line_number\":101"
-                        "},"
-                        "\"birth_thread\":\"CrBrowserMain\","
-                        "\"death_data\":{"
-                        "\"alloc_ops\":127,"
-                        "\"alloc_overhead_bytes\":201.0,"
-                        "\"allocated_bytes\":2013.0,"
-                        "\"count\":37,"
-                        "\"free_ops\":120,"
-                        "\"freed_bytes\":1092.0,"
-                        "\"max_allocated_bytes\":1500,"
-                        "\"queue_ms\":79,"
-                        "\"queue_ms_max\":53,"
-                        "\"queue_ms_sample\":13,"
-                        "\"run_ms\":17,"
-                        "\"run_ms_max\":5,"
-                        "\"run_ms_sample\":3"
-                        "},"
-                        "\"death_thread\":\"WorkerPool/-1340960768\""
-                        "},{"
-                        "\"birth_location\":{"
-                        "\"file_name\":\"path/to/bar.cc\","
-                        "\"function_name\":\"FizzBoom\","
-                        "\"line_number\":433"
-                        "},"
-                        "\"birth_thread\":\"Chrome_IOThread\","
-                        "\"death_data\":{"
-                        "\"alloc_ops\":1207,"
-                        "\"alloc_overhead_bytes\":2001.0,"
-                        "\"allocated_bytes\":20013.0,"
-                        "\"count\":41,"
-                        "\"free_ops\":1200,"
-                        "\"freed_bytes\":10092.0,"
-                        "\"max_allocated_bytes\":15000,"
-                        "\"queue_ms\":2079,"
-                        "\"queue_ms_max\":2053,"
-                        "\"queue_ms_sample\":2013,"
-                        "\"run_ms\":2017,"
-                        "\"run_ms_max\":205,"
-                        "\"run_ms_sample\":203"
-                        "},"
-                        "\"death_thread\":\"PAC thread #3\""
-                        "}],"
-                        "\"process_id\":239,"
-                        "\"process_type\":\"Tab\""
-                        "}");
-  }
-}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index ee575e1..b3d1de3 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -398,8 +398,6 @@
     "webui/predictors/predictors_ui.h",
     "webui/prefs_internals_source.cc",
     "webui/prefs_internals_source.h",
-    "webui/profiler_ui.cc",
-    "webui/profiler_ui.h",
     "webui/quota_internals/quota_internals_handler.cc",
     "webui/quota_internals/quota_internals_handler.h",
     "webui/quota_internals/quota_internals_proxy.cc",
@@ -1320,8 +1318,6 @@
       "ash/media_client.h",
       "ash/multi_user/multi_user_context_menu.h",
       "ash/multi_user/multi_user_context_menu_chromeos.cc",
-      "ash/multi_user/multi_user_notification_blocker_chromeos.cc",
-      "ash/multi_user/multi_user_notification_blocker_chromeos.h",
       "ash/multi_user/multi_user_util.cc",
       "ash/multi_user/multi_user_util.h",
       "ash/multi_user/multi_user_warning_dialog.cc",
diff --git a/chrome/browser/ui/android/ssl_client_certificate_request.cc b/chrome/browser/ui/android/ssl_client_certificate_request.cc
index 8e6f9893..3c37a9f 100644
--- a/chrome/browser/ui/android/ssl_client_certificate_request.cc
+++ b/chrome/browser/ui/android/ssl_client_certificate_request.cc
@@ -183,8 +183,12 @@
     net::SSLCertRequestInfo* cert_request_info,
     net::ClientCertIdentityList unused_client_certs,
     std::unique_ptr<content::ClientCertificateDelegate> delegate) {
+  // TODO(asimjour): This should be removed once we have proper
+  // implementation of SSL client certificate selector in VR.
   if (vr::VrTabHelper::IsInVr(contents)) {
     delegate->ContinueWithCertificate(nullptr, nullptr);
+    vr::VrTabHelper::UISuppressed(
+        vr::UiSuppressedElement::kSslClientCertificate);
     return;
   }
 
diff --git a/chrome/browser/ui/android/usb_chooser_dialog_android.cc b/chrome/browser/ui/android/usb_chooser_dialog_android.cc
index 22a4988..81255e9a 100644
--- a/chrome/browser/ui/android/usb_chooser_dialog_android.cc
+++ b/chrome/browser/ui/android/usb_chooser_dialog_android.cc
@@ -62,9 +62,13 @@
       weak_factory_(this) {
   content::WebContents* web_contents =
       content::WebContents::FromRenderFrameHost(render_frame_host_);
+
+  // TODO(asimjour): This should be removed once we have proper
+  // implementation of USB chooser in VR.
   if (vr::VrTabHelper::IsInVr(web_contents)) {
     DCHECK(!callback_.is_null());
     std::move(callback_).Run(nullptr);
+    vr::VrTabHelper::UISuppressed(vr::UiSuppressedElement::kUsbChooser);
     return;
   }
 
diff --git a/chrome/browser/ui/app_list/app_list_view_delegate.cc b/chrome/browser/ui/app_list/app_list_view_delegate.cc
index 5d8c0b4..64b8caa 100644
--- a/chrome/browser/ui/app_list/app_list_view_delegate.cc
+++ b/chrome/browser/ui/app_list/app_list_view_delegate.cc
@@ -12,7 +12,6 @@
 #include "base/command_line.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
-#include "base/profiler/scoped_tracker.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile.h"
@@ -148,14 +147,8 @@
 
 #if defined(GOOGLE_CHROME_BUILD)
   gfx::ImageSkia* image;
-  {
-    // TODO(tapted): Remove ScopedTracker below once crbug.com/431326 is fixed.
-    tracked_objects::ScopedTracker tracking_profile(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION("431326 GetImageSkiaNamed()"));
-    image = ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
-        IDR_APP_LIST_GOOGLE_LOGO_VOICE_SEARCH);
-  }
-
+  image = ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
+      IDR_APP_LIST_GOOGLE_LOGO_VOICE_SEARCH);
   speech_ui_->set_logo(*image);
 #endif
 
@@ -212,31 +205,24 @@
   DCHECK(!profile_->IsGuestSession() || profile_->IsOffTheRecord())
       << "Guest mode must use incognito profile";
 
-  {
-    // TODO(tapted): Remove ScopedTracker below once crbug.com/431326 is fixed.
-    tracked_objects::ScopedTracker tracking_profile(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "431326 AppListViewDelegate TemplateURL etc."));
-    TemplateURLService* template_url_service =
-        TemplateURLServiceFactory::GetForProfile(profile_);
-    template_url_service_observer_.Add(template_url_service);
+  TemplateURLService* template_url_service =
+      TemplateURLServiceFactory::GetForProfile(profile_);
+  template_url_service_observer_.Add(template_url_service);
 
-    model_ = app_list::AppListSyncableServiceFactory::GetForProfile(profile_)
-                 ->GetModel();
+  model_ = app_list::AppListSyncableServiceFactory::GetForProfile(profile_)
+               ->GetModel();
 
-    // After |model_| is initialized, make a GetWallpaperColors mojo call to set
-    // wallpaper colors for |model_|.
-    wallpaper_controller_ptr_->GetWallpaperColors(
-        base::Bind(&AppListViewDelegate::OnGetWallpaperColorsCallback,
-                   weak_ptr_factory_.GetWeakPtr()));
+  // After |model_| is initialized, make a GetWallpaperColors mojo call to set
+  // wallpaper colors for |model_|.
+  wallpaper_controller_ptr_->GetWallpaperColors(
+      base::Bind(&AppListViewDelegate::OnGetWallpaperColorsCallback,
+                 weak_ptr_factory_.GetWeakPtr()));
 
-    app_sync_ui_state_watcher_.reset(
-        new AppSyncUIStateWatcher(profile_, model_));
+  app_sync_ui_state_watcher_.reset(new AppSyncUIStateWatcher(profile_, model_));
 
-    SetUpSearchUI();
-    SetUpCustomLauncherPages();
-    OnTemplateURLServiceChanged();
-  }
+  SetUpSearchUI();
+  SetUpCustomLauncherPages();
+  OnTemplateURLServiceChanged();
 
   // Clear search query.
   model_->search_box()->Update(base::string16(), false);
diff --git a/chrome/browser/ui/ash/app_list/app_list_presenter_service.cc b/chrome/browser/ui/ash/app_list/app_list_presenter_service.cc
index f456a0e5..8bc6c86 100644
--- a/chrome/browser/ui/ash/app_list/app_list_presenter_service.cc
+++ b/chrome/browser/ui/ash/app_list/app_list_presenter_service.cc
@@ -79,6 +79,10 @@
   GetPresenter()->EndDragFromShelf(app_list_state);
 }
 
+void AppListPresenterService::ProcessMouseWheelOffset(int y_scroll_offset) {
+  GetPresenter()->ProcessMouseWheelOffset(y_scroll_offset);
+}
+
 app_list::AppListPresenterImpl* AppListPresenterService::GetPresenter() {
   return AppListServiceAsh::GetInstance()->GetAppListPresenter();
 }
diff --git a/chrome/browser/ui/ash/app_list/app_list_presenter_service.h b/chrome/browser/ui/ash/app_list/app_list_presenter_service.h
index a373849..5aa204c 100644
--- a/chrome/browser/ui/ash/app_list/app_list_presenter_service.h
+++ b/chrome/browser/ui/ash/app_list/app_list_presenter_service.h
@@ -31,6 +31,7 @@
   void UpdateYPositionAndOpacity(int y_position_in_screen,
                                  float background_opacity) override;
   void EndDragFromShelf(app_list::mojom::AppListState app_list_state) override;
+  void ProcessMouseWheelOffset(int y_scroll_offset) override;
 
  private:
   app_list::AppListPresenterImpl* GetPresenter();
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.cc b/chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.cc
deleted file mode 100644
index 9f4ac11c..0000000
--- a/chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h"
-
-#include "ash/shell.h"
-#include "ash/shell_delegate.h"
-#include "ash/system/system_notifier.h"
-#include "components/signin/core/account_id/account_id.h"
-#include "ui/message_center/message_center.h"
-#include "ui/message_center/notifier_settings.h"
-
-MultiUserNotificationBlockerChromeOS::MultiUserNotificationBlockerChromeOS(
-    message_center::MessageCenter* message_center,
-    const AccountId& initial_account_id)
-    : NotificationBlocker(message_center),
-      active_account_id_(initial_account_id) {}
-
-MultiUserNotificationBlockerChromeOS::~MultiUserNotificationBlockerChromeOS() {
-}
-
-bool MultiUserNotificationBlockerChromeOS::ShouldShowNotification(
-    const message_center::Notification& notification) const {
-  if (!ash::Shell::HasInstance() ||
-      !ash::Shell::Get()->shell_delegate()->IsMultiProfilesEnabled())
-    return true;
-
-  if (ash::system_notifier::IsAshSystemNotifier(notification.notifier_id()))
-    return true;
-
-  return AccountId::FromUserEmail(notification.notifier_id().profile_id) ==
-         active_account_id_;
-}
-
-bool MultiUserNotificationBlockerChromeOS::ShouldShowNotificationAsPopup(
-    const message_center::Notification& notification) const {
-  return ShouldShowNotification(notification);
-}
-
-void MultiUserNotificationBlockerChromeOS::ActiveUserChanged(
-    const AccountId& account_id) {
-  if (active_account_id_ == account_id)
-    return;
-
-  quiet_modes_[active_account_id_] = message_center()->IsQuietMode();
-  active_account_id_ = account_id;
-  std::map<AccountId, bool>::const_iterator iter =
-      quiet_modes_.find(active_account_id_);
-  if (iter != quiet_modes_.end() &&
-      iter->second != message_center()->IsQuietMode()) {
-    message_center()->SetQuietMode(iter->second);
-  }
-  NotifyBlockingStateChanged();
-}
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h b/chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h
deleted file mode 100644
index 7456c4a2..0000000
--- a/chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_ASH_MULTI_USER_MULTI_USER_NOTIFICATION_BLOCKER_CHROMEOS_H_
-#define CHROME_BROWSER_UI_ASH_MULTI_USER_MULTI_USER_NOTIFICATION_BLOCKER_CHROMEOS_H_
-
-#include <map>
-#include <set>
-#include <string>
-
-#include "base/macros.h"
-#include "components/signin/core/account_id/account_id.h"
-#include "ui/message_center/notification_blocker.h"
-
-// A notification blocker for per-profile stream switching. Owned and controlled
-// by MultiUserWindowManagerChromeOS.
-class MultiUserNotificationBlockerChromeOS
-    : public message_center::NotificationBlocker {
- public:
-  MultiUserNotificationBlockerChromeOS(
-      message_center::MessageCenter* message_center,
-      const AccountId& initial_account_id);
-  ~MultiUserNotificationBlockerChromeOS() override;
-
-  // Called by MultiUserWindowManager when the active user has changed.
-  void ActiveUserChanged(const AccountId& account_id);
-
-  // message_center::NotificationBlocker overrides:
-  bool ShouldShowNotification(
-      const message_center::Notification& notification) const override;
-  bool ShouldShowNotificationAsPopup(
-      const message_center::Notification& notification) const override;
-
- private:
-  AccountId active_account_id_;
-  std::map<AccountId, bool> quiet_modes_;
-
-  DISALLOW_COPY_AND_ASSIGN(MultiUserNotificationBlockerChromeOS);
-};
-
-#endif  // CHROME_BROWSER_UI_ASH_MULTI_USER_MULTI_USER_NOTIFICATION_BLOCKER_CHROMEOS_H_
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos_unittest.cc b/chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos_unittest.cc
deleted file mode 100644
index 9a39c6b..0000000
--- a/chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos_unittest.cc
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h"
-
-#include "ash/shell.h"
-#include "ash/system/system_notifier.h"
-#include "ash/test/ash_test_base.h"
-#include "ash/test_shell_delegate.h"
-#include "base/macros.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
-#include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
-#include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
-#include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "chrome/test/base/testing_profile_manager.h"
-#include "components/signin/core/account_id/account_id.h"
-#include "ui/message_center/message_center.h"
-#include "ui/message_center/notification.h"
-
-using base::UTF8ToUTF16;
-
-class MultiUserNotificationBlockerChromeOSTest
-    : public ash::AshTestBase,
-      public message_center::NotificationBlocker::Observer {
- public:
-  MultiUserNotificationBlockerChromeOSTest()
-      : state_changed_count_(0),
-        testing_profile_manager_(TestingBrowserProcess::GetGlobal()),
-        window_id_(0),
-        fake_user_manager_(new chromeos::FakeChromeUserManager),
-        user_manager_enabler_(fake_user_manager_) {}
-  ~MultiUserNotificationBlockerChromeOSTest() override {}
-
-  // ash::AshTestBase overrides:
-  void SetUp() override {
-    ash::AshTestBase::SetUp();
-    ASSERT_TRUE(testing_profile_manager_.SetUp());
-
-    // MultiUserWindowManager is initialized after the log in.
-    testing_profile_manager_.CreateTestingProfile(GetDefaultUserId());
-    fake_user_manager_->AddUser(AccountId::FromUserEmail(GetDefaultUserId()));
-
-    ash::TestShellDelegate* shell_delegate =
-        static_cast<ash::TestShellDelegate*>(
-            ash::Shell::Get()->shell_delegate());
-    shell_delegate->set_multi_profiles_enabled(true);
-    chrome::MultiUserWindowManager::CreateInstance();
-
-    const AccountId test2_account_id =
-        AccountId::FromUserEmail("test2@example.com");
-    fake_user_manager_->AddUser(test2_account_id);
-
-    chromeos::WallpaperManager::Initialize();
-
-    // Disable any animations for the test.
-    GetMultiUserWindowManager()->SetAnimationSpeedForTest(
-        chrome::MultiUserWindowManagerChromeOS::ANIMATION_SPEED_DISABLED);
-    GetMultiUserWindowManager()->notification_blocker_->AddObserver(this);
-  }
-
-  void TearDown() override {
-    GetMultiUserWindowManager()->notification_blocker_->RemoveObserver(this);
-    if (chrome::MultiUserWindowManager::GetInstance())
-      chrome::MultiUserWindowManager::DeleteInstance();
-    ash::AshTestBase::TearDown();
-    chromeos::WallpaperManager::Shutdown();
-  }
-
-  // message_center::NotificationBlocker::Observer ovverrides:
-  void OnBlockingStateChanged(
-      message_center::NotificationBlocker* blocker) override {
-    state_changed_count_++;
-  }
-
- protected:
-  chrome::MultiUserWindowManagerChromeOS* GetMultiUserWindowManager() {
-    return static_cast<chrome::MultiUserWindowManagerChromeOS*>(
-        chrome::MultiUserWindowManager::GetInstance());
-  }
-
-  const std::string GetDefaultUserId() { return "test1@example.com"; }
-
-  const message_center::NotificationBlocker* blocker() {
-    return GetMultiUserWindowManager()->notification_blocker_.get();
-  }
-
-  void CreateProfile(const std::string& name) {
-    testing_profile_manager_.CreateTestingProfile(name);
-  }
-
-  void SwitchActiveUser(const std::string& name) {
-    const AccountId account_id(AccountId::FromUserEmail(name));
-    if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
-        chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_ON) {
-      static_cast<chrome::MultiUserWindowManagerChromeOS*>(
-          chrome::MultiUserWindowManager::GetInstance())
-          ->ActiveUserChanged(fake_user_manager_->FindUser(account_id));
-    }
-  }
-
-  int GetStateChangedCountAndReset() {
-    int result = state_changed_count_;
-    state_changed_count_ = 0;
-    return result;
-  }
-
-  bool ShouldShowNotificationAsPopup(
-      const message_center::NotifierId& notifier_id,
-      const std::string profile_id) {
-    message_center::NotifierId id_with_profile = notifier_id;
-    id_with_profile.profile_id = profile_id;
-
-    message_center::Notification notification(
-        message_center::NOTIFICATION_TYPE_SIMPLE, "popup-id",
-        UTF8ToUTF16("popup-title"), UTF8ToUTF16("popup-message"), gfx::Image(),
-        UTF8ToUTF16("popup-source"), GURL(), id_with_profile,
-        message_center::RichNotificationData(), nullptr);
-
-    return blocker()->ShouldShowNotificationAsPopup(notification);
-  }
-
-  bool ShouldShowNotification(
-      const message_center::NotifierId& notifier_id,
-      const std::string profile_id) {
-    message_center::NotifierId id_with_profile = notifier_id;
-    id_with_profile.profile_id = profile_id;
-
-    message_center::Notification notification(
-        message_center::NOTIFICATION_TYPE_SIMPLE, "notification-id",
-        UTF8ToUTF16("notification-title"), UTF8ToUTF16("notification-message"),
-        gfx::Image(), UTF8ToUTF16("notification-source"), GURL(),
-        id_with_profile, message_center::RichNotificationData(), nullptr);
-
-    return blocker()->ShouldShowNotification(notification);
-  }
-
-  aura::Window* CreateWindowForProfile(const std::string& name) {
-    aura::Window* window = CreateTestWindowInShellWithId(window_id_++);
-    chrome::MultiUserWindowManager::GetInstance()->SetWindowOwner(
-        window, AccountId::FromUserEmail(name));
-    return window;
-  }
-
- private:
-  int state_changed_count_;
-  TestingProfileManager testing_profile_manager_;
-  int window_id_;
-
-  // Owned by ScopedUserManagerEnabler.
-  chromeos::FakeChromeUserManager* fake_user_manager_ = nullptr;
-  chromeos::ScopedUserManagerEnabler user_manager_enabler_;
-
-  DISALLOW_COPY_AND_ASSIGN(MultiUserNotificationBlockerChromeOSTest);
-};
-
-TEST_F(MultiUserNotificationBlockerChromeOSTest, Basic) {
-  ASSERT_EQ(chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_ON,
-            chrome::MultiUserWindowManager::GetMultiProfileMode());
-
-  message_center::NotifierId notifier_id(
-      message_center::NotifierId::APPLICATION, "test-app");
-  // Only allowed the system notifier.
-  message_center::NotifierId ash_system_notifier(
-      message_center::NotifierId::SYSTEM_COMPONENT,
-      ash::system_notifier::kNotifierDisplay);
-  // Other system notifiers should be treated as same as a normal notifier.
-  message_center::NotifierId random_system_notifier(
-      message_center::NotifierId::SYSTEM_COMPONENT, "random_system_component");
-
-  EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id, ""));
-  EXPECT_TRUE(ShouldShowNotificationAsPopup(ash_system_notifier, ""));
-  EXPECT_FALSE(ShouldShowNotificationAsPopup(random_system_notifier, ""));
-  EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id, GetDefaultUserId()));
-  EXPECT_FALSE(ShouldShowNotification(notifier_id, ""));
-  EXPECT_TRUE(ShouldShowNotification(ash_system_notifier, ""));
-  EXPECT_FALSE(ShouldShowNotification(random_system_notifier, ""));
-  EXPECT_TRUE(ShouldShowNotification(notifier_id, GetDefaultUserId()));
-  EXPECT_TRUE(ShouldShowNotification(random_system_notifier,
-                                     GetDefaultUserId()));
-
-  CreateProfile("test2@example.com");
-  EXPECT_EQ(0, GetStateChangedCountAndReset());
-  EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id, ""));
-  EXPECT_TRUE(ShouldShowNotificationAsPopup(ash_system_notifier, ""));
-  EXPECT_FALSE(ShouldShowNotificationAsPopup(random_system_notifier, ""));
-  EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id, GetDefaultUserId()));
-  EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id, "test2@example.com"));
-  EXPECT_TRUE(ShouldShowNotificationAsPopup(random_system_notifier,
-                                            GetDefaultUserId()));
-  EXPECT_FALSE(ShouldShowNotificationAsPopup(random_system_notifier,
-                                             "test2@example.com"));
-  EXPECT_FALSE(ShouldShowNotification(notifier_id, ""));
-  EXPECT_TRUE(ShouldShowNotification(ash_system_notifier, ""));
-  EXPECT_FALSE(ShouldShowNotification(random_system_notifier, ""));
-  EXPECT_TRUE(ShouldShowNotification(notifier_id, GetDefaultUserId()));
-  EXPECT_FALSE(ShouldShowNotification(notifier_id, "test2@example.com"));
-  EXPECT_TRUE(ShouldShowNotification(random_system_notifier,
-                                     GetDefaultUserId()));
-  EXPECT_FALSE(ShouldShowNotification(random_system_notifier,
-                                      "test2@example.com"));
-
-  SwitchActiveUser("test2@example.com");
-  EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id, ""));
-  EXPECT_TRUE(ShouldShowNotificationAsPopup(ash_system_notifier, ""));
-  EXPECT_FALSE(ShouldShowNotificationAsPopup(random_system_notifier, ""));
-  EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id, GetDefaultUserId()));
-  EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id, "test2@example.com"));
-  EXPECT_FALSE(ShouldShowNotificationAsPopup(random_system_notifier,
-                                             GetDefaultUserId()));
-  EXPECT_TRUE(ShouldShowNotificationAsPopup(random_system_notifier,
-                                            "test2@example.com"));
-  EXPECT_FALSE(ShouldShowNotification(notifier_id, ""));
-  EXPECT_TRUE(ShouldShowNotification(ash_system_notifier, ""));
-  EXPECT_FALSE(ShouldShowNotification(random_system_notifier, ""));
-  EXPECT_FALSE(ShouldShowNotification(notifier_id, GetDefaultUserId()));
-  EXPECT_TRUE(ShouldShowNotification(notifier_id, "test2@example.com"));
-  EXPECT_FALSE(ShouldShowNotification(random_system_notifier,
-                                      GetDefaultUserId()));
-  EXPECT_TRUE(ShouldShowNotification(random_system_notifier,
-                                     "test2@example.com"));
-
-  SwitchActiveUser(GetDefaultUserId());
-  EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id, ""));
-  EXPECT_TRUE(ShouldShowNotificationAsPopup(ash_system_notifier, ""));
-  EXPECT_FALSE(ShouldShowNotificationAsPopup(random_system_notifier, ""));
-  EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id, GetDefaultUserId()));
-  EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id, "test2@example.com"));
-  EXPECT_TRUE(ShouldShowNotificationAsPopup(random_system_notifier,
-                                            GetDefaultUserId()));
-  EXPECT_FALSE(ShouldShowNotificationAsPopup(random_system_notifier,
-                                             "test2@example.com"));
-  EXPECT_FALSE(ShouldShowNotification(notifier_id, ""));
-  EXPECT_TRUE(ShouldShowNotification(ash_system_notifier, ""));
-  EXPECT_FALSE(ShouldShowNotification(random_system_notifier, ""));
-  EXPECT_TRUE(ShouldShowNotification(notifier_id, GetDefaultUserId()));
-  EXPECT_FALSE(ShouldShowNotification(notifier_id, "test2@example.com"));
-  EXPECT_TRUE(ShouldShowNotification(random_system_notifier,
-                                     GetDefaultUserId()));
-  EXPECT_FALSE(ShouldShowNotification(random_system_notifier,
-                                      "test2@example.com"));
-}
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc
index 28a27ae..20cd200b 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc
@@ -21,7 +21,6 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h"
 #include "chrome/browser/ui/ash/session_controller_client.h"
@@ -180,9 +179,6 @@
 MultiUserWindowManagerChromeOS::MultiUserWindowManagerChromeOS(
     const AccountId& current_account_id)
     : current_account_id_(current_account_id),
-      notification_blocker_(new MultiUserNotificationBlockerChromeOS(
-          message_center::MessageCenter::Get(),
-          current_account_id)),
       suppress_visibility_changes_(false),
       animation_speed_(ANIMATION_SPEED_NORMAL) {}
 
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h
index db262fb..5609370f 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h
@@ -21,8 +21,6 @@
 #include "ui/wm/core/transient_window_observer.h"
 
 class Browser;
-class MultiUserNotificationBlockerChromeOS;
-class MultiUserNotificationBlockerChromeOSTest;
 
 namespace content {
 class BrowserContext;
@@ -181,12 +179,8 @@
   void NotifyAfterUserSwitchAnimationFinished();
 
   const WindowToEntryMap& window_to_entry() { return window_to_entry_; }
-  MultiUserNotificationBlockerChromeOS* notification_blocker() {
-    return notification_blocker_.get();
-  }
 
  private:
-  friend class ::MultiUserNotificationBlockerChromeOSTest;
   friend class ash::MultiUserWindowManagerChromeOSTest;
 
   typedef std::map<AccountId, AppObserver*> AccountIdToAppWindowObserver;
@@ -242,10 +236,6 @@
   // being read from the user manager to be in sync while a switch occurs.
   AccountId current_account_id_;
 
-  // The blocker which controls the desktop notification visibility based on the
-  // current multi-user status.
-  std::unique_ptr<MultiUserNotificationBlockerChromeOS> notification_blocker_;
-
   // The notification registrar to track the creation of browser windows.
   content::NotificationRegistrar registrar_;
 
diff --git a/chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.cc b/chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.cc
index 7f50e97..acd26b5b 100644
--- a/chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.cc
+++ b/chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.cc
@@ -17,7 +17,6 @@
 #include "base/macros.h"
 #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
-#include "chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h"
 #include "ui/app_list/presenter/app_list.h"
 #include "ui/compositor/layer_animation_observer.h"
@@ -378,7 +377,6 @@
         }
       }
 
-      owner_->notification_blocker()->ActiveUserChanged(new_account_id_);
       break;
     }
     case ANIMATION_STEP_ENDED:
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 7f5410b..9ccc34d4 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -20,7 +20,6 @@
 #include "base/metrics/user_metrics.h"
 #include "base/optional.h"
 #include "base/process/process_info.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -1515,20 +1514,10 @@
 
 void Browser::NavigationStateChanged(WebContents* source,
                                      content::InvalidateTypes changed_flags) {
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466285
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "466285 Browser::NavigationStateChanged::ScheduleUIUpdate"));
   // Only update the UI when something visible has changed.
   if (changed_flags)
     ScheduleUIUpdate(source, changed_flags);
 
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466285
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "466285 Browser::NavigationStateChanged::TabStateChanged"));
   // We can synchronously update commands since they will only change once per
   // navigation, so we don't have to worry about flickering. We do, however,
   // need to update the command state early on load to always present usable
@@ -2178,11 +2167,6 @@
   int index = tab_strip_model_->GetIndexOfWebContents(source);
   DCHECK_NE(TabStripModel::kNoTab, index);
 
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466285
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "466285 Browser::ScheduleUIUpdate::Toolbar"));
   // Do some synchronous updates.
   if (changed_flags & content::INVALIDATE_TYPE_URL) {
     if (source == tab_strip_model_->GetActiveWebContents()) {
@@ -2199,11 +2183,6 @@
     changed_flags &= ~content::INVALIDATE_TYPE_URL;
   }
 
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466285
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "466285 Browser::ScheduleUIUpdate::TabStripModel"));
   if (changed_flags & content::INVALIDATE_TYPE_LOAD) {
     // Update the loading state synchronously. This is so the throbber will
     // immediately start/stop, which gives a more snappy feel. We want to do
@@ -2263,12 +2242,6 @@
 
   chrome_updater_factory_.InvalidateWeakPtrs();
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467185
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "467185 Browser::ProcessPendingUIUpdates1"));
-
   for (UpdateMap::const_iterator i = scheduled_updates_.begin();
        i != scheduled_updates_.end(); ++i) {
     // Do not dereference |contents|, it may be out-of-date!
@@ -2276,12 +2249,6 @@
     unsigned flags = i->second;
 
     if (contents == tab_strip_model_->GetActiveWebContents()) {
-      // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467185
-      // is fixed.
-      tracked_objects::ScopedTracker tracking_profile2(
-          FROM_HERE_WITH_EXPLICIT_FUNCTION(
-              "467185 Browser::ProcessPendingUIUpdates2"));
-
       // Updates that only matter when the tab is selected go here.
 
       // Updating the URL happens synchronously in ScheduleUIUpdate.
@@ -2299,11 +2266,6 @@
     // Updates that don't depend upon the selected state go here.
     if (flags &
         (content::INVALIDATE_TYPE_TAB | content::INVALIDATE_TYPE_TITLE)) {
-      // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467185
-      // is fixed.
-      tracked_objects::ScopedTracker tracking_profile3(
-          FROM_HERE_WITH_EXPLICIT_FUNCTION(
-              "467185 Browser::ProcessPendingUIUpdates3"));
       tab_strip_model_->UpdateWebContentsStateAt(
           tab_strip_model_->GetIndexOfWebContents(contents),
           TabStripModelObserver::ALL);
@@ -2311,14 +2273,8 @@
 
     // Update the bookmark bar. It may happen that the tab is crashed, and if
     // so, the bookmark bar should be hidden.
-    if (flags & content::INVALIDATE_TYPE_TAB) {
-      // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467185
-      // is fixed.
-      tracked_objects::ScopedTracker tracking_profile4(
-          FROM_HERE_WITH_EXPLICIT_FUNCTION(
-              "467185 Browser::ProcessPendingUIUpdates4"));
+    if (flags & content::INVALIDATE_TYPE_TAB)
       UpdateBookmarkBarState(BOOKMARK_BAR_STATE_CHANGE_TAB_STATE);
-    }
 
     // We don't need to process INVALIDATE_STATE, since that's not visible.
   }
@@ -2534,11 +2490,6 @@
 }
 
 void Browser::UpdateBookmarkBarState(BookmarkBarStateChangeReason reason) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467185 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "467185 Browser::UpdateBookmarkBarState1"));
   BookmarkBar::State state;
   // The bookmark bar is always hidden for Guest Sessions and in fullscreen
   // mode, unless on the new tab page.
@@ -2549,11 +2500,6 @@
       !ShouldHideUIForFullscreen()) {
     state = BookmarkBar::SHOW;
   } else {
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467185
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile2(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "467185 Browser::UpdateBookmarkBarState2"));
     WebContents* web_contents = tab_strip_model_->GetActiveWebContents();
     BookmarkTabHelper* bookmark_tab_helper =
         web_contents ? BookmarkTabHelper::FromWebContents(web_contents) : NULL;
@@ -2578,12 +2524,6 @@
     return;
   }
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467185 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile3(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "467185 Browser::UpdateBookmarkBarState3"));
-
   bool should_animate = reason == BOOKMARK_BAR_STATE_CHANGE_PREF_CHANGE;
   window_->BookmarkBarStateChanged(should_animate ?
       BookmarkBar::ANIMATE_STATE_CHANGE :
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h
index 3e0d3192..abe9f85e 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h
@@ -27,7 +27,7 @@
 @property(readonly, retain, nonatomic) NSAttributedString* prefix;
 
 // Common icon that shows next to most rows in the list.
-@property(readonly, retain, nonatomic) NSImage* image;
+@property(retain, nonatomic) NSImage* image;
 
 // Uncommon icon that only shows on answer rows (e.g. weather).
 @property(readonly, retain, nonatomic) NSImage* answerImage;
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.h b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.h
index 9b8bbd45..413ad26 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.h
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.h
@@ -32,6 +32,9 @@
 // Set the hovered highlight.
 - (void)setHighlightedRow:(NSInteger)rowIndex;
 
+// Sets a custom match icon.
+- (void)setMatchIcon:(NSImage*)icon forRow:(NSInteger)rowIndex;
+
 // Which row has the hovered highlight.
 - (NSInteger)highlightedRow;
 
@@ -102,6 +105,9 @@
 // Setup the NSTableView data source.
 - (void)setController:(OmniboxPopupTableController*)controller;
 
+// Sets a custom match icon.
+- (void)setMatchIcon:(NSImage*)icon forRow:(NSInteger)rowIndex;
+
 @end
 
 #endif  // CHROME_BROWSER_UI_COCOA_OMNIBOX_OMNIBOX_POPUP_MATRIX_H_
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.mm
index fbd49ec..282602e 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_matrix.mm
@@ -38,10 +38,10 @@
   for (const AutocompleteMatch& match : result) {
     base::scoped_nsobject<OmniboxPopupCellData> cellData(
         [[OmniboxPopupCellData alloc]
-             initWithMatch:match
-                     image:popupView.ImageForMatch(match)
-               answerImage:(match.answer ? answerImage : nil)
-              forDarkTheme:isDarkTheme]);
+            initWithMatch:match
+                    image:popupView.ImageForMatch(match)
+              answerImage:(match.answer ? answerImage : nil)
+             forDarkTheme:isDarkTheme]);
     [array addObject:cellData];
   }
 
@@ -94,6 +94,13 @@
   hoveredIndex_ = rowIndex;
 }
 
+- (void)setMatchIcon:(NSImage*)icon forRow:(NSInteger)rowIndex {
+  OmniboxPopupCellData* cellData =
+      base::mac::ObjCCastStrict<OmniboxPopupCellData>(
+          [array_ objectAtIndex:rowIndex]);
+  [cellData setImage:icon];
+}
+
 - (CGFloat)tableView:(NSTableView*)tableView heightOfRow:(NSInteger)row {
   BOOL isAnswer = [[array_ objectAtIndex:row] isAnswer];
   BOOL isDoubleLine = !isAnswer && base::FeatureList::IsEnabled(
@@ -315,4 +322,9 @@
   return NO;
 }
 
+- (void)setMatchIcon:(NSImage*)icon forRow:(NSInteger)rowIndex {
+  [[self controller] setMatchIcon:icon forRow:rowIndex];
+  [self setNeedsDisplayInRect:[self rectOfRow:rowIndex]];
+}
+
 @end
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h
index 1021811..5d72e795 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h
@@ -40,7 +40,7 @@
   void InvalidateLine(size_t line) override {}
   void OnLineSelected(size_t line) override {}
   void UpdatePopupAppearance() override;
-  void SetMatchIcon(size_t match_index, const gfx::Image& icon) override;
+  void OnMatchIconUpdated(size_t match_index) override;
   gfx::Rect GetTargetBounds() override;
   // This is only called by model in SetSelectedLine() after updating
   // everything.  Popup should already be visible.
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm
index d9af395..fdc0b67 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.mm
@@ -132,9 +132,9 @@
   PositionPopup(NSHeight([matrix_ frame]));
 }
 
-void OmniboxPopupViewMac::SetMatchIcon(size_t match_index,
-                                       const gfx::Image& icon) {
-  // TODO(tommycli): Implement favicons for Cocoa.
+void OmniboxPopupViewMac::OnMatchIconUpdated(size_t match_index) {
+  [matrix_ setMatchIcon:ImageForMatch(GetResult().match_at(match_index))
+                 forRow:match_index];
 }
 
 gfx::Rect OmniboxPopupViewMac::GetTargetBounds() {
@@ -347,20 +347,10 @@
 
 NSImage* OmniboxPopupViewMac::ImageForMatch(
     const AutocompleteMatch& match) const {
-  gfx::Image image = model_->GetIconIfExtensionMatch(match);
-  if (!image.IsEmpty())
-    return image.AsNSImage();
-
   bool is_dark_mode = [matrix_ hasDarkTheme];
-  const SkColor icon_color =
+  const SkColor vector_icon_color =
       is_dark_mode ? SkColorSetA(SK_ColorWHITE, 0xCC) : gfx::kChromeIconGrey;
-  const gfx::VectorIcon& vector_icon =
-      model_->IsStarredMatch(match)
-          ? toolbar::kStarIcon
-          : AutocompleteMatch::TypeToVectorIcon(match.type);
-  const int kIconSize = 16;
-  return NSImageFromImageSkia(
-      gfx::CreateVectorIcon(vector_icon, kIconSize, icon_color));
+  return model_->GetMatchIcon(match, vector_icon_color).ToNSImage();
 }
 
 void OmniboxPopupViewMac::OpenURLForRow(size_t row,
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac_unittest.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac_unittest.mm
index 4a92758b0..7405f19 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac_unittest.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac_unittest.mm
@@ -60,7 +60,7 @@
   void InvalidateLine(size_t line) override {}
   void OnLineSelected(size_t line) override {}
   void UpdatePopupAppearance() override {}
-  void SetMatchIcon(size_t match_index, const gfx::Image& icon) override {}
+  void OnMatchIconUpdated(size_t match_index) override {}
   gfx::Rect GetTargetBounds() override { return gfx::Rect(); }
   void PaintUpdatesNow() override {}
   void OnDragCanceled() override {}
diff --git a/chrome/browser/ui/cocoa/tab_contents/chrome_web_contents_view_delegate_mac.mm b/chrome/browser/ui/cocoa/tab_contents/chrome_web_contents_view_delegate_mac.mm
index 4d77258..c407de1 100644
--- a/chrome/browser/ui/cocoa/tab_contents/chrome_web_contents_view_delegate_mac.mm
+++ b/chrome/browser/ui/cocoa/tab_contents/chrome_web_contents_view_delegate_mac.mm
@@ -6,7 +6,6 @@
 
 #include <utility>
 
-#include "base/profiler/scoped_tracker.h"
 #import "chrome/browser/renderer_host/chrome_render_widget_host_view_mac_delegate.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -53,10 +52,6 @@
 void ChromeWebContentsViewDelegateMac::ShowContextMenu(
     content::RenderFrameHost* render_frame_host,
     const content::ContextMenuParams& params) {
-  // TODO(erikchen): Remove ScopedTracker below once crbug.com/458401 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "458401 ChromeWebContentsViewDelegateMac::ShowContextMenu"));
   ShowMenu(
       BuildMenu(content::WebContents::FromRenderFrameHost(render_frame_host),
                 params));
@@ -64,11 +59,6 @@
 
 void ChromeWebContentsViewDelegateMac::ShowMenu(
     std::unique_ptr<RenderViewContextMenuBase> menu) {
-  // TODO(erikchen): Remove ScopedTracker below once crbug.com/458401 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "458401 ChromeWebContentsViewDelegateMac::ShowMenu"));
-
   context_menu_ = std::move(menu);
   if (!context_menu_.get())
     return;
@@ -90,10 +80,6 @@
 ChromeWebContentsViewDelegateMac::BuildMenu(
     content::WebContents* web_contents,
     const content::ContextMenuParams& params) {
-  // TODO(erikchen): Remove ScopedTracker below once crbug.com/458401 is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "458401 ChromeWebContentsViewDelegateMac::BuildMenu"));
   std::unique_ptr<RenderViewContextMenuBase> menu;
   content::RenderFrameHost* focused_frame = web_contents->GetFocusedFrame();
   // If the frame tree does not have a focused frame at this point, do not
@@ -103,20 +89,8 @@
   if (focused_frame) {
     content::RenderWidgetHostView* widget_view =
         GetActiveRenderWidgetHostView();
-
-    // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/458401
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile2(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "458401 ChromeWebContentsViewDelegateMac::BuildMenu::MakeMenu"));
     menu.reset(new RenderViewContextMenuMac(
         focused_frame, params, widget_view->GetNativeView()));
-
-    // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/458401
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile3(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "458401 ChromeWebContentsViewDelegateMac::BuildMenu::InitMenu"));
     menu->Init();
   }
 
diff --git a/chrome/browser/ui/prefs/prefs_tab_helper.cc b/chrome/browser/ui/prefs/prefs_tab_helper.cc
index 4d4fd0e7..149dbc5 100644
--- a/chrome/browser/ui/prefs/prefs_tab_helper.cc
+++ b/chrome/browser/ui/prefs/prefs_tab_helper.cc
@@ -21,6 +21,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/font_pref_change_notifier_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/renderer_preferences_util.h"
@@ -134,23 +135,6 @@
     }
   }
 }
-
-// Registers |obs| to observe per-script font prefs under the path |map_name|.
-// On android, there's no exposed way to change these prefs, so we can save
-// ~715KB of heap and some startup cycles by avoiding observing these prefs
-// since they will never change.
-void RegisterFontFamilyMapObserver(
-    PrefChangeRegistrar* registrar,
-    const char* map_name,
-    const PrefChangeRegistrar::NamedChangeCallback& obs) {
-  DCHECK(base::StartsWith(map_name, "webkit.webprefs.",
-                          base::CompareCase::SENSITIVE));
-
-  for (size_t i = 0; i < prefs::kWebKitScriptsForFontFamilyMapsLength; ++i) {
-    const char* script = prefs::kWebKitScriptsForFontFamilyMaps[i];
-    registrar->Add(base::StringPrintf("%s.%s", map_name, script), obs);
-  }
-}
 #endif  // !defined(OS_ANDROID)
 
 #if defined(OS_WIN)
@@ -368,30 +352,6 @@
       const char* pref_name = kPrefsToObserve[i];
       pref_change_registrar_.Add(pref_name, webkit_callback);
     }
-
-#if !defined(OS_ANDROID)
-    RegisterFontFamilyMapObserver(&pref_change_registrar_,
-                                  prefs::kWebKitStandardFontFamilyMap,
-                                  webkit_callback);
-    RegisterFontFamilyMapObserver(&pref_change_registrar_,
-                                  prefs::kWebKitFixedFontFamilyMap,
-                                  webkit_callback);
-    RegisterFontFamilyMapObserver(&pref_change_registrar_,
-                                  prefs::kWebKitSerifFontFamilyMap,
-                                  webkit_callback);
-    RegisterFontFamilyMapObserver(&pref_change_registrar_,
-                                  prefs::kWebKitSansSerifFontFamilyMap,
-                                  webkit_callback);
-    RegisterFontFamilyMapObserver(&pref_change_registrar_,
-                                  prefs::kWebKitCursiveFontFamilyMap,
-                                  webkit_callback);
-    RegisterFontFamilyMapObserver(&pref_change_registrar_,
-                                  prefs::kWebKitFantasyFontFamilyMap,
-                                  webkit_callback);
-    RegisterFontFamilyMapObserver(&pref_change_registrar_,
-                                  prefs::kWebKitPictographFontFamilyMap,
-                                  webkit_callback);
-#endif  // !defined(OS_ANDROID)
   }
 
   static PrefWatcher* Get(Profile* profile);
@@ -483,6 +443,11 @@
       default_zoom_level_subscription_ =
           zoom_level_prefs->RegisterDefaultZoomLevelCallback(renderer_callback);
     }
+
+    // Unretained is safe because the registrar will be scoped to this class.
+    font_change_registrar_.Register(
+        FontPrefChangeNotifierFactory::GetForProfile(profile_),
+        base::Bind(&PrefsTabHelper::OnWebPrefChanged, base::Unretained(this)));
 #endif  // !defined(OS_ANDROID)
 
     PrefWatcher::Get(profile_)->RegisterHelper(this);
@@ -637,6 +602,7 @@
       prefs, profile_, web_contents_);
   web_contents_->GetRenderViewHost()->SyncRendererPrefs();
 }
+
 void PrefsTabHelper::OnFontFamilyPrefChanged(const std::string& pref_name) {
   // When a font family pref's value goes from non-empty to the empty string, we
   // must add it to the usual WebPreferences struct passed to the renderer.
diff --git a/chrome/browser/ui/prefs/prefs_tab_helper.h b/chrome/browser/ui/prefs/prefs_tab_helper.h
index 9fd3acb1..22f58e38 100644
--- a/chrome/browser/ui/prefs/prefs_tab_helper.h
+++ b/chrome/browser/ui/prefs/prefs_tab_helper.h
@@ -9,6 +9,7 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/font_pref_change_notifier.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/web_contents_user_data.h"
@@ -66,6 +67,7 @@
 #if !defined(OS_ANDROID)
   std::unique_ptr<ChromeZoomLevelPrefs::DefaultZoomLevelSubscription>
       default_zoom_level_subscription_;
+  FontPrefChangeNotifier::Registrar font_change_registrar_;
 #endif  // !defined(OS_ANDROID)
   base::WeakPtrFactory<PrefsTabHelper> weak_ptr_factory_;
 
diff --git a/chrome/browser/ui/tab_contents/core_tab_helper.cc b/chrome/browser/ui/tab_contents/core_tab_helper.cc
index 4ed2cee3..25ea578 100644
--- a/chrome/browser/ui/tab_contents/core_tab_helper.cc
+++ b/chrome/browser/ui/tab_contents/core_tab_helper.cc
@@ -11,7 +11,6 @@
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
@@ -128,11 +127,6 @@
 // static
 bool CoreTabHelper::GetStatusTextForWebContents(
     base::string16* status_text, content::WebContents* source) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467185 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "467185 CoreTabHelper::GetStatusTextForWebContents1"));
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   auto* guest_manager = guest_view::GuestViewManager::FromBrowserContext(
       source->GetBrowserContext());
@@ -140,11 +134,6 @@
   if (!source->IsLoading() ||
       source->GetLoadState().state == net::LOAD_STATE_IDLE) {
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467185
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile2(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "467185 CoreTabHelper::GetStatusTextForWebContents2"));
     if (!guest_manager)
       return false;
     return guest_manager->ForEachGuest(
@@ -155,12 +144,6 @@
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
   }
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467185
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile3(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "467185 CoreTabHelper::GetStatusTextForWebContents3"));
-
   switch (source->GetLoadState().state) {
     case net::LOAD_STATE_WAITING_FOR_STALLED_SOCKET_POOL:
     case net::LOAD_STATE_WAITING_FOR_AVAILABLE_SOCKET:
@@ -240,11 +223,6 @@
   if (!guest_manager)
     return false;
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467185 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile4(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "467185 CoreTabHelper::GetStatusTextForWebContents4"));
   return guest_manager->ForEachGuest(
       source, base::Bind(&CoreTabHelper::GetStatusTextForWebContents,
                          status_text));
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
index c94054b..2b6b47a 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
@@ -9,7 +9,6 @@
 
 #include "base/auto_reset.h"
 #include "base/location.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
@@ -321,34 +320,13 @@
     return;
 
   {
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/463337
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile1(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION("ToolbarActionsBar::CreateActions1"));
     // We don't redraw the view while creating actions.
     base::AutoReset<bool> layout_resetter(&suppress_layout_, true);
 
     // Get the toolbar actions.
     toolbar_actions_ = model_->CreateActions(browser_, this);
-    if (!model_->is_highlighting()) {
-      // TODO(robliao): Remove ScopedTracker below once https://crbug.com/463337
-      // is fixed.
-      tracked_objects::ScopedTracker tracking_profile2(
-          FROM_HERE_WITH_EXPLICIT_FUNCTION(
-              "ToolbarActionsBar::CreateActions2"));
-    }
-
-    if (!toolbar_actions_.empty()) {
-      // TODO(robliao): Remove ScopedTracker below once https://crbug.com/463337
-      // is fixed.
-      tracked_objects::ScopedTracker tracking_profile3(
-          FROM_HERE_WITH_EXPLICIT_FUNCTION(
-              "ToolbarActionsBar::CreateActions3"));
+    if (!toolbar_actions_.empty())
       ReorderActions();
-    }
-
-    tracked_objects::ScopedTracker tracking_profile4(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION("ToolbarActionsBar::CreateActions4"));
 
     for (size_t i = 0; i < toolbar_actions_.size(); ++i)
       delegate_->AddViewForAction(toolbar_actions_[i].get(), i);
@@ -766,12 +744,6 @@
   // We shouldn't have any actions before the model is initialized.
   CHECK(toolbar_actions_.empty());
   CreateActions();
-
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/463337 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "ToolbarActionsBar::OnToolbarModelInitialized"));
   ResizeDelegate(gfx::Tween::EASE_OUT);
 }
 
diff --git a/chrome/browser/ui/views/chrome_views_delegate_win.cc b/chrome/browser/ui/views/chrome_views_delegate_win.cc
index ba43156..07a387d9 100644
--- a/chrome/browser/ui/views/chrome_views_delegate_win.cc
+++ b/chrome/browser/ui/views/chrome_views_delegate_win.cc
@@ -7,7 +7,6 @@
 #include <dwmapi.h>
 #include <shellapi.h>
 
-#include "base/profiler/scoped_tracker.h"
 #include "base/task_scheduler/post_task.h"
 #include "base/win/windows_version.h"
 #include "chrome/browser/ui/views/native_widget_factory.h"
@@ -21,11 +20,6 @@
   APPBARDATA taskbar_data = {sizeof(APPBARDATA), NULL, 0, edge};
   taskbar_data.hWnd = ::GetForegroundWindow();
 
-  // TODO(robliao): Remove ScopedTracker below once crbug.com/462368 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "462368 MonitorHasAutohideTaskbarForEdge"));
-
   // MSDN documents an ABM_GETAUTOHIDEBAREX, which supposedly takes a monitor
   // rect and returns autohide bars on that monitor.  This sounds like a good
   // idea for multi-monitor systems.  Unfortunately, it appears to not work at
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 8bf3e9f..cd887fd 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -17,7 +17,6 @@
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -678,19 +677,9 @@
 }
 
 void BrowserView::UpdateTitleBar() {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467185 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("467185 BrowserView::UpdateTitleBar1"));
   frame_->UpdateWindowTitle();
-  if (ShouldShowWindowIcon() && !loading_animation_timer_.IsRunning()) {
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467185
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile2(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "467185 BrowserView::UpdateTitleBar2"));
+  if (ShouldShowWindowIcon() && !loading_animation_timer_.IsRunning())
     frame_->UpdateWindowIcon();
-  }
 }
 
 void BrowserView::BookmarkBarStateChanged(
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
index f9fec81f..e3cbe51a 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
@@ -318,10 +318,8 @@
   Layout();
 }
 
-void OmniboxPopupContentsView::SetMatchIcon(size_t match_index,
-                                            const gfx::Image& icon) {
-  OmniboxResultView* view = result_view_at(match_index);
-  view->SetCustomIcon(icon.AsImageSkia());
+void OmniboxPopupContentsView::OnMatchIconUpdated(size_t match_index) {
+  result_view_at(match_index)->OnMatchIconUpdated();
 }
 
 gfx::Rect OmniboxPopupContentsView::GetTargetBounds() {
@@ -343,16 +341,10 @@
   return index == model_->selected_line();
 }
 
-gfx::Image OmniboxPopupContentsView::GetIconIfExtensionMatch(
-    size_t index) const {
-  if (!HasMatchAt(index))
-    return gfx::Image();
-  return model_->GetIconIfExtensionMatch(GetMatchAtIndex(index));
-}
-
-bool OmniboxPopupContentsView::IsStarredMatch(
-    const AutocompleteMatch& match) const {
-  return model_->IsStarredMatch(match);
+gfx::Image OmniboxPopupContentsView::GetMatchIcon(
+    const AutocompleteMatch& match,
+    SkColor vector_icon_color) const {
+  return model_->GetMatchIcon(match, vector_icon_color);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h
index 114f1bb..ac9d340 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h
@@ -46,7 +46,7 @@
   void InvalidateLine(size_t line) override;
   void OnLineSelected(size_t line) override;
   void UpdatePopupAppearance() override;
-  void SetMatchIcon(size_t match_index, const gfx::Image& icon) override;
+  void OnMatchIconUpdated(size_t match_index) override;
   gfx::Rect GetTargetBounds() override;
   void PaintUpdatesNow() override;
   void OnDragCanceled() override;
@@ -63,8 +63,8 @@
   void OnGestureEvent(ui::GestureEvent* event) override;
 
   bool IsSelectedIndex(size_t index) const;
-  gfx::Image GetIconIfExtensionMatch(size_t index) const;
-  bool IsStarredMatch(const AutocompleteMatch& match) const;
+  gfx::Image GetMatchIcon(const AutocompleteMatch& match,
+                          SkColor vector_icon_color) const;
 
  protected:
   OmniboxPopupContentsView(const gfx::FontList& font_list,
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
index cb97c42..7671b76 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -227,7 +227,8 @@
   CHECK_GE(model_index, 0);
   keyword_icon_->set_owned_by_client();
   keyword_icon_->EnableCanvasFlippingForRTLUI(true);
-  keyword_icon_->SetImage(GetVectorIcon(omnibox::kKeywordSearchIcon));
+  keyword_icon_->SetImage(gfx::CreateVectorIcon(omnibox::kKeywordSearchIcon, 16,
+                                                GetVectorIconColor()));
   keyword_icon_->SizeToPreferredSize();
 }
 
@@ -499,8 +500,8 @@
   return render_text;
 }
 
-void OmniboxResultView::SetCustomIcon(const gfx::ImageSkia& icon) {
-  custom_icon_ = icon;
+void OmniboxResultView::OnMatchIconUpdated() {
+  // The new icon will be fetched during repaint.
   SchedulePaint();
 }
 
@@ -513,28 +514,16 @@
   return "OmniboxResultView";
 }
 
-gfx::ImageSkia OmniboxResultView::GetIcon() const {
-  // TODO(tommycli): Consolidate the extension icons, custom icons, and vector
-  // icons into a single concept at the cross-platform layer.
-  const gfx::Image image = model_->GetIconIfExtensionMatch(model_index_);
-  if (!image.IsEmpty())
-    return image.AsImageSkia();
-
-  if (!custom_icon_.isNull())
-    return custom_icon_;
-
-  return GetVectorIcon(model_->IsStarredMatch(match_)
-                           ? omnibox::kStarIcon
-                           : AutocompleteMatch::TypeToVectorIcon(match_.type));
+gfx::Image OmniboxResultView::GetIcon() const {
+  return model_->GetMatchIcon(match_, GetVectorIconColor());
 }
 
-gfx::ImageSkia OmniboxResultView::GetVectorIcon(
-    const gfx::VectorIcon& icon) const {
+SkColor OmniboxResultView::GetVectorIconColor() const {
   // For selected rows, paint the icon the same color as the text.
   SkColor color = GetColor(GetState(), TEXT);
   if (GetState() != SELECTED)
     color = color_utils::DeriveDefaultIconColor(color);
-  return gfx::CreateVectorIcon(icon, 16, color);
+  return color;
 }
 
 bool OmniboxResultView::ShowOnlyKeywordMatch() const {
@@ -572,14 +561,14 @@
                       horizontal_padding;
   const int end_x = width() - start_x;
 
-  const gfx::ImageSkia icon = GetIcon();
+  const gfx::Image icon = GetIcon();
 
   int row_height = GetTextHeight();
   if (base::FeatureList::IsEnabled(omnibox::kUIExperimentVerticalLayout))
     row_height += match_.answer ? GetAnswerHeight() : GetTextHeight();
 
-  const int icon_y = GetVerticalMargin() + (row_height - icon.height()) / 2;
-  icon_bounds_.SetRect(start_x, icon_y, icon.width(), icon.height());
+  const int icon_y = GetVerticalMargin() + (row_height - icon.Height()) / 2;
+  icon_bounds_.SetRect(start_x, icon_y, icon.Width(), icon.Height());
 
   const int text_x = start_x + LocationBarView::kIconWidth + horizontal_padding;
   int text_width = end_x - text_x;
@@ -609,8 +598,8 @@
   // NOTE: While animating the keyword match, both matches may be visible.
 
   if (!ShowOnlyKeywordMatch()) {
-    canvas->DrawImageInt(GetIcon(), GetMirroredXForRect(icon_bounds_),
-                         icon_bounds_.y());
+    canvas->DrawImageInt(GetIcon().AsImageSkia(),
+                         GetMirroredXForRect(icon_bounds_), icon_bounds_.y());
     int x = GetMirroredXForRect(text_bounds_);
     mirroring_context_->Initialize(x, text_bounds_.width());
     InitContentsRenderTextIfNecessary();
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.h b/chrome/browser/ui/views/omnibox/omnibox_result_view.h
index b1c2f981..55dd7ca 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.h
@@ -7,8 +7,6 @@
 
 #include <stddef.h>
 
-#include <vector>
-
 #include "base/macros.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/suggestion_answer.h"
@@ -24,6 +22,7 @@
 
 namespace gfx {
 class Canvas;
+class Image;
 class RenderText;
 }
 
@@ -80,8 +79,8 @@
   // class, this is the height of one line of text.
   virtual int GetTextHeight() const;
 
-  // Stores a custom icon as a local data member and schedules a repaint.
-  void SetCustomIcon(const gfx::ImageSkia& icon);
+  // Notification that the match icon has changed and schedules a repaint.
+  void OnMatchIconUpdated();
 
   // Stores the image in a local data member and schedules a repaint.
   void SetAnswerImage(const gfx::ImageSkia& image);
@@ -133,10 +132,9 @@
   // views::View:
   const char* GetClassName() const override;
 
-  gfx::ImageSkia GetIcon() const;
+  gfx::Image GetIcon() const;
 
-  // Utility function for creating vector icons.
-  gfx::ImageSkia GetVectorIcon(const gfx::VectorIcon& icon_id) const;
+  SkColor GetVectorIconColor() const;
 
   // Whether to render only the keyword match.  Returns true if |match_| has an
   // associated keyword match that has been animated so close to the start that
@@ -211,8 +209,6 @@
 
   std::unique_ptr<gfx::SlideAnimation> animation_;
 
-  gfx::ImageSkia custom_icon_;
-
   // If the answer has an icon, cache the image.
   gfx::ImageSkia answer_image_;
 
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index e7e5756..379bd67 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -49,7 +49,6 @@
 #include "chrome/browser/ui/webui/physical_web/physical_web_ui.h"
 #include "chrome/browser/ui/webui/policy_ui.h"
 #include "chrome/browser/ui/webui/predictors/predictors_ui.h"
-#include "chrome/browser/ui/webui/profiler_ui.h"
 #include "chrome/browser/ui/webui/quota_internals/quota_internals_ui.h"
 #include "chrome/browser/ui/webui/settings/md_settings_ui.h"
 #include "chrome/browser/ui/webui/settings_utils.h"
@@ -355,8 +354,6 @@
     return &NewWebUI<PhysicalWebUI>;
   if (url.host_piece() == chrome::kChromeUIPredictorsHost)
     return &NewWebUI<PredictorsUI>;
-  if (url.host_piece() == chrome::kChromeUIProfilerHost)
-    return &NewWebUI<ProfilerUI>;
   if (url.host() == chrome::kChromeUIQuotaInternalsHost)
     return &NewWebUI<QuotaInternalsUI>;
   if (url.host() == safe_browsing::kChromeUISafeBrowsingHost)
diff --git a/chrome/browser/ui/webui/chromeos/login/network_dropdown.cc b/chrome/browser/ui/webui/chromeos/login/network_dropdown.cc
index daa26f8..27e7b3a8 100644
--- a/chrome/browser/ui/webui/chromeos/login/network_dropdown.cc
+++ b/chrome/browser/ui/webui/chromeos/login/network_dropdown.cc
@@ -125,7 +125,7 @@
   network_menu_.reset(new NetworkMenuWebUI(this, web_ui));
   DCHECK(NetworkHandler::IsInitialized());
   NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
-  handler->RequestScan();
+  handler->RequestScan(NetworkTypePattern::WiFi());
   handler->AddObserver(this, FROM_HERE);
   Refresh();
   network_scan_timer_.Start(
@@ -208,7 +208,8 @@
 }
 
 void NetworkDropdown::RequestNetworkScan() {
-  NetworkHandler::Get()->network_state_handler()->RequestScan();
+  NetworkHandler::Get()->network_state_handler()->RequestScan(
+      NetworkTypePattern::WiFi());
   Refresh();
 }
 
diff --git a/chrome/browser/ui/webui/profiler_ui.cc b/chrome/browser/ui/webui/profiler_ui.cc
deleted file mode 100644
index 2bf297e4..0000000
--- a/chrome/browser/ui/webui/profiler_ui.cc
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/profiler_ui.h"
-
-#include <string>
-
-// When testing the javacript code, it is cumbersome to have to keep
-// re-building the resouces package and reloading the browser. To solve
-// this, enable the following flag to read the webapp's source files
-// directly off disk, so all you have to do is refresh the page to
-// test the modifications.
-// #define USE_SOURCE_FILES_DIRECTLY
-
-#include "base/bind.h"
-#include "base/debug/debugging_flags.h"
-#include "base/debug/thread_heap_usage_tracker.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/strings/string_util.h"
-#include "base/tracked_objects.h"
-#include "base/values.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/task_profiler/task_profiler_data_serializer.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/grit/browser_resources.h"
-#include "components/metrics/profiler/tracking_synchronizer.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/url_data_source.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/browser/web_ui_message_handler.h"
-
-#ifdef USE_SOURCE_FILES_DIRECTLY
-#include "base/base_paths.h"
-#include "base/files/file_util.h"
-#include "base/memory/ref_counted_memory.h"
-#include "base/path_service.h"
-#endif  //  USE_SOURCE_FILES_DIRECTLY
-
-using content::BrowserThread;
-using content::WebContents;
-using content::WebUIMessageHandler;
-using metrics::TrackingSynchronizer;
-
-namespace {
-
-#ifdef USE_SOURCE_FILES_DIRECTLY
-
-class ProfilerWebUIDataSource : public content::URLDataSource {
- public:
-  ProfilerWebUIDataSource() {
-  }
-
- protected:
-  // content::URLDataSource implementation.
-  std::string GetSource() override {
-    return chrome::kChromeUIProfilerHost;
-  }
-
-  std::string GetMimeType(const std::string& path) const override {
-    if (base::EndsWith(path, ".js", base::CompareCase::INSENSITIVE_ASCII))
-      return "application/javascript";
-    return "text/html";
-  }
-
-  void StartDataRequest(
-      const std::string& path,
-      bool is_incognito,
-      const content::URLDataSource::GotDataCallback& callback) override {
-    base::FilePath base_path;
-    PathService::Get(base::DIR_SOURCE_ROOT, &base_path);
-    base_path = base_path.AppendASCII("chrome");
-    base_path = base_path.AppendASCII("browser");
-    base_path = base_path.AppendASCII("resources");
-    base_path = base_path.AppendASCII("profiler");
-
-    // If no resource was specified, default to profiler.html.
-    std::string filename = path.empty() ? "profiler.html" : path;
-
-    base::FilePath file_path;
-    file_path = base_path.AppendASCII(filename);
-
-    // Read the file synchronously and send it as the response.
-    base::ThreadRestrictions::ScopedAllowIO allow;
-    std::string file_contents;
-    if (!base::ReadFileToString(file_path, &file_contents))
-      LOG(ERROR) << "Couldn't read file: " << file_path.value();
-    scoped_refptr<base::RefCountedString> response =
-        new base::RefCountedString();
-    response->data() = file_contents;
-    callback.Run(response);
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ProfilerWebUIDataSource);
-};
-
-#else  // USE_SOURCE_FILES_DIRECTLY
-
-content::WebUIDataSource* CreateProfilerHTMLSource() {
-  content::WebUIDataSource* source =
-      content::WebUIDataSource::Create(chrome::kChromeUIProfilerHost);
-
-  source->SetJsonPath("strings.js");
-  source->AddResourcePath("profiler.js", IDR_PROFILER_JS);
-  source->SetDefaultResource(IDR_PROFILER_HTML);
-  source->UseGzip(std::unordered_set<std::string>());
-  source->AddBoolean(
-      "enableMemoryTaskProfiler",
-      base::debug::ThreadHeapUsageTracker::IsHeapTrackingEnabled());
-
-  return source;
-}
-
-#endif
-
-// This class receives javascript messages from the renderer.
-// Note that the WebUI infrastructure runs on the UI thread, therefore all of
-// this class's methods are expected to run on the UI thread.
-class ProfilerMessageHandler : public WebUIMessageHandler {
- public:
-  ProfilerMessageHandler() {}
-
-  // WebUIMessageHandler implementation.
-  void RegisterMessages() override;
-
-  // Messages.
-  void OnGetData(const base::ListValue* list);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ProfilerMessageHandler);
-};
-
-void ProfilerMessageHandler::RegisterMessages() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  web_ui()->RegisterMessageCallback("getData",
-      base::Bind(&ProfilerMessageHandler::OnGetData, base::Unretained(this)));
-}
-
-void ProfilerMessageHandler::OnGetData(const base::ListValue* list) {
-  ProfilerUI* profiler_ui = static_cast<ProfilerUI*>(web_ui()->GetController());
-  profiler_ui->GetData();
-}
-
-}  // namespace
-
-ProfilerUI::ProfilerUI(content::WebUI* web_ui)
-    : WebUIController(web_ui),
-      weak_ptr_factory_(this) {
-  web_ui->AddMessageHandler(base::MakeUnique<ProfilerMessageHandler>());
-
-  // Set up the chrome://profiler/ source.
-  Profile* profile = Profile::FromWebUI(web_ui);
-#if defined(USE_SOURCE_FILES_DIRECTLY)
-  content::URLDataSource::Add(profile, new ProfilerWebUIDataSource);
-#else
-  content::WebUIDataSource::Add(profile, CreateProfilerHTMLSource());
-#endif
-}
-
-ProfilerUI::~ProfilerUI() {
-}
-
-void ProfilerUI::GetData() {
-  TrackingSynchronizer::FetchProfilerDataAsynchronously(
-      weak_ptr_factory_.GetWeakPtr());
-}
-
-void ProfilerUI::ReceivedProfilerData(
-    const metrics::ProfilerDataAttributes& attributes,
-    const tracked_objects::ProcessDataPhaseSnapshot& process_data_phase,
-    const metrics::ProfilerEvents& past_events) {
-  // Serialize the data to JSON.
-  base::DictionaryValue json_data;
-  task_profiler::TaskProfilerDataSerializer::ToValue(
-      process_data_phase, attributes.process_id, attributes.process_type,
-      &json_data);
-
-  // Send the data to the renderer.
-  web_ui()->CallJavascriptFunctionUnsafe("g_browserBridge.receivedData",
-                                         json_data);
-}
diff --git a/chrome/browser/ui/webui/profiler_ui.h b/chrome/browser/ui/webui/profiler_ui.h
deleted file mode 100644
index 350fe7e..0000000
--- a/chrome/browser/ui/webui/profiler_ui.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_PROFILER_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_PROFILER_UI_H_
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "components/metrics/profiler/tracking_synchronizer_observer.h"
-#include "content/public/browser/web_ui_controller.h"
-
-// The C++ back-end for the chrome://profiler webui page.
-class ProfilerUI : public content::WebUIController,
-                   public metrics::TrackingSynchronizerObserver {
- public:
-  explicit ProfilerUI(content::WebUI* web_ui);
-  ~ProfilerUI() override;
-
-  // Get the tracking data from TrackingSynchronizer.
-  void GetData();
-
- private:
-  // TrackingSynchronizerObserver:
-  void ReceivedProfilerData(
-      const metrics::ProfilerDataAttributes& attributes,
-      const tracked_objects::ProcessDataPhaseSnapshot& process_data_phase,
-      const metrics::ProfilerEvents& past_events) override;
-
-  // Used to get |weak_ptr_| to self on the UI thread.
-  base::WeakPtrFactory<ProfilerUI> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(ProfilerUI);
-};
-
-#endif  // CHROME_BROWSER_UI_WEBUI_PROFILER_UI_H_
diff --git a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
index f66590d..f5da4f8 100644
--- a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
+++ b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
@@ -14,7 +14,6 @@
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -261,11 +260,6 @@
 
   void OnProfileHighResAvatarLoaded(
       const base::FilePath& profile_path) override {
-    // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461175
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "461175 UserManagerScreenHandler::OnProfileHighResAvatarLoaded"));
     user_manager_handler_->SendUserList();
   }
 
diff --git a/chrome/browser/ui/webui/signin_internals_ui.cc b/chrome/browser/ui/webui/signin_internals_ui.cc
index dc95d86..f1748a38 100644
--- a/chrome/browser/ui/webui/signin_internals_ui.cc
+++ b/chrome/browser/ui/webui/signin_internals_ui.cc
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "base/hash.h"
-#include "base/profiler/scoped_tracker.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/about_signin_internals_factory.h"
 #include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
@@ -106,12 +105,6 @@
 
 void SignInInternalsUI::OnSigninStateChanged(
     const base::DictionaryValue* info) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 SignInInternalsUI::OnSigninStateChanged"));
-
   web_ui()->CallJavascriptFunctionUnsafe(
       "chrome.signin.onSigninInfoChanged.fire", *info);
 }
diff --git a/chrome/browser/vr/color_scheme.cc b/chrome/browser/vr/color_scheme.cc
index 2ff3358..ef53ecb 100644
--- a/chrome/browser/vr/color_scheme.cc
+++ b/chrome/browser/vr/color_scheme.cc
@@ -65,7 +65,7 @@
   normal_scheme.dimmer_inner = 0xCC0D0D0D;
   normal_scheme.dimmer_outer = 0xE6000000;
   normal_scheme.splash_screen_background = SK_ColorBLACK;
-  normal_scheme.splash_screen_text_color = SK_ColorWHITE;
+  normal_scheme.splash_screen_text_color = 0xA6FFFFFF;
 
   gColorSchemes[ColorScheme::kModeFullscreen] =
       gColorSchemes[ColorScheme::kModeNormal];
diff --git a/chrome/browser/vr/ui_scene_manager.cc b/chrome/browser/vr/ui_scene_manager.cc
index cd641eb..00b821a 100644
--- a/chrome/browser/vr/ui_scene_manager.cc
+++ b/chrome/browser/vr/ui_scene_manager.cc
@@ -197,10 +197,11 @@
 //   kWebVrRoot
 //     kWebVrContent
 //     kWebVrViewportAwareRoot
+//       kSplashScreenText
 //       kWebVrPresentationToast
-//       kWebVrPermanentHttpSecurityWarning,
-//       kWebVrTransientHttpSecurityWarning,
-//       kTransientUrlBar
+//       kWebVrPermanentHttpSecurityWarning
+//       kWebVrTransientHttpSecurityWarning
+//       kWebVrUrlToast
 //
 // TODO(vollick): The above hierarchy is complex, brittle, and would be easier
 // to manage if it were specified in a declarative format.
@@ -422,13 +423,14 @@
       }),
       IDS_VR_POWERED_BY_CHROME_MESSAGE);
   text->set_name(kSplashScreenText);
+  text->set_viewport_aware(true);
   text->set_draw_phase(kPhaseForeground);
   text->set_hit_testable(false);
   text->SetSize(kSplashScreenTextWidthM, kSplashScreenTextHeightM);
   text->SetTranslate(0, kSplashScreenTextVerticalOffset,
                      -kSplashScreenTextDistance);
   splash_screen_text_ = text.get();
-  scene_->AddUiElement(kWebVrRoot, std::move(text));
+  scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(text));
 }
 
 void UiSceneManager::CreateUnderDevelopmentNotice() {
diff --git a/chrome/browser/vr/ui_suppressed_element.h b/chrome/browser/vr/ui_suppressed_element.h
index a5594a63..b9c4189 100644
--- a/chrome/browser/vr/ui_suppressed_element.h
+++ b/chrome/browser/vr/ui_suppressed_element.h
@@ -22,6 +22,8 @@
   kFileAccessPermission,
   kPasswordManager,
   kAutofill,
+  kUsbChooser,
+  kSslClientCertificate,
 
   // This must be last.
   kCount,
diff --git a/chrome/common/channel_info.cc b/chrome/common/channel_info.cc
index 0596f54..0eba8de 100644
--- a/chrome/common/channel_info.cc
+++ b/chrome/common/channel_info.cc
@@ -4,19 +4,12 @@
 
 #include "chrome/common/channel_info.h"
 
-#include "base/profiler/scoped_tracker.h"
 #include "components/version_info/version_info.h"
 #include "components/version_info/version_string.h"
 
 namespace chrome {
 
 std::string GetVersionString() {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 VersionInfo::CreateVersionString"));
-
   return version_info::GetVersionStringWithModifier(GetChannelString());
 }
 
diff --git a/chrome/common/channel_info_win.cc b/chrome/common/channel_info_win.cc
index cd8a3dd..40d452a 100644
--- a/chrome/common/channel_info_win.cc
+++ b/chrome/common/channel_info_win.cc
@@ -5,7 +5,6 @@
 #include "chrome/common/channel_info.h"
 
 #include "base/debug/profiler.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/install_static/install_util.h"
@@ -13,12 +12,6 @@
 namespace chrome {
 
 std::string GetChannelString() {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 VersionInfo::GetVersionStringModifier"));
-
 #if defined(GOOGLE_CHROME_BUILD)
   base::string16 channel(install_static::GetChromeChannelName());
 #if defined(SYZYASAN)
diff --git a/chrome/common/child_process_logging_win.cc b/chrome/common/child_process_logging_win.cc
index b98f2ad..242c455 100644
--- a/chrome/common/child_process_logging_win.cc
+++ b/chrome/common/child_process_logging_win.cc
@@ -22,11 +22,12 @@
 
 void SetCrashKeyValueTrampoline(const base::StringPiece& key,
                                 const base::StringPiece& value) {
-  SetCrashKeyValueEx_ExportThunk(key.data(), value.data());
+  SetCrashKeyValueEx_ExportThunk(key.data(), key.size(), value.data(),
+                                 value.size());
 }
 
 void ClearCrashKeyValueTrampoline(const base::StringPiece& key) {
-  ClearCrashKeyValueEx_ExportThunk(key.data());
+  ClearCrashKeyValueEx_ExportThunk(key.data(), key.size());
 }
 
 }  // namespace
diff --git a/chrome/common/extensions/docs/examples/tutorials/getstarted/manifest.json b/chrome/common/extensions/docs/examples/tutorials/getstarted/manifest.json
index 4c9192c..28a1fda3 100644
--- a/chrome/common/extensions/docs/examples/tutorials/getstarted/manifest.json
+++ b/chrome/common/extensions/docs/examples/tutorials/getstarted/manifest.json
@@ -2,7 +2,7 @@
   "manifest_version": 2,
 
   "name": "Getting started example",
-  "description": "This extension shows a Google Image search result for the current page",
+  "description": "This extension allows the user to change the background color of the current page.",
   "version": "1.0",
 
   "browser_action": {
@@ -11,6 +11,6 @@
   },
   "permissions": [
     "activeTab",
-    "https://ajax.googleapis.com/"
+    "storage"
   ]
 }
diff --git a/chrome/common/extensions/docs/examples/tutorials/getstarted/popup.html b/chrome/common/extensions/docs/examples/tutorials/getstarted/popup.html
index c88cc9c0..1a96b226 100644
--- a/chrome/common/extensions/docs/examples/tutorials/getstarted/popup.html
+++ b/chrome/common/extensions/docs/examples/tutorials/getstarted/popup.html
@@ -7,17 +7,20 @@
 <html>
   <head>
     <title>Getting Started Extension's Popup</title>
-    <style>
+    <style type="text/css">
       body {
-        font-family: "Segoe UI", "Lucida Grande", Tahoma, sans-serif;
-        font-size: 100%;
+        margin: 10px;
+        white-space: nowrap;
       }
-      #status {
-        /* avoid an excessively wide status text */
-        white-space: pre;
-        text-overflow: ellipsis;
-        overflow: hidden;
-        max-width: 400px;
+
+      h1 {
+        font-size: 15px;
+      }
+
+      #container {
+        align-items: center;
+        display: flex;
+        justify-content: space-between;
       }
     </style>
 
@@ -26,12 +29,21 @@
       - Policy documentation[1] for details and explanation.
       -
       - [1]: https://developer.chrome.com/extensions/contentSecurityPolicy
-     -->
+    -->
     <script src="popup.js"></script>
   </head>
+
   <body>
-    <div id="status"></div>
-    <img id="image-result" hidden>
+    <h1>Background Color Changer</h1>
+    <div id="container">
+      <span>Choose a color</span>
+      <select id="dropdown">
+        <option selected disabled hidden value=''></option>
+        <option value="white">White</option>
+        <option value="pink">Pink</option>
+        <option value="green">Green</option>
+        <option value="yellow">Yellow</option>
+      </select>
+    </div>
   </body>
 </html>
-
diff --git a/chrome/common/extensions/docs/examples/tutorials/getstarted/popup.js b/chrome/common/extensions/docs/examples/tutorials/getstarted/popup.js
index 964f3297..30ea3f55 100644
--- a/chrome/common/extensions/docs/examples/tutorials/getstarted/popup.js
+++ b/chrome/common/extensions/docs/examples/tutorials/getstarted/popup.js
@@ -5,7 +5,7 @@
 /**
  * Get the current URL.
  *
- * @param {function(string)} callback - called when the URL of the current tab
+ * @param {function(string)} callback called when the URL of the current tab
  *   is found.
  */
 function getCurrentTabUrl(callback) {
@@ -16,7 +16,7 @@
     currentWindow: true
   };
 
-  chrome.tabs.query(queryInfo, function(tabs) {
+  chrome.tabs.query(queryInfo, (tabs) => {
     // chrome.tabs.query invokes the callback with a list of tabs that match the
     // query. When the popup is opened, there is certainly a window and at least
     // one tab, so we can safely assume that |tabs| is a non-empty array.
@@ -41,78 +41,86 @@
   // you CANNOT do something like this:
   //
   // var url;
-  // chrome.tabs.query(queryInfo, function(tabs) {
+  // chrome.tabs.query(queryInfo, (tabs) => {
   //   url = tabs[0].url;
   // });
   // alert(url); // Shows "undefined", because chrome.tabs.query is async.
 }
 
 /**
- * @param {string} searchTerm - Search term for Google Image search.
- * @param {function(string,number,number)} callback - Called when an image has
- *   been found. The callback gets the URL, width and height of the image.
- * @param {function(string)} errorCallback - Called when the image is not found.
- *   The callback gets a string that describes the failure reason.
+ * Change the background color of the current page.
+ *
+ * @param {string} color The new background color.
  */
-function getImageUrl(searchTerm, callback, errorCallback) {
-  // Google image search - 100 searches per day.
-  // https://developers.google.com/image-search/
-  var searchUrl = 'https://ajax.googleapis.com/ajax/services/search/images' +
-    '?v=1.0&q=' + encodeURIComponent(searchTerm);
-  var x = new XMLHttpRequest();
-  x.open('GET', searchUrl);
-  // The Google image search API responds with JSON, so let Chrome parse it.
-  x.responseType = 'json';
-  x.onload = function() {
-    // Parse and process the response from Google Image Search.
-    var response = x.response;
-    if (!response || !response.responseData || !response.responseData.results ||
-        response.responseData.results.length === 0) {
-      errorCallback('No response from Google Image search!');
-      return;
-    }
-    var firstResult = response.responseData.results[0];
-    // Take the thumbnail instead of the full image to get an approximately
-    // consistent image size.
-    var imageUrl = firstResult.tbUrl;
-    var width = parseInt(firstResult.tbWidth);
-    var height = parseInt(firstResult.tbHeight);
-    console.assert(
-        typeof imageUrl == 'string' && !isNaN(width) && !isNaN(height),
-        'Unexpected respose from the Google Image Search API!');
-    callback(imageUrl, width, height);
-  };
-  x.onerror = function() {
-    errorCallback('Network error.');
-  };
-  x.send();
+function changeBackgroundColor(color) {
+  var script = 'document.body.style.backgroundColor="' + color + '";';
+  // See https://developer.chrome.com/extensions/tabs#method-executeScript.
+  // chrome.tabs.executeScript allows us to programmatically inject JavaScript
+  // into a page. Since we omit the optional first argument "tabId", the script
+  // is inserted into the active tab of the current window, which serves as the
+  // default.
+  chrome.tabs.executeScript({
+    code: script
+  });
 }
 
-function renderStatus(statusText) {
-  document.getElementById('status').textContent = statusText;
+/**
+ * Gets the saved background color for url.
+ *
+ * @param {string} url URL whose background color is to be retrieved.
+ * @param {function(string)} callback called with the saved background color for
+ *     the given url on success, or a falsy value if no color is retrieved.
+ */
+function getSavedBackgroundColor(url, callback) {
+  // See https://developer.chrome.com/apps/storage#type-StorageArea. We check
+  // for chrome.runtime.lastError to ensure correctness even when the API call
+  // fails.
+  chrome.storage.sync.get(url, (items) => {
+    callback(chrome.runtime.lastError ? null : items[url]);
+  });
 }
 
-document.addEventListener('DOMContentLoaded', function() {
-  getCurrentTabUrl(function(url) {
-    // Put the image URL in Google search.
-    renderStatus('Performing Google Image search for ' + url);
+/**
+ * Sets the given background color for url.
+ *
+ * @param {string} url URL for which background color is to be saved.
+ * @param {string} color The background color to be saved.
+ */
+function saveBackgroundColor(url, color) {
+  var items = {};
+  items[url] = color;
+  // See https://developer.chrome.com/apps/storage#type-StorageArea. We omit the
+  // optional callback since we don't need to perform any action once the
+  // background color is saved.
+  chrome.storage.sync.set(items);
+}
 
-    getImageUrl(url, function(imageUrl, width, height) {
+// This extension loads the saved background color for the current tab if one
+// exists. The user can select a new background color from the dropdown for the
+// current page, and it will be saved as part of the extension's isolated
+// storage. The chrome.storage API is used for this purpose. This is different
+// from the window.localStorage API, which is synchronous and stores data bound
+// to a document's origin. Also, using chrome.storage.sync instead of
+// chrome.storage.local allows the extension data to be synced across multiple
+// user devices.
+document.addEventListener('DOMContentLoaded', () => {
+  getCurrentTabUrl((url) => {
+    var dropdown = document.getElementById('dropdown');
 
-      renderStatus('Search term: ' + url + '\n' +
-          'Google image search result: ' + imageUrl);
-      var imageResult = document.getElementById('image-result');
-      // Explicitly set the width/height to minimize the number of reflows. For
-      // a single image, this does not matter, but if you're going to embed
-      // multiple external images in your page, then the absence of width/height
-      // attributes causes the popup to resize multiple times.
-      imageResult.width = width;
-      imageResult.height = height;
-      imageResult.src = imageUrl;
-      imageResult.hidden = false;
+    // Load the saved background color for this page and modify the dropdown
+    // value, if needed.
+    getSavedBackgroundColor(url, (savedColor) => {
+      if (savedColor) {
+        changeBackgroundColor(savedColor);
+        dropdown.value = savedColor;
+      }
+    });
 
-    }, function(errorMessage) {
-      renderStatus('Cannot display image. ' + errorMessage);
+    // Ensure the background color is changed and saved when the dropdown
+    // selection changes.
+    dropdown.addEventListener('change', () => {
+      changeBackgroundColor(dropdown.value);
+      saveBackgroundColor(url, dropdown.value);
     });
   });
 });
diff --git a/chrome/common/extensions/docs/static/images/gettingstarted-preview.png b/chrome/common/extensions/docs/static/images/gettingstarted-preview.png
index 2bbad13..10b519a7 100644
--- a/chrome/common/extensions/docs/static/images/gettingstarted-preview.png
+++ b/chrome/common/extensions/docs/static/images/gettingstarted-preview.png
Binary files differ
diff --git a/chrome/common/extensions/docs/static/images/gettingstarted-tooltip-after.png b/chrome/common/extensions/docs/static/images/gettingstarted-tooltip-after.png
index 59e61c75..d9438ae 100644
--- a/chrome/common/extensions/docs/static/images/gettingstarted-tooltip-after.png
+++ b/chrome/common/extensions/docs/static/images/gettingstarted-tooltip-after.png
Binary files differ
diff --git a/chrome/common/extensions/docs/static/images/gettingstarted-tooltip-before.png b/chrome/common/extensions/docs/static/images/gettingstarted-tooltip-before.png
index 386e0cb..cc075de 100644
--- a/chrome/common/extensions/docs/static/images/gettingstarted-tooltip-before.png
+++ b/chrome/common/extensions/docs/static/images/gettingstarted-tooltip-before.png
Binary files differ
diff --git a/chrome/common/extensions/docs/templates/articles/getstarted.html b/chrome/common/extensions/docs/templates/articles/getstarted.html
index e1227a0..25977ed 100644
--- a/chrome/common/extensions/docs/templates/articles/getstarted.html
+++ b/chrome/common/extensions/docs/templates/articles/getstarted.html
@@ -6,21 +6,23 @@
   technologies that you're already familiar with from web development: HTML,
   CSS, and JavaScript. If you've ever built a web page, you should feel right at
   home with extensions pretty quickly; we'll put that to the test right now by
-  walking through the construction of a simple extension that will fetch an
-  image from Google using the current page's URL as a search term.
+  walking through the construction of a simple extension that will allow the
+  user to change the background color of the current webpage.
 </p>
 
 <p>
   We'll do so by implementing a UI element we call a
   <a href="browserAction">browser action</a>, which allows us to place a
   clickable icon right next to Chrome's Omnibox for easy access. Clicking that
-  icon will open a popup window filled with an image derived from the current
-  page, which will look something like this:
+  icon will open a popup window that will allow the user to choose the
+  background color of the current page. If the user had selected a background
+  color for the page earlier, the extension will remember the user's choice and
+  use it as the default, once the popup is clicked. Here is how it will look:
 </p>
 
 <img src="{{static}}/images/gettingstarted-preview"
-     width="558"
-     height="264"
+     width="614"
+     height="390"
      alt="Chrome with an extension's popup open.">
 
 <p>
@@ -45,8 +47,8 @@
   In our example's manifest,
   we will declare a <a href="browserAction">browser action</a>,
   the <a href="activeTab">activeTab permission</a> to see the URL of the current
-  tab, and the host <a href="declare_permissions">permission</a> to access the
-  external Google Image search API.
+  tab and the <a href="storage">storage permission</a> to remember the user's
+  choice of background color for a page.
 </p>
 
 <pre data-filename="manifest.json">
@@ -54,7 +56,7 @@
   "manifest_version": 2,
 
   "name": "Getting started example",
-  "description": "This extension shows a Google Image search result for the current page",
+  "description": "This extension allows the user to change the background color of the current page.",
   "version": "1.0",
 
   "browser_action": {
@@ -63,7 +65,7 @@
   },
   "permissions": [
     "activeTab",
-    "https://ajax.googleapis.com/"
+    "storage"
   ]
 }
 </pre>
@@ -114,8 +116,8 @@
     <p>
       The actual logic of rendering the content of the popup is implemented by
       <a href="examples/tutorials/getstarted/popup.js">popup.js</a>. You are
-      encouraged to read the elaborate comments in this file to learn more
-      about the logic.<br>
+      encouraged to read the comments in this file to learn more about the
+      logic.<br>
       <a href="examples/tutorials/getstarted/popup.js" download="popup.js">
         Download a copy of <code>popup.js</code> from our sample repository
       </a>, and save it into the directory you're working in.
@@ -150,8 +152,8 @@
            height="29"
            width="29"
            alt="The menu's icon is three horizontal bars."> and
-      select <strong>Extensions</strong> under the <strong>Tools</strong> menu
-      to get to the same place).
+      select <strong>Extensions</strong> under the <strong>More Tools</strong>
+      menu to get to the same place).
     </p>
   </li>
   <li>
@@ -216,7 +218,7 @@
   The manifest file is only parsed when the extension is loaded. If you want to
   see the previous changes in action, the extension has to be reloaded.
   Visit the extensions page (go to <strong>chrome://extensions</strong>, or
-  <strong>Tools &gt; Extensions</strong> under the Chrome menu), and click
+  <strong>More Tools &gt; Extensions</strong> under the Chrome menu), and click
   <strong>Reload</strong> under your extension.
   All extensions are also reloaded when the extensions page is reloaded, e.g.
   after hitting <kbd>F5<kbd> or <kbd>Ctrl</kbd>-<kbd>R</kbd>.
@@ -226,12 +228,12 @@
   Once you've reloaded the extension, hover over the browser action badge to see
   the new tooltip!<br>
   <img src="{{static}}/images/gettingstarted-tooltip-before.png"
-       width="160"
+       width="169"
        height="120"
        alt="&quot;Getting started example&quot; tooltip.">
 
   <img src="{{static}}/images/gettingstarted-tooltip-after.png"
-       width="160"
+       width="169"
        height="120"
        alt="&quot;Click here!&quot; tooltip, after modifying manifest.json and reloading the extension.">
 </p>
diff --git a/chrome/common/pref_names_util.h b/chrome/common/pref_names_util.h
index d418e9b..bdabc9f5 100644
--- a/chrome/common/pref_names_util.h
+++ b/chrome/common/pref_names_util.h
@@ -9,6 +9,9 @@
 
 namespace pref_names_util {
 
+// Prefs prefix for all font types. Ends in a period.
+extern const char kWebKitFontPrefPrefix[];
+
 // Extracts the generic family and script from font name pref path |pref_path|.
 // For example, if |pref_path| is "webkit.webprefs.fonts.serif.Hang", returns
 // true and sets |generic_family| to "serif" and |script| to "Hang".
diff --git a/chrome/common/profiling/BUILD.gn b/chrome/common/profiling/BUILD.gn
index 803f226..65bc121 100644
--- a/chrome/common/profiling/BUILD.gn
+++ b/chrome/common/profiling/BUILD.gn
@@ -16,7 +16,6 @@
     "memlog_sender_pipe_posix.h",
     "memlog_sender_pipe_win.cc",
     "memlog_sender_pipe_win.h",
-    "memlog_stream.cc",
     "memlog_stream.h",
     "profiling_constants.cc",
     "profiling_constants.h",
diff --git a/chrome/common/profiling/memlog.mojom b/chrome/common/profiling/memlog.mojom
index 8b8a7e60..fe780a72 100644
--- a/chrome/common/profiling/memlog.mojom
+++ b/chrome/common/profiling/memlog.mojom
@@ -28,5 +28,5 @@
   // On success, returns a valid shared_buffer handle and the size of the
   // data written. On failure, returns an invalid shared_buffer handle.
   DumpProcessForTracing(mojo.common.mojom.ProcessId pid) =>
-      (handle<shared_buffer> consumer, uint32 size);
+      (handle<shared_buffer>? consumer, uint32 size);
 };
diff --git a/chrome/common/profiling/memlog_sender_pipe_posix.cc b/chrome/common/profiling/memlog_sender_pipe_posix.cc
index d6c777b6..01f168bf 100644
--- a/chrome/common/profiling/memlog_sender_pipe_posix.cc
+++ b/chrome/common/profiling/memlog_sender_pipe_posix.cc
@@ -26,7 +26,16 @@
 
 bool MemlogSenderPipe::Send(const void* data, size_t sz) {
   base::AutoLock lock(lock_);
-  return mojo::edk::PlatformChannelWrite(handle_.get(), data, sz) != -1;
+  int size = static_cast<int>(sz);
+  while (size > 0) {
+    int r = mojo::edk::PlatformChannelWrite(handle_.get(), data, size);
+    if (r == -1)
+      return false;
+    DCHECK_LE(r, size);
+    size -= r;
+    data = static_cast<const char*>(data) + r;
+  }
+  return true;
 }
 
 }  // namespace profiling
diff --git a/chrome/common/profiling/memlog_stream.cc b/chrome/common/profiling/memlog_stream.cc
deleted file mode 100644
index f6234f4..0000000
--- a/chrome/common/profiling/memlog_stream.cc
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/common/profiling/memlog_stream.h"
-
-namespace profiling {
-
-#if defined(OS_WIN)
-const wchar_t kWindowsPipePrefix[] = L"\\\\.\\pipe\\chrome_mem.";
-#endif  // OS_WIN
-
-}  // namespace profiling
diff --git a/chrome/common/profiling/memlog_stream.h b/chrome/common/profiling/memlog_stream.h
index 034c9ff..08678e14 100644
--- a/chrome/common/profiling/memlog_stream.h
+++ b/chrome/common/profiling/memlog_stream.h
@@ -41,12 +41,6 @@
 };
 #pragma pack(pop)
 
-#if defined(OS_WIN)
-// Prefix for pipe name for communicating between chrome processes and the
-// memlog process. The pipe ID is appended to this to get the pipe name.
-extern const wchar_t kWindowsPipePrefix[];
-#endif  // OS_WIN
-
 }  // namespace profiling
 
 #endif  // CHROME_COMMON_PROFILING_MEMLOG_STREAM_H_
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 2e7c3c2..7e9f895 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -232,7 +232,6 @@
 const char kChromeUIPolicyHost[] = "policy";
 const char kChromeUIMdUserManagerHost[] = "md-user-manager";
 const char kChromeUIPredictorsHost[] = "predictors";
-const char kChromeUIProfilerHost[] = "profiler";
 const char kChromeUIQuotaInternalsHost[] = "quota-internals";
 const char kChromeUIQuitHost[] = "quit";
 const char kChromeUIRestartHost[] = "restart";
@@ -665,7 +664,6 @@
     kChromeUIPasswordManagerInternalsHost,
     kChromeUIPolicyHost,
     kChromeUIPredictorsHost,
-    kChromeUIProfilerHost,
     kChromeUIQuotaInternalsHost,
     kChromeUISignInInternalsHost,
     kChromeUISiteEngagementHost,
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index c1a55a2..a5237a7 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -218,7 +218,6 @@
 extern const char kChromeUIPrefsInternalsHost[];
 extern const char kChromeUIMdUserManagerHost[];
 extern const char kChromeUIPredictorsHost[];
-extern const char kChromeUIProfilerHost[];
 extern const char kChromeUIQuotaInternalsHost[];
 extern const char kChromeUIQuitHost[];
 extern const char kChromeUIRestartHost[];
diff --git a/chrome/installer/linux/debian/check-package-constraints-satisfied.py b/chrome/installer/linux/debian/check-package-constraints-satisfied.py
index 8141ed8..b69ef4b 100755
--- a/chrome/installer/linux/debian/check-package-constraints-satisfied.py
+++ b/chrome/installer/linux/debian/check-package-constraints-satisfied.py
@@ -14,6 +14,7 @@
 import sys
 
 import deb_version
+import package_version_interval
 
 if len(sys.argv) != 5:
   print 'Usage: %s binary_path sysroot_path arch stamp_path' % sys.argv[0]
@@ -47,43 +48,13 @@
   print 'stderr was ' + stderr
   sys.exit(1)
 
-def version_statement_satisfied(left_version, op, right_version):
-  # Allowed relationship operators are specified in:
-  # https://www.debian.org/doc/debian-policy/ch-relationships.html
-  if op == '>=':
-    return left_version >= right_version
-  if op == '<=':
-    return left_version <= right_version
-  if op == '>>':
-    return left_version > right_version
-  if op == '<<':
-    return left_version < right_version
-  assert op == '='
-  return left_version == right_version
-
-def get_package_and_version_requirement(dep):
-  package_name_regex = '[a-z][a-z0-9\+\-\.]+'
-  match = re.match('^(%s)$' % package_name_regex, dep)
-  if match:
-    return (match.group(1), lambda version: True)
-  match = re.match('^(%s) \(([\>\=\<]+) ([\~0-9A-Za-z\+\-\.\:]+)\)$' %
-                   package_name_regex, dep)
-  if match:
-    return (match.group(1), lambda version: version_statement_satisfied(
-        version, match.group(2), deb_version.DebVersion(match.group(3))))
-  # At the time of writing this script, Chrome does not have any
-  # complex version requirements like 'version >> 3 | version << 2'.
-  print ('Conjunctions and disjunctions in package version requirements are ' +
-         'not implemented at this time.')
-  sys.exit(1)
-
 deps_str = stdout.replace('shlibs:Depends=', '').replace('\n', '')
 deps = deps_str.split(', ')
-package_requirements = {}
+package_intervals = {}
 if deps_str != '':
   for dep in deps:
-    (package, requirement) = get_package_and_version_requirement(dep)
-    package_requirements[package] = requirement
+    (package, interval) = package_version_interval.parse_dep(dep)
+    package_intervals[package] = interval
 
 script_dir = os.path.dirname(os.path.abspath(__file__))
 deps_file = os.path.join(script_dir, 'dist-package-versions.json')
@@ -91,7 +62,7 @@
 
 ret_code = 0
 for distro in distro_package_versions:
-  for package in package_requirements:
+  for package in package_intervals:
     if package not in distro_package_versions[distro]:
       print >> sys.stderr, (
           'Unexpected new dependency %s on distro %s caused by binary %s' % (
@@ -100,7 +71,7 @@
       continue
     distro_version = deb_version.DebVersion(
         distro_package_versions[distro][package])
-    if not package_requirements[package](distro_version):
+    if not package_intervals[package].contains(distro_version):
       print >> sys.stderr, 'Dependency on package %s not satisfiable on %s' % (
           package, distro)
       ret_code = 1
diff --git a/chrome/installer/linux/debian/package_version_interval.py b/chrome/installer/linux/debian/package_version_interval.py
new file mode 100755
index 0000000..0aca52fe
--- /dev/null
+++ b/chrome/installer/linux/debian/package_version_interval.py
@@ -0,0 +1,152 @@
+#!/usr/bin/env python
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+import sys
+
+import deb_version
+
+class PackageVersionIntervalEndpoint:
+  def __init__(self, is_open, is_inclusive, version):
+    self._is_open = is_open;
+    self._is_inclusive = is_inclusive
+    self._version = version
+
+  def _intersect(self, other, is_start):
+    if self._is_open and other._is_open:
+      return self
+    if self._is_open:
+      return other
+    if other._is_open:
+      return self
+    cmp_code = self._version.__cmp__(other._version)
+    if not is_start:
+      cmp_code *= -1
+    if cmp_code > 0:
+      return self
+    if cmp_code < 0:
+      return other
+    if not self._is_inclusive:
+      return self
+    return other
+
+  def __str__(self):
+    return 'PackageVersionIntervalEndpoint(%s, %s, %s)' % (
+        self._is_open, self._is_inclusive, self._version)
+
+  def __eq__(self, other):
+    if self._is_open and other._is_open:
+      return True
+    return (self._is_open == other._is_open and
+            self._is_inclusive == other._is_inclusive and
+            self._version == other._version)
+
+
+class PackageVersionInterval:
+  def __init__(self, start, end):
+    self.start = start
+    self.end = end
+
+  def contains(self, version):
+    if not self.start._is_open:
+      if self.start._is_inclusive:
+        if version < self.start._version:
+          return False
+      elif version <= self.start._version:
+        return False
+    if not self.end._is_open:
+      if self.end._is_inclusive:
+        if version > self.end._version:
+          return False
+      elif version >= self.end._version:
+        return False
+    return True
+
+  def intersect(self, other):
+    return PackageVersionInterval(self.start._intersect(other.start, True),
+                                  self.end._intersect(other.end, False))
+
+  def __str__(self):
+    return 'PackageVersionInterval(%s, %s)' % (str(self.start), str(self.end))
+
+  def __eq__(self, other):
+    return self.start == other.start and self.end == other.end
+
+
+def version_interval_from_exp(op, version):
+  open_endpoint = PackageVersionIntervalEndpoint(True, None, None)
+  inclusive_endpoint = PackageVersionIntervalEndpoint(False, True, version)
+  exclusive_endpoint = PackageVersionIntervalEndpoint(False, False, version)
+  if op == '>=':
+    return PackageVersionInterval(inclusive_endpoint, open_endpoint)
+  if op == '<=':
+    return PackageVersionInterval(open_endpoint, inclusive_endpoint)
+  if op == '>>' or op == '>':
+    return PackageVersionInterval(exclusive_endpoint, open_endpoint)
+  if op == '<<' or op == '<':
+    return PackageVersionInterval(open_endpoing, exclusive_endpoint)
+  assert op == '='
+  return PackageVersionInterval(inclusive_endpoint, inclusive_endpoint)
+
+
+def parse_dep(dep):
+  """Parses a package and version requirement formatted by dpkg-shlibdeps.
+
+  Args:
+      dep: A string of the format "package (op version)"
+
+  Returns:
+      A tuple of the form (package_string, PackageVersionInterval)
+  """
+  package_name_regex = '[a-z][a-z0-9\+\-\.]+'
+  match = re.match('^(%s)$' % package_name_regex, dep)
+  if match:
+    return (match.group(1), PackageVersionInterval(
+        PackageVersionIntervalEndpoint(True, None, None),
+        PackageVersionIntervalEndpoint(True, None, None)))
+  match = re.match('^(%s) \(([\>\=\<]+) ([\~0-9A-Za-z\+\-\.\:]+)\)$' %
+                   package_name_regex, dep)
+  if match:
+    return (match.group(1), version_interval_from_exp(
+        match.group(2), deb_version.DebVersion(match.group(3))))
+  # At the time of writing this script, Chrome does not have any
+  # complex version requirements like 'version >> 3 | version << 2'.
+  print ('Conjunctions and disjunctions in package version requirements are ' +
+         'not implemented at this time.')
+  sys.exit(1)
+
+
+def format_package_intervals(m):
+  """Formats package versions suitable for use by dpkg-deb.
+
+  Args:
+      m: A map from package names to PackageVersionInterval's
+
+  Returns:
+      A formatted string of package-versions for use by dpkg-deb.  Ex:
+          package1 (< left_endpoint1)
+          package1 (>= right_endpoint1)
+          package2 (< left_endpoint2)
+          package2 (>= right_endpoint2)
+  """
+  lines = []
+  for package in m:
+    interval = m[package]
+    if interval.start._is_open and interval.end._is_open:
+      lines.append(package + '\n')
+    elif (not interval.start._is_open and not interval.end._is_open and
+          interval.start._version == interval.end._version):
+      assert interval.start._is_inclusive and interval.end._is_inclusive
+      lines.append(package + ' (= ' + str(interval.start._version) + ')\n')
+    else:
+      if not interval.start._is_open:
+        op = '>=' if interval.start._is_inclusive else '>>'
+        lines.append(package + ' (' + op + ' ' +
+                     str(interval.start._version) +')\n')
+      if not interval.end._is_open:
+        op = '<=' if interval.end._is_inclusive else '<<'
+        lines.append(package + ' (' + op + ' ' +
+                     str(interval.end._version) + ')\n')
+  return ''.join(sorted(lines))
diff --git a/chrome/installer/linux/debian/package_version_interval_test.py b/chrome/installer/linux/debian/package_version_interval_test.py
new file mode 100755
index 0000000..bdd66054
--- /dev/null
+++ b/chrome/installer/linux/debian/package_version_interval_test.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import deb_version
+import package_version_interval
+
+def make_interval(start_open, start_inclusive, start_cmp,
+                  end_open, end_inclusive, end_cmp):
+  start = package_version_interval.PackageVersionIntervalEndpoint(
+      start_open, start_inclusive, start_cmp)
+  end = package_version_interval.PackageVersionIntervalEndpoint(
+      end_open, end_inclusive, end_cmp)
+  return package_version_interval.PackageVersionInterval(start, end)
+
+
+# intersect() test.
+assert (make_interval(True, None, None, False, True, 10).intersect(
+    make_interval(False, True, 5, True, None, None)) ==
+      make_interval(False, True, 5, False, True, 10))
+assert (make_interval(False, True, 3, False, True, 7).intersect(
+    make_interval(False, True, 4, False, True, 6)) ==
+      make_interval(False, True, 4, False, True, 6))
+assert (make_interval(False, False, 3, False, False, 7).intersect(
+    make_interval(False, True, 3, False, True, 7)) ==
+      make_interval(False, False, 3, False, False, 7))
+
+# contains() test.
+assert make_interval(False, False, 3, False, False, 7).contains(5)
+assert not make_interval(False, False, 3, False, False, 7).contains(3)
+assert not make_interval(False, False, 3, False, False, 7).contains(7)
+assert make_interval(False, True, 3, False, True, 7).contains(3)
+assert make_interval(False, True, 3, False, True, 7).contains(7)
+assert make_interval(True, None, None, False, True, 7).contains(5)
+assert make_interval(False, True, 3, True, None, None).contains(5)
+assert not make_interval(True, None, None, False, True, 7).contains(8)
+assert not make_interval(False, True, 3, True, None, None).contains(2)
+
+# parse_dep() test.
+assert (package_version_interval.parse_dep('libfoo (> 1.0)') ==
+        ('libfoo', make_interval(False, False, deb_version.DebVersion('1.0'),
+                                 True, None, None)))
+assert (package_version_interval.parse_dep('libbar (>> a.b.c)') ==
+        ('libbar', make_interval(False, False, deb_version.DebVersion('a.b.c'),
+                                 True, None, None)))
+assert (package_version_interval.parse_dep('libbaz (= 2:1.2.3-1)') ==
+        ('libbaz', make_interval(
+            False, True, deb_version.DebVersion('2:1.2.3-1'),
+            False, True, deb_version.DebVersion('2:1.2.3-1'))))
+
+# format_package_intervals() test.
+actual = package_version_interval.format_package_intervals({
+    'a': make_interval(True, None, None, True, None, None),
+    'b': make_interval(False, False, 1, True, None, None),
+    'c': make_interval(True, None, None, False, False, 2),
+    'd': make_interval(False, True, 3, True, None, None),
+    'e': make_interval(True, None, None, False, True, 4),
+    'f': make_interval(False, True, 5, False, True, 5),
+    'g': make_interval(False, False, 6, False, False, 7),
+})
+expected = """a
+b (>> 1)
+c (<< 2)
+d (>= 3)
+e (<= 4)
+f (= 5)
+g (<< 7)
+g (>> 6)
+"""
+assert expected == actual
diff --git a/chrome/profiling/json_exporter.cc b/chrome/profiling/json_exporter.cc
index 02a25b1..669b467 100644
--- a/chrome/profiling/json_exporter.cc
+++ b/chrome/profiling/json_exporter.cc
@@ -6,6 +6,7 @@
 
 #include <map>
 
+#include "base/containers/adapters.h"
 #include "base/format_macros.h"
 #include "base/json/json_writer.h"
 #include "base/json/string_escape.h"
@@ -126,7 +127,8 @@
                               BacktraceTable* backtrace_table,
                               StringTable* string_table) {
   int parent = -1;
-  for (const Address& addr : backtrace.addrs()) {
+  // Addresses must be outputted in reverse order.
+  for (const Address& addr : base::Reversed(backtrace.addrs())) {
     static constexpr char kPcPrefix[] = "pc:";
     // std::numeric_limits<>::digits gives the number of bits in the value.
     // Dividing by 4 gives the number of hex digits needed to store the value.
diff --git a/chrome/profiling/json_exporter_unittest.cc b/chrome/profiling/json_exporter_unittest.cc
index 3163ab70..9921337 100644
--- a/chrome/profiling/json_exporter_unittest.cc
+++ b/chrome/profiling/json_exporter_unittest.cc
@@ -182,14 +182,14 @@
   BacktraceStorage backtrace_storage;
 
   std::vector<Address> stack1;
-  stack1.push_back(Address(0x1234));
   stack1.push_back(Address(0x5678));
+  stack1.push_back(Address(0x1234));
   const Backtrace* bt1 = backtrace_storage.Insert(std::move(stack1));
 
   std::vector<Address> stack2;
-  stack2.push_back(Address(0x1234));
-  stack2.push_back(Address(0x9012));
   stack2.push_back(Address(0x9013));
+  stack2.push_back(Address(0x9012));
+  stack2.push_back(Address(0x1234));
   const Backtrace* bt2 = backtrace_storage.Insert(std::move(stack2));
 
   AllocationEventSet events;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 143a19f..2feafadf 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3149,6 +3149,7 @@
     "../browser/engagement/site_engagement_service_unittest.cc",
     "../browser/external_protocol/external_protocol_handler_unittest.cc",
     "../browser/file_select_helper_unittest.cc",
+    "../browser/font_pref_change_notifier_unittest.cc",
     "../browser/gcm/fake_gcm_profile_service.cc",
     "../browser/gcm/fake_gcm_profile_service.h",
     "../browser/geolocation/geolocation_permission_context_unittest.cc",
@@ -3372,7 +3373,6 @@
     "../browser/sync/profile_sync_service_factory_unittest.cc",
     "../browser/sync/sessions/sync_sessions_web_contents_router_unittest.cc",
     "../browser/sync/sync_startup_tracker_unittest.cc",
-    "../browser/task_profiler/task_profiler_data_serializer_unittest.cc",
     "../browser/thumbnails/thumbnail_service_unittest.cc",
     "../browser/thumbnails/thumbnail_utils_unittest.cc",
     "../browser/tracing/background_tracing_field_trial_unittest.cc",
@@ -4092,6 +4092,7 @@
       "../browser/extensions/extension_user_script_loader_unittest.cc",
       "../browser/extensions/extension_web_ui_unittest.cc",
       "../browser/extensions/external_policy_loader_unittest.cc",
+      "../browser/extensions/external_pref_loader_unittest.cc",
       "../browser/extensions/external_provider_impl_chromeos_unittest.cc",
       "../browser/extensions/external_provider_impl_unittest.cc",
       "../browser/extensions/favicon_downloader_unittest.cc",
@@ -4297,7 +4298,6 @@
       "../browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc",
       "../browser/ui/ash/launcher/launcher_context_menu_unittest.cc",
       "../browser/ui/ash/multi_user/multi_user_context_menu_chromeos_unittest.cc",
-      "../browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos_unittest.cc",
       "../browser/ui/ash/multi_user/multi_user_util_chromeos_unittest.cc",
       "../browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc",
       "../browser/ui/ash/multi_user/user_switch_util_unittest.cc",
diff --git a/chrome/test/data/extensions/api_test/networking_private/chromeos/test.js b/chrome/test/data/extensions/api_test/networking_private/chromeos/test.js
index b24f3f0..0274335 100644
--- a/chrome/test/data/extensions/api_test/networking_private/chromeos/test.js
+++ b/chrome/test/data/extensions/api_test/networking_private/chromeos/test.js
@@ -59,18 +59,33 @@
     chrome.networkingPrivate.onNetworksChanged.addListener(
         this.onNetworkChange);
   },
-  listListener: function(expected, done) {
-    var self = this;
-    this.listenForChanges = function(list) {
+  networkListChangedListener: function(expected, done) {
+    function listener(list) {
       assertEq(expected, list);
-      chrome.networkingPrivate.onNetworkListChanged.removeListener(
-          self.listenForChanges);
+      chrome.networkingPrivate.onNetworkListChanged.removeListener(listener);
       done();
     };
+    this.start = function() {
+      chrome.networkingPrivate.onNetworkListChanged.addListener(listener);
+    };
   },
-  watchForCaptivePortalState: function(expectedGuid,
-                                       expectedState,
-                                       done) {
+  networksChangedListener: function(guid, test, done) {
+    function listener(changes) {
+      for (let c of changes) {
+        if (c != guid)
+          continue;
+        chrome.networkingPrivate.onNetworksChanged.removeListener(listener);
+        chrome.networkingPrivate.getProperties(guid, function(result) {
+          if (test(result))
+            done();
+        });
+      }
+    };
+    this.start = function() {
+      chrome.networkingPrivate.onNetworksChanged.addListener(listener);
+    };
+  },
+  watchForCaptivePortalState: function(expectedGuid, expectedState, done) {
     var self = this;
     this.onPortalDetectionCompleted = function(guid, state) {
       assertEq(expectedGuid, guid);
@@ -509,11 +524,22 @@
                     'stub_vpn2_guid',
                     'stub_wifi2_guid'];
     var done = chrome.test.callbackAdded();
-    var listener = new privateHelpers.listListener(expected, done);
-    chrome.networkingPrivate.onNetworkListChanged.addListener(
-      listener.listenForChanges);
+    var listener =
+        new privateHelpers.networkListChangedListener(expected, done);
+    listener.start();
     chrome.networkingPrivate.requestNetworkScan();
   },
+  function requestNetworkScanCellular() {
+    var done = chrome.test.callbackAdded();
+    var listener = new privateHelpers.networksChangedListener(
+        kCellularGuid, function(result) {
+          var cellular = result.Cellular;
+          return cellular && cellular.FoundNetworks &&
+              cellular.FoundNetworks[0].Status == 'available';
+        }, done);
+    listener.start();
+    chrome.networkingPrivate.requestNetworkScan('Cellular');
+  },
   function getProperties() {
     chrome.networkingPrivate.getProperties(
       'stub_wifi1_guid',
@@ -803,9 +829,9 @@
                     'stub_wifi1_guid',
                     'stub_vpn2_guid'];
     var done = chrome.test.callbackAdded();
-    var listener = new privateHelpers.listListener(expected, done);
-    chrome.networkingPrivate.onNetworkListChanged.addListener(
-      listener.listenForChanges);
+    var listener =
+        new privateHelpers.networkListChangedListener(expected, done);
+    listener.start();
     var network = 'stub_wifi2_guid';
     chrome.networkingPrivate.startConnect(network, networkCallbackPass());
   },
diff --git a/chrome/test/data/extensions/api_test/networking_private/test.js b/chrome/test/data/extensions/api_test/networking_private/test.js
index 3ca9ca05..febf79e 100644
--- a/chrome/test/data/extensions/api_test/networking_private/test.js
+++ b/chrome/test/data/extensions/api_test/networking_private/test.js
@@ -80,6 +80,7 @@
   },
   function requestNetworkScan() {
     chrome.networkingPrivate.requestNetworkScan();
+    chrome.networkingPrivate.requestNetworkScan('Cellular');
     chrome.test.succeed();
   },
   function startConnect() {
diff --git a/chrome/test/data/extensions/platform_apps/web_view/focus_sync/focus_test.html b/chrome/test/data/extensions/platform_apps/web_view/focus_sync/focus_test.html
new file mode 100644
index 0000000..727d01e6
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/web_view/focus_sync/focus_test.html
@@ -0,0 +1,51 @@
+<!--
+ * Copyright 2017 The Chromium Authors. All rights reserved.  Use of this
+ * source code is governed by a BSD-style license that can be found in the
+ * LICENSE file.
+-->
+
+<html>
+<style type="text/css">
+input {
+  background-color: rgb(200, 200, 200);
+}
+form :focus {
+  background-color: rgb(100, 100, 100);
+}
+</style>
+
+<script>
+
+function $(id) {
+  return document.getElementById(id);
+}
+
+/**
+  * checkValid returns true if the test should pass. It asserts that #text is
+  * the focused element and that #pass is not focused. It does so by matching
+  * the style properties to ensure they match with the :focus selector defined
+  * above.
+  */
+window.checkValid = function() {
+  var textColor = window.getComputedStyle($('text'),
+      null).getPropertyValue('background-color');
+  var passColor = window.getComputedStyle($('pass'),
+      null).getPropertyValue('background-color');
+  return textColor == 'rgb(100, 100, 100)' && passColor== 'rgb(200, 200, 200)';
+};
+
+window.addEventListener('DOMContentLoaded', function() {
+  $('text').focus();
+});
+
+
+</script>
+
+<body>
+  <form id="form">
+    <input id="text" type="text" placeholder="text input">
+    <input id="pass" type="password" placeholder="password">
+  </form>
+</body>
+</html>
+
diff --git a/chrome/test/data/extensions/platform_apps/web_view/focus_sync/main.html b/chrome/test/data/extensions/platform_apps/web_view/focus_sync/main.html
new file mode 100644
index 0000000..79afbc8
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/web_view/focus_sync/main.html
@@ -0,0 +1,20 @@
+<!--
+ * Copyright 2017 The Chromium Authors. All rights reserved.  Use of this
+ * source code is governed by a BSD-style license that can be found in the
+ * LICENSE file.
+-->
+
+<html>
+  <head>
+    <script src="main.js"></script>
+    <style>
+      .full-overlay {
+        width: 100%;
+        height: 200px;
+      }
+    </style>
+  </head>
+  <body>
+    <webview id="webview" tabIndex="0" class='full-overlay'></webview>
+  </body>
+</html>
diff --git a/chrome/test/data/extensions/platform_apps/web_view/focus_sync/main.js b/chrome/test/data/extensions/platform_apps/web_view/focus_sync/main.js
new file mode 100644
index 0000000..1585b26
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/web_view/focus_sync/main.js
@@ -0,0 +1,33 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+'use strict';
+
+function $(id) {
+  return document.getElementById(id);
+}
+
+window.reloadWebview = function() {
+    $('webview').reload();
+    $('webview').focus();
+};
+
+window.addEventListener('DOMContentLoaded', function() {
+  var webview = document.querySelector('#webview');
+  webview.addEventListener('loadstop', function() {
+    setTimeout(function() {
+      chrome.test.sendMessage('WebViewTest.LAUNCHED');
+    }, 0);
+    if(document.hasFocus()) {
+      chrome.test.sendMessage('WebViewTest.WEBVIEW_LOADED');
+    } else {
+      var wasFocused = function() {
+        chrome.test.sendMessage('WebViewTest.WEBVIEW_LOADED');
+        window.removeEventListener('focus', wasFocused);
+      }
+      window.addEventListener('focus', wasFocused);
+    }
+  });
+  webview.focus();
+  webview.src = 'focus_test.html';
+});
diff --git a/chrome/test/data/extensions/platform_apps/web_view/focus_sync/manifest.json b/chrome/test/data/extensions/platform_apps/web_view/focus_sync/manifest.json
new file mode 100644
index 0000000..836e6111
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/web_view/focus_sync/manifest.json
@@ -0,0 +1,18 @@
+{
+  "name": "Packaged App Test: <webview> Focus Sync",
+  "description": "Loads and focuses a guest which will focus one of its elements",
+  "version": "1",
+  "permissions": [
+    "webview"
+  ],
+  "app": {
+    "background": {
+      "scripts": ["test.js"]
+    }
+  },
+  "webview" : {
+    "partitions": [
+      {"name": "*", "accessible_resources": ["*"]}
+    ]
+  }
+}
diff --git a/chrome/test/data/extensions/platform_apps/web_view/focus_sync/test.js b/chrome/test/data/extensions/platform_apps/web_view/focus_sync/test.js
new file mode 100644
index 0000000..f469dd2
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/web_view/focus_sync/test.js
@@ -0,0 +1,12 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+chrome.app.runtime.onLaunched.addListener(function() {
+  chrome.app.window.create(
+      'main.html',
+      {
+        'focused': false,
+        'innerBounds': { "width": 640, "height": 480 }
+      });
+});
diff --git a/chromecast/media/cma/backend/android/cast_media_android.cc b/chromecast/media/cma/backend/android/cast_media_android.cc
index d4df095..ffe05f8 100644
--- a/chromecast/media/cma/backend/android/cast_media_android.cc
+++ b/chromecast/media/cma/backend/android/cast_media_android.cc
@@ -6,13 +6,16 @@
 #include "base/at_exit.h"
 #include "base/logging.h"
 #include "chromecast/chromecast_features.h"
-#include "chromecast/media/cma/backend/android/loopback_audio_manager.h"
 #include "chromecast/media/cma/backend/android/media_pipeline_backend_android.h"
 #include "chromecast/public/cast_media_shlib.h"
 #include "chromecast/public/graphics_types.h"
 #include "chromecast/public/video_plane.h"
 #include "chromecast/public/volume_control.h"
 
+#if BUILDFLAG(ENABLE_ATHINGS_LOOPBACK)
+#include "chromecast/media/cma/backend/android/loopback_audio_manager.h"
+#endif  // BUILDFLAG(ENABLE_ATHINGS_LOOPBACK)
+
 namespace chromecast {
 namespace media {
 namespace {
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 98ece30..f106c4f3 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-9913.0.0
\ No newline at end of file
+9916.0.0
\ No newline at end of file
diff --git a/chromeos/dbus/fake_shill_device_client.cc b/chromeos/dbus/fake_shill_device_client.cc
index a309178..ed8ca28a 100644
--- a/chromeos/dbus/fake_shill_device_client.cc
+++ b/chromeos/dbus/fake_shill_device_client.cc
@@ -11,6 +11,7 @@
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
 #include "base/single_thread_task_runner.h"
+#include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
@@ -96,6 +97,7 @@
 
 void FakeShillDeviceClient::ProposeScan(const dbus::ObjectPath& device_path,
                                         VoidDBusMethodCallback callback) {
+  // Deprecated.
   PostVoidCallback(std::move(callback), DBUS_METHOD_CALL_SUCCESS);
 }
 
@@ -399,6 +401,10 @@
     properties->SetKey(shill::kCellularAllowRoamingProperty,
                        base::Value(false));
   }
+  if (type == shill::kTypeWifi) {
+    properties->SetKey(shill::kMACAddressRandomizationSupportedProperty,
+                       base::Value(false));
+  }
 }
 
 void FakeShillDeviceClient::RemoveDevice(const std::string& device_path) {
@@ -458,6 +464,44 @@
   SetSimLockStatus(device_path, status);
 }
 
+void FakeShillDeviceClient::AddCellularFoundNetwork(
+    const std::string& device_path) {
+  base::Value* device_properties = stub_devices_.FindKey(device_path);
+  if (!device_properties || !device_properties->is_dict()) {
+    LOG(ERROR) << "Device path not found: " << device_path;
+    return;
+  }
+  std::string type =
+      device_properties->FindKey(shill::kTypeProperty)->GetString();
+  if (type != shill::kTypeCellular) {
+    LOG(ERROR) << "AddCellularNetwork called for non Cellular network: "
+               << device_path;
+    return;
+  }
+
+  // Add a new scan result entry
+  base::Value* scan_results =
+      device_properties->FindKey(shill::kFoundNetworksProperty);
+  if (!scan_results) {
+    scan_results = device_properties->SetKey(shill::kFoundNetworksProperty,
+                                             base::ListValue());
+  }
+  base::DictionaryValue new_result;
+  int idx = static_cast<int>(scan_results->GetList().size());
+  new_result.SetKey(shill::kNetworkIdProperty,
+                    base::Value(base::StringPrintf("network%d", idx)));
+  new_result.SetKey(shill::kLongNameProperty,
+                    base::Value(base::StringPrintf("Network %d", idx)));
+  new_result.SetKey(shill::kTechnologyProperty, base::Value("GSM"));
+  new_result.SetKey(shill::kStatusProperty, base::Value("available"));
+  scan_results->GetList().push_back(std::move(new_result));
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::Bind(&FakeShillDeviceClient::NotifyObserversPropertyChanged,
+                 weak_ptr_factory_.GetWeakPtr(), dbus::ObjectPath(device_path),
+                 shill::kFoundNetworksProperty));
+}
+
 // Private Methods -------------------------------------------------------------
 
 FakeShillDeviceClient::SimLockStatus FakeShillDeviceClient::GetSimLockStatus(
diff --git a/chromeos/dbus/fake_shill_device_client.h b/chromeos/dbus/fake_shill_device_client.h
index 9a9933c8..3ce1e9eca 100644
--- a/chromeos/dbus/fake_shill_device_client.h
+++ b/chromeos/dbus/fake_shill_device_client.h
@@ -112,6 +112,7 @@
   void SetTDLSBusyCount(int count) override;
   void SetTDLSState(const std::string& state) override;
   void SetSimLocked(const std::string& device_path, bool locked) override;
+  void AddCellularFoundNetwork(const std::string& device_path) override;
 
   static const char kDefaultSimPin[];
   static const int kSimPinRetryCount;
diff --git a/chromeos/dbus/fake_shill_manager_client.cc b/chromeos/dbus/fake_shill_manager_client.cc
index a1000d4..a9c8eca 100644
--- a/chromeos/dbus/fake_shill_manager_client.cc
+++ b/chromeos/dbus/fake_shill_manager_client.cc
@@ -195,15 +195,15 @@
                                          const ErrorCallback& error_callback) {
   VLOG(1) << "RequestScan: " << type;
   // For Stub purposes, default to a Wifi scan.
-  std::string device_type = shill::kTypeWifi;
-  if (!type.empty())
-    device_type = type;
+  std::string device_type = type.empty() ? shill::kTypeWifi : type;
   ShillDeviceClient::TestInterface* device_client =
       DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface();
   std::string device_path = device_client->GetDevicePathForType(device_type);
   if (!device_path.empty()) {
     device_client->SetDeviceProperty(device_path, shill::kScanningProperty,
                                      base::Value(true));
+    if (device_type == shill::kTypeCellular)
+      device_client->AddCellularFoundNetwork(device_path);
   }
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
diff --git a/chromeos/dbus/power_policy_controller.cc b/chromeos/dbus/power_policy_controller.cc
index 1996e2f..da224d450 100644
--- a/chromeos/dbus/power_policy_controller.cc
+++ b/chromeos/dbus/power_policy_controller.cc
@@ -124,6 +124,16 @@
     str += base::StringPrintf("battery_idle=%d ", policy.battery_idle_action());
   if (policy.has_lid_closed_action())
     str += base::StringPrintf("lid_closed=%d ", policy.lid_closed_action());
+  if (policy.has_screen_wake_lock()) {
+    str +=
+        base::StringPrintf("screen_wake_lock=%d ", policy.screen_wake_lock());
+  }
+  if (policy.has_dim_wake_lock())
+    str += base::StringPrintf("dim_wake_lock=%d ", policy.dim_wake_lock());
+  if (policy.has_system_wake_lock()) {
+    str +=
+        base::StringPrintf("system_wake_lock=%d ", policy.system_wake_lock());
+  }
   if (policy.has_use_audio_activity())
     str += base::StringPrintf("use_audio=%d ", policy.use_audio_activity());
   if (policy.has_use_video_activity())
@@ -347,34 +357,21 @@
     causes += (causes.empty() ? "" : ", ") + it.second.description;
   }
 
-  if (honor_screen_wake_locks_ && have_screen_wake_locks) {
-    policy.mutable_ac_delays()->set_screen_dim_ms(0);
-    policy.mutable_ac_delays()->set_screen_off_ms(0);
-    policy.mutable_ac_delays()->set_screen_lock_ms(0);
-    policy.mutable_battery_delays()->set_screen_dim_ms(0);
-    policy.mutable_battery_delays()->set_screen_off_ms(0);
-    policy.mutable_battery_delays()->set_screen_lock_ms(0);
+  // Downgrade full-brightness and dimmed-brightness locks to system locks if
+  // wake locks aren't allowed to keep the screen on.
+  if (!honor_screen_wake_locks_ &&
+      (have_screen_wake_locks || have_dim_wake_locks)) {
+    have_system_wake_locks = true;
+    have_screen_wake_locks = false;
+    have_dim_wake_locks = false;
   }
 
-  if (honor_screen_wake_locks_ && have_dim_wake_locks) {
-    policy.mutable_ac_delays()->set_screen_off_ms(0);
-    policy.mutable_ac_delays()->set_screen_lock_ms(0);
-    policy.mutable_battery_delays()->set_screen_off_ms(0);
-    policy.mutable_battery_delays()->set_screen_lock_ms(0);
-  }
-
-  if (have_screen_wake_locks || have_dim_wake_locks || have_system_wake_locks) {
-    if (!policy.has_ac_idle_action() || policy.ac_idle_action() ==
-        power_manager::PowerManagementPolicy_Action_SUSPEND) {
-      policy.set_ac_idle_action(
-          power_manager::PowerManagementPolicy_Action_DO_NOTHING);
-    }
-    if (!policy.has_battery_idle_action() || policy.battery_idle_action() ==
-        power_manager::PowerManagementPolicy_Action_SUSPEND) {
-      policy.set_battery_idle_action(
-          power_manager::PowerManagementPolicy_Action_DO_NOTHING);
-    }
-  }
+  if (have_screen_wake_locks)
+    policy.set_screen_wake_lock(true);
+  if (have_dim_wake_locks)
+    policy.set_dim_wake_lock(true);
+  if (have_system_wake_locks)
+    policy.set_system_wake_lock(true);
 
   if (encryption_migration_active_ &&
       policy.lid_closed_action() !=
diff --git a/chromeos/dbus/power_policy_controller_unittest.cc b/chromeos/dbus/power_policy_controller_unittest.cc
index acff4a36..f35a3ec 100644
--- a/chromeos/dbus/power_policy_controller_unittest.cc
+++ b/chromeos/dbus/power_policy_controller_unittest.cc
@@ -147,15 +147,14 @@
             PowerPolicyController::GetPolicyDebugString(
                 fake_power_client_->policy()));
 
-  // Set the "allow screen wake locks" pref to false.  The system should be
-  // prevented from suspending due to user inactivity on AC power but the
-  // pref-supplied screen-related delays should be left untouched.
+  // Set the "allow screen wake locks" pref to false and add a screen wake lock.
+  // It should be downgraded to a system wake lock, and the pref-supplied delays
+  // should be left untouched.
   prefs.allow_screen_wake_locks = false;
   policy_controller_->ApplyPrefs(prefs);
   policy_controller_->AddScreenWakeLock(PowerPolicyController::REASON_OTHER,
                                         "Screen");
-  expected_policy.set_ac_idle_action(
-      power_manager::PowerManagementPolicy_Action_DO_NOTHING);
+  expected_policy.set_system_wake_lock(true);
   expected_policy.set_reason(std::string(PowerPolicyController::kPrefsReason) +
                              ", Screen");
   EXPECT_EQ(PowerPolicyController::GetPolicyDebugString(expected_policy),
@@ -163,112 +162,34 @@
                 fake_power_client_->policy()));
 }
 
-TEST_F(PowerPolicyControllerTest, WakeLocks) {
-  // If our highest lock type is system, we only worry about
-  // the idle action.
-  const char kSystemWakeLockReason[] = "system";
-  power_manager::PowerManagementPolicy expected_policy_system;
-  expected_policy_system.set_ac_idle_action(
-      power_manager::PowerManagementPolicy_Action_DO_NOTHING);
-  expected_policy_system.set_battery_idle_action(
-      power_manager::PowerManagementPolicy_Action_DO_NOTHING);
-
-  // The dim lock type prevents the screen from turning off or
-  // locking, and we won't have an idle action.
-  const char kDimWakeLockReason[] = "dim";
-  power_manager::PowerManagementPolicy expected_policy_dim;
-  expected_policy_dim.set_ac_idle_action(
-      power_manager::PowerManagementPolicy_Action_DO_NOTHING);
-  expected_policy_dim.set_battery_idle_action(
-      power_manager::PowerManagementPolicy_Action_DO_NOTHING);
-  expected_policy_dim.mutable_ac_delays()->set_screen_off_ms(0);
-  expected_policy_dim.mutable_ac_delays()->set_screen_lock_ms(0);
-  expected_policy_dim.mutable_battery_delays()->set_screen_off_ms(0);
-  expected_policy_dim.mutable_battery_delays()->set_screen_lock_ms(0);
-
-  // The screen lock keeps the screen bright. We won't turn off,
-  // lock the screen or do anything for idle.
-  const char kScreenWakeLockReason[] = "screen";
-  power_manager::PowerManagementPolicy expected_policy_screen;
-  expected_policy_screen.set_ac_idle_action(
-      power_manager::PowerManagementPolicy_Action_DO_NOTHING);
-  expected_policy_screen.set_battery_idle_action(
-      power_manager::PowerManagementPolicy_Action_DO_NOTHING);
-  expected_policy_screen.mutable_ac_delays()->set_screen_dim_ms(0);
-  expected_policy_screen.mutable_ac_delays()->set_screen_off_ms(0);
-  expected_policy_screen.mutable_ac_delays()->set_screen_lock_ms(0);
-  expected_policy_screen.mutable_battery_delays()->set_screen_dim_ms(0);
-  expected_policy_screen.mutable_battery_delays()->set_screen_off_ms(0);
-  expected_policy_screen.mutable_battery_delays()->set_screen_lock_ms(0);
-
-  // With no locks our policy should be the default.
-  power_manager::PowerManagementPolicy expected_policy_none;
-
-  // There are eight different possibilities for combinations of
-  // different locks, as there are three types. We will go through
-  // each possibility by adding or removing one lock.
-
-  // System lock only.
-  const int system_id = policy_controller_->AddSystemWakeLock(
-      PowerPolicyController::REASON_OTHER, kSystemWakeLockReason);
-  expected_policy_system.set_reason(kSystemWakeLockReason);
-  EXPECT_EQ(PowerPolicyController::GetPolicyDebugString(expected_policy_system),
+TEST_F(PowerPolicyControllerTest, SystemWakeLock) {
+  policy_controller_->AddSystemWakeLock(PowerPolicyController::REASON_OTHER,
+                                        "1");
+  power_manager::PowerManagementPolicy expected_policy;
+  expected_policy.set_system_wake_lock(true);
+  expected_policy.set_reason("1");
+  EXPECT_EQ(PowerPolicyController::GetPolicyDebugString(expected_policy),
             PowerPolicyController::GetPolicyDebugString(
                 fake_power_client_->policy()));
+}
 
-  // System and dim locks.
-  const int dim_id = policy_controller_->AddDimWakeLock(
-      PowerPolicyController::REASON_OTHER, kDimWakeLockReason);
-  expected_policy_dim.set_reason(std::string(kSystemWakeLockReason) + ", " +
-                                 kDimWakeLockReason);
-  EXPECT_EQ(PowerPolicyController::GetPolicyDebugString(expected_policy_dim),
+TEST_F(PowerPolicyControllerTest, DimWakeLock) {
+  policy_controller_->AddDimWakeLock(PowerPolicyController::REASON_OTHER, "1");
+  power_manager::PowerManagementPolicy expected_policy;
+  expected_policy.set_dim_wake_lock(true);
+  expected_policy.set_reason("1");
+  EXPECT_EQ(PowerPolicyController::GetPolicyDebugString(expected_policy),
             PowerPolicyController::GetPolicyDebugString(
                 fake_power_client_->policy()));
+}
 
-  // Dim lock only.
-  policy_controller_->RemoveWakeLock(system_id);
-  expected_policy_dim.set_reason(kDimWakeLockReason);
-  EXPECT_EQ(PowerPolicyController::GetPolicyDebugString(expected_policy_dim),
-            PowerPolicyController::GetPolicyDebugString(
-                fake_power_client_->policy()));
-
-  // Dim and screen locks.
-  const int screen_id = policy_controller_->AddScreenWakeLock(
-      PowerPolicyController::REASON_OTHER, kScreenWakeLockReason);
-  expected_policy_screen.set_reason(std::string(kDimWakeLockReason) + ", " +
-                                    kScreenWakeLockReason);
-  EXPECT_EQ(PowerPolicyController::GetPolicyDebugString(expected_policy_screen),
-            PowerPolicyController::GetPolicyDebugString(
-                fake_power_client_->policy()));
-
-  // System, dim and screen locks.
-  const int system_id_2 = policy_controller_->AddSystemWakeLock(
-      PowerPolicyController::REASON_OTHER, kSystemWakeLockReason);
-  expected_policy_screen.set_reason(std::string(kDimWakeLockReason) + ", " +
-                                    std::string(kScreenWakeLockReason) + ", " +
-                                    kSystemWakeLockReason);
-  EXPECT_EQ(PowerPolicyController::GetPolicyDebugString(expected_policy_screen),
-            PowerPolicyController::GetPolicyDebugString(
-                fake_power_client_->policy()));
-
-  // System and screen locks.
-  policy_controller_->RemoveWakeLock(dim_id);
-  expected_policy_screen.set_reason(std::string(kScreenWakeLockReason) + ", " +
-                                    kSystemWakeLockReason);
-  EXPECT_EQ(PowerPolicyController::GetPolicyDebugString(expected_policy_screen),
-            PowerPolicyController::GetPolicyDebugString(
-                fake_power_client_->policy()));
-
-  // Screen lock only.
-  policy_controller_->RemoveWakeLock(system_id_2);
-  expected_policy_screen.set_reason(kScreenWakeLockReason);
-  EXPECT_EQ(PowerPolicyController::GetPolicyDebugString(expected_policy_screen),
-            PowerPolicyController::GetPolicyDebugString(
-                fake_power_client_->policy()));
-
-  // No locks.
-  policy_controller_->RemoveWakeLock(screen_id);
-  EXPECT_EQ(PowerPolicyController::GetPolicyDebugString(expected_policy_none),
+TEST_F(PowerPolicyControllerTest, ScreenWakeLock) {
+  policy_controller_->AddScreenWakeLock(PowerPolicyController::REASON_OTHER,
+                                        "1");
+  power_manager::PowerManagementPolicy expected_policy;
+  expected_policy.set_screen_wake_lock(true);
+  expected_policy.set_reason("1");
+  EXPECT_EQ(PowerPolicyController::GetPolicyDebugString(expected_policy),
             PowerPolicyController::GetPolicyDebugString(
                 fake_power_client_->policy()));
 }
@@ -302,16 +223,7 @@
   const int other_id = policy_controller_->AddScreenWakeLock(
       PowerPolicyController::REASON_OTHER, "other");
 
-  expected_policy.set_ac_idle_action(
-      power_manager::PowerManagementPolicy_Action_DO_NOTHING);
-  expected_policy.set_battery_idle_action(
-      power_manager::PowerManagementPolicy_Action_DO_NOTHING);
-  expected_policy.mutable_ac_delays()->set_screen_dim_ms(0);
-  expected_policy.mutable_ac_delays()->set_screen_off_ms(0);
-  expected_policy.mutable_ac_delays()->set_screen_lock_ms(0);
-  expected_policy.mutable_battery_delays()->set_screen_dim_ms(0);
-  expected_policy.mutable_battery_delays()->set_screen_off_ms(0);
-  expected_policy.mutable_battery_delays()->set_screen_lock_ms(0);
+  expected_policy.set_screen_wake_lock(true);
   expected_policy.set_reason(std::string(PowerPolicyController::kPrefsReason) +
                              ", other");
   EXPECT_EQ(PowerPolicyController::GetPolicyDebugString(expected_policy),
@@ -325,16 +237,26 @@
 
   expected_policy = kDefaultPolicy;
   expected_policy.set_use_video_activity(false);
-  expected_policy.set_ac_idle_action(
-      power_manager::PowerManagementPolicy_Action_DO_NOTHING);
-  expected_policy.set_battery_idle_action(
-      power_manager::PowerManagementPolicy_Action_DO_NOTHING);
+  expected_policy.set_system_wake_lock(true);
   expected_policy.set_reason(std::string(PowerPolicyController::kPrefsReason) +
                              ", audio");
   EXPECT_EQ(PowerPolicyController::GetPolicyDebugString(expected_policy),
             PowerPolicyController::GetPolicyDebugString(
                 fake_power_client_->policy()));
 
+  // Now honor video activity as well.
+  prefs.use_video_activity = true;
+  policy_controller_->ApplyPrefs(prefs);
+
+  expected_policy = kDefaultPolicy;
+  expected_policy.set_screen_wake_lock(true);
+  expected_policy.set_system_wake_lock(true);
+  expected_policy.set_reason(std::string(PowerPolicyController::kPrefsReason) +
+                             ", audio, video");
+  EXPECT_EQ(PowerPolicyController::GetPolicyDebugString(expected_policy),
+            PowerPolicyController::GetPolicyDebugString(
+                fake_power_client_->policy()));
+
   policy_controller_->RemoveWakeLock(audio_id);
   policy_controller_->RemoveWakeLock(video_id);
 }
diff --git a/chromeos/dbus/shill_device_client.h b/chromeos/dbus/shill_device_client.h
index 027a318..28a4f879 100644
--- a/chromeos/dbus/shill_device_client.h
+++ b/chromeos/dbus/shill_device_client.h
@@ -64,6 +64,8 @@
     // otherwise clears LockType. (This will unblock a PUK locked SIM).
     // Sets RetriesLeft to the PIN retry default. LockEnabled is unaffected.
     virtual void SetSimLocked(const std::string& device_path, bool enabled) = 0;
+    // Adds a new entry to Cellular.FoundNetworks.
+    virtual void AddCellularFoundNetwork(const std::string& device_path) = 0;
 
    protected:
     virtual ~TestInterface() {}
diff --git a/chromeos/network/auto_connect_handler.cc b/chromeos/network/auto_connect_handler.cc
index 93de05b..e2b567c 100644
--- a/chromeos/network/auto_connect_handler.cc
+++ b/chromeos/network/auto_connect_handler.cc
@@ -214,7 +214,7 @@
   connect_to_best_services_after_scan_ = true;
   if (!network_state_handler_->GetScanningByType(
           NetworkTypePattern::Primitive(shill::kTypeWifi))) {
-    network_state_handler_->RequestScan();
+    network_state_handler_->RequestScan(NetworkTypePattern::WiFi());
   }
 }
 
diff --git a/chromeos/network/network_device_handler_impl.cc b/chromeos/network/network_device_handler_impl.cc
index 992bbf7..4535cca 100644
--- a/chromeos/network/network_device_handler_impl.cc
+++ b/chromeos/network/network_device_handler_impl.cc
@@ -12,6 +12,7 @@
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/stringprintf.h"
+#include "base/sys_info.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -84,7 +85,6 @@
   const base::ListValue* ip_configs;
   if (!properties.GetListWithoutPathExpansion(
           shill::kIPConfigsProperty, &ip_configs)) {
-    NET_LOG(ERROR) << "RequestRefreshIPConfigs Failed: " << device_path;
     network_handler::ShillErrorCallbackFunction(
         "RequestRefreshIPConfigs Failed",
         device_path,
@@ -605,14 +605,10 @@
   bool supported;
   if (!properties.GetBooleanWithoutPathExpansion(
           shill::kMACAddressRandomizationSupportedProperty, &supported)) {
-    NET_LOG(ERROR) << "Failed to determine if device " << device_path
-                   << " supports MAC address randomization";
-    network_handler::ShillErrorCallbackFunction(
-        "Failed to determine if device supports MAC address randomization",
-        device_path, network_handler::ErrorCallback(),
-        std::string("Missing ") +
-            shill::kMACAddressRandomizationSupportedProperty,
-        "");
+    if (base::SysInfo::IsRunningOnChromeOS()) {
+      NET_LOG(ERROR) << "Failed to determine if device " << device_path
+                     << " supports MAC address randomization";
+    }
     return;
   }
 
diff --git a/chromeos/network/network_state_handler.cc b/chromeos/network/network_state_handler.cc
index 918184e..884ba3a 100644
--- a/chromeos/network/network_state_handler.cc
+++ b/chromeos/network/network_state_handler.cc
@@ -22,6 +22,7 @@
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/network/device_state.h"
 #include "chromeos/network/network_connection_handler.h"
+#include "chromeos/network/network_device_handler.h"
 #include "chromeos/network/network_event_log.h"
 #include "chromeos/network/network_handler_callbacks.h"
 #include "chromeos/network/network_state.h"
@@ -879,9 +880,20 @@
   }
 }
 
-void NetworkStateHandler::RequestScan() {
-  NET_LOG_USER("RequestScan", "");
-  shill_property_handler_->RequestScan();
+void NetworkStateHandler::RequestScan(const NetworkTypePattern& type) {
+  NET_LOG_USER("RequestScan", type.ToDebugString());
+  bool did_scan = false;
+  if (type.MatchesType(shill::kTypeWifi)) {
+    shill_property_handler_->RequestScanByType(shill::kTypeWifi);
+    did_scan = true;
+  }
+  if (type.Equals(NetworkTypePattern::Primitive(shill::kTypeCellular))) {
+    // Only request a Cellular scan if Cellular is requested explicitly.
+    shill_property_handler_->RequestScanByType(shill::kTypeCellular);
+    did_scan = true;
+  }
+  if (!did_scan)
+    NET_LOG(ERROR) << "RequestScan: Invalid type: " << type.ToDebugString();
   NotifyScanRequested();
 }
 
diff --git a/chromeos/network/network_state_handler.h b/chromeos/network/network_state_handler.h
index 3245d8c..f35a9bb7 100644
--- a/chromeos/network/network_state_handler.h
+++ b/chromeos/network/network_state_handler.h
@@ -294,8 +294,9 @@
                            DeviceStateList* list) const;
 
   // Requests a network scan. This may trigger updates to the network
-  // list, which will trigger the appropriate observer calls.
-  void RequestScan();
+  // list, which will trigger the appropriate observer calls. If |type| is
+  // Cellular, a mobile network scan will be requested if supported.
+  void RequestScan(const NetworkTypePattern& type);
 
   // Requests an update for an existing NetworkState, e.g. after configuring
   // a network. This is a no-op if an update request is already pending. To
diff --git a/chromeos/network/network_state_handler_unittest.cc b/chromeos/network/network_state_handler_unittest.cc
index ef0dbc1..f22e047 100644
--- a/chromeos/network/network_state_handler_unittest.cc
+++ b/chromeos/network/network_state_handler_unittest.cc
@@ -1537,7 +1537,7 @@
 
 TEST_F(NetworkStateHandlerTest, RequestScan) {
   EXPECT_EQ(0u, test_observer_->scan_requested_count());
-  network_state_handler_->RequestScan();
+  network_state_handler_->RequestScan(NetworkTypePattern::WiFi());
   EXPECT_EQ(1u, test_observer_->scan_requested_count());
 }
 
diff --git a/chromeos/network/shill_property_handler.cc b/chromeos/network/shill_property_handler.cc
index 260c3595..1f43b9c 100644
--- a/chromeos/network/shill_property_handler.cc
+++ b/chromeos/network/shill_property_handler.cc
@@ -241,11 +241,11 @@
                  network_handler::ErrorCallback()));
 }
 
-void ShillPropertyHandler::RequestScan() const {
+void ShillPropertyHandler::RequestScanByType(const std::string& type) const {
   shill_manager_->RequestScan(
-      "", base::Bind(&base::DoNothing),
+      type, base::Bind(&base::DoNothing),
       base::Bind(&network_handler::ShillErrorCallbackFunction,
-                 "RequestScan Failed", "", network_handler::ErrorCallback()));
+                 "RequestScan Failed", type, network_handler::ErrorCallback()));
 }
 
 void ShillPropertyHandler::RequestProperties(ManagedState::ManagedType type,
diff --git a/chromeos/network/shill_property_handler.h b/chromeos/network/shill_property_handler.h
index a514634..2049d46 100644
--- a/chromeos/network/shill_property_handler.h
+++ b/chromeos/network/shill_property_handler.h
@@ -147,8 +147,8 @@
                                   uint32_t upload_rate_kbits,
                                   uint32_t download_rate_kbits);
 
-  // Requests an immediate network scan.
-  void RequestScan() const;
+  // Requests an immediate network scan for |type|.
+  void RequestScanByType(const std::string& type) const;
 
   // Requests all properties for the service or device (called for new items).
   void RequestProperties(ManagedState::ManagedType type,
diff --git a/components/BUILD.gn b/components/BUILD.gn
index cafd946..1c3342e 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -281,6 +281,7 @@
       # See comment in components/guest_view/browser/BUILD.gn for why
       # guest_view is currently non-mobile.
       "//components/guest_view/browser:unit_tests",
+      "//components/multidevice:unit_tests",
       "//components/proximity_auth:unit_tests",
       "//components/storage_monitor:unit_tests",
       "//components/web_modal:unit_tests",
diff --git a/components/arc/common/accessibility_helper.mojom b/components/arc/common/accessibility_helper.mojom
index 2d738280..cf46d7d 100644
--- a/components/arc/common/accessibility_helper.mojom
+++ b/components/arc/common/accessibility_helper.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Next MinVersion: 5
+// Next MinVersion: 6
 
 module arc.mojom;
 
@@ -12,6 +12,9 @@
 // from their equivalents in the Android source. Keep them in the
 // order given below and add as needed. The initial order matches the
 // order they appear in source files.
+//
+// If not explicitly called out, the structs and enums below come from:
+// https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.html
 
 // AccessibilityEventType lists the possible accessibility events on Android.
 // https://developer.android.com/reference/android/view/accessibility/AccessibilityEvent.html
@@ -44,8 +47,7 @@
   ASSIST_READING_CONTEXT,
 };
 
-// AccessibilityActionType lists possible accessibility actions on Android.
-// https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.html
+// Possible actions that can be performed on an AccessibilityNodeInfo.
 [Extensible]
 enum AccessibilityActionType {
   FOCUS,
@@ -82,7 +84,6 @@
 };
 
 // Possible boolean properties set on an AccessibilityNodeInfo.
-// https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.html
 // The enum values appear in the same order as they do within
 // AccessibilityNodeInfo.java.
 [Extensible]
@@ -116,7 +117,9 @@
   CLASS_NAME,
   TEXT,
   CONTENT_DESCRIPTION,
-  VIEW_ID_RESOURCE_NAME
+  VIEW_ID_RESOURCE_NAME,
+  CHROME_ROLE,  // Chrome only
+  ROLE_DESCRIPTION  // Chrome only
 };
 
 // These fields are taken from int instance members of
@@ -142,14 +145,71 @@
   CUSTOM_ACTION_IDS
 };
 
+// These fields are taken from List<String> instance members of
+// AccessibilityNodeInfo.
 [Extensible]
 enum AccessibilityStringListProperty {
   CUSTOM_ACTION_DESCRIPTIONS
 };
 
+// This type is a subset of spans available under android.text.style.
+[Extensible]
+enum SpanType {
+  URL,
+  CLICKABLE
+};
+
+// Groups data about a Spannable.
+struct SpanEntry {
+  int32 start;
+  int32 end;
+  SpanType span_type;
+};
+
+// These fields are taken from AccessibilityNodeInfo.CollectionItemInfo.
+[Extensible]
+enum AccessibilitySelectionMode {
+  NONE,
+  SINGLE,
+  MULTIPLE
+};
+
+// These fields are taken from AccessibilityNodeInfo.CollectionInfo.
+struct AccessibilityCollectionInfoData {
+  int32 row_count;
+  int32 column_count;
+  bool is_hierarchical;
+  AccessibilitySelectionMode selection_mode;
+};
+
+// These fields are taken from AccessibilityNodeInfo.CollectionItemInfo.
+struct AccessibilityCollectionItemInfoData {
+  int32 row_index;
+  int32 column_index;
+  int32 row_span;
+  int32 column_span;
+  bool is_heading;
+  bool is_selected;
+};
+
+// These fields are taken from AccessibilityNodeInfo.RangeInfo.
+[Extensible]
+enum AccessibilityRangeType {
+  INT,
+  FLOAT,
+  PERCENT
+};
+
+// These fields are taken from AccessibilityNodeInfo.RangeInfo.
+struct AccessibilityRangeInfoData {
+  AccessibilityRangeType range_type;
+  float min;
+  float max;
+  float current;
+};
+
 // AccessibilityNodeInfoData is a struct to contain info of
 // AccessibilityNodeInfo in Android.
-// https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.html
 struct AccessibilityNodeInfoData {
   ScreenRect bounds_in_screen;
   [MinVersion=1]int32 id;
@@ -160,6 +220,11 @@
       map<AccessibilityIntListProperty, array<int32>>? int_list_properties;
   [MinVersion=3]map<AccessibilityStringListProperty, array<string>>?
       string_list_properties;
+  [MinVersion=5]map<AccessibilityStringProperty, array<SpanEntry>>?
+      spannable_string_properties;
+  [MinVersion=5]AccessibilityCollectionInfoData? collection_info;
+  [MinVersion=5]AccessibilityCollectionItemInfoData? collection_item_info;
+  [MinVersion=5]AccessibilityRangeInfoData? range_info;
 };
 
 // Filters the event type (and implicitly the data) sent by the ARC
diff --git a/components/arc/net/arc_net_host_impl.cc b/components/arc/net/arc_net_host_impl.cc
index f06a094..92c4bc3 100644
--- a/components/arc/net/arc_net_host_impl.cc
+++ b/components/arc/net/arc_net_host_impl.cc
@@ -60,7 +60,7 @@
   std::string value;
   dict->GetString(key, &value);
   if (required && value.empty())
-    VLOG(1) << "Required parameter " << key << " was not found.";
+    NOTREACHED() << "Required parameter " << key << " was not found.";
   return value;
 }
 
@@ -115,45 +115,48 @@
   return strings;
 }
 
+arc::mojom::IPConfigurationPtr TranslateONCIPConfig(
+    const base::DictionaryValue* ip_dict) {
+  arc::mojom::IPConfigurationPtr configuration =
+      arc::mojom::IPConfiguration::New();
+
+  if (ip_dict->FindKey(onc::ipconfig::kIPAddress)) {
+    configuration->ip_address = GetStringFromOncDictionary(
+        ip_dict, onc::ipconfig::kIPAddress, true /* required */);
+    if (!ip_dict->GetInteger(onc::ipconfig::kRoutingPrefix,
+                             &configuration->routing_prefix)) {
+      NOTREACHED();
+    }
+    configuration->gateway = GetStringFromOncDictionary(
+        ip_dict, onc::ipconfig::kGateway, true /* required */);
+  }
+
+  const base::ListValue* dns_list;
+  if (ip_dict->GetList(onc::ipconfig::kNameServers, &dns_list))
+    configuration->name_servers = TranslateStringArray(dns_list);
+
+  std::string type = GetStringFromOncDictionary(ip_dict, onc::ipconfig::kType,
+                                                true /* required */);
+  configuration->type = type == onc::ipconfig::kIPv6
+                            ? arc::mojom::IPAddressType::IPV6
+                            : arc::mojom::IPAddressType::IPV4;
+
+  configuration->web_proxy_auto_discovery_url = GetStringFromOncDictionary(
+      ip_dict, onc::ipconfig::kWebProxyAutoDiscoveryUrl, false /* required */);
+
+  return configuration;
+}
+
 std::vector<arc::mojom::IPConfigurationPtr> TranslateONCIPConfigs(
     const base::ListValue* list) {
   std::vector<arc::mojom::IPConfigurationPtr> configs;
 
   for (size_t i = 0; i < list->GetSize(); i++) {
     const base::DictionaryValue* ip_dict = nullptr;
-    arc::mojom::IPConfigurationPtr configuration =
-        arc::mojom::IPConfiguration::New();
 
     list->GetDictionary(i, &ip_dict);
     DCHECK(ip_dict);
-
-    // crbug.com/625229 - Gateway is not always present (but it should be).
-    configuration->gateway = GetStringFromOncDictionary(
-        ip_dict, onc::ipconfig::kGateway, false /* required */);
-    configuration->ip_address = GetStringFromOncDictionary(
-        ip_dict, onc::ipconfig::kIPAddress, true /* required */);
-
-    const base::ListValue* dns_list;
-    if (!ip_dict->GetList(onc::ipconfig::kNameServers, &dns_list))
-      NOTREACHED();
-    configuration->name_servers = TranslateStringArray(dns_list);
-
-    if (!ip_dict->GetInteger(onc::ipconfig::kRoutingPrefix,
-                             &configuration->routing_prefix)) {
-      NOTREACHED();
-    }
-
-    std::string type = GetStringFromOncDictionary(ip_dict, onc::ipconfig::kType,
-                                                  true /* required */);
-    configuration->type = type == onc::ipconfig::kIPv6
-                              ? arc::mojom::IPAddressType::IPV6
-                              : arc::mojom::IPAddressType::IPV4;
-
-    configuration->web_proxy_auto_discovery_url = GetStringFromOncDictionary(
-        ip_dict, onc::ipconfig::kWebProxyAutoDiscoveryUrl,
-        false /* required */);
-
-    configs.push_back(std::move(configuration));
+    configs.push_back(TranslateONCIPConfig(ip_dict));
   }
   return configs;
 }
@@ -161,16 +164,12 @@
 arc::mojom::ConnectionStateType TranslateONCConnectionState(
     const base::DictionaryValue* dict) {
   std::string connection_state = GetStringFromOncDictionary(
-      dict, onc::network_config::kConnectionState, true /* required */);
+      dict, onc::network_config::kConnectionState, false /* required */);
 
   if (connection_state == onc::connection_state::kConnected)
     return arc::mojom::ConnectionStateType::CONNECTED;
   else if (connection_state == onc::connection_state::kConnecting)
     return arc::mojom::ConnectionStateType::CONNECTING;
-  else if (connection_state == onc::connection_state::kNotConnected)
-    return arc::mojom::ConnectionStateType::NOT_CONNECTED;
-
-  NOTREACHED();
   return arc::mojom::ConnectionStateType::NOT_CONNECTED;
 }
 
@@ -208,16 +207,26 @@
   mojo->guid = GetStringFromOncDictionary(dict, onc::network_config::kGUID,
                                           true /* required */);
 
+  // crbug.com/761708 - VPNs do not currently have an IPConfigs array,
+  // so in order to fetch the parameters (particularly the DNS server list),
+  // fall back to StaticIPConfig or SavedIPConfig.
   const base::ListValue* ip_config_list = nullptr;
+  const base::DictionaryValue* ip_dict = nullptr;
   if (dict->GetList(onc::network_config::kIPConfigs, &ip_config_list)) {
-    DCHECK(ip_config_list);
     mojo->ip_configs = TranslateONCIPConfigs(ip_config_list);
+  } else if (dict->GetDictionary(onc::network_config::kStaticIPConfig,
+                                 &ip_dict) ||
+             dict->GetDictionary(onc::network_config::kSavedIPConfig,
+                                 &ip_dict)) {
+    std::vector<arc::mojom::IPConfigurationPtr> configs;
+    configs.push_back(TranslateONCIPConfig(ip_dict));
+    mojo->ip_configs = std::move(configs);
   }
 
   mojo->guid = GetStringFromOncDictionary(dict, onc::network_config::kGUID,
                                           true /* required */);
   mojo->mac_address = GetStringFromOncDictionary(
-      dict, onc::network_config::kMacAddress, true /* required */);
+      dict, onc::network_config::kMacAddress, false /* required */);
   TranslateONCNetworkTypeDetails(dict, mojo.get());
 
   return mojo;
@@ -617,7 +626,7 @@
 }
 
 void ArcNetHostImpl::StartScan() {
-  GetStateHandler()->RequestScan();
+  GetStateHandler()->RequestScan(chromeos::NetworkTypePattern::WiFi());
 }
 
 void ArcNetHostImpl::ScanCompleted(const chromeos::DeviceState* /*unused*/) {
diff --git a/components/autofill/core/browser/autocomplete_history_manager.cc b/components/autofill/core/browser/autocomplete_history_manager.cc
index ec5738c..6683d0e 100644
--- a/components/autofill/core/browser/autocomplete_history_manager.cc
+++ b/components/autofill/core/browser/autocomplete_history_manager.cc
@@ -6,7 +6,6 @@
 
 #include <vector>
 
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/browser/autofill_client.h"
@@ -141,12 +140,6 @@
 void AutocompleteHistoryManager::OnWebDataServiceRequestDone(
     WebDataServiceBase::Handle h,
     std::unique_ptr<WDTypedResult> result) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AutocompleteHistoryManager::OnWebDataServiceRequestDone"));
-
   DCHECK(pending_query_handle_);
   pending_query_handle_ = 0;
 
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index f8566fa5..387ee5c1 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -16,7 +16,6 @@
 #include "base/i18n/case_conversion.h"
 #include "base/i18n/timezone.h"
 #include "base/memory/ptr_util.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -355,12 +354,6 @@
   DCHECK(pending_profiles_query_ || pending_server_profiles_query_ ||
          pending_creditcards_query_ || pending_server_creditcards_query_);
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 PersonalDataManager::OnWebDataServiceRequestDone"));
-
   if (!result) {
     // Error from the web database.
     if (h == pending_creditcards_query_)
diff --git a/components/bookmarks/browser/bookmark_model.cc b/components/bookmarks/browser/bookmark_model.cc
index 179083b..8200203 100644
--- a/components/bookmarks/browser/bookmark_model.cc
+++ b/components/bookmarks/browser/bookmark_model.cc
@@ -15,7 +15,6 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_util.h"
 #include "components/bookmarks/browser/bookmark_expanded_state_tracker.h"
 #include "components/bookmarks/browser/bookmark_model_observer.h"
@@ -818,19 +817,9 @@
     return;
   }
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading1"));
-
   next_node_id_ = details->max_id();
   if (details->computed_checksum() != details->stored_checksum() ||
       details->ids_reassigned()) {
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile2(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading2"));
-
     // If bookmarks file changed externally, the IDs may have changed
     // externally. In that case, the decoder may have reassigned IDs to make
     // them unique. So when the file has changed externally, we should save the
@@ -854,11 +843,6 @@
   std::vector<std::unique_ptr<BookmarkPermanentNode>> extra_nodes =
       details->owned_extra_nodes();
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile3(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading3"));
-
   // WARNING: order is important here, various places assume the order is
   // constant (but can vary between embedders with the initial visibility
   // of permanent nodes).
@@ -869,11 +853,6 @@
   std::move(extra_nodes.begin(), extra_nodes.end(),
             std::back_inserter(root_children));
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile4(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading4"));
-
   std::stable_sort(root_children.begin(),
                    root_children.end(),
                    VisibilityComparator(client_.get()));
@@ -883,11 +862,6 @@
   root_.SetMetaInfoMap(details->model_meta_info_map());
   root_.set_sync_transaction_version(details->model_sync_transaction_version());
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile5(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading5"));
-
   {
     base::AutoLock url_lock(url_lock_);
     // Update nodes_ordered_by_url_set_ from the nodes.
@@ -898,11 +872,6 @@
 
   loaded_signal_.Signal();
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile6(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading6"));
-
   // Notify our direct observers.
   for (BookmarkModelObserver& observer : observers_)
     observer.BookmarkModelLoaded(this, details->ids_reassigned());
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc
index d27531a..6bb12ee 100644
--- a/components/browser_sync/profile_sync_service.cc
+++ b/components/browser_sync/profile_sync_service.cc
@@ -19,7 +19,6 @@
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "base/metrics/histogram.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_restrictions.h"
@@ -633,12 +632,6 @@
 void ProfileSyncService::OnRefreshTokenAvailable(
     const std::string& account_id) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 ProfileSyncService::OnRefreshTokenAvailable"));
-
   if (account_id == signin_->GetAccountIdToUse())
     OnRefreshTokensLoaded();
 }
diff --git a/components/crash/content/app/crash_export_stubs.cc b/components/crash/content/app/crash_export_stubs.cc
index 4e10970..b1b5b56 100644
--- a/components/crash/content/app/crash_export_stubs.cc
+++ b/components/crash/content/app/crash_export_stubs.cc
@@ -31,9 +31,12 @@
 
 void ClearCrashKeyValue_ExportThunk(const wchar_t* key) {}
 
-void SetCrashKeyValueEx_ExportThunk(const char* key, const char* value) {}
+void SetCrashKeyValueEx_ExportThunk(const char* key,
+                                    size_t key_len,
+                                    const char* value,
+                                    size_t value_len) {}
 
-void ClearCrashKeyValueEx_ExportThunk(const char* key) {}
+void ClearCrashKeyValueEx_ExportThunk(const char* key, size_t key_len) {}
 
 HANDLE InjectDumpForHungInput_ExportThunk(HANDLE process,
                                           void* serialized_crash_keys) {
diff --git a/components/crash/content/app/crash_export_thunks.cc b/components/crash/content/app/crash_export_thunks.cc
index 630956f..cfc8bf1 100644
--- a/components/crash/content/app/crash_export_thunks.cc
+++ b/components/crash/content/app/crash_export_thunks.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <type_traits>
 
+#include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "components/crash/content/app/crashpad.h"
@@ -65,12 +66,16 @@
   crash_reporter::ClearCrashKey(base::UTF16ToUTF8(key));
 }
 
-void SetCrashKeyValueEx_ExportThunk(const char* key, const char* value) {
-  crash_reporter::SetCrashKeyValue(key, value);
+void SetCrashKeyValueEx_ExportThunk(const char* key,
+                                    size_t key_len,
+                                    const char* value,
+                                    size_t value_len) {
+  crash_reporter::SetCrashKeyValue(base::StringPiece(key, key_len),
+                                   base::StringPiece(value, value_len));
 }
 
-void ClearCrashKeyValueEx_ExportThunk(const char* key) {
-  crash_reporter::ClearCrashKey(key);
+void ClearCrashKeyValueEx_ExportThunk(const char* key, size_t key_len) {
+  crash_reporter::ClearCrashKey(base::StringPiece(key, key_len));
 }
 
 HANDLE InjectDumpForHungInput_ExportThunk(HANDLE process,
diff --git a/components/crash/content/app/crash_export_thunks.h b/components/crash/content/app/crash_export_thunks.h
index 4e04054f..6f3138e 100644
--- a/components/crash/content/app/crash_export_thunks.h
+++ b/components/crash/content/app/crash_export_thunks.h
@@ -58,9 +58,12 @@
 
 void ClearCrashKeyValue_ExportThunk(const wchar_t* key);
 
-void SetCrashKeyValueEx_ExportThunk(const char* key, const char* value);
+void SetCrashKeyValueEx_ExportThunk(const char* key,
+                                    size_t key_len,
+                                    const char* value,
+                                    size_t value_len);
 
-void ClearCrashKeyValueEx_ExportThunk(const char* key);
+void ClearCrashKeyValueEx_ExportThunk(const char* key, size_t key_len);
 
 // Injects a thread into a remote process to dump state when there is no crash.
 // |serialized_crash_keys| is a nul terminated string in the address space of
diff --git a/components/crash/content/app/crashpad.cc b/components/crash/content/app/crashpad.cc
index 5e1302b..af31c1a 100644
--- a/components/crash/content/app/crashpad.cc
+++ b/components/crash/content/app/crashpad.cc
@@ -188,11 +188,11 @@
 
 void SetCrashKeyValue(const base::StringPiece& key,
                       const base::StringPiece& value) {
-  g_simple_string_dictionary->SetKeyValue(key.data(), value.data());
+  g_simple_string_dictionary->SetKeyValue(key, value);
 }
 
 void ClearCrashKey(const base::StringPiece& key) {
-  g_simple_string_dictionary->RemoveKey(key.data());
+  g_simple_string_dictionary->RemoveKey(key);
 }
 
 void InitializeCrashpad(bool initial_client, const std::string& process_type) {
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index b3b5391..242f7f6 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -474,7 +474,6 @@
     "test/src/org/chromium/net/MockUrlRequestJobFactory.java",
     "test/src/org/chromium/net/NativeTestServer.java",
     "test/src/org/chromium/net/QuicTestServer.java",
-    "test/src/org/chromium/net/SdchObserver.java",
     "test/src/org/chromium/net/TestUploadDataStreamHandler.java",
   ]
   jni_package = "cronet_tests"
@@ -499,7 +498,6 @@
     "test/mock_url_request_job_factory.cc",
     "test/native_test_server.cc",
     "test/quic_test_server.cc",
-    "test/sdch_test_util.cc",
     "test/test_upload_data_stream_handler.cc",
     "test/test_upload_data_stream_handler.h",
   ]
@@ -544,7 +542,6 @@
     "test/src/org/chromium/net/MockUrlRequestJobFactory.java",
     "test/src/org/chromium/net/NativeTestServer.java",
     "test/src/org/chromium/net/QuicTestServer.java",
-    "test/src/org/chromium/net/SdchObserver.java",
     "test/src/org/chromium/net/TestFilesInstaller.java",
     "test/src/org/chromium/net/TestUploadDataStreamHandler.java",
   ]
@@ -612,19 +609,13 @@
     "test/assets/test/redirect_invalid_scheme.html.mock-http-headers",
     "test/assets/test/set_cookie.html",
     "test/assets/test/set_cookie.html.mock-http-headers",
-    "test/assets/test/sdch/dict/LeQxM80O",
-    "test/assets/test/sdch/dict/LeQxM80O.mock-http-headers",
-    "test/assets/test/sdch/index",
-    "test/assets/test/sdch/index.mock-http-headers",
-    "test/assets/test/sdch/LeQxM80O_encoded",
-    "test/assets/test/sdch/LeQxM80O_encoded.mock-http-headers",
     "test/assets/test/secureproxychecksuccess.txt",
     "test/assets/test/secureproxychecksuccess.txt.mock-http-headers",
     "test/assets/test/success.txt",
     "test/assets/test/success.txt.mock-http-headers",
   ]
 
-  # Maintain directory structure. Example entry: "test/sdch/index".
+  # Maintain directory structure.
   renaming_destinations = rebase_path(renaming_sources, "test/assets")
 }
 
@@ -682,7 +673,6 @@
     "test/javatests/src/org/chromium/net/PkpTest.java",
     "test/javatests/src/org/chromium/net/QuicTest.java",
     "test/javatests/src/org/chromium/net/RequestFinishedInfoTest.java",
-    "test/javatests/src/org/chromium/net/SdchTest.java",
     "test/javatests/src/org/chromium/net/TestBidirectionalStreamCallback.java",
     "test/javatests/src/org/chromium/net/TestDrivenDataProvider.java",
     "test/javatests/src/org/chromium/net/TestNetworkQualityRttListener.java",
diff --git a/components/cronet/android/api/src/org/chromium/net/CronetEngine.java b/components/cronet/android/api/src/org/chromium/net/CronetEngine.java
index 02bd1549..fe38ac57 100644
--- a/components/cronet/android/api/src/org/chromium/net/CronetEngine.java
+++ b/components/cronet/android/api/src/org/chromium/net/CronetEngine.java
@@ -159,15 +159,11 @@
         }
 
         /**
-         * Sets whether
-         * <a
-         * href="https://lists.w3.org/Archives/Public/ietf-http-wg/2008JulSep/att-0441/Shared_Dictionary_Compression_over_HTTP.pdf">
-         * SDCH</a> compression is enabled. Defaults to disabled.
-         * @param value {@code true} to enable SDCH, {@code false} to disable.
-         * @return the builder to facilitate chaining.
+         * @deprecated SDCH is deprecated in Cronet M63. This method is a no-op.
+         * {@hide exclude from JavaDoc}.
          */
+        @Deprecated
         public Builder enableSdch(boolean value) {
-            mBuilderDelegate.enableSdch(value);
             return this;
         }
 
diff --git a/components/cronet/android/api/src/org/chromium/net/ExperimentalCronetEngine.java b/components/cronet/android/api/src/org/chromium/net/ExperimentalCronetEngine.java
index 0ef59fe..8c0a3b1 100644
--- a/components/cronet/android/api/src/org/chromium/net/ExperimentalCronetEngine.java
+++ b/components/cronet/android/api/src/org/chromium/net/ExperimentalCronetEngine.java
@@ -210,7 +210,6 @@
 
         @Override
         public Builder enableSdch(boolean value) {
-            super.enableSdch(value);
             return this;
         }
 
diff --git a/components/cronet/android/cronet_url_request_context_adapter.cc b/components/cronet/android/cronet_url_request_context_adapter.cc
index 285e4fc..388d831 100644
--- a/components/cronet/android/cronet_url_request_context_adapter.cc
+++ b/components/cronet/android/cronet_url_request_context_adapter.cc
@@ -58,7 +58,6 @@
 #include "net/proxy/proxy_config_service_android.h"
 #include "net/proxy/proxy_service.h"
 #include "net/quic/core/quic_versions.h"
-#include "net/sdch/sdch_owner.h"
 #include "net/ssl/channel_id_service.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_builder.h"
@@ -417,14 +416,6 @@
   if (config->load_disable_cache)
     default_load_flags_ |= net::LOAD_DISABLE_CACHE;
 
-  if (config->enable_sdch) {
-    DCHECK(context_->sdch_manager());
-    sdch_owner_.reset(
-        new net::SdchOwner(context_->sdch_manager(), context_.get()));
-    if (cronet_prefs_manager_)
-      cronet_prefs_manager_->SetupSdchPersistence(sdch_owner_.get());
-  }
-
   if (config->enable_quic) {
     for (const auto& quic_hint : config->quic_hints) {
       if (quic_hint->host.empty()) {
@@ -783,7 +774,6 @@
     jboolean jquic_enabled,
     const JavaParamRef<jstring>& jquic_default_user_agent_id,
     jboolean jhttp2_enabled,
-    jboolean jsdch_enabled,
     jboolean jbrotli_enabled,
     jboolean jdisable_cache,
     jint jhttp_cache_mode,
@@ -796,7 +786,7 @@
   return reinterpret_cast<jlong>(new URLRequestContextConfig(
       jquic_enabled,
       ConvertNullableJavaStringToUTF8(env, jquic_default_user_agent_id),
-      jhttp2_enabled, jsdch_enabled, jbrotli_enabled,
+      jhttp2_enabled, jbrotli_enabled,
       static_cast<URLRequestContextConfig::HttpCacheType>(jhttp_cache_mode),
       jhttp_cache_max_size, jdisable_cache,
       ConvertNullableJavaStringToUTF8(env, jstorage_path),
diff --git a/components/cronet/android/cronet_url_request_context_adapter.h b/components/cronet/android/cronet_url_request_context_adapter.h
index 7759470..cdfa4ee 100644
--- a/components/cronet/android/cronet_url_request_context_adapter.h
+++ b/components/cronet/android/cronet_url_request_context_adapter.h
@@ -34,7 +34,6 @@
 namespace net {
 class NetLog;
 class ProxyConfigService;
-class SdchOwner;
 class URLRequestContext;
 class FileNetLogObserver;
 }  // namespace net
@@ -227,20 +226,12 @@
 
   // Manages the PrefService and all associated persistence managers
   // such as NetworkQualityPrefsManager, HostCachePersistenceManager, etc.
-  // It should be destroyed before |network_quality_estimator_| but after
-  // |sdch_owner_|. It also owns a PrefService object should outlive |context_|.
+  // It should be destroyed before |network_quality_estimator_|. It also owns a
+  // PrefService object should outlive |context_|.
   std::unique_ptr<CronetPrefsManager> cronet_prefs_manager_;
 
   std::unique_ptr<net::URLRequestContext> context_;
 
-  // |sdch_owner_| should be destroyed before |cronet_prefs_manager_|, because
-  // tearing down |sdch_owner_| forces |json_pref_store_| to flush pending
-  // writes to the disk. |json_pref_store_| is owned by |cronet_prefs_manager_|.
-  // |sdch_owner_| should also be destroyed before |context_|. This will
-  // unregister SdchManager observers before the context is destroyed.
-  // SdchManager should not be destroy until all observers are unregistered.
-  std::unique_ptr<net::SdchOwner> sdch_owner_;
-
   std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
 
   // Context config is only valid until context is initialized.
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CronetEngineBuilderImpl.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetEngineBuilderImpl.java
index e695731..2080d8a 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/CronetEngineBuilderImpl.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetEngineBuilderImpl.java
@@ -86,7 +86,6 @@
     private VersionSafeCallbacks.LibraryLoader mLibraryLoader;
     private boolean mQuicEnabled;
     private boolean mHttp2Enabled;
-    private boolean mSdchEnabled;
     private boolean mBrotiEnabled;
     private boolean mDisableCache;
     private int mHttpCacheMode;
@@ -105,7 +104,6 @@
         mApplicationContext = context.getApplicationContext();
         enableQuic(false);
         enableHttp2(true);
-        enableSdch(false);
         enableBrotli(false);
         enableHttpCache(HTTP_CACHE_DISABLED, 0);
         enableNetworkQualityEstimator(false);
@@ -182,14 +180,9 @@
 
     @Override
     public CronetEngineBuilderImpl enableSdch(boolean value) {
-        mSdchEnabled = value;
         return this;
     }
 
-    boolean sdchEnabled() {
-        return mSdchEnabled;
-    }
-
     @Override
     public CronetEngineBuilderImpl enableBrotli(boolean value) {
         mBrotiEnabled = value;
diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java
index 141fe63..71f8b12 100644
--- a/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java
@@ -198,7 +198,7 @@
     public static long createNativeUrlRequestContextConfig(CronetEngineBuilderImpl builder) {
         final long urlRequestContextConfig = nativeCreateRequestContextConfig(
                 builder.getUserAgent(), builder.storagePath(), builder.quicEnabled(),
-                builder.getDefaultQuicUserAgentId(), builder.http2Enabled(), builder.sdchEnabled(),
+                builder.getDefaultQuicUserAgentId(), builder.http2Enabled(),
                 builder.brotliEnabled(), builder.cacheDisabled(), builder.httpCacheMode(),
                 builder.httpCacheMaxSize(), builder.experimentalOptions(),
                 builder.mockCertVerifier(), builder.networkQualityEstimatorEnabled(),
@@ -694,8 +694,8 @@
     // Native methods are implemented in cronet_url_request_context_adapter.cc.
     private static native long nativeCreateRequestContextConfig(String userAgent,
             String storagePath, boolean quicEnabled, String quicUserAgentId, boolean http2Enabled,
-            boolean sdchEnabled, boolean brotliEnabled, boolean disableCache, int httpCacheMode,
-            long httpCacheMaxSize, String experimentalOptions, long mockCertVerifier,
+            boolean brotliEnabled, boolean disableCache, int httpCacheMode, long httpCacheMaxSize,
+            String experimentalOptions, long mockCertVerifier,
             boolean enableNetworkQualityEstimator,
             boolean bypassPublicKeyPinningForLocalTrustAnchors, String certVerifierData);
 
diff --git a/components/cronet/android/test/assets/test/sdch/LeQxM80O_encoded b/components/cronet/android/test/assets/test/sdch/LeQxM80O_encoded
deleted file mode 100644
index ae6d433..0000000
--- a/components/cronet/android/test/assets/test/sdch/LeQxM80O_encoded
+++ /dev/null
Binary files differ
diff --git a/components/cronet/android/test/assets/test/sdch/LeQxM80O_encoded.mock-http-headers b/components/cronet/android/test/assets/test/sdch/LeQxM80O_encoded.mock-http-headers
deleted file mode 100644
index 11453b9..0000000
--- a/components/cronet/android/test/assets/test/sdch/LeQxM80O_encoded.mock-http-headers
+++ /dev/null
@@ -1,3 +0,0 @@
-HTTP/1.1 200 OK
-Content-Type: text/plain
-Content-Encoding: sdch
diff --git a/components/cronet/android/test/assets/test/sdch/dict/LeQxM80O b/components/cronet/android/test/assets/test/sdch/dict/LeQxM80O
deleted file mode 100644
index ab9a35e2..0000000
--- a/components/cronet/android/test/assets/test/sdch/dict/LeQxM80O
+++ /dev/null
@@ -1,4 +0,0 @@
-Domain: fake.sdch.domain
-Path: /sdch/test
-
-The quick brown fox jumps over the lazy dog.\n
diff --git a/components/cronet/android/test/assets/test/sdch/dict/LeQxM80O.mock-http-headers b/components/cronet/android/test/assets/test/sdch/dict/LeQxM80O.mock-http-headers
deleted file mode 100644
index 691e4137..0000000
--- a/components/cronet/android/test/assets/test/sdch/dict/LeQxM80O.mock-http-headers
+++ /dev/null
@@ -1,3 +0,0 @@
-HTTP/1.1 200 OK
-Cache-Control: public, max-age=31536000
-Content-Type: application/x-sdch-dictionary
diff --git a/components/cronet/android/test/assets/test/sdch/index b/components/cronet/android/test/assets/test/sdch/index
deleted file mode 100644
index e0638b6..0000000
--- a/components/cronet/android/test/assets/test/sdch/index
+++ /dev/null
@@ -1 +0,0 @@
-This is an index page.
diff --git a/components/cronet/android/test/assets/test/sdch/index.mock-http-headers b/components/cronet/android/test/assets/test/sdch/index.mock-http-headers
deleted file mode 100644
index 5c695b91..0000000
--- a/components/cronet/android/test/assets/test/sdch/index.mock-http-headers
+++ /dev/null
@@ -1,2 +0,0 @@
-HTTP/1.1 200 OK
-Content-Type: text/plain
diff --git a/components/cronet/android/test/cronet_url_request_context_config_test.cc b/components/cronet/android/test/cronet_url_request_context_config_test.cc
index dcbbec7..3289b45 100644
--- a/components/cronet/android/test/cronet_url_request_context_config_test.cc
+++ b/components/cronet/android/test/cronet_url_request_context_config_test.cc
@@ -30,7 +30,6 @@
       reinterpret_cast<URLRequestContextConfig*>(jurl_request_context_config);
   CHECK_EQ(config->enable_spdy, false);
   CHECK_EQ(config->enable_quic, true);
-  CHECK_EQ(config->enable_sdch, true);
   CHECK_EQ(config->bypass_public_key_pinning_for_local_trust_anchors, false);
   CHECK_EQ(config->quic_hints.size(), 1u);
   CHECK_EQ((*config->quic_hints.begin())->host, "example.com");
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
index 1c1304dc..ae45cba 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
@@ -1253,7 +1253,6 @@
                 new ExperimentalCronetEngine.Builder(getContext());
         builder.enableHttp2(false);
         builder.enableQuic(true);
-        builder.enableSdch(true);
         builder.addQuicHint("example.com", 12, 34);
         builder.setCertVerifierData("test_cert_verifier_data");
         builder.enableHttpCache(HTTP_CACHE_IN_MEMORY, 54321);
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/SdchTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/SdchTest.java
deleted file mode 100644
index e2b4674..0000000
--- a/components/cronet/android/test/javatests/src/org/chromium/net/SdchTest.java
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.net;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import static org.chromium.net.CronetTestRule.getContext;
-import static org.chromium.net.CronetTestRule.getTestStorage;
-
-import android.support.test.filters.SmallTest;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.junit.After;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.base.test.util.DisabledTest;
-import org.chromium.base.test.util.Feature;
-import org.chromium.net.CronetTestRule.OnlyRunNativeCronet;
-import org.chromium.net.impl.CronetUrlRequestContext;
-
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.Arrays;
-
-/**
- * Tests Sdch support.
- */
-@RunWith(BaseJUnit4ClassRunner.class)
-public class SdchTest {
-    @Rule
-    public final CronetTestRule mTestRule = new CronetTestRule();
-
-    private enum Sdch {
-        ENABLED,
-        DISABLED,
-    }
-
-    private CronetEngine.Builder createCronetEngineBuilder(Sdch setting) throws JSONException {
-        ExperimentalCronetEngine.Builder builder =
-                new ExperimentalCronetEngine.Builder(getContext());
-        builder.enableSdch(setting == Sdch.ENABLED);
-        mTestRule.enableDiskCache(builder);
-        JSONObject hostResolverParams = CronetTestUtil.generateHostResolverRules();
-        JSONObject experimentalOptions =
-                new JSONObject().put("HostResolverRules", hostResolverParams);
-        builder.setExperimentalOptions(experimentalOptions.toString());
-        // Start NativeTestServer.
-        assertTrue(NativeTestServer.startNativeTestServer(getContext()));
-        return builder;
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        NativeTestServer.shutdownNativeTestServer();
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Cronet"})
-    @OnlyRunNativeCronet
-    @DisabledTest(message = "Disabled due to flakiness. See https://crbug.com/760655.")
-    public void testSdchEnabled() throws Exception {
-        CronetEngine.Builder cronetEngineBuilder = createCronetEngineBuilder(Sdch.ENABLED);
-        CronetEngine cronetEngine = cronetEngineBuilder.build();
-        String targetUrl = NativeTestServer.getSdchURL() + "/sdch/test";
-        long contextAdapter = getContextAdapter((CronetUrlRequestContext) cronetEngine);
-        SdchObserver observer = new SdchObserver(targetUrl, contextAdapter);
-
-        // Make a request to /sdch which advertises the dictionary.
-        TestUrlRequestCallback callback1 = startAndWaitForComplete(
-                cronetEngine, NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O");
-        assertEquals(200, callback1.mResponseInfo.getHttpStatusCode());
-        assertEquals("This is an index page.\n", callback1.mResponseAsString);
-        assertEquals(Arrays.asList("/sdch/dict/LeQxM80O"),
-                callback1.mResponseInfo.getAllHeaders().get("Get-Dictionary"));
-
-        observer.waitForDictionaryAdded();
-
-        // Make a request to fetch encoded response at /sdch/test.
-        TestUrlRequestCallback callback2 = startAndWaitForComplete(cronetEngine, targetUrl);
-        assertEquals(200, callback2.mResponseInfo.getHttpStatusCode());
-        assertEquals("The quick brown fox jumps over the lazy dog.\n", callback2.mResponseAsString);
-
-        cronetEngine.shutdown();
-
-        // Shutting down the context will make JsonPrefStore to flush pending
-        // writes to disk.
-        String dictUrl = NativeTestServer.getSdchURL() + "/sdch/dict/LeQxM80O";
-        assertTrue(fileContainsString("local_prefs.json", dictUrl));
-
-        // Test persistence.
-        cronetEngine = cronetEngineBuilder.build();
-        CronetUrlRequestContext newContext = (CronetUrlRequestContext) cronetEngine;
-        long newContextAdapter = getContextAdapter(newContext);
-        SdchObserver newObserver = new SdchObserver(targetUrl, newContextAdapter);
-        newObserver.waitForDictionaryAdded();
-
-        // Make a request to fetch encoded response at /sdch/test.
-        TestUrlRequestCallback callback3 = startAndWaitForComplete(cronetEngine, targetUrl);
-        assertEquals(200, callback3.mResponseInfo.getHttpStatusCode());
-        assertEquals("The quick brown fox jumps over the lazy dog.\n", callback3.mResponseAsString);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Cronet"})
-    @OnlyRunNativeCronet
-    public void testSdchDisabled() throws Exception {
-        CronetEngine cronetEngine = createCronetEngineBuilder(Sdch.DISABLED).build();
-        // Make a request to /sdch.
-        // Since Sdch is not enabled, no dictionary should be advertised.
-        TestUrlRequestCallback callback = startAndWaitForComplete(
-                cronetEngine, NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O");
-        assertEquals(200, callback.mResponseInfo.getHttpStatusCode());
-        assertEquals("This is an index page.\n", callback.mResponseAsString);
-        assertEquals(null, callback.mResponseInfo.getAllHeaders().get("Get-Dictionary"));
-        cronetEngine.shutdown();
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Cronet"})
-    @OnlyRunNativeCronet
-    public void testDictionaryNotFound() throws Exception {
-        CronetEngine cronetEngine = createCronetEngineBuilder(Sdch.ENABLED).build();
-        // Make a request to /sdch/index which advertises a bad dictionary that
-        // does not exist.
-        TestUrlRequestCallback callback1 = startAndWaitForComplete(
-                cronetEngine, NativeTestServer.getSdchURL() + "/sdch/index?q=NotFound");
-        assertEquals(200, callback1.mResponseInfo.getHttpStatusCode());
-        assertEquals("This is an index page.\n", callback1.mResponseAsString);
-        assertEquals(Arrays.asList("/sdch/dict/NotFound"),
-                callback1.mResponseInfo.getAllHeaders().get("Get-Dictionary"));
-
-        // Make a request to fetch /sdch/test, and make sure Sdch encoding is not used.
-        TestUrlRequestCallback callback2 =
-                startAndWaitForComplete(cronetEngine, NativeTestServer.getSdchURL() + "/sdch/test");
-        assertEquals(200, callback2.mResponseInfo.getHttpStatusCode());
-        assertEquals("Sdch is not used.\n", callback2.mResponseAsString);
-        cronetEngine.shutdown();
-    }
-
-    private long getContextAdapter(CronetUrlRequestContext requestContext) {
-        return requestContext.getUrlRequestContextAdapter();
-    }
-
-    private TestUrlRequestCallback startAndWaitForComplete(CronetEngine cronetEngine, String url)
-            throws Exception {
-        TestUrlRequestCallback callback = new TestUrlRequestCallback();
-        UrlRequest.Builder builder =
-                cronetEngine.newUrlRequestBuilder(url, callback, callback.getExecutor());
-        builder.build().start();
-        callback.blockForDone();
-        return callback;
-    }
-
-    // Returns whether a file contains a particular string.
-    private boolean fileContainsString(String filename, String content) throws IOException {
-        BufferedReader reader = new BufferedReader(new FileReader(
-                getTestStorage(getContext()) + "/prefs/" + filename));
-        String line;
-        while ((line = reader.readLine()) != null) {
-            if (line.contains(content)) {
-                reader.close();
-                return true;
-            }
-        }
-        reader.close();
-        return false;
-    }
-}
diff --git a/components/cronet/android/test/native_test_server.cc b/components/cronet/android/test/native_test_server.cc
index 9603df8..55ec675 100644
--- a/components/cronet/android/test/native_test_server.cc
+++ b/components/cronet/android/test/native_test_server.cc
@@ -43,14 +43,6 @@
 const char kEchoMethodPath[] = "/echo_method";
 const char kRedirectToEchoBodyPath[] = "/redirect_to_echo_body";
 const char kExabyteResponsePath[] = "/exabyte_response";
-// Path that advertises the dictionary passed in query params if client
-// supports Sdch encoding. E.g. /sdch/index?q=LeQxM80O will make the server
-// responds with "Get-Dictionary: /sdch/dict/LeQxM80O".
-const char kSdchPath[] = "/sdch/index";
-// Path that returns encoded response if client has the right dictionary.
-const char kSdchTestPath[] = "/sdch/test";
-// Path where dictionaries are stored.
-const char kSdchDictPath[] = "/sdch/dict/";
 
 net::EmbeddedTestServer* g_test_server = nullptr;
 
@@ -81,21 +73,6 @@
   DISALLOW_COPY_AND_ASSIGN(ExabyteResponse);
 };
 
-std::unique_ptr<net::test_server::RawHttpResponse> ConstructResponseBasedOnFile(
-    const base::FilePath& file_path) {
-  std::string file_contents;
-  bool read_file = base::ReadFileToString(file_path, &file_contents);
-  DCHECK(read_file) << file_path.value();
-  base::FilePath headers_path(
-      file_path.AddExtension(FILE_PATH_LITERAL("mock-http-headers")));
-  std::string headers_contents;
-  bool read_headers = base::ReadFileToString(headers_path, &headers_contents);
-  DCHECK(read_headers);
-  std::unique_ptr<net::test_server::RawHttpResponse> http_response(
-      new net::test_server::RawHttpResponse(headers_contents, file_contents));
-  return http_response;
-}
-
 std::unique_ptr<net::test_server::HttpResponse> NativeTestServerRequestHandler(
     const net::test_server::HttpRequest& request) {
   DCHECK(g_test_server);
@@ -144,53 +121,6 @@
   return std::unique_ptr<net::test_server::BasicHttpResponse>();
 }
 
-std::unique_ptr<net::test_server::HttpResponse> SdchRequestHandler(
-    const base::FilePath& test_files_root,
-    const net::test_server::HttpRequest& request) {
-  DCHECK(g_test_server);
-  base::FilePath dir_path = test_files_root;
-
-  if (base::StartsWith(request.relative_url, kSdchPath,
-                       base::CompareCase::SENSITIVE)) {
-    base::FilePath file_path = dir_path.Append("sdch/index");
-    std::unique_ptr<net::test_server::RawHttpResponse> response =
-        ConstructResponseBasedOnFile(file_path);
-    // Check for query params to see which dictionary to advertise.
-    // For instance, ?q=dictionaryA will make the server advertise dictionaryA.
-    GURL url = g_test_server->GetURL(request.relative_url);
-    std::string dictionary;
-    if (!net::GetValueForKeyInQuery(url, "q", &dictionary)) {
-      CHECK(false) << "dictionary is not found in query params of "
-                   << request.relative_url;
-    }
-    auto accept_encoding_header = request.headers.find("Accept-Encoding");
-    if (accept_encoding_header != request.headers.end()) {
-      if (accept_encoding_header->second.find("sdch") != std::string::npos)
-        response->AddHeader(base::StringPrintf(
-            "Get-Dictionary: %s%s", kSdchDictPath, dictionary.c_str()));
-    }
-    return std::move(response);
-  }
-
-  if (base::StartsWith(request.relative_url, kSdchTestPath,
-                       base::CompareCase::SENSITIVE)) {
-    auto avail_dictionary_header = request.headers.find("Avail-Dictionary");
-    if (avail_dictionary_header != request.headers.end()) {
-      base::FilePath file_path = dir_path.Append(
-          "sdch/" + avail_dictionary_header->second + "_encoded");
-      return ConstructResponseBasedOnFile(file_path);
-    }
-    std::unique_ptr<net::test_server::BasicHttpResponse> response(
-        new net::test_server::BasicHttpResponse());
-    response->set_content_type("text/plain");
-    response->set_content("Sdch is not used.\n");
-    return std::move(response);
-  }
-
-  // Unhandled requests result in the Embedded test server sending a 404.
-  return std::unique_ptr<net::test_server::BasicHttpResponse>();
-}
-
 std::unique_ptr<net::test_server::HttpResponse> HandleExabyteRequest(
     const net::test_server::HttpRequest& request) {
   return base::WrapUnique(new ExabyteResponse);
@@ -218,8 +148,6 @@
                  base::Bind(&HandleExabyteRequest)));
   base::FilePath test_files_root(
       base::android::ConvertJavaStringToUTF8(env, jtest_files_root));
-  g_test_server->RegisterRequestHandler(
-      base::Bind(&SdchRequestHandler, test_files_root));
 
   // Add a third handler for paths that NativeTestServerRequestHandler does not
   // handle.
diff --git a/components/cronet/android/test/sdch_test_util.cc b/components/cronet/android/test/sdch_test_util.cc
deleted file mode 100644
index 7e896ba..0000000
--- a/components/cronet/android/test/sdch_test_util.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string>
-
-#include "base/android/jni_android.h"
-#include "base/android/jni_string.h"
-#include "base/android/scoped_java_ref.h"
-#include "base/bind.h"
-#include "base/macros.h"
-#include "components/cronet/android/test/cronet_test_util.h"
-#include "jni/SdchObserver_jni.h"
-#include "net/base/sdch_manager.h"
-#include "net/base/sdch_observer.h"
-#include "net/url_request/url_request_context.h"
-#include "url/gurl.h"
-
-using base::android::JavaParamRef;
-
-namespace cronet {
-
-namespace {
-
-class TestSdchObserver : public net::SdchObserver {
- public:
-  TestSdchObserver(
-      const GURL& target_url,
-      net::SdchManager* manager,
-      const base::android::ScopedJavaGlobalRef<jobject>& jsdch_observer_ref)
-      : target_url_(target_url), manager_(manager) {
-    jsdch_observer_ref_.Reset(jsdch_observer_ref);
-  }
-
-  // SdchObserver implementation
-  void OnDictionaryAdded(const GURL& dictionary_url,
-                         const std::string& server_hash) override {
-    // Only notify if the dictionary for the |target_url_| has been added.
-    if (manager_->GetDictionarySet(target_url_)) {
-      JNIEnv* env = base::android::AttachCurrentThread();
-      Java_SdchObserver_onDictionaryAdded(env, jsdch_observer_ref_);
-      manager_->RemoveObserver(this);
-      delete this;
-    }
-  }
-
-  void OnDictionaryRemoved(const std::string& server_hash) override {}
-
-  void OnDictionaryUsed(const std::string& server_hash) override {}
-
-  void OnGetDictionary(const GURL& request_url,
-                       const GURL& dictionary_url) override {}
-
-  void OnClearDictionaries() override {}
-
- private:
-  GURL target_url_;
-  net::SdchManager* manager_;
-  base::android::ScopedJavaGlobalRef<jobject> jsdch_observer_ref_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestSdchObserver);
-};
-
-void AddSdchObserverHelper(
-    const GURL& target_url,
-    const base::android::ScopedJavaGlobalRef<jobject>& jsdch_observer_ref,
-    jlong jadapter) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  net::URLRequestContext* url_request_context =
-      TestUtil::GetURLRequestContext(jadapter);
-  // If dictionaries for |target_url| are already added, skip adding the
-  // observer.
-  if (url_request_context->sdch_manager()->GetDictionarySet(target_url)) {
-    Java_SdchObserver_onDictionarySetAlreadyPresent(env, jsdch_observer_ref);
-    return;
-  }
-
-  url_request_context->sdch_manager()->AddObserver(new TestSdchObserver(
-      target_url, url_request_context->sdch_manager(), jsdch_observer_ref));
-  Java_SdchObserver_onAddSdchObserverCompleted(env, jsdch_observer_ref);
-}
-
-}  // namespace
-
-void AddSdchObserver(JNIEnv* env,
-                     const JavaParamRef<jobject>& jsdch_observer,
-                     const JavaParamRef<jstring>& jtarget_url,
-                     jlong jadapter) {
-  base::android::ScopedJavaGlobalRef<jobject> jsdch_observer_ref;
-  // ScopedJavaGlobalRef do not hold onto the env reference, so it is safe to
-  // use it across threads. |AddSdchObserverHelper| will acquire a new
-  // JNIEnv before calling into Java.
-  jsdch_observer_ref.Reset(env, jsdch_observer);
-
-  GURL target_url(base::android::ConvertJavaStringToUTF8(env, jtarget_url));
-  TestUtil::RunAfterContextInit(jadapter,
-                                base::Bind(&AddSdchObserverHelper, target_url,
-                                           jsdch_observer_ref, jadapter));
-}
-
-}  // namespace cronet
diff --git a/components/cronet/android/test/src/org/chromium/net/CronetTestUtil.java b/components/cronet/android/test/src/org/chromium/net/CronetTestUtil.java
index 5032df5..f697c37 100644
--- a/components/cronet/android/test/src/org/chromium/net/CronetTestUtil.java
+++ b/components/cronet/android/test/src/org/chromium/net/CronetTestUtil.java
@@ -17,13 +17,12 @@
  */
 @JNINamespace("cronet")
 public class CronetTestUtil {
-    static final String SDCH_FAKE_HOST = "fake.sdch.domain";
     // QUIC test domain must match the certificate used
     // (quic_test.example.com.crt and quic_test.example.com.key.pkcs8), and
     // the file served (
     // components/cronet/android/test/assets/test/quic_data/simple.txt).
     static final String QUIC_FAKE_HOST = "test.example.com";
-    private static final String[] TEST_DOMAINS = {SDCH_FAKE_HOST, QUIC_FAKE_HOST};
+    private static final String[] TEST_DOMAINS = {QUIC_FAKE_HOST};
     private static final String LOOPBACK_ADDRESS = "127.0.0.1";
 
     /**
@@ -31,7 +30,6 @@
      * namely:
      * <ul>
      * <li>{@link QuicTestServer#getServerHost}</li>
-     * <li>{@link NativeTestServer#getSdchURL}</li>'s host
      * </ul>
      * Maps the test hostnames to 127.0.0.1.
      */
@@ -44,7 +42,6 @@
      * namely:
      * <ul>
      * <li>{@link QuicTestServer#getServerHost}</li>
-     * <li>{@link NativeTestServer#getSdchURL}</li>'s host
      * </ul>
      * @param destination host to map to
      */
diff --git a/components/cronet/android/test/src/org/chromium/net/NativeTestServer.java b/components/cronet/android/test/src/org/chromium/net/NativeTestServer.java
index 325cfcc7..6f703abe 100644
--- a/components/cronet/android/test/src/org/chromium/net/NativeTestServer.java
+++ b/components/cronet/android/test/src/org/chromium/net/NativeTestServer.java
@@ -9,9 +9,6 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.test.util.UrlUtils;
 
-import java.net.MalformedURLException;
-import java.net.URL;
-
 /**
  * Wrapper class to start an in-process native test server, and get URLs
  * needed to talk to it.
@@ -55,10 +52,6 @@
         return nativeGetFileURL(filePath);
     }
 
-    public static String getSdchURL() throws MalformedURLException {
-        return new URL("http", CronetTestUtil.SDCH_FAKE_HOST, getPort(), "").toString();
-    }
-
     // Returns a URL that the server will return an Exabyte of data
     public static String getExabyteResponseURL() {
         return nativeGetExabyteResponseURL();
diff --git a/components/cronet/android/test/src/org/chromium/net/SdchObserver.java b/components/cronet/android/test/src/org/chromium/net/SdchObserver.java
deleted file mode 100644
index 2028c090..0000000
--- a/components/cronet/android/test/src/org/chromium/net/SdchObserver.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.net;
-
-import android.os.ConditionVariable;
-
-import junit.framework.Assert;
-
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.base.annotations.JNINamespace;
-
-/**
- * Class to watch for Sdch dictionary events. The native implementation
- * unregisters itself when an event happens. Therefore, an instance of this
- * class is only able to receive a notification of the earliest event.
- */
-@JNINamespace("cronet")
-public class SdchObserver {
-    private static final int BLOCK_WAIT_TIMEOUT_SEC = 20;
-    private final ConditionVariable mAddBlock = new ConditionVariable();
-
-    /**
-     * Constructor.
-     * @param targetUrl the target url on which sdch encoding will be used.
-     * @param contextAdapter the native context adapter to register the observer.
-     */
-    public SdchObserver(String targetUrl, long contextAdapter) {
-        nativeAddSdchObserver(targetUrl, contextAdapter);
-    }
-
-    /**
-     * Called when a dictionary is added to the SdchManager for the target url.
-     */
-    @CalledByNative
-    protected void onDictionaryAdded() {
-        mAddBlock.open();
-    }
-
-    /**
-     * Called after the observer has been registered.
-     */
-    @CalledByNative
-    protected void onAddSdchObserverCompleted() {
-        // Left blank;
-    }
-
-    /**
-     * Called if the dictionary was added before the observer registration.
-     */
-    @CalledByNative
-    protected void onDictionarySetAlreadyPresent() {
-        mAddBlock.open();
-    }
-
-    public void waitForDictionaryAdded() {
-        boolean success = mAddBlock.block(BLOCK_WAIT_TIMEOUT_SEC * 1000);
-        if (!success) {
-            Assert.fail("Timeout: the dictionary hasn't been added after waiting for "
-                    + BLOCK_WAIT_TIMEOUT_SEC + " seconds");
-        }
-    }
-
-    private native void nativeAddSdchObserver(String targetUrl, long contextAdapter);
-}
diff --git a/components/cronet/cronet_prefs_manager.cc b/components/cronet/cronet_prefs_manager.cc
index 66c78087..32acc7bb 100644
--- a/components/cronet/cronet_prefs_manager.cc
+++ b/components/cronet/cronet_prefs_manager.cc
@@ -16,7 +16,6 @@
 #include "components/prefs/pref_service_factory.h"
 #include "net/http/http_server_properties_manager.h"
 #include "net/nqe/network_qualities_prefs_manager.h"
-#include "net/sdch/sdch_owner.h"
 #include "net/url_request/url_request_context_builder.h"
 
 namespace cronet {
@@ -198,105 +197,6 @@
   DISALLOW_COPY_AND_ASSIGN(NetworkQualitiesPrefDelegateImpl);
 };
 
-// Connects the SdchOwner's storage to the prefs.
-class SdchOwnerPrefStorage : public net::SdchOwner::PrefStorage,
-                             public PrefStore::Observer {
- public:
-  explicit SdchOwnerPrefStorage(PersistentPrefStore* storage)
-      : storage_(storage), storage_key_("SDCH"), init_observer_(nullptr) {}
-  ~SdchOwnerPrefStorage() override {
-    if (init_observer_)
-      storage_->RemoveObserver(this);
-  }
-
-  ReadError GetReadError() const override {
-    PersistentPrefStore::PrefReadError error = storage_->GetReadError();
-
-    DCHECK_NE(
-        error,
-        PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE);
-    DCHECK_NE(error, PersistentPrefStore::PREF_READ_ERROR_MAX_ENUM);
-
-    switch (error) {
-      case PersistentPrefStore::PREF_READ_ERROR_NONE:
-        return PERSISTENCE_FAILURE_NONE;
-
-      case PersistentPrefStore::PREF_READ_ERROR_NO_FILE:
-        return PERSISTENCE_FAILURE_REASON_NO_FILE;
-
-      case PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE:
-      case PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE:
-      case PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER:
-      case PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED:
-      case PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT:
-        return PERSISTENCE_FAILURE_REASON_READ_FAILED;
-
-      case PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED:
-      case PersistentPrefStore::PREF_READ_ERROR_FILE_NOT_SPECIFIED:
-      case PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE:
-      case PersistentPrefStore::PREF_READ_ERROR_MAX_ENUM:
-      default:
-        // We don't expect these other failures given our usage of prefs.
-        NOTREACHED();
-        return PERSISTENCE_FAILURE_REASON_OTHER;
-    }
-  }
-
-  bool GetValue(const base::DictionaryValue** result) const override {
-    const base::Value* result_value = nullptr;
-    if (!storage_->GetValue(storage_key_, &result_value))
-      return false;
-    return result_value->GetAsDictionary(result);
-  }
-
-  bool GetMutableValue(base::DictionaryValue** result) override {
-    base::Value* result_value = nullptr;
-    if (!storage_->GetMutableValue(storage_key_, &result_value))
-      return false;
-    return result_value->GetAsDictionary(result);
-  }
-
-  void SetValue(std::unique_ptr<base::DictionaryValue> value) override {
-    storage_->SetValue(storage_key_, std::move(value),
-                       WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
-  }
-
-  void ReportValueChanged() override {
-    storage_->ReportValueChanged(storage_key_,
-                                 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
-  }
-
-  bool IsInitializationComplete() override {
-    return storage_->IsInitializationComplete();
-  }
-
-  void StartObservingInit(net::SdchOwner* observer) override {
-    DCHECK(!init_observer_);
-    init_observer_ = observer;
-    storage_->AddObserver(this);
-  }
-
-  void StopObservingInit() override {
-    DCHECK(init_observer_);
-    init_observer_ = nullptr;
-    storage_->RemoveObserver(this);
-  }
-
- private:
-  // PrefStore::Observer implementation.
-  void OnPrefValueChanged(const std::string& key) override {}
-  void OnInitializationCompleted(bool succeeded) override {
-    init_observer_->OnPrefStorageInitializationComplete(succeeded);
-  }
-
-  PersistentPrefStore* storage_;  // Non-owning.
-  const std::string storage_key_;
-
-  net::SdchOwner* init_observer_;  // Non-owning.
-
-  DISALLOW_COPY_AND_ASSIGN(SdchOwnerPrefStorage);
-};
-
 }  // namespace
 
 CronetPrefsManager::CronetPrefsManager(
@@ -383,12 +283,6 @@
           net_log);
 }
 
-void CronetPrefsManager::SetupSdchPersistence(net::SdchOwner* sdch_owner) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  sdch_owner->EnablePersistentStorage(
-      base::MakeUnique<SdchOwnerPrefStorage>(json_pref_store_.get()));
-}
-
 void CronetPrefsManager::PrepareForShutdown() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (pref_service_)
diff --git a/components/cronet/cronet_prefs_manager.h b/components/cronet/cronet_prefs_manager.h
index d87063e91..adf6550e 100644
--- a/components/cronet/cronet_prefs_manager.h
+++ b/components/cronet/cronet_prefs_manager.h
@@ -26,7 +26,6 @@
 class NetLog;
 class NetworkQualitiesPrefsManager;
 class NetworkQualityEstimator;
-class SdchOwner;
 class URLRequestContextBuilder;
 }  // namespace net
 
@@ -56,8 +55,6 @@
                                  int host_cache_persistence_delay_ms,
                                  net::NetLog* net_log);
 
-  void SetupSdchPersistence(net::SdchOwner* sdch_owner);
-
   // Prepares |this| for shutdown.
   void PrepareForShutdown();
 
diff --git a/components/cronet/stale_host_resolver_unittest.cc b/components/cronet/stale_host_resolver_unittest.cc
index 108f396..ad86b5d9 100644
--- a/components/cronet/stale_host_resolver_unittest.cc
+++ b/components/cronet/stale_host_resolver_unittest.cc
@@ -452,8 +452,6 @@
       "Default QUIC User Agent ID",
       // Enable SPDY.
       true,
-      // Enable SDCH.
-      false,
       // Enable Brotli.
       false,
       // Type of http cache.
diff --git a/components/cronet/url_request_context_config.cc b/components/cronet/url_request_context_config.cc
index f51cb9f..c70a1f1b 100644
--- a/components/cronet/url_request_context_config.cc
+++ b/components/cronet/url_request_context_config.cc
@@ -139,7 +139,6 @@
     bool enable_quic,
     const std::string& quic_user_agent_id,
     bool enable_spdy,
-    bool enable_sdch,
     bool enable_brotli,
     HttpCacheType http_cache,
     int http_cache_max_size,
@@ -154,7 +153,6 @@
     : enable_quic(enable_quic),
       quic_user_agent_id(quic_user_agent_id),
       enable_spdy(enable_spdy),
-      enable_sdch(enable_sdch),
       enable_brotli(enable_brotli),
       http_cache(http_cache),
       http_cache_max_size(http_cache_max_size),
@@ -444,7 +442,6 @@
     context_builder->DisableHttpCache();
   }
   context_builder->set_user_agent(user_agent);
-  context_builder->set_sdch_enabled(enable_sdch);
   net::HttpNetworkSession::Params session_params;
   session_params.enable_http2 = enable_spdy;
   session_params.enable_quic = enable_quic;
@@ -480,9 +477,9 @@
 std::unique_ptr<URLRequestContextConfig>
 URLRequestContextConfigBuilder::Build() {
   return base::MakeUnique<URLRequestContextConfig>(
-      enable_quic, quic_user_agent_id, enable_spdy, enable_sdch, enable_brotli,
-      http_cache, http_cache_max_size, load_disable_cache, storage_path,
-      user_agent, experimental_options, std::move(mock_cert_verifier),
+      enable_quic, quic_user_agent_id, enable_spdy, enable_brotli, http_cache,
+      http_cache_max_size, load_disable_cache, storage_path, user_agent,
+      experimental_options, std::move(mock_cert_verifier),
       enable_network_quality_estimator,
       bypass_public_key_pinning_for_local_trust_anchors, cert_verifier_data);
 }
diff --git a/components/cronet/url_request_context_config.h b/components/cronet/url_request_context_config.h
index b18f8b4..f9ab7ef 100644
--- a/components/cronet/url_request_context_config.h
+++ b/components/cronet/url_request_context_config.h
@@ -86,8 +86,6 @@
       const std::string& quic_user_agent_id,
       // Enable SPDY.
       bool enable_spdy,
-      // Enable SDCH.
-      bool enable_sdch,
       // Enable Brotli.
       bool enable_brotli,
       // Type of http cache.
@@ -124,8 +122,6 @@
   const std::string quic_user_agent_id;
   // Enable SPDY.
   const bool enable_spdy;
-  // Enable SDCH.
-  const bool enable_sdch;
   // Enable Brotli.
   const bool enable_brotli;
   // Type of http cache.
@@ -215,8 +211,6 @@
   std::string quic_user_agent_id = "";
   // Enable SPDY.
   bool enable_spdy = true;
-  // Enable SDCH.
-  bool enable_sdch = false;
   // Enable Brotli.
   bool enable_brotli = false;
   // Type of http cache.
diff --git a/components/cronet/url_request_context_config_unittest.cc b/components/cronet/url_request_context_config_unittest.cc
index 047a073..ead63c6a 100644
--- a/components/cronet/url_request_context_config_unittest.cc
+++ b/components/cronet/url_request_context_config_unittest.cc
@@ -30,8 +30,6 @@
       "Default QUIC User Agent ID",
       // Enable SPDY.
       true,
-      // Enable SDCH.
-      false,
       // Enable Brotli.
       false,
       // Type of http cache.
@@ -123,8 +121,6 @@
       "Default QUIC User Agent ID",
       // Enable SPDY.
       true,
-      // Enable SDCH.
-      false,
       // Enable Brotli.
       false,
       // Type of http cache.
diff --git a/components/exo/pointer.cc b/components/exo/pointer.cc
index c74dfd0..982ef307 100644
--- a/components/exo/pointer.cc
+++ b/components/exo/pointer.cc
@@ -362,9 +362,11 @@
   host_window()->SetTransform(gfx::GetScaleTransform(gfx::Point(), scale));
 
   std::unique_ptr<viz::CopyOutputRequest> request =
-      viz::CopyOutputRequest::CreateBitmapRequest(base::BindOnce(
-          &Pointer::OnCursorCaptured,
-          cursor_capture_weak_ptr_factory_.GetWeakPtr(), hotspot));
+      std::make_unique<viz::CopyOutputRequest>(
+          viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+          base::BindOnce(&Pointer::OnCursorCaptured,
+                         cursor_capture_weak_ptr_factory_.GetWeakPtr(),
+                         hotspot));
 
   request->set_source(cursor_capture_source_id_);
   host_window()->layer()->RequestCopyOfOutput(std::move(request));
@@ -378,8 +380,8 @@
   if (result->IsEmpty()) {
     cursor_bitmap_.reset();
   } else {
-    DCHECK(result->HasBitmap());
-    cursor_bitmap_ = *result->TakeBitmap();
+    cursor_bitmap_ = result->AsSkBitmap();
+    DCHECK(cursor_bitmap_.readyToDraw());
     cursor_hotspot_ = hotspot;
   }
 
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index e1a4eba..6abb1ecc 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -2280,7 +2280,7 @@
       float scale_factor =
           WMHelper::GetInstance()->GetDefaultDeviceScaleFactor();
       // Send using 16.16 fixed point.
-      const int kDecimalBits = 16;
+      const int kDecimalBits = 24;
       int32_t fixed_scale =
           static_cast<int32_t>(scale_factor * (1 << kDecimalBits));
       zcr_remote_shell_v1_send_default_device_scale_factor(
diff --git a/components/gcm_driver/gcm_driver_desktop.cc b/components/gcm_driver/gcm_driver_desktop.cc
index 67867c3..79dd5c7 100644
--- a/components/gcm_driver/gcm_driver_desktop.cc
+++ b/components/gcm_driver/gcm_driver_desktop.cc
@@ -14,7 +14,6 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/sequenced_task_runner.h"
 #include "base/task_runner_util.h"
 #include "base/threading/sequenced_worker_pool.h"
@@ -148,10 +147,6 @@
     const base::FilePath& store_path,
     const scoped_refptr<net::URLRequestContextGetter>& request_context,
     const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "477117 GCMDriverDesktop::IOWorker::Initialize"));
   DCHECK(io_thread_->RunsTasksInCurrentSequence());
 
   gcm_client_ = gcm_client_factory->BuildInstance();
diff --git a/components/invalidation/impl/non_blocking_invalidator.cc b/components/invalidation/impl/non_blocking_invalidator.cc
index 0a5bca0..8d014f9 100644
--- a/components/invalidation/impl/non_blocking_invalidator.cc
+++ b/components/invalidation/impl/non_blocking_invalidator.cc
@@ -11,7 +11,6 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -184,10 +183,6 @@
 
 void NonBlockingInvalidator::Core::UpdateCredentials(const std::string& email,
                                                      const std::string& token) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "477117 NonBlockingInvalidator::Core::UpdateCredentials"));
   DCHECK(network_task_runner_->BelongsToCurrentThread());
   invalidation_notifier_->UpdateCredentials(email, token);
 }
diff --git a/components/leveldb/BUILD.gn b/components/leveldb/BUILD.gn
index 6c343f4a7..0b0f2cb 100644
--- a/components/leveldb/BUILD.gn
+++ b/components/leveldb/BUILD.gn
@@ -58,6 +58,7 @@
 
 service_test("leveldb_service_unittests") {
   sources = [
+    "leveldb_mojo_unittest.cc",
     "leveldb_service_unittest.cc",
     "remote_iterator_unittest.cc",
   ]
diff --git a/components/leveldb/OWNERS b/components/leveldb/OWNERS
index 6b664e6..e03714b 100644
--- a/components/leveldb/OWNERS
+++ b/components/leveldb/OWNERS
@@ -4,5 +4,11 @@
 per-file manifest.json=set noparent
 per-file manifest.json=file://ipc/SECURITY_OWNERS
 
+per-file *_struct_traits*.*=set noparent
+per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
+
 per-file test_manifest.json=set noparent
 per-file test_manifest.json=file://ipc/SECURITY_OWNERS
+
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/components/leveldb/leveldb.typemap b/components/leveldb/leveldb.typemap
new file mode 100644
index 0000000..52d2f394
--- /dev/null
+++ b/components/leveldb/leveldb.typemap
@@ -0,0 +1,16 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//components/leveldb/public/interfaces/leveldb.mojom"
+public_headers = [ "//third_party/leveldatabase/env_chromium.h" ]
+traits_headers = [ "//components/leveldb/leveldb_struct_traits.h" ]
+sources = [
+  "//components/leveldb/leveldb_struct_traits.cc",
+]
+deps = []
+public_deps = [
+  "//mojo/public/cpp/bindings",
+  "//third_party/leveldatabase",
+]
+type_mappings = [ "leveldb.mojom.OpenOptions=::leveldb_env::Options" ]
diff --git a/components/leveldb/leveldb_database_impl.cc b/components/leveldb/leveldb_database_impl.cc
index f0f509a..33eaefa 100644
--- a/components/leveldb/leveldb_database_impl.cc
+++ b/components/leveldb/leveldb_database_impl.cc
@@ -290,42 +290,16 @@
 bool LevelDBDatabaseImpl::OnMemoryDump(
     const base::trace_event::MemoryDumpArgs& args,
     base::trace_event::ProcessMemoryDump* pmd) {
-  std::string name = base::StringPrintf("leveldb/mojo/0x%" PRIXPTR,
-                                        reinterpret_cast<uintptr_t>(db_.get()));
-  auto* mad = pmd->CreateAllocatorDump(name);
-
-  uint64_t memory_usage = 0;
-  std::string memory_usage_string;
-  bool got_memory_usage =
-      db_->GetProperty("leveldb.approximate-memory-usage",
-                       &memory_usage_string) &&
-      base::StringToUint64(memory_usage_string, &memory_usage);
-  DCHECK(got_memory_usage);
-  mad->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
-                 base::trace_event::MemoryAllocatorDump::kUnitsBytes,
-                 memory_usage);
-  if (cache_) {
-    auto* cache_mad = pmd->CreateAllocatorDump(name + "/block_cache");
-    cache_mad->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+  auto* dump = leveldb_env::DBTracker::GetOrCreateAllocatorDump(pmd, db_.get());
+  if (!dump)
+    return true;
+  auto* global_dump = pmd->CreateSharedGlobalAllocatorDump(*memory_dump_id_);
+  pmd->AddOwnershipEdge(global_dump->guid(), dump->guid());
+  // Add size to global dump to propagate the size of the database to the
+  // client's dump.
+  global_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
                          base::trace_event::MemoryAllocatorDump::kUnitsBytes,
-                         cache_->TotalCharge());
-  }
-
-  // All leveldb databases are already dumped by leveldb_env::DBTracker. Add
-  // an edge to avoid double counting.
-  pmd->AddSuballocation(mad->guid(),
-                        leveldb_env::DBTracker::GetMemoryDumpName(db_.get()));
-
-  if (memory_dump_id_) {
-    auto* global_dump = pmd->CreateSharedGlobalAllocatorDump(*memory_dump_id_);
-    pmd->AddOwnershipEdge(global_dump->guid(), mad->guid());
-    // Add size to global dump to propagate the size of the database to the
-    // client's dump.
-    global_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
-                           base::trace_event::MemoryAllocatorDump::kUnitsBytes,
-                           memory_usage);
-  }
-
+                         dump->GetSizeInternal());
   return true;
 }
 
diff --git a/components/leveldb/leveldb_mojo_unittest.cc b/components/leveldb/leveldb_mojo_unittest.cc
new file mode 100644
index 0000000..48e08f2
--- /dev/null
+++ b/components/leveldb/leveldb_mojo_unittest.cc
@@ -0,0 +1,35 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+#include "components/leveldb/leveldb_struct_traits.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class LevelDBServiceMojoTest : public testing::Test {};
+
+}  // namespace
+
+TEST(LevelDBServiceMojoTest, TestSerialization) {
+  leveldb_env::Options input;
+  // Tweak all input values to have a non-default value.
+  input.create_if_missing = !input.create_if_missing;
+  input.error_if_exists = !input.error_if_exists;
+  input.paranoid_checks = !input.paranoid_checks;
+  input.write_buffer_size += 1;
+  input.max_open_files += 1;
+  input.block_cache = leveldb_env::SharedWebBlockCache();
+
+  leveldb_env::Options output;
+  ASSERT_TRUE(leveldb::mojom::OpenOptions::Deserialize(
+      leveldb::mojom::OpenOptions::Serialize(&input), &output));
+
+  EXPECT_EQ(output.create_if_missing, output.create_if_missing);
+  EXPECT_EQ(output.error_if_exists, output.error_if_exists);
+  EXPECT_EQ(output.paranoid_checks, output.paranoid_checks);
+  EXPECT_EQ(output.write_buffer_size, output.write_buffer_size);
+  EXPECT_EQ(output.max_open_files, output.max_open_files);
+  EXPECT_EQ(leveldb_env::SharedWebBlockCache(), output.block_cache);
+}
diff --git a/components/leveldb/leveldb_service_impl.cc b/components/leveldb/leveldb_service_impl.cc
index 6707026..8a12e355 100644
--- a/components/leveldb/leveldb_service_impl.cc
+++ b/components/leveldb/leveldb_service_impl.cc
@@ -34,46 +34,35 @@
         memory_dump_id,
     leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
     OpenCallback callback) {
-  OpenWithOptions(leveldb::mojom::OpenOptions::New(), std::move(directory),
-                  dbname, memory_dump_id, std::move(database),
-                  std::move(callback));
+  leveldb_env::Options options;
+  // the default here to 80 instead of leveldb's default 1000 because we don't
+  // want to consume all file descriptors. See
+  // https://code.google.com/p/chromium/issues/detail?id=227313#c11 for
+  // details.)
+  options.max_open_files = 80;
+
+  OpenWithOptions(options, std::move(directory), dbname, memory_dump_id,
+                  std::move(database), std::move(callback));
 }
 
 void LevelDBServiceImpl::OpenWithOptions(
-    leveldb::mojom::OpenOptionsPtr open_options,
+    const leveldb_env::Options& options,
     filesystem::mojom::DirectoryPtr directory,
     const std::string& dbname,
     const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
         memory_dump_id,
     leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
     OpenCallback callback) {
-  leveldb_env::Options options;
-  options.create_if_missing = open_options->create_if_missing;
-  options.error_if_exists = open_options->error_if_exists;
-  options.paranoid_checks = open_options->paranoid_checks;
-  options.write_buffer_size = open_options->write_buffer_size;
-  options.max_open_files = open_options->max_open_files;
-
-  options.compression = leveldb::kSnappyCompression;
-
   // Register our directory with the file thread.
   LevelDBMojoProxy::OpaqueDir* dir =
       thread_->RegisterDirectory(std::move(directory));
 
   std::unique_ptr<MojoEnv> env_mojo(new MojoEnv(thread_, dir));
-  options.env = env_mojo.get();
-
-  switch (open_options->shared_block_read_cache) {
-    case leveldb::mojom::SharedReadCache::Web:
-      options.block_cache = leveldb_env::SharedWebBlockCache();
-      break;
-    case leveldb::mojom::SharedReadCache::Default:
-      // fallthrough
-      break;
-  }
+  leveldb_env::Options open_options = options;
+  open_options.env = env_mojo.get();
 
   std::unique_ptr<leveldb::DB> db;
-  leveldb::Status s = leveldb_env::OpenDB(options, dbname, &db);
+  leveldb::Status s = leveldb_env::OpenDB(open_options, dbname, &db);
 
   if (s.ok()) {
     mojo::MakeStrongAssociatedBinding(
diff --git a/components/leveldb/leveldb_service_impl.h b/components/leveldb/leveldb_service_impl.h
index 1d5fab0..bae33e8 100644
--- a/components/leveldb/leveldb_service_impl.h
+++ b/components/leveldb/leveldb_service_impl.h
@@ -33,7 +33,7 @@
             leveldb::mojom::LevelDBDatabaseAssociatedRequest database,
             OpenCallback callback) override;
   void OpenWithOptions(
-      leveldb::mojom::OpenOptionsPtr open_options,
+      const leveldb_env::Options& open_options,
       filesystem::mojom::DirectoryPtr directory,
       const std::string& dbname,
       const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
diff --git a/components/leveldb/leveldb_service_unittest.cc b/components/leveldb/leveldb_service_unittest.cc
index 0ffefeb..2c9192d 100644
--- a/components/leveldb/leveldb_service_unittest.cc
+++ b/components/leveldb/leveldb_service_unittest.cc
@@ -260,9 +260,9 @@
     temp_directory->Clone(MakeRequest(&directory));
 
     mojom::LevelDBDatabaseAssociatedPtr database;
-    leveldb::mojom::OpenOptionsPtr options = leveldb::mojom::OpenOptions::New();
-    options->error_if_exists = true;
-    options->create_if_missing = true;
+    leveldb_env::Options options;
+    options.error_if_exists = true;
+    options.create_if_missing = true;
     base::RunLoop run_loop;
     leveldb()->OpenWithOptions(std::move(options), std::move(directory), "test",
                                base::nullopt, MakeRequest(&database),
@@ -311,9 +311,9 @@
     temp_directory->Clone(MakeRequest(&directory));
 
     mojom::LevelDBDatabaseAssociatedPtr database;
-    leveldb::mojom::OpenOptionsPtr options = leveldb::mojom::OpenOptions::New();
-    options->error_if_exists = true;
-    options->create_if_missing = true;
+    leveldb_env::Options options;
+    options.error_if_exists = true;
+    options.create_if_missing = true;
     base::RunLoop run_loop;
     leveldb()->OpenWithOptions(std::move(options), std::move(directory), "test",
                                base::nullopt, MakeRequest(&database),
diff --git a/components/leveldb/leveldb_struct_traits.cc b/components/leveldb/leveldb_struct_traits.cc
new file mode 100644
index 0000000..69c500b
--- /dev/null
+++ b/components/leveldb/leveldb_struct_traits.cc
@@ -0,0 +1,79 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/leveldb/leveldb_struct_traits.h"
+
+#include "third_party/leveldatabase/env_chromium.h"
+
+namespace mojo {
+
+bool StructTraits<leveldb::mojom::OpenOptionsDataView, leveldb_env::Options>::
+    create_if_missing(const leveldb_env::Options& options) {
+  return options.create_if_missing;
+}
+
+bool StructTraits<leveldb::mojom::OpenOptionsDataView, leveldb_env::Options>::
+    error_if_exists(const leveldb_env::Options& options) {
+  return options.error_if_exists;
+}
+
+bool StructTraits<leveldb::mojom::OpenOptionsDataView, leveldb_env::Options>::
+    paranoid_checks(const leveldb_env::Options& options) {
+  return options.paranoid_checks;
+}
+
+uint64_t
+StructTraits<leveldb::mojom::OpenOptionsDataView, leveldb_env::Options>::
+    write_buffer_size(const leveldb_env::Options& options) {
+  return options.write_buffer_size;
+}
+
+int32_t StructTraits<
+    leveldb::mojom::OpenOptionsDataView,
+    leveldb_env::Options>::max_open_files(const leveldb_env::Options& options) {
+  return options.max_open_files;
+}
+
+leveldb::mojom::SharedReadCache
+StructTraits<leveldb::mojom::OpenOptionsDataView, leveldb_env::Options>::
+    shared_block_read_cache(const leveldb_env::Options& options) {
+  // The Mojo wrapper for leveldb only supports using one of two different
+  // shared caches. Chrome's Mojo wrapper does not currently support custom
+  // caches, nor NULL to have leveldb create the block read cache.
+  if (!options.block_cache) {
+    // Specify either Default or Web.
+    NOTREACHED();
+    return leveldb::mojom::SharedReadCache::Default;
+  }
+  if (options.block_cache == leveldb_env::SharedWebBlockCache())
+    return leveldb::mojom::SharedReadCache::Web;
+
+  leveldb_env::Options default_options;
+  // If failing see comment above.
+  DCHECK_EQ(default_options.block_cache, options.block_cache);
+
+  return leveldb::mojom::SharedReadCache::Default;
+}
+
+bool StructTraits<leveldb::mojom::OpenOptionsDataView, leveldb_env::Options>::
+    Read(leveldb::mojom::OpenOptionsDataView data, leveldb_env::Options* out) {
+  out->create_if_missing = data.create_if_missing();
+  out->error_if_exists = data.error_if_exists();
+  out->paranoid_checks = data.paranoid_checks();
+  out->write_buffer_size = data.write_buffer_size();
+  out->max_open_files = data.max_open_files();
+  switch (data.shared_block_read_cache()) {
+    case leveldb::mojom::SharedReadCache::Default: {
+      leveldb_env::Options options;
+      out->block_cache = options.block_cache;
+    } break;
+    case leveldb::mojom::SharedReadCache::Web:
+      out->block_cache = leveldb_env::SharedWebBlockCache();
+      break;
+  }
+
+  return true;
+}
+
+}  // namespace mojo
diff --git a/components/leveldb/leveldb_struct_traits.h b/components/leveldb/leveldb_struct_traits.h
new file mode 100644
index 0000000..d167d31
--- /dev/null
+++ b/components/leveldb/leveldb_struct_traits.h
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_LEVELDB_PROTO_LEVELDB_STRUCT_TRAITS_H_
+#define COMPONENTS_LEVELDB_PROTO_LEVELDB_STRUCT_TRAITS_H_
+
+#include "components/leveldb/public/interfaces/leveldb.mojom.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<leveldb::mojom::OpenOptionsDataView, leveldb_env::Options> {
+  static bool create_if_missing(const leveldb_env::Options& options);
+  static bool error_if_exists(const leveldb_env::Options& options);
+  static bool paranoid_checks(const leveldb_env::Options& options);
+  static uint64_t write_buffer_size(const leveldb_env::Options& options);
+  static int32_t max_open_files(const leveldb_env::Options& options);
+  static ::leveldb::mojom::SharedReadCache shared_block_read_cache(
+      const leveldb_env::Options& options);
+  static bool Read(::leveldb::mojom::OpenOptionsDataView data,
+                   leveldb_env::Options* out);
+};
+
+}  // namespace mojo
+
+#endif  // COMPONENTS_LEVELDB_PROTO_LEVELDB_STRUCT_TRAITS_H_
diff --git a/components/leveldb/public/interfaces/leveldb.mojom b/components/leveldb/public/interfaces/leveldb.mojom
index e7f6ca4..061c81f 100644
--- a/components/leveldb/public/interfaces/leveldb.mojom
+++ b/components/leveldb/public/interfaces/leveldb.mojom
@@ -41,30 +41,31 @@
 
 // Options which control the behavior of a database. (This struct corresponds
 // with the struct in leveldb's options.h.)
+//
+// Note: This struct does not have default values. The values are set by a
+// struct trait which copies values to/from a leveldb_env::Options instance.
 struct OpenOptions {
   // TODO(erg): Find all comparators and copy them into the service.
 
   // If true, the database will be created if it is missing.
-  bool create_if_missing = false;
+  bool create_if_missing;
 
   // If true, an error is raised if the database already exists.
-  bool error_if_exists = false;
+  bool error_if_exists;
 
   // If true, the implementation will do aggressive checking of the
   // data it is processing and will stop early if it detects any
   // errors.
-  bool paranoid_checks = false;
+  bool paranoid_checks;
 
-  // Default size is 4 megabytes.
-  uint64 write_buffer_size = 4194304;
+  // Amount of data to build up in memory (backed by an unsorted log
+  // on disk) before converting to a sorted on-disk file.
+  uint64 write_buffer_size;
 
-  // Number of open files that can be used by the DB. (Note: we globally set
-  // the default here to 80 instead of leveldb's default 1000 because we don't
-  // want to consume all file descriptors. See
-  // https://code.google.com/p/chromium/issues/detail?id=227313#c11 for
-  // details.)
-  int32 max_open_files = 80;
+  // Number of open files that can be used by the DB.
+  int32 max_open_files;
 
+  // The shared read cache to use.
   SharedReadCache shared_block_read_cache = SharedReadCache.Default;
 };
 
diff --git a/components/metrics/proto/system_profile.proto b/components/metrics/proto/system_profile.proto
index 96203965..3ae0b0d 100644
--- a/components/metrics/proto/system_profile.proto
+++ b/components/metrics/proto/system_profile.proto
@@ -809,7 +809,9 @@
     // The organizationally unique identifier, for example "08:9E:08".
     // OUI is the highest three bytes of MAC address
     // Google's OUI (08:9E:08) is encoded as 0x00089E08
-    optional int32 vendor_prefix = 5;
+    // Never recorded server side, but some old clients may send values with
+    // this tag.
+    reserved 5;
   }
   optional ExternalAccessPoint external_access_point = 15;
 
diff --git a/components/multidevice/BUILD.gn b/components/multidevice/BUILD.gn
new file mode 100644
index 0000000..f41d7bf
--- /dev/null
+++ b/components/multidevice/BUILD.gn
@@ -0,0 +1,12 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+if (!is_ios && !is_android) {
+  source_set("unit_tests") {
+    testonly = true
+    deps = [
+      "//components/multidevice/service:multidevice_service_unittest",
+    ]
+  }
+}
diff --git a/components/multidevice/service/BUILD.gn b/components/multidevice/service/BUILD.gn
new file mode 100644
index 0000000..02bb27a
--- /dev/null
+++ b/components/multidevice/service/BUILD.gn
@@ -0,0 +1,76 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+if (!is_ios && !is_android) {
+  import("//mojo/public/tools/bindings/mojom.gni")
+  import("//services/service_manager/public/cpp/service.gni")
+  import("//services/service_manager/public/service_manifest.gni")
+  import("//services/service_manager/public/tools/test/service_test.gni")
+
+  static_library("service") {
+    sources = [
+      "device_sync_impl.cc",
+      "device_sync_impl.h",
+      "multidevice_service.cc",
+      "multidevice_service.h",
+    ]
+
+    deps = [
+      "//base",
+    ]
+
+    public_deps = [
+      "//components/cryptauth",
+      "//components/multidevice/service/public/interfaces",
+      "//services/identity/public/interfaces",
+      "//services/service_manager/public/cpp",
+    ]
+  }
+
+  service_manifest("multidevice_manifest") {
+    name = "multidevice"
+    source = "multidevice_service_manifest.json"
+  }
+
+  service("multidevice") {
+    sources = [
+      "main.cc",
+    ]
+
+    deps = [
+      ":service",
+      "//base",
+      "//components/cryptauth",
+    ]
+  }
+
+  service_test("multidevice_service_unittest") {
+    sources = [
+      "multidevice_service_unittest.cc",
+    ]
+
+    catalog = ":multidevice_service_unittest_catalog"
+
+    include_dirs = [ "testing/gmock/include" ]
+    deps = [
+      ":multidevice",
+      "//base",
+      "//components/multidevice/service/public/interfaces",
+      "//mojo/common",
+      "//services/service_manager/public/cpp:service_test_support",
+      "//testing/gmock",
+      "//testing/gtest",
+    ]
+  }
+
+  service_manifest("multidevice_service_unittest_manifest") {
+    name = "multidevice_service_unittest"
+    source = "multidevice_service_unittest_manifest.json"
+  }
+
+  catalog("multidevice_service_unittest_catalog") {
+    embedded_services = [ ":multidevice_service_unittest_manifest" ]
+    standalone_services = [ ":multidevice_manifest" ]
+  }
+}
diff --git a/components/multidevice/service/DEPS b/components/multidevice/service/DEPS
new file mode 100644
index 0000000..1ca2df556
--- /dev/null
+++ b/components/multidevice/service/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+  "+mojo/public/cpp",
+  "+services/service_manager/public/cpp",
+  "+components/cryptauth"
+]
+
+specific_include_rules = {
+  "main\.cc": [
+    "+services/service_manager/public",
+  ]
+}
\ No newline at end of file
diff --git a/components/multidevice/service/OWNERS b/components/multidevice/service/OWNERS
new file mode 100644
index 0000000..736eb15
--- /dev/null
+++ b/components/multidevice/service/OWNERS
@@ -0,0 +1,4 @@
+per-file multidevice_service_unittest_manifest.json=set noparent
+per-file multidevice_service_unittest_manifest.json=file://ipc/SECURITY_OWNERS
+per-file multidevice_service_manifest.json=set noparent
+per-file multidevice_service_manifest.json=file://ipc/SECURITY_OWNERS
\ No newline at end of file
diff --git a/components/multidevice/service/device_sync_impl.cc b/components/multidevice/service/device_sync_impl.cc
new file mode 100644
index 0000000..228f31005
--- /dev/null
+++ b/components/multidevice/service/device_sync_impl.cc
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/multidevice/service/device_sync_impl.h"
+
+namespace multidevice {
+
+DeviceSyncImpl::DeviceSyncImpl(
+    std::unique_ptr<service_manager::ServiceContextRef> service_ref)
+    : service_ref_(std::move(service_ref)) {}
+
+DeviceSyncImpl::~DeviceSyncImpl() {}
+
+void DeviceSyncImpl::ForceEnrollmentNow() {
+  observers_.ForAllPtrs([](device_sync::mojom::DeviceSyncObserver* observer) {
+    // TODO(hsuregan): Actually enroll observers, and pass the success/failure
+    // status to observer->OnEnrollmentFinished().
+    observer->OnEnrollmentFinished(true /* success */);
+  });
+}
+
+void DeviceSyncImpl::ForceSyncNow() {
+  observers_.ForAllPtrs([](device_sync::mojom::DeviceSyncObserver* observer) {
+    // TODO(hsuregan): Actually sync observers, and pass the success/failure
+    // status to observer->OnEnrollmentFinished().
+    observer->OnDevicesSynced(true /* success */);
+  });
+}
+
+void DeviceSyncImpl::AddObserver(
+    device_sync::mojom::DeviceSyncObserverPtr observer) {
+  observers_.AddPtr(std::move(observer));
+}
+
+}  // namespace multidevice
\ No newline at end of file
diff --git a/components/multidevice/service/device_sync_impl.h b/components/multidevice/service/device_sync_impl.h
new file mode 100644
index 0000000..134de283
--- /dev/null
+++ b/components/multidevice/service/device_sync_impl.h
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MULTIDEVICE_DEVICE_SYNC_IMPL_H_
+#define COMPONENTS_MULTIDEVICE_DEVICE_SYNC_IMPL_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "components/multidevice/service/public/interfaces/device_sync.mojom.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+
+namespace multidevice {
+
+// This class syncs metadata about other devices tied to a given Google account.
+// It contacts the back-end to enroll the current device
+// and sync down new data about other devices.
+class DeviceSyncImpl : public device_sync::mojom::DeviceSync {
+ public:
+  explicit DeviceSyncImpl(
+      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
+
+  ~DeviceSyncImpl() override;
+
+  // mojom::DeviceSync:
+  void ForceEnrollmentNow() override;
+  void ForceSyncNow() override;
+  void AddObserver(device_sync::mojom::DeviceSyncObserverPtr observer) override;
+
+ private:
+  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
+  mojo::InterfacePtrSet<device_sync::mojom::DeviceSyncObserver> observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceSyncImpl);
+};
+
+}  // namespace multidevice
+
+#endif  // COMPONENTS_MULTIDEVICE_DEVICE_SYNC_IMPL_H_
diff --git a/components/multidevice/service/main.cc b/components/multidevice/service/main.cc
new file mode 100644
index 0000000..c839160
--- /dev/null
+++ b/components/multidevice/service/main.cc
@@ -0,0 +1,12 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/service_manager/public/c/main.h"
+#include "components/multidevice/service/multidevice_service.h"
+#include "services/service_manager/public/cpp/service_runner.h"
+
+MojoResult ServiceMain(MojoHandle service_request_handle) {
+  service_manager::ServiceRunner runner(new multidevice::MultiDeviceService());
+  return runner.Run(service_request_handle);
+}
diff --git a/components/multidevice/service/multidevice_service.cc b/components/multidevice/service/multidevice_service.cc
new file mode 100644
index 0000000..f29f587
--- /dev/null
+++ b/components/multidevice/service/multidevice_service.cc
@@ -0,0 +1,40 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/multidevice/service/multidevice_service.h"
+#include "components/multidevice/service/device_sync_impl.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "services/service_manager/public/cpp/service_context.h"
+
+namespace multidevice {
+
+MultiDeviceService::MultiDeviceService() : weak_ptr_factory_(this) {}
+
+MultiDeviceService::~MultiDeviceService() {}
+
+void MultiDeviceService::OnStart() {
+  ref_factory_ = base::MakeUnique<service_manager::ServiceContextRefFactory>(
+      base::Bind(&service_manager::ServiceContext::RequestQuit,
+                 base::Unretained(context())));
+  registry_.AddInterface<device_sync::mojom::DeviceSync>(
+      base::Bind(&MultiDeviceService::CreateDeviceSyncImpl,
+                 base::Unretained(this), ref_factory_.get()));
+}
+
+void MultiDeviceService::OnBindInterface(
+    const service_manager::BindSourceInfo& source_info,
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle interface_pipe) {
+  registry_.BindInterface(interface_name, std::move(interface_pipe));
+}
+
+void MultiDeviceService::CreateDeviceSyncImpl(
+    service_manager::ServiceContextRefFactory* ref_factory,
+    device_sync::mojom::DeviceSyncRequest request) {
+  mojo::MakeStrongBinding(
+      base::MakeUnique<DeviceSyncImpl>(ref_factory->CreateRef()),
+      std::move(request));
+}
+
+}  // namespace multidevice
\ No newline at end of file
diff --git a/components/multidevice/service/multidevice_service.h b/components/multidevice/service/multidevice_service.h
new file mode 100644
index 0000000..e92d9cd7
--- /dev/null
+++ b/components/multidevice/service/multidevice_service.h
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MULTIDEVICE_SERVICE_H_
+#define COMPONENTS_MULTIDEVICE_SERVICE_H_
+
+#include "base/memory/weak_ptr.h"
+#include "components/multidevice/service/public/interfaces/device_sync.mojom.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "services/service_manager/public/cpp/service.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+
+namespace multidevice {
+
+// This class provides convenient APIs for creating connections
+// between multiple devices tied to a single Google Account, and sending as well
+// as recieving messages through these connections.
+class MultiDeviceService : public service_manager::Service {
+ public:
+  MultiDeviceService();
+  ~MultiDeviceService() override;
+
+  // service_manager::Service:
+  void OnStart() override;
+  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
+                       const std::string& interface_name,
+                       mojo::ScopedMessagePipeHandle interface_pipe) override;
+
+ private:
+  void CreateDeviceSyncImpl(
+      service_manager::ServiceContextRefFactory* ref_factory,
+      device_sync::mojom::DeviceSyncRequest request);
+
+  std::unique_ptr<service_manager::ServiceContextRefFactory> ref_factory_;
+  service_manager::BinderRegistry registry_;
+  base::WeakPtrFactory<MultiDeviceService> weak_ptr_factory_;
+};
+
+}  // namespace multidevice
+
+#endif  // COMPONENTS_MULTIDEVICE_SERVICE_H_
\ No newline at end of file
diff --git a/components/multidevice/service/multidevice_service_manifest.json b/components/multidevice/service/multidevice_service_manifest.json
new file mode 100644
index 0000000..859d5582
--- /dev/null
+++ b/components/multidevice/service/multidevice_service_manifest.json
@@ -0,0 +1,14 @@
+{
+  "name": "multidevice",
+  "display_name": "MultiDevice Service",
+  "interface_provider_specs": {
+    "service_manager:connector": {
+      "provides": {
+        "multidevice:device_sync": [ "device_sync::mojom::DeviceSync" ]
+       },
+       "requires": {
+         "service_manager": [ "service_manager:all_users" ]
+       }
+     }
+  }
+}
\ No newline at end of file
diff --git a/components/multidevice/service/multidevice_service_unittest.cc b/components/multidevice/service/multidevice_service_unittest.cc
new file mode 100644
index 0000000..cc80b09
--- /dev/null
+++ b/components/multidevice/service/multidevice_service_unittest.cc
@@ -0,0 +1,195 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// #include "multidevice_service.h"
+
+#include <memory>
+
+#include "base/barrier_closure.h"
+#include "components/multidevice/service/public/interfaces/constants.mojom.h"
+#include "components/multidevice/service/public/interfaces/device_sync.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/service_manager/public/cpp/service_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kServiceTestName[] = "multidevice_service_unittest";
+
+enum class MultiDeviceServiceActionType {
+  FORCE_ENROLLMENT_NOW,
+  FORCE_SYNC_NOW
+};
+
+}  // namespace
+
+namespace multidevice {
+
+class MultiDeviceServiceTest : public service_manager::test::ServiceTest {
+ public:
+  class DeviceSyncObserverImpl : public device_sync::mojom::DeviceSyncObserver {
+   public:
+    DeviceSyncObserverImpl(
+        device_sync::mojom::DeviceSyncObserverRequest request)
+        : binding_(this, std::move(request)) {}
+
+    void OnEnrollmentFinished(bool success) override {
+      if (success) {
+        num_times_enrollment_finished_called_.success_count++;
+      } else {
+        num_times_enrollment_finished_called_.failure_count++;
+      }
+      on_callback_invoked_->Run();
+    }
+
+    void OnDevicesSynced(bool success) override {
+      if (success) {
+        num_times_device_synced_.success_count++;
+      } else {
+        num_times_device_synced_.failure_count++;
+      }
+      on_callback_invoked_->Run();
+    }
+
+    // Sets the necessary callback that will be invoked upon each interface
+    // method call in order to return control to the test.
+    void SetOnCallbackInvokedClosure(base::Closure* on_callback_invoked) {
+      on_callback_invoked_ = on_callback_invoked;
+    }
+
+    int GetNumCalls(MultiDeviceServiceActionType type, bool success_count) {
+      switch (type) {
+        case MultiDeviceServiceActionType::FORCE_ENROLLMENT_NOW:
+          return num_times_enrollment_finished_called_.CountType(success_count);
+        case MultiDeviceServiceActionType::FORCE_SYNC_NOW:
+          return num_times_device_synced_.CountType(success_count);
+        default:
+          NOTREACHED();
+      }
+      return 0;
+    }
+
+   private:
+    struct ObserverCallbackCount {
+      int success_count = 0;
+      int failure_count = 0;
+      int CountType(bool success) {
+        return success ? success_count : failure_count;
+      }
+    };
+
+    mojo::Binding<device_sync::mojom::DeviceSyncObserver> binding_;
+    base::Closure* on_callback_invoked_ = nullptr;
+
+    ObserverCallbackCount num_times_enrollment_finished_called_;
+    ObserverCallbackCount num_times_device_synced_;
+  };
+
+  MultiDeviceServiceTest() : ServiceTest(kServiceTestName){};
+
+  ~MultiDeviceServiceTest() override{};
+
+  void SetUp() override {
+    ServiceTest::SetUp();
+    connector()->BindInterface(multidevice::mojom::kServiceName,
+                               &device_sync_ptr_);
+  }
+
+  void AddDeviceSyncObservers(int num) {
+    device_sync::mojom::DeviceSyncObserverPtr device_sync_observer_ptr;
+    for (int i = 0; i < num; i++) {
+      device_sync_observer_ptr = device_sync::mojom::DeviceSyncObserverPtr();
+      observers_.emplace_back(base::MakeUnique<DeviceSyncObserverImpl>(
+          mojo::MakeRequest(&device_sync_observer_ptr)));
+      device_sync_ptr_->AddObserver(std::move(device_sync_observer_ptr));
+    }
+  }
+
+  void MultDeviceServiceAction(MultiDeviceServiceActionType type) {
+    base::RunLoop run_loop;
+    base::Closure closure = base::BarrierClosure(
+        static_cast<int>(observers_.size()), run_loop.QuitClosure());
+    for (auto& observer : observers_) {
+      observer->SetOnCallbackInvokedClosure(&closure);
+    }
+
+    switch (type) {
+      case MultiDeviceServiceActionType::FORCE_ENROLLMENT_NOW:
+        device_sync_ptr_->ForceEnrollmentNow();
+        break;
+      case MultiDeviceServiceActionType::FORCE_SYNC_NOW:
+        device_sync_ptr_->ForceSyncNow();
+        break;
+      default:
+        NOTREACHED();
+    }
+
+    run_loop.Run();
+  }
+
+  device_sync::mojom::DeviceSyncPtr device_sync_ptr_;
+  std::vector<std::unique_ptr<DeviceSyncObserverImpl>> observers_;
+};
+
+TEST_F(MultiDeviceServiceTest, MultipleCallTest) {
+  AddDeviceSyncObservers(2);
+
+  MultDeviceServiceAction(MultiDeviceServiceActionType::FORCE_ENROLLMENT_NOW);
+  EXPECT_EQ(1, observers_[0]->GetNumCalls(
+                   MultiDeviceServiceActionType::FORCE_ENROLLMENT_NOW,
+                   true /* success_count */));
+  EXPECT_EQ(1, observers_[1]->GetNumCalls(
+                   MultiDeviceServiceActionType::FORCE_ENROLLMENT_NOW,
+                   true /* success_count */));
+  EXPECT_EQ(0, observers_[0]->GetNumCalls(
+                   MultiDeviceServiceActionType::FORCE_ENROLLMENT_NOW,
+                   false /* success_count */));
+  EXPECT_EQ(0, observers_[1]->GetNumCalls(
+                   MultiDeviceServiceActionType::FORCE_ENROLLMENT_NOW,
+                   false /* success_count */));
+
+  MultDeviceServiceAction(MultiDeviceServiceActionType::FORCE_SYNC_NOW);
+  EXPECT_EQ(1, observers_[0]->GetNumCalls(
+                   MultiDeviceServiceActionType::FORCE_SYNC_NOW,
+                   true /* success_count */));
+  EXPECT_EQ(1, observers_[1]->GetNumCalls(
+                   MultiDeviceServiceActionType::FORCE_SYNC_NOW,
+                   true /* success_count */));
+  EXPECT_EQ(0, observers_[0]->GetNumCalls(
+                   MultiDeviceServiceActionType::FORCE_SYNC_NOW,
+                   false /* success_count */));
+  EXPECT_EQ(0, observers_[1]->GetNumCalls(
+                   MultiDeviceServiceActionType::FORCE_SYNC_NOW,
+                   false /* success_count */));
+
+  MultDeviceServiceAction(MultiDeviceServiceActionType::FORCE_ENROLLMENT_NOW);
+  EXPECT_EQ(2, observers_[0]->GetNumCalls(
+                   MultiDeviceServiceActionType::FORCE_ENROLLMENT_NOW,
+                   true /* success_count */));
+  EXPECT_EQ(2, observers_[1]->GetNumCalls(
+                   MultiDeviceServiceActionType::FORCE_ENROLLMENT_NOW,
+                   true /* success_count */));
+  EXPECT_EQ(0, observers_[0]->GetNumCalls(
+                   MultiDeviceServiceActionType::FORCE_ENROLLMENT_NOW,
+                   false /* success_count */));
+  EXPECT_EQ(0, observers_[1]->GetNumCalls(
+                   MultiDeviceServiceActionType::FORCE_ENROLLMENT_NOW,
+                   false /* success_count */));
+
+  MultDeviceServiceAction(MultiDeviceServiceActionType::FORCE_SYNC_NOW);
+  EXPECT_EQ(2, observers_[0]->GetNumCalls(
+                   MultiDeviceServiceActionType::FORCE_SYNC_NOW,
+                   true /* success_count */));
+  EXPECT_EQ(2, observers_[1]->GetNumCalls(
+                   MultiDeviceServiceActionType::FORCE_SYNC_NOW,
+                   true /* success_count */));
+  EXPECT_EQ(0, observers_[0]->GetNumCalls(
+                   MultiDeviceServiceActionType::FORCE_SYNC_NOW,
+                   false /* success_count */));
+  EXPECT_EQ(0, observers_[1]->GetNumCalls(
+                   MultiDeviceServiceActionType::FORCE_SYNC_NOW,
+                   false /* success_count */));
+}
+
+}  // namespace multidevice
\ No newline at end of file
diff --git a/components/multidevice/service/multidevice_service_unittest_manifest.json b/components/multidevice/service/multidevice_service_unittest_manifest.json
new file mode 100644
index 0000000..e4aae4d
--- /dev/null
+++ b/components/multidevice/service/multidevice_service_unittest_manifest.json
@@ -0,0 +1,17 @@
+{
+  "name": "multidevice_service_unittest",
+  "display_name": "MultiDevice Service Unittest",
+  "interface_provider_specs": {
+    "service_manager:connector": {
+      "provides": {
+        "service_manager:service_factory": [
+          "service_manager::mojom::ServiceFactory"
+        ]
+      },
+      "requires": {
+        "multidevice": [ "multidevice:device_sync" ],
+        "service_manager": [ "service_manager:service_manager" ]
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/components/multidevice/service/public/interfaces/BUILD.gn b/components/multidevice/service/public/interfaces/BUILD.gn
index ed79d32..e488361a 100644
--- a/components/multidevice/service/public/interfaces/BUILD.gn
+++ b/components/multidevice/service/public/interfaces/BUILD.gn
@@ -2,10 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//mojo/public/tools/bindings/mojom.gni")
+if (!is_ios && !is_android) {
+  import("//mojo/public/tools/bindings/mojom.gni")
 
-mojom("interfaces") {
-  sources = [
-    "device_sync.mojom",
-  ]
+  mojom("interfaces") {
+    sources = [
+      "constants.mojom",
+      "device_sync.mojom",
+    ]
+  }
 }
diff --git a/components/multidevice/service/public/interfaces/constants.mojom b/components/multidevice/service/public/interfaces/constants.mojom
new file mode 100644
index 0000000..2bbd087
--- /dev/null
+++ b/components/multidevice/service/public/interfaces/constants.mojom
@@ -0,0 +1,7 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module multidevice.mojom;
+
+const string kServiceName = "multidevice";
\ No newline at end of file
diff --git a/components/multidevice/service/public/interfaces/device_sync.mojom b/components/multidevice/service/public/interfaces/device_sync.mojom
index 18695a9..593c17a 100644
--- a/components/multidevice/service/public/interfaces/device_sync.mojom
+++ b/components/multidevice/service/public/interfaces/device_sync.mojom
@@ -93,9 +93,27 @@
 };
 
 interface DeviceSyncObserver {
-  // TODO(khorimoto): Flesh out Observer.
+  // Invoked when the current device has finished enrolling itself (i.e., when
+  // it has contacted the server to provide device metadata). Devices must
+  // enroll themselves before other device can establish connections to them,
+  // and they continue to re-enroll themselves on a periodic basis after that.
+  OnEnrollmentFinished(bool success);
+
+  // Invoked when new devices have been synced from the server.
+  OnDevicesSynced(bool success);
 };
 
 interface DeviceSync {
+  // Triggers an enrollment. Result of the enrollment will be relayed via the
+  // OnEnrollmentFinished() observer function.
+  ForceEnrollmentNow();
+
+  // Triggers a device sync. Result of the sync will be relayed via the
+  // OnDevicesSynced() observer function.
+  ForceSyncNow();
+
+  // Adds an Observer of this API.
+  AddObserver(DeviceSyncObserver observer);
+
   // TODO(khorimoto): Flesh out API.
-};
+};
\ No newline at end of file
diff --git a/components/offline_pages/core/offline_page_feature.cc b/components/offline_pages/core/offline_page_feature.cc
index 457f1be..230932f5 100644
--- a/components/offline_pages/core/offline_page_feature.cc
+++ b/components/offline_pages/core/offline_page_feature.cc
@@ -57,6 +57,9 @@
 const base::Feature kOfflinePagesCTV2Feature{"OfflinePagesCTV2",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kOfflinePagesPrefetchingUIFeature{
+    "OfflinePagesPrefetchingUI", base::FEATURE_DISABLED_BY_DEFAULT};
+
 bool IsOfflineBookmarksEnabled() {
   return base::FeatureList::IsEnabled(kOfflineBookmarksFeature);
 }
@@ -90,6 +93,10 @@
   return base::FeatureList::IsEnabled(kPrefetchingOfflinePagesFeature);
 }
 
+bool IsOfflinePagesPrefetchingUIEnabled() {
+  return base::FeatureList::IsEnabled(kOfflinePagesPrefetchingUIFeature);
+}
+
 bool IsOfflinePagesLoadSignalCollectingEnabled() {
   return base::FeatureList::IsEnabled(kOfflinePagesLoadSignalCollectingFeature);
 }
diff --git a/components/offline_pages/core/offline_page_feature.h b/components/offline_pages/core/offline_page_feature.h
index 383f6d1..3b5fb94c 100644
--- a/components/offline_pages/core/offline_page_feature.h
+++ b/components/offline_pages/core/offline_page_feature.h
@@ -22,6 +22,7 @@
 extern const base::Feature kOfflinePagesLoadSignalCollectingFeature;
 extern const base::Feature kOfflinePagesCTV2Feature;
 extern const base::Feature kOfflinePagesRenovationsFeature;
+extern const base::Feature kOfflinePagesPrefetchingUIFeature;
 
 // Returns true if saving bookmarked pages for offline viewing is enabled.
 bool IsOfflineBookmarksEnabled();
@@ -48,6 +49,9 @@
 // Returns true if prefetching offline pages is enabled.
 bool IsPrefetchingOfflinePagesEnabled();
 
+// Returns true if we should show UI for prefetched pages.
+bool IsOfflinePagesPrefetchingUIEnabled();
+
 // Returns true if we enable load timing signals to be collected.
 bool IsOfflinePagesLoadSignalCollectingEnabled();
 
diff --git a/components/omnibox/browser/omnibox_popup_model.cc b/components/omnibox/browser/omnibox_popup_model.cc
index 0af1fd6..c727578 100644
--- a/components/omnibox/browser/omnibox_popup_model.cc
+++ b/components/omnibox/browser/omnibox_popup_model.cc
@@ -9,7 +9,6 @@
 #include "base/feature_list.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "build/build_config.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/omnibox_client.h"
@@ -21,7 +20,21 @@
 #include "third_party/icu/source/common/unicode/ubidi.h"
 #include "ui/gfx/geometry/rect.h"
 
-using bookmarks::BookmarkModel;
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
+#include "components/omnibox/browser/vector_icons.h"  // nogncheck
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/gfx/vector_icon_types.h"
+#endif
+
+namespace {
+
+size_t GetFaviconCacheSize() {
+  // Set cache size to twice the number of maximum results to avoid favicon
+  // refetches as the user types. Favicon fetches are uncached and can hit disk.
+  return 2 * AutocompleteResult::GetMaxMatches();
+}
+
+}  // namespace
 
 ///////////////////////////////////////////////////////////////////////////////
 // OmniboxPopupModel
@@ -30,7 +43,8 @@
 
 OmniboxPopupModel::OmniboxPopupModel(OmniboxPopupView* popup_view,
                                      OmniboxEditModel* edit_model)
-    : view_(popup_view),
+    : favicons_cache_(GetFaviconCacheSize()),
+      view_(popup_view),
       edit_model_(edit_model),
       selected_line_(kNoMatch),
       selected_line_state_(NORMAL),
@@ -231,13 +245,8 @@
   }
 }
 
-gfx::Image OmniboxPopupModel::GetIconIfExtensionMatch(
-    const AutocompleteMatch& match) const {
-  return edit_model_->client()->GetIconIfExtensionMatch(match);
-}
-
 bool OmniboxPopupModel::IsStarredMatch(const AutocompleteMatch& match) const {
-  BookmarkModel* bookmark_model = edit_model_->client()->GetBookmarkModel();
+  auto* bookmark_model = edit_model_->client()->GetBookmarkModel();
   return bookmark_model && bookmark_model->IsBookmarked(match.destination_url);
 }
 
@@ -251,41 +260,6 @@
   manually_selected_match_.Clear();
   selected_line_state_ = NORMAL;
 
-#if !defined(OS_ANDROID) && !defined(OS_IOS)
-  // Update all match icons.
-  if (base::FeatureList::IsEnabled(
-          omnibox::kUIExperimentShowSuggestionFavicons)) {
-    favicon_task_tracker_.TryCancelAll();
-
-    if (result.size() != displayed_page_favicons_.size())
-      displayed_page_favicons_.resize(result.size());
-
-    for (size_t i = 0; i < result.size(); i++) {
-      const AutocompleteMatch& match = result.match_at(i);
-      if (AutocompleteMatch::IsSearchType(match.type)) {
-        // Clear any existing icon.
-        if (!displayed_page_favicons_[i].is_empty())
-          OnPageFaviconFetched(i, GURL(), gfx::Image());
-        continue;
-      }
-
-      // If we already show the correct favicon, skip refetching to avoid
-      // hitting the favicon database and to avoid flicker.
-      //
-      // TODO(tommycli): Investigate whether the fetching can be done in the
-      // autocomplete controller, which already has knowledge of whether and
-      // when the matches are changing.
-      if (match.destination_url == displayed_page_favicons_[i])
-        continue;
-
-      edit_model_->client()->GetFaviconForPageUrl(
-          &favicon_task_tracker_, match.destination_url,
-          base::Bind(&OmniboxPopupModel::OnPageFaviconFetched,
-                     weak_factory_.GetWeakPtr(), i, match.destination_url));
-    }
-  }
-#endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
-
   bool popup_was_open = view_->IsOpen();
   view_->UpdatePopupAppearance();
   // If popup has just been shown or hidden, notify observers.
@@ -308,12 +282,59 @@
   view_->UpdatePopupAppearance();
 }
 
-void OmniboxPopupModel::OnPageFaviconFetched(size_t match_index,
-                                             const GURL& page_url,
-                                             const gfx::Image& icon) {
-  DCHECK_LT(match_index, displayed_page_favicons_.size());
-  DCHECK_NE(displayed_page_favicons_[match_index], page_url);
+// Android and iOS have their own platform-specific icon logic.
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
+gfx::Image OmniboxPopupModel::GetMatchIcon(const AutocompleteMatch& match,
+                                           SkColor vector_icon_color) {
+  gfx::Image extension_icon =
+      edit_model_->client()->GetIconIfExtensionMatch(match);
+  if (!extension_icon.IsEmpty())
+    return extension_icon;
 
-  displayed_page_favicons_[match_index] = page_url;
-  view_->SetMatchIcon(match_index, icon);
+  if (base::FeatureList::IsEnabled(
+          omnibox::kUIExperimentShowSuggestionFavicons) &&
+      !AutocompleteMatch::IsSearchType(match.type)) {
+    const GURL& page_url = match.destination_url;
+    auto cache_iterator = favicons_cache_.Get(page_url);
+    if (cache_iterator != favicons_cache_.end() &&
+        !cache_iterator->second.IsEmpty()) {
+      return cache_iterator->second;
+    }
+
+    // We don't have the favicon in the cache. We kick off the request, but
+    // don't early return. We proceed to return the vector icon for the match
+    // type. If and when we ever get the favicon back, we send a notification.
+    //
+    // Note: We're relying on GetFaviconForPageUrl to call the callback
+    // asynchronously. If the callback is called synchronously, the fetched
+    // favicon may get clobbered by the vector icon once this method returns.
+    edit_model_->client()->GetFaviconForPageUrl(
+        &favicon_task_tracker_, match.destination_url,
+        base::Bind(&OmniboxPopupModel::OnFaviconFetched,
+                   weak_factory_.GetWeakPtr(), match.destination_url));
+  }
+
+  const auto& vector_icon_type =
+      IsStarredMatch(match) ? omnibox::kStarIcon
+                            : AutocompleteMatch::TypeToVectorIcon(match.type);
+  return gfx::Image(
+      gfx::CreateVectorIcon(vector_icon_type, 16, vector_icon_color));
+}
+#endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
+
+void OmniboxPopupModel::OnFaviconFetched(const GURL& page_url,
+                                         const gfx::Image& icon) {
+  if (icon.IsEmpty())
+    return;
+
+  favicons_cache_.Put(page_url, icon);
+
+  // Notify all affected matches.
+  for (size_t i = 0; i < result().size(); ++i) {
+    auto& match = result().match_at(i);
+    if (!AutocompleteMatch::IsSearchType(match.type) &&
+        match.destination_url == page_url) {
+      view_->OnMatchIconUpdated(i);
+    }
+  }
 }
\ No newline at end of file
diff --git a/components/omnibox/browser/omnibox_popup_model.h b/components/omnibox/browser/omnibox_popup_model.h
index 20108db..981ee8f89 100644
--- a/components/omnibox/browser/omnibox_popup_model.h
+++ b/components/omnibox/browser/omnibox_popup_model.h
@@ -8,10 +8,12 @@
 #include <stddef.h>
 #include <vector>
 
+#include "base/containers/mru_cache.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/task/cancelable_task_tracker.h"
+#include "build/build_config.h"
 #include "components/omnibox/browser/autocomplete_controller.h"
 #include "components/omnibox/browser/autocomplete_result.h"
 #include "components/omnibox/browser/omnibox_edit_model.h"
@@ -114,10 +116,6 @@
   // can be removed from history, and if so, remove it and update the popup.
   void TryDeletingCurrentItem();
 
-  // If |match| is from an extension, returns the extension icon; otherwise
-  // returns an empty Image.
-  gfx::Image GetIconIfExtensionMatch(const AutocompleteMatch& match) const;
-
   // Returns true if the destination URL of the match is bookmarked.
   bool IsStarredMatch(const AutocompleteMatch& match) const;
 
@@ -138,20 +136,24 @@
   void SetAnswerBitmap(const SkBitmap& bitmap);
   const SkBitmap& answer_bitmap() const { return answer_bitmap_; }
 
+#if !defined(OS_ANDROID) && !defined(OS_IOS)
+  // Gets the icon for the match index.
+  gfx::Image GetMatchIcon(const AutocompleteMatch& match,
+                          SkColor vector_icon_color);
+#endif
+
   // The token value for selected_line_ and functions dealing with a "line
   // number" that indicates "no line".
   static const size_t kNoMatch;
 
  private:
-  void OnPageFaviconFetched(size_t match_index,
-                            const GURL& page_url,
-                            const gfx::Image& icon);
+  void OnFaviconFetched(const GURL& page_url, const gfx::Image& icon);
 
   SkBitmap answer_bitmap_;
 
-  // The GURLs track which pages' favicon is displayed for each match view.
-  // An empty GURL means no favicon is displayed for that match view.
-  std::vector<GURL> displayed_page_favicons_;
+  // We cache a very small number of favicons so we can synchronously deliver
+  // them to prevent flicker as the user types.
+  base::MRUCache<GURL, gfx::Image> favicons_cache_;
   base::CancelableTaskTracker favicon_task_tracker_;
 
   OmniboxPopupView* view_;
diff --git a/components/omnibox/browser/omnibox_popup_view.h b/components/omnibox/browser/omnibox_popup_view.h
index 89e4ad7..781d302 100644
--- a/components/omnibox/browser/omnibox_popup_view.h
+++ b/components/omnibox/browser/omnibox_popup_view.h
@@ -16,7 +16,6 @@
 #include "build/build_config.h"
 
 namespace gfx {
-class Image;
 class Rect;
 }
 
@@ -37,10 +36,8 @@
   // mean opening or closing the window.
   virtual void UpdatePopupAppearance() = 0;
 
-  // Updates the icon used for the given match. The passed |icon| is not
-  // retained by the caller, and implementing classes are expected to make
-  // a copy if they wish to use |icon|.
-  virtual void SetMatchIcon(size_t match_index, const gfx::Image& icon) = 0;
+  // Notification that the icon used for the given match has been updated.
+  virtual void OnMatchIconUpdated(size_t match_index) = 0;
 
   // Returns the target bounds for the popup. This returns the popup's current
   // bounds when not animating, or the desired target bounds when animating.
diff --git a/components/prefs/pref_change_registrar.h b/components/prefs/pref_change_registrar.h
index 2ae3b195..a83c9ca 100644
--- a/components/prefs/pref_change_registrar.h
+++ b/components/prefs/pref_change_registrar.h
@@ -23,10 +23,10 @@
  public:
   // You can register this type of callback if you need to know the
   // path of the preference that is changing.
-  typedef base::Callback<void(const std::string&)> NamedChangeCallback;
+  using NamedChangeCallback = base::RepeatingCallback<void(const std::string&)>;
 
   PrefChangeRegistrar();
-  virtual ~PrefChangeRegistrar();
+  ~PrefChangeRegistrar();
 
   // Must be called before adding or removing observers. Can be called more
   // than once as long as the value of |service| doesn't change.
@@ -70,7 +70,7 @@
   static void InvokeUnnamedCallback(const base::Closure& callback,
                                     const std::string& pref_name);
 
-  typedef std::map<std::string, NamedChangeCallback> ObserverMap;
+  using ObserverMap = std::map<std::string, NamedChangeCallback>;
 
   ObserverMap observers_;
   PrefService* service_;
diff --git a/components/prefs/pref_notifier_impl.cc b/components/prefs/pref_notifier_impl.cc
index a28fbaf..0e4d0cb 100644
--- a/components/prefs/pref_notifier_impl.cc
+++ b/components/prefs/pref_notifier_impl.cc
@@ -61,6 +61,16 @@
   observer_list->RemoveObserver(obs);
 }
 
+void PrefNotifierImpl::AddPrefObserverAllPrefs(PrefObserver* observer) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  all_prefs_pref_observers_.AddObserver(observer);
+}
+
+void PrefNotifierImpl::RemovePrefObserverAllPrefs(PrefObserver* observer) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  all_prefs_pref_observers_.RemoveObserver(observer);
+}
+
 void PrefNotifierImpl::AddInitObserver(base::Callback<void(bool)> obs) {
   init_observers_.push_back(obs);
 }
@@ -89,6 +99,10 @@
   if (!pref_service_->FindPreference(path))
     return;
 
+  // Fire observers for any preference change.
+  for (auto& observer : all_prefs_pref_observers_)
+    observer.OnPreferenceChanged(pref_service_, path);
+
   auto observer_iterator = pref_observers_.find(path);
   if (observer_iterator == pref_observers_.end())
     return;
diff --git a/components/prefs/pref_notifier_impl.h b/components/prefs/pref_notifier_impl.h
index 5a8a137..d80d310 100644
--- a/components/prefs/pref_notifier_impl.h
+++ b/components/prefs/pref_notifier_impl.h
@@ -33,6 +33,13 @@
   void AddPrefObserver(const std::string& path, PrefObserver* observer);
   void RemovePrefObserver(const std::string& path, PrefObserver* observer);
 
+  // These observers are called for any pref changes.
+  //
+  // AVOID ADDING THESE. See the long comment in the identically-named
+  // functions on PrefService for background.
+  void AddPrefObserverAllPrefs(PrefObserver* observer);
+  void RemovePrefObserverAllPrefs(PrefObserver* observer);
+
   // We run the callback once, when initialization completes. The bool
   // parameter will be set to true for successful initialization,
   // false for unsuccessful.
@@ -67,6 +74,9 @@
   PrefObserverMap pref_observers_;
   PrefInitObserverList init_observers_;
 
+  // Observers for changes to any preference.
+  PrefObserverList all_prefs_pref_observers_;
+
   base::ThreadChecker thread_checker_;
 
   DISALLOW_COPY_AND_ASSIGN(PrefNotifierImpl);
diff --git a/components/prefs/pref_service.cc b/components/prefs/pref_service.cc
index fd1dea3b..7d69b64 100644
--- a/components/prefs/pref_service.cc
+++ b/components/prefs/pref_service.cc
@@ -390,6 +390,14 @@
   user_pref_store_->OnStoreDeletionFromDisk();
 }
 
+void PrefService::AddPrefObserverAllPrefs(PrefObserver* obs) {
+  pref_notifier_->AddPrefObserverAllPrefs(obs);
+}
+
+void PrefService::RemovePrefObserverAllPrefs(PrefObserver* obs) {
+  pref_notifier_->RemovePrefObserverAllPrefs(obs);
+}
+
 void PrefService::Set(const std::string& path, const base::Value& value) {
   SetUserPrefValue(path, value.CreateDeepCopy());
 }
diff --git a/components/prefs/pref_service.h b/components/prefs/pref_service.h
index 5d3b101b..cab5de01 100644
--- a/components/prefs/pref_service.h
+++ b/components/prefs/pref_service.h
@@ -320,6 +320,21 @@
   // to tangentially cleanup data it may have saved outside the store.
   void OnStoreDeletionFromDisk();
 
+  // A low level function for registering an observer for every single
+  // preference changed notification. The caller must ensure that the observer
+  // remains valid as long as it is registered. Pointer ownership is not
+  // transferred.
+  //
+  // Almost all calling code should use a PrefChangeRegistrar instead.
+  //
+  // AVOID ADDING THESE. These are low-level observer notifications that are
+  // called for every pref change. This can lead to inefficiency, and the lack
+  // of a "registrar" model makes it easy to forget to undregister. It is
+  // really designed for integrating other notification systems, not for normal
+  // observation.
+  void AddPrefObserverAllPrefs(PrefObserver* obs);
+  void RemovePrefObserverAllPrefs(PrefObserver* obs);
+
  protected:
   // The PrefNotifier handles registering and notifying preference observers.
   // It is created and owned by this PrefService. Subclasses may access it for
diff --git a/components/proximity_auth/unlock_manager_impl.cc b/components/proximity_auth/unlock_manager_impl.cc
index 64bb272..3be7379 100644
--- a/components/proximity_auth/unlock_manager_impl.cc
+++ b/components/proximity_auth/unlock_manager_impl.cc
@@ -212,7 +212,8 @@
     AcceptAuthAttempt(false);
   } else {
     sign_in_secret_.reset(new std::string(decrypted_bytes));
-    GetMessenger()->DispatchUnlockEvent();
+    if (GetMessenger())
+      GetMessenger()->DispatchUnlockEvent();
   }
 }
 
@@ -225,14 +226,15 @@
 
   PA_LOG(INFO) << "Unlock response from remote device: "
                << (success ? "success" : "failure");
-  if (success)
+  if (success && GetMessenger())
     GetMessenger()->DispatchUnlockEvent();
   else
     AcceptAuthAttempt(false);
 }
 
 void UnlockManagerImpl::OnDisconnected() {
-  GetMessenger()->RemoveObserver(this);
+  if (GetMessenger())
+    GetMessenger()->RemoveObserver(this);
 }
 
 void UnlockManagerImpl::OnProximityStateChanged() {
@@ -297,7 +299,7 @@
 
   is_attempting_auth_ = true;
 
-  if (!life_cycle_) {
+  if (!life_cycle_ || !GetMessenger()) {
     PA_LOG(ERROR) << "No life_cycle active when auth is attempted";
     AcceptAuthAttempt(false);
     UpdateLockScreen();
@@ -351,7 +353,8 @@
 
 void UnlockManagerImpl::OnGotSignInChallenge(const std::string& challenge) {
   PA_LOG(INFO) << "Got sign-in challenge, sending for decryption...";
-  GetMessenger()->RequestDecryption(challenge);
+  if (GetMessenger())
+    GetMessenger()->RequestDecryption(challenge);
 }
 
 ScreenlockState UnlockManagerImpl::GetScreenlockState() {
diff --git a/components/search_engines/template_url_service.cc b/components/search_engines/template_url_service.cc
index e64736a..14f718a 100644
--- a/components/search_engines/template_url_service.cc
+++ b/components/search_engines/template_url_service.cc
@@ -17,7 +17,6 @@
 #include "base/i18n/case_conversion.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -737,23 +736,11 @@
 void TemplateURLService::OnWebDataServiceRequestDone(
     KeywordWebDataService::Handle h,
     std::unique_ptr<WDTypedResult> result) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 TemplateURLService::OnWebDataServiceRequestDone"));
-
   // Reset the load_handle so that we don't try and cancel the load in
   // the destructor.
   load_handle_ = 0;
 
   if (!result) {
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile1(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "422460 TemplateURLService::OnWebDataServiceRequestDone 1"));
-
     // Results are null if the database went away or (most likely) wasn't
     // loaded.
     load_failed_ = true;
@@ -766,12 +753,6 @@
       base::MakeUnique<OwnedTemplateURLVector>();
   int new_resource_keyword_version = 0;
   {
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile2(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "422460 TemplateURLService::OnWebDataServiceRequestDone 2"));
-
     GetSearchProvidersUsingKeywordResult(
         *result, web_data_service_.get(), prefs_, template_urls.get(),
         (default_search_provider_source_ == DefaultSearchManager::FROM_USER)
@@ -783,61 +764,24 @@
   KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get());
 
   {
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile4(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "422460 TemplateURLService::OnWebDataServiceRequestDone 4"));
-
     PatchMissingSyncGUIDs(template_urls.get());
-
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile41(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "422460 TemplateURLService::OnWebDataServiceRequestDone 41"));
-
     SetTemplateURLs(std::move(template_urls));
 
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile42(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "422460 TemplateURLService::OnWebDataServiceRequestDone 42"));
-
     // This initializes provider_map_ which should be done before
     // calling UpdateKeywordSearchTermsForURL.
     // This also calls NotifyObservers.
     ChangeToLoadedState();
 
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile43(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "422460 TemplateURLService::OnWebDataServiceRequestDone 43"));
-
     // Index any visits that occurred before we finished loading.
     for (size_t i = 0; i < visits_to_add_.size(); ++i)
       UpdateKeywordSearchTermsForURL(visits_to_add_[i]);
     visits_to_add_.clear();
 
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile44(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "422460 TemplateURLService::OnWebDataServiceRequestDone 44"));
-
     if (new_resource_keyword_version)
       web_data_service_->SetBuiltinKeywordVersion(new_resource_keyword_version);
   }
 
   if (default_search_provider_) {
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile5(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "422460 TemplateURLService::OnWebDataServiceRequestDone 5"));
-
     UMA_HISTOGRAM_ENUMERATION(
         "Search.DefaultSearchProviderType",
         default_search_provider_->GetEngineType(search_terms_data()),
@@ -1586,23 +1530,11 @@
 }
 
 void TemplateURLService::ChangeToLoadedState() {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 TemplateURLService::ChangeToLoadedState 1"));
-
   DCHECK(!loaded_);
 
   provider_map_->Init(template_urls_, search_terms_data());
   loaded_ = true;
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 TemplateURLService::ChangeToLoadedState 2"));
-
   // This will cause a call to NotifyObservers().
   ApplyDefaultSearchChangeNoMetrics(
       initial_default_search_provider_
@@ -1611,12 +1543,6 @@
       default_search_provider_source_);
   initial_default_search_provider_.reset();
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile3(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 TemplateURLService::ChangeToLoadedState 3"));
-
   on_loaded_callbacks_.Notify();
 }
 
diff --git a/components/signin/core/browser/about_signin_internals.cc b/components/signin/core/browser/about_signin_internals.cc
index aa6578f..39b8021b 100644
--- a/components/signin/core/browser/about_signin_internals.cc
+++ b/components/signin/core/browser/about_signin_internals.cc
@@ -16,7 +16,6 @@
 #include "base/i18n/time_formatting.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
@@ -288,31 +287,13 @@
   if (!signin_observers_.might_have_observers())
     return;
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AboutSigninInternals::NotifyObservers"));
-
   const std::string product_version = client_->GetProductVersion();
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile05(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AboutSigninInternals::NotifyObservers 0.5"));
-
   std::unique_ptr<base::DictionaryValue> signin_status_value =
       signin_status_.ToValue(account_tracker_, signin_manager_,
                              signin_error_controller_, token_service_,
                              cookie_manager_service_, product_version);
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AboutSigninInternals::NotifyObservers1"));
-
   for (auto& observer : signin_observers_)
     observer.OnSigninStateChanged(signin_status_value.get());
 }
@@ -327,12 +308,6 @@
     const std::string& account_id,
     const std::string& consumer_id,
     const OAuth2TokenService::ScopeSet& scopes) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AboutSigninInternals::OnAccessTokenRequested"));
-
   TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
   if (token) {
     *token = TokenInfo(consumer_id, scopes);
@@ -523,12 +498,6 @@
     ProfileOAuth2TokenService* token_service,
     GaiaCookieManagerService* cookie_manager_service_,
     const std::string& product_version) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AboutSigninInternals::SigninStatus::ToValue1"));
-
   auto signin_status = base::MakeUnique<base::DictionaryValue>();
   auto signin_info = base::MakeUnique<base::ListValue>();
 
@@ -546,12 +515,6 @@
   AddSectionEntry(basic_info, "TokenService Status",
                   TokenServiceLoadCredentialsStateToLabel(load_tokens_state));
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AboutSigninInternals::SigninStatus::ToValue2"));
-
   if (signin_manager->IsAuthenticated()) {
     std::string account_id = signin_manager->GetAuthenticatedAccountId();
     AddSectionEntry(basic_info,
@@ -581,12 +544,6 @@
   }
 
 #if !defined(OS_CHROMEOS)
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile3(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AboutSigninInternals::SigninStatus::ToValue3"));
-
   // Time and status information of the possible sign in types.
   base::ListValue* detailed_info =
       AddSection(signin_info.get(), "Last Signin Details");
@@ -638,41 +595,13 @@
 
 #endif  // !defined(OS_CHROMEOS)
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile4(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AboutSigninInternals::SigninStatus::ToValue4"));
-
   // Token information for all services.
   auto token_info = base::MakeUnique<base::ListValue>();
   for (auto it = token_info_map.begin(); it != token_info_map.end(); ++it) {
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile41(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "422460 AboutSigninInternals::SigninStatus::ToValue41"));
-
     base::ListValue* token_details = AddSection(token_info.get(), it->first);
-
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile42(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "422460 AboutSigninInternals::SigninStatus::ToValue42"));
-
     std::sort(it->second.begin(), it->second.end(), TokenInfo::LessThan);
-    const auto& tokens = it->second;
-
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile43(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "422460 AboutSigninInternals::SigninStatus::ToValue43"));
-
-    for (const std::unique_ptr<TokenInfo>& token : tokens) {
+    for (const std::unique_ptr<TokenInfo>& token : it->second)
       token_details->Append(token->ToValue());
-    }
   }
   signin_status->Set("token_info", std::move(token_info));
 
diff --git a/components/signin/core/browser/account_fetcher_service.cc b/components/signin/core/browser/account_fetcher_service.cc
index 6203942..1f0edc2b 100644
--- a/components/signin/core/browser/account_fetcher_service.cc
+++ b/components/signin/core/browser/account_fetcher_service.cc
@@ -9,7 +9,6 @@
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "components/pref_registry/pref_registry_syncable.h"
@@ -234,12 +233,6 @@
 void AccountFetcherService::RefreshAccountInfo(const std::string& account_id,
                                                bool only_fetch_if_invalid) {
   DCHECK(network_fetches_enabled_);
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AccountFetcherService::RefreshAccountInfo"));
-
   account_tracker_service_->StartTrackingAccount(account_id);
   const AccountInfo& info =
       account_tracker_service_->GetAccountInfo(account_id);
diff --git a/components/translate/core/browser/translate_manager.cc b/components/translate/core/browser/translate_manager.cc
index 19bd76a..5292f00 100644
--- a/components/translate/core/browser/translate_manager.cc
+++ b/components/translate/core/browser/translate_manager.cc
@@ -11,7 +11,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
@@ -145,11 +144,6 @@
 }
 
 void TranslateManager::InitiateTranslation(const std::string& page_lang) {
-  // TODO(rogerm): Remove ScopedTracker below once crbug.com/646711 is closed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "646711 translate::TranslateManager::InitiateTranslation"));
-
   // Short-circuit out if not in a state where initiating translation makes
   // sense (this method may be called muhtiple times for a given page).
   if (!language_state_.page_needs_translation() ||
diff --git a/components/translate/core/browser/translate_ranker_impl.cc b/components/translate/core/browser/translate_ranker_impl.cc
index 27f36f69..6e70ab0 100644
--- a/components/translate/core/browser/translate_ranker_impl.cc
+++ b/components/translate/core/browser/translate_ranker_impl.cc
@@ -14,7 +14,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/metrics_hashes.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_util.h"
 #include "base/task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -228,11 +227,6 @@
 
   SCOPED_UMA_HISTOGRAM_TIMER("Translate.Ranker.Timer.ShouldOfferTranslation");
 
-  // TODO(rogerm): Remove ScopedTracker below once crbug.com/646711 is closed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "646711 translate::TranslateRankerImpl::ShouldOfferTranslation"));
-
   bool result = GetModelDecision(*translate_event);
 
   UMA_HISTOGRAM_BOOLEAN("Translate.Ranker.QueryResult", result);
diff --git a/components/typemaps.gni b/components/typemaps.gni
index f19028a..b578315 100644
--- a/components/typemaps.gni
+++ b/components/typemaps.gni
@@ -6,6 +6,7 @@
   "//components/autofill/content/common/autofill_types.typemap",
   "//components/chrome_cleaner/public/typemaps/chrome_prompt.typemap",
   "//components/content_settings/core/common/content_settings.typemap",
+  "//components/leveldb/leveldb.typemap",
   "//components/nacl/common/nacl.typemap",
   "//components/password_manager/content/common/credential_manager.typemap",
   "//components/signin/public/interfaces/account_id.typemap",
diff --git a/components/viz/OWNERS b/components/viz/OWNERS
index 88e5ae4..5f814b9 100644
--- a/components/viz/OWNERS
+++ b/components/viz/OWNERS
@@ -12,6 +12,9 @@
 vmpstr@chromium.org
 weiliangc@chromium.org
 
+# CopyOutputRequests/Results
+miu@chromium.org
+
 # display_embedder / ozone
 reveman@chromium.org
 rjkroege@chromium.org
diff --git a/components/viz/common/quads/copy_output_request.cc b/components/viz/common/quads/copy_output_request.cc
index de505d6..2791f73 100644
--- a/components/viz/common/quads/copy_output_request.cc
+++ b/components/viz/common/quads/copy_output_request.cc
@@ -13,19 +13,19 @@
 
 namespace viz {
 
-CopyOutputRequest::CopyOutputRequest() : force_bitmap_result_(false) {}
-
-CopyOutputRequest::CopyOutputRequest(bool force_bitmap_result,
+CopyOutputRequest::CopyOutputRequest(ResultFormat result_format,
                                      CopyOutputRequestCallback result_callback)
-    : force_bitmap_result_(force_bitmap_result),
+    : result_format_(result_format),
       result_callback_(std::move(result_callback)) {
   DCHECK(!result_callback_.is_null());
   TRACE_EVENT_ASYNC_BEGIN0("viz", "CopyOutputRequest", this);
 }
 
 CopyOutputRequest::~CopyOutputRequest() {
-  if (!result_callback_.is_null())
-    SendResult(CopyOutputResult::CreateEmptyResult());
+  if (!result_callback_.is_null()) {
+    // Send an empty result to indicate the request was never satisfied.
+    SendResult(std::make_unique<CopyOutputResult>(result_format_, gfx::Rect()));
+  }
 }
 
 void CopyOutputRequest::SendResult(std::unique_ptr<CopyOutputResult> result) {
@@ -41,28 +41,18 @@
   }
 }
 
-void CopyOutputRequest::SendEmptyResult() {
-  SendResult(CopyOutputResult::CreateEmptyResult());
-}
-
-void CopyOutputRequest::SendBitmapResult(std::unique_ptr<SkBitmap> bitmap) {
-  SendResult(CopyOutputResult::CreateBitmapResult(std::move(bitmap)));
-}
-
-void CopyOutputRequest::SendTextureResult(
-    const gfx::Size& size,
-    const TextureMailbox& texture_mailbox,
-    std::unique_ptr<SingleReleaseCallback> release_callback) {
-  DCHECK(texture_mailbox.IsTexture());
-  SendResult(CopyOutputResult::CreateTextureResult(
-      size, texture_mailbox, std::move(release_callback)));
-}
-
 void CopyOutputRequest::SetTextureMailbox(
     const TextureMailbox& texture_mailbox) {
-  DCHECK(!force_bitmap_result_);
+  DCHECK_EQ(result_format_, ResultFormat::RGBA_TEXTURE);
   DCHECK(texture_mailbox.IsTexture());
   texture_mailbox_ = texture_mailbox;
 }
 
+// static
+std::unique_ptr<CopyOutputRequest> CopyOutputRequest::CreateStubForTesting() {
+  return std::make_unique<CopyOutputRequest>(
+      ResultFormat::RGBA_BITMAP,
+      base::BindOnce([](std::unique_ptr<CopyOutputResult>) {}));
+}
+
 }  // namespace viz
diff --git a/components/viz/common/quads/copy_output_request.h b/components/viz/common/quads/copy_output_request.h
index ccb42ad..83d490b 100644
--- a/components/viz/common/quads/copy_output_request.h
+++ b/components/viz/common/quads/copy_output_request.h
@@ -12,44 +12,47 @@
 #include "base/optional.h"
 #include "base/task_runner.h"
 #include "base/unguessable_token.h"
+#include "components/viz/common/quads/copy_output_result.h"
 #include "components/viz/common/quads/single_release_callback.h"
 #include "components/viz/common/quads/texture_mailbox.h"
 #include "components/viz/common/viz_common_export.h"
-#include "mojo/public/cpp/bindings/struct_traits.h"
 #include "ui/gfx/geometry/rect.h"
 
-class SkBitmap;
-
 namespace viz {
 
 namespace mojom {
 class CopyOutputRequestDataView;
 }
 
-class CopyOutputResult;
-
+// Holds all the properties pertaining to a copy of a surface or layer.
+// Implementations that execute these requests must provide the requested
+// ResultFormat or else an "empty" result. Likewise, this means that any
+// transient or permanent errors preventing the successful execution of a
+// copy request will result in an "empty" result.
+//
+// Usage: Client code creates a CopyOutputRequest, optionally sets some/all of
+// its properties, and then submits it to the compositing pipeline via one of a
+// number of possible entry points (usually methods named RequestCopyOfOutput()
+// or RequestCopyOfSurface()). Then, some time later, the given result callback
+// will be run and the client processes the CopyOutputResult containing the
+// image.
+//
+// Note: This should be used for one-off screen capture only, and NOT for video
+// screen capture use cases (please use FrameSinkVideoCapturer instead).
 class VIZ_COMMON_EXPORT CopyOutputRequest {
  public:
+  using ResultFormat = CopyOutputResult::Format;
+
   using CopyOutputRequestCallback =
       base::OnceCallback<void(std::unique_ptr<CopyOutputResult> result)>;
 
-  static std::unique_ptr<CopyOutputRequest> CreateEmptyRequest() {
-    return base::WrapUnique(new CopyOutputRequest);
-  }
-  static std::unique_ptr<CopyOutputRequest> CreateRequest(
-      CopyOutputRequestCallback result_callback) {
-    return base::WrapUnique(
-        new CopyOutputRequest(false, std::move(result_callback)));
-  }
-  static std::unique_ptr<CopyOutputRequest> CreateBitmapRequest(
-      CopyOutputRequestCallback result_callback) {
-    return base::WrapUnique(
-        new CopyOutputRequest(true, std::move(result_callback)));
-  }
+  CopyOutputRequest(ResultFormat result_format,
+                    CopyOutputRequestCallback result_callback);
 
   ~CopyOutputRequest();
 
-  bool IsEmpty() const { return result_callback_.is_null(); }
+  // Returns the requested result format.
+  ResultFormat result_format() const { return result_format_; }
 
   // Requests that the result callback be run as a task posted to the given
   // |task_runner|. If this is not set, the result callback could be run from
@@ -66,45 +69,41 @@
   bool has_source() const { return source_.has_value(); }
   const base::UnguessableToken& source() const { return *source_; }
 
-  bool force_bitmap_result() const { return force_bitmap_result_; }
-
-  // By default copy requests copy the entire layer's subtree output. If an
-  // area is given, then the intersection of this rect (in layer space) with
-  // the layer's subtree output will be returned.
+  // By default copy requests copy the entire surface (or layer's subtree
+  // output). Specifying an area requests that only a portion be copied. Note
+  // that in some cases it may be necessary to sample the pixels surrounding the
+  // area.
   void set_area(const gfx::Rect& area) { area_ = area; }
   bool has_area() const { return area_.has_value(); }
   const gfx::Rect& area() const { return *area_; }
 
-  // By default copy requests create a new TextureMailbox to return contents
-  // in. This allows a client to provide a TextureMailbox, and the compositor
-  // will place the result inside the TextureMailbox.
+  // Legacy support for providing textures up-front, to copy results into.
+  // TODO(miu): Remove these methods after tab capture is moved to VIZ.
+  // http://crbug.com/754872
   void SetTextureMailbox(const TextureMailbox& texture_mailbox);
   bool has_texture_mailbox() const { return texture_mailbox_.has_value(); }
   const TextureMailbox& texture_mailbox() const { return *texture_mailbox_; }
 
-  void SendEmptyResult();
-  void SendBitmapResult(std::unique_ptr<SkBitmap> bitmap);
-  void SendTextureResult(
-      const gfx::Size& size,
-      const TextureMailbox& texture_mailbox,
-      std::unique_ptr<SingleReleaseCallback> release_callback);
-
+  // Sends the result from executing this request. Called by the internal
+  // implementation, usually a DirectRenderer.
   void SendResult(std::unique_ptr<CopyOutputResult> result);
 
+  // Creates a RGBA_BITMAP request that ignores results, for testing purposes.
+  static std::unique_ptr<CopyOutputRequest> CreateStubForTesting();
+
  private:
+  // Note: The StructTraits may "steal" the |result_callback_|, to allow it to
+  // outlive this CopyOutputRequest (and wait for the result from another
+  // process).
   friend struct mojo::StructTraits<mojom::CopyOutputRequestDataView,
                                    std::unique_ptr<CopyOutputRequest>>;
 
-  CopyOutputRequest();
-  CopyOutputRequest(bool force_bitmap_result,
-                    CopyOutputRequestCallback result_callback);
-
+  const ResultFormat result_format_;
+  CopyOutputRequestCallback result_callback_;
   scoped_refptr<base::TaskRunner> result_task_runner_;
   base::Optional<base::UnguessableToken> source_;
-  bool force_bitmap_result_;
   base::Optional<gfx::Rect> area_;
   base::Optional<TextureMailbox> texture_mailbox_;
-  CopyOutputRequestCallback result_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(CopyOutputRequest);
 };
diff --git a/components/viz/common/quads/copy_output_result.cc b/components/viz/common/quads/copy_output_result.cc
index 62f723c..eb7a8ff 100644
--- a/components/viz/common/quads/copy_output_result.cc
+++ b/components/viz/common/quads/copy_output_result.cc
@@ -5,43 +5,108 @@
 #include "components/viz/common/quads/copy_output_result.h"
 
 #include "base/logging.h"
-#include "components/viz/common/quads/texture_mailbox.h"
 
 namespace viz {
 
-CopyOutputResult::CopyOutputResult() {}
-
-CopyOutputResult::CopyOutputResult(std::unique_ptr<SkBitmap> bitmap)
-    : size_(bitmap->width(), bitmap->height()), bitmap_(std::move(bitmap)) {
-  DCHECK(bitmap_);
+CopyOutputResult::CopyOutputResult(Format format, const gfx::Rect& rect)
+    : format_(format), rect_(rect) {
+  DCHECK(format_ == Format::RGBA_BITMAP || format_ == Format::RGBA_TEXTURE);
 }
 
-CopyOutputResult::CopyOutputResult(
-    const gfx::Size& size,
+CopyOutputResult::~CopyOutputResult() = default;
+
+bool CopyOutputResult::IsEmpty() const {
+  if (rect_.IsEmpty())
+    return true;
+  switch (format_) {
+    case Format::RGBA_BITMAP:
+      return false;
+    case Format::RGBA_TEXTURE:
+      if (auto* mailbox = GetTextureMailbox())
+        return !mailbox->IsTexture();
+      else
+        return true;
+  }
+  NOTREACHED();
+  return true;
+}
+
+const SkBitmap& CopyOutputResult::AsSkBitmap() const {
+  return cached_bitmap_;
+}
+
+const TextureMailbox* CopyOutputResult::GetTextureMailbox() const {
+  return nullptr;
+}
+
+std::unique_ptr<SingleReleaseCallback>
+CopyOutputResult::TakeTextureOwnership() {
+  return nullptr;
+}
+
+CopyOutputSkBitmapResult::CopyOutputSkBitmapResult(const gfx::Rect& rect,
+                                                   const SkBitmap& bitmap)
+    : CopyOutputResult(Format::RGBA_BITMAP, rect) {
+  if (!rect.IsEmpty()) {
+    // Hold a reference to the |bitmap|'s pixels, for AsSkBitmap().
+    *(cached_bitmap()) = bitmap;
+  }
+}
+
+const SkBitmap& CopyOutputSkBitmapResult::AsSkBitmap() const {
+  SkBitmap* const bitmap = cached_bitmap();
+
+  if (rect().IsEmpty())
+    return *bitmap;  // Return "null" bitmap for empty result.
+
+  const SkImageInfo image_info = SkImageInfo::MakeN32Premul(
+      rect().width(), rect().height(), bitmap->refColorSpace());
+  if (bitmap->info() == image_info && bitmap->readyToDraw())
+    return *bitmap;  // Return bitmap in expected format.
+
+  // The bitmap is not in the "native optimized" format. Convert it once for
+  // this and all future calls of this method.
+  SkBitmap replacement;
+  replacement.allocPixels(image_info);
+  replacement.eraseColor(SK_ColorBLACK);
+  SkPixmap src_pixmap;
+  if (bitmap->peekPixels(&src_pixmap)) {
+    // Note: writePixels() can fail, but then the replacement bitmap will be
+    // left with part/all solid black due to the eraseColor() call above.
+    replacement.writePixels(src_pixmap);
+  }
+  *bitmap = replacement;
+  bitmap->setImmutable();
+
+  return *bitmap;
+}
+
+CopyOutputSkBitmapResult::~CopyOutputSkBitmapResult() = default;
+
+CopyOutputTextureResult::CopyOutputTextureResult(
+    const gfx::Rect& rect,
     const TextureMailbox& texture_mailbox,
     std::unique_ptr<SingleReleaseCallback> release_callback)
-    : size_(size),
+    : CopyOutputResult(Format::RGBA_TEXTURE, rect),
       texture_mailbox_(texture_mailbox),
       release_callback_(std::move(release_callback)) {
-  DCHECK(texture_mailbox_.IsTexture());
+  DCHECK(rect.IsEmpty() || texture_mailbox_.IsTexture());
+  DCHECK(release_callback_ || !texture_mailbox_.IsTexture());
 }
 
-CopyOutputResult::~CopyOutputResult() {
+CopyOutputTextureResult::~CopyOutputTextureResult() {
   if (release_callback_)
     release_callback_->Run(gpu::SyncToken(), false);
 }
 
-std::unique_ptr<SkBitmap> CopyOutputResult::TakeBitmap() {
-  return std::move(bitmap_);
+const TextureMailbox* CopyOutputTextureResult::GetTextureMailbox() const {
+  return &texture_mailbox_;
 }
 
-void CopyOutputResult::TakeTexture(
-    TextureMailbox* texture_mailbox,
-    std::unique_ptr<SingleReleaseCallback>* release_callback) {
-  *texture_mailbox = texture_mailbox_;
-  *release_callback = std::move(release_callback_);
-
+std::unique_ptr<SingleReleaseCallback>
+CopyOutputTextureResult::TakeTextureOwnership() {
   texture_mailbox_ = TextureMailbox();
+  return std::move(release_callback_);
 }
 
 }  // namespace viz
diff --git a/components/viz/common/quads/copy_output_result.h b/components/viz/common/quads/copy_output_result.h
index 901ea5c9..ff2a9bf 100644
--- a/components/viz/common/quads/copy_output_result.h
+++ b/components/viz/common/quads/copy_output_result.h
@@ -7,71 +7,109 @@
 
 #include <memory>
 
-#include "base/memory/ptr_util.h"
 #include "components/viz/common/quads/single_release_callback.h"
 #include "components/viz/common/quads/texture_mailbox.h"
 #include "components/viz/common/viz_common_export.h"
-#include "mojo/public/cpp/bindings/struct_traits.h"
 #include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/rect.h"
 
 class SkBitmap;
 
-namespace cc {
-namespace mojom {
-class CopyOutputResultDataView;
-}
-}  // namespace cc
-
 namespace viz {
 
-class TextureMailbox;
-
+// Base class for providing the result of a CopyOutputRequest. Implementations
+// that execute CopyOutputRequests will use a subclass implementation to define
+// data storage, access and ownership semantics relative to the lifetime of the
+// CopyOutputResult instance.
 class VIZ_COMMON_EXPORT CopyOutputResult {
  public:
-  static std::unique_ptr<CopyOutputResult> CreateEmptyResult() {
-    return base::WrapUnique(new CopyOutputResult);
-  }
-  static std::unique_ptr<CopyOutputResult> CreateBitmapResult(
-      std::unique_ptr<SkBitmap> bitmap) {
-    return base::WrapUnique(new CopyOutputResult(std::move(bitmap)));
-  }
-  static std::unique_ptr<CopyOutputResult> CreateTextureResult(
-      const gfx::Size& size,
-      const TextureMailbox& texture_mailbox,
-      std::unique_ptr<SingleReleaseCallback> release_callback) {
-    return base::WrapUnique(new CopyOutputResult(size, texture_mailbox,
-                                                 std::move(release_callback)));
-  }
+  enum class Format : uint8_t {
+    // A normal bitmap in system memory. AsSkBitmap() will return a bitmap in
+    // "N32Premul" form.
+    RGBA_BITMAP,
+    // A GL_RGBA texture, referenced by a TextureMailbox. Client code can
+    // optionally take ownership of the texture (via TakeTextureOwnership()), if
+    // it is needed beyond the lifetime of CopyOutputResult.
+    RGBA_TEXTURE,
+  };
 
-  ~CopyOutputResult();
+  CopyOutputResult(Format format, const gfx::Rect& rect);
 
-  bool IsEmpty() const { return !HasBitmap() && !HasTexture(); }
-  bool HasBitmap() const { return !!bitmap_ && !bitmap_->isNull(); }
-  bool HasTexture() const { return texture_mailbox_.IsValid(); }
+  virtual ~CopyOutputResult();
 
-  gfx::Size size() const { return size_; }
-  std::unique_ptr<SkBitmap> TakeBitmap();
-  void TakeTexture(TextureMailbox* texture_mailbox,
-                   std::unique_ptr<SingleReleaseCallback>* release_callback);
+  // Returns false if the request succeeded and the data accessors will return
+  // valid references.
+  bool IsEmpty() const;
+
+  // Returns the format of this result.
+  Format format() const { return format_; }
+
+  // Returns the result Rect, which is the position and size of the image data
+  // within the surface/layer (see CopyOutputRequest::set_area()).
+  const gfx::Rect& rect() const { return rect_; }
+  const gfx::Size& size() const { return rect_.size(); }
+
+  // Convenience to provide this result in SkBitmap form. Returns a
+  // !readyToDraw() bitmap if this result is empty or if a conversion is not
+  // possible in the current implementation.
+  virtual const SkBitmap& AsSkBitmap() const;
+
+  // Returns a pointer to the TextureMailbox referencing a RGBA_TEXTURE result,
+  // or null if this is not a RGBA_TEXTURE result. Clients can either:
+  //   1. Let CopyOutputResult retain ownership and the texture will only be
+  //      valid for use during CopyOutputResult's lifetime.
+  //   2. Take over ownership of the texture by calling TakeTextureOwnership(),
+  //      and the client must guarantee the release callback will be run at some
+  //      point.
+  virtual const TextureMailbox* GetTextureMailbox() const;
+  virtual std::unique_ptr<SingleReleaseCallback> TakeTextureOwnership();
+
+ protected:
+  // Accessor for subclasses to initialize the cached SkBitmap.
+  SkBitmap* cached_bitmap() const { return &cached_bitmap_; }
 
  private:
-  friend struct mojo::StructTraits<cc::mojom::CopyOutputResultDataView,
-                                   std::unique_ptr<CopyOutputResult>>;
+  const Format format_;
+  const gfx::Rect rect_;
 
-  CopyOutputResult();
-  explicit CopyOutputResult(std::unique_ptr<SkBitmap> bitmap);
-  explicit CopyOutputResult(
-      const gfx::Size& size,
+  // Cached bitmap returned by the default implementation of AsSkBitmap().
+  mutable SkBitmap cached_bitmap_;
+
+  DISALLOW_COPY_AND_ASSIGN(CopyOutputResult);
+};
+
+// Subclass of CopyOutputResult that provides a RGBA_BITMAP result from an
+// SkBitmap.
+class VIZ_COMMON_EXPORT CopyOutputSkBitmapResult : public CopyOutputResult {
+ public:
+  CopyOutputSkBitmapResult(const gfx::Rect& rect, const SkBitmap& bitmap);
+  ~CopyOutputSkBitmapResult() override;
+
+  const SkBitmap& AsSkBitmap() const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CopyOutputSkBitmapResult);
+};
+
+// Subclass of CopyOutputResult that holds a reference to a texture (via
+// TextureMailbox) and owns the texture, calling its SingleReleaseCallback at
+// destruction time.
+class VIZ_COMMON_EXPORT CopyOutputTextureResult : public CopyOutputResult {
+ public:
+  CopyOutputTextureResult(
+      const gfx::Rect& rect,
       const TextureMailbox& texture_mailbox,
       std::unique_ptr<SingleReleaseCallback> release_callback);
+  ~CopyOutputTextureResult() override;
 
-  gfx::Size size_;
-  std::unique_ptr<SkBitmap> bitmap_;
+  const TextureMailbox* GetTextureMailbox() const override;
+  std::unique_ptr<SingleReleaseCallback> TakeTextureOwnership() override;
+
+ private:
   TextureMailbox texture_mailbox_;
   std::unique_ptr<SingleReleaseCallback> release_callback_;
 
-  DISALLOW_COPY_AND_ASSIGN(CopyOutputResult);
+  DISALLOW_COPY_AND_ASSIGN(CopyOutputTextureResult);
 };
 
 }  // namespace viz
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index de26799..97d1aee 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -25,6 +25,8 @@
     "display/gl_renderer.h",
     "display/gl_renderer_draw_cache.cc",
     "display/gl_renderer_draw_cache.h",
+    "display/layer_quad.cc",
+    "display/layer_quad.h",
     "display/program_binding.cc",
     "display/program_binding.h",
     "display/shader.cc",
@@ -171,6 +173,7 @@
     "display/display_scheduler_unittest.cc",
     "display/display_unittest.cc",
     "display/gl_renderer_unittest.cc",
+    "display/layer_quad_unittest.cc",
     "display/shader_unittest.cc",
     "display/surface_aggregator_pixeltest.cc",
     "display/surface_aggregator_unittest.cc",
diff --git a/components/viz/service/display/display_unittest.cc b/components/viz/service/display/display_unittest.cc
index 81f2818..c495eab 100644
--- a/components/viz/service/display/display_unittest.cc
+++ b/components/viz/service/display/display_unittest.cc
@@ -329,7 +329,8 @@
     pass->output_rect = gfx::Rect(0, 0, 100, 100);
     pass->damage_rect = gfx::Rect(10, 10, 0, 0);
     bool copy_called = false;
-    pass->copy_requests.push_back(CopyOutputRequest::CreateRequest(
+    pass->copy_requests.push_back(std::make_unique<CopyOutputRequest>(
+        CopyOutputRequest::ResultFormat::RGBA_BITMAP,
         base::BindOnce(&CopyCallback, &copy_called)));
     pass->id = 1u;
 
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc
index 7788976c..c7d1295 100644
--- a/components/viz/service/display/gl_renderer.cc
+++ b/components/viz/service/display/gl_renderer.cc
@@ -31,7 +31,6 @@
 #include "cc/debug/debug_colors.h"
 #include "cc/output/compositor_frame.h"
 #include "cc/output/compositor_frame_metadata.h"
-#include "cc/output/layer_quad.h"
 #include "cc/output/output_surface.h"
 #include "cc/output/output_surface_frame.h"
 #include "cc/output/texture_mailbox_deleter.h"
@@ -47,6 +46,7 @@
 #include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/quads/copy_output_request.h"
 #include "components/viz/service/display/dynamic_geometry_binding.h"
+#include "components/viz/service/display/layer_quad.h"
 #include "components/viz/service/display/static_geometry_binding.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/context_support.h"
@@ -296,6 +296,7 @@
   PendingAsyncReadPixels() : buffer(0) {}
 
   std::unique_ptr<CopyOutputRequest> copy_request;
+  gfx::Rect copy_rect;
   unsigned buffer;
 
  private:
@@ -1555,7 +1556,7 @@
 }  // anonymous namespace
 
 static gfx::QuadF GetDeviceQuadWithAntialiasingOnExteriorEdges(
-    const cc::LayerQuad& device_layer_edges,
+    const LayerQuad& device_layer_edges,
     const gfx::Transform& device_transform,
     const gfx::QuadF& tile_quad,
     const gfx::QuadF* clip_region,
@@ -1577,10 +1578,10 @@
   top_left = cc::MathUtil::MapPoint(device_transform, top_left, &clipped);
   top_right = cc::MathUtil::MapPoint(device_transform, top_right, &clipped);
 
-  cc::LayerQuad::Edge bottom_edge(bottom_right, bottom_left);
-  cc::LayerQuad::Edge left_edge(bottom_left, top_left);
-  cc::LayerQuad::Edge top_edge(top_left, top_right);
-  cc::LayerQuad::Edge right_edge(top_right, bottom_right);
+  LayerQuad::Edge bottom_edge(bottom_right, bottom_left);
+  LayerQuad::Edge left_edge(bottom_left, top_left);
+  LayerQuad::Edge top_edge(top_left, top_right);
+  LayerQuad::Edge right_edge(top_right, bottom_right);
 
   // Only apply anti-aliasing to edges not clipped by culling or scissoring.
   // If an edge is degenerate we do not want to replace it with a "proper" edge
@@ -1609,7 +1610,7 @@
   right_edge.scale(sign);
 
   // Create device space quad.
-  return cc::LayerQuad(left_edge, top_edge, right_edge, bottom_edge).ToQuadF();
+  return LayerQuad(left_edge, top_edge, right_edge, bottom_edge).ToQuadF();
 }
 
 float GetTotalQuadError(const gfx::QuadF* clipped_quad,
@@ -1639,10 +1640,10 @@
 }
 
 void InflateAntiAliasingDistances(const gfx::QuadF& quad,
-                                  cc::LayerQuad* device_layer_edges,
+                                  LayerQuad* device_layer_edges,
                                   float edge[24]) {
   DCHECK(!quad.BoundingBox().IsEmpty());
-  cc::LayerQuad device_layer_bounds(gfx::QuadF(quad.BoundingBox()));
+  LayerQuad device_layer_bounds(gfx::QuadF(quad.BoundingBox()));
 
   device_layer_edges->InflateAntiAliasingDistance();
   device_layer_edges->ToFloatArray(edge);
@@ -1693,7 +1694,7 @@
     return;
   }
 
-  cc::LayerQuad device_layer_edges(*aa_quad);
+  LayerQuad device_layer_edges(*aa_quad);
   InflateAntiAliasingDistances(*aa_quad, &device_layer_edges, edge);
 
   // If we have a clip region then we are split, and therefore
@@ -1745,7 +1746,7 @@
     return;
   }
 
-  cc::LayerQuad device_layer_edges(*aa_quad);
+  LayerQuad device_layer_edges(*aa_quad);
   InflateAntiAliasingDistances(*aa_quad, &device_layer_edges, edge);
 
   gfx::QuadF device_quad;
@@ -2614,9 +2615,9 @@
   is_scissor_enabled_ = false;
 }
 
-void GLRenderer::CopyCurrentRenderPassToBitmap(
+void GLRenderer::CopyDrawnRenderPass(
     std::unique_ptr<CopyOutputRequest> request) {
-  TRACE_EVENT0("cc", "GLRenderer::CopyCurrentRenderPassToBitmap");
+  TRACE_EVENT0("cc", "GLRenderer::CopyDrawnRenderPass");
   gfx::Rect copy_rect = current_frame()->current_render_pass->output_rect;
   if (request->has_area())
     copy_rect.Intersect(request->area());
@@ -2807,11 +2808,8 @@
 void GLRenderer::GetFramebufferPixelsAsync(
     const gfx::Rect& rect,
     std::unique_ptr<CopyOutputRequest> request) {
-  DCHECK(!request->IsEmpty());
-  if (request->IsEmpty())
-    return;
   if (rect.IsEmpty())
-    return;
+    return;  // |request| auto-sends empty result on out-of-scope.
 
   if (overdraw_feedback_)
     FlushOverdrawFeedback(rect);
@@ -2821,90 +2819,104 @@
   DCHECK_GE(window_rect.y(), 0);
   DCHECK_LE(window_rect.right(), current_surface_size_.width());
   DCHECK_LE(window_rect.bottom(), current_surface_size_.height());
+  DCHECK_EQ(window_rect.width(), rect.width());
+  DCHECK_EQ(window_rect.height(), rect.height());
 
-  if (!request->force_bitmap_result()) {
-    bool own_mailbox = !request->has_texture_mailbox();
+  switch (request->result_format()) {
+    case CopyOutputRequest::ResultFormat::RGBA_TEXTURE: {
+      bool own_mailbox = !request->has_texture_mailbox();
 
-    GLuint texture_id = 0;
-    gpu::Mailbox mailbox;
-    if (own_mailbox) {
-      gl_->GenMailboxCHROMIUM(mailbox.name);
-      gl_->GenTextures(1, &texture_id);
-      gl_->BindTexture(GL_TEXTURE_2D, texture_id);
+      GLuint texture_id = 0;
+      gpu::Mailbox mailbox;
+      if (own_mailbox) {
+        gl_->GenMailboxCHROMIUM(mailbox.name);
+        gl_->GenTextures(1, &texture_id);
+        gl_->BindTexture(GL_TEXTURE_2D, texture_id);
 
-      gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-      gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-      gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-      gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-      gl_->ProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
-    } else {
-      mailbox = request->texture_mailbox().mailbox();
-      DCHECK_EQ(static_cast<unsigned>(GL_TEXTURE_2D),
-                request->texture_mailbox().target());
-      DCHECK(!mailbox.IsZero());
-      const gpu::SyncToken& incoming_sync_token =
-          request->texture_mailbox().sync_token();
-      if (incoming_sync_token.HasData())
-        gl_->WaitSyncTokenCHROMIUM(incoming_sync_token.GetConstData());
+        gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+        gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        gl_->ProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
+      } else {
+        mailbox = request->texture_mailbox().mailbox();
+        DCHECK_EQ(static_cast<unsigned>(GL_TEXTURE_2D),
+                  request->texture_mailbox().target());
+        DCHECK(!mailbox.IsZero());
+        const gpu::SyncToken& incoming_sync_token =
+            request->texture_mailbox().sync_token();
+        if (incoming_sync_token.HasData())
+          gl_->WaitSyncTokenCHROMIUM(incoming_sync_token.GetConstData());
 
-      texture_id =
-          gl_->CreateAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
-    }
-    GetFramebufferTexture(texture_id, window_rect);
+        texture_id =
+            gl_->CreateAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
+      }
+      GetFramebufferTexture(texture_id, window_rect);
 
-    const GLuint64 fence_sync = gl_->InsertFenceSyncCHROMIUM();
-    gl_->ShallowFlushCHROMIUM();
+      const GLuint64 fence_sync = gl_->InsertFenceSyncCHROMIUM();
+      gl_->ShallowFlushCHROMIUM();
 
-    gpu::SyncToken sync_token;
-    gl_->GenSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
+      gpu::SyncToken sync_token;
+      gl_->GenSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
 
-    TextureMailbox texture_mailbox(mailbox, sync_token, GL_TEXTURE_2D);
+      TextureMailbox texture_mailbox(mailbox, sync_token, GL_TEXTURE_2D);
+      // TODO(miu): Set |texture_mailbox.color_space_|. http://crbug.com/758057
 
-    std::unique_ptr<SingleReleaseCallback> release_callback;
-    if (own_mailbox) {
-      gl_->BindTexture(GL_TEXTURE_2D, 0);
-      release_callback = texture_mailbox_deleter_->GetReleaseCallback(
-          output_surface_->context_provider(), texture_id);
-    } else {
-      gl_->DeleteTextures(1, &texture_id);
+      std::unique_ptr<SingleReleaseCallback> release_callback;
+      if (own_mailbox) {
+        gl_->BindTexture(GL_TEXTURE_2D, 0);
+        release_callback = texture_mailbox_deleter_->GetReleaseCallback(
+            output_surface_->context_provider(), texture_id);
+      } else {
+        gl_->DeleteTextures(1, &texture_id);
+        // Create a no-op release callback, since the client that made the
+        // request owns the texture. This wart is going away soon, per work on
+        // http://crbug.com/754872.
+        release_callback = SingleReleaseCallback::Create(
+            base::Bind([](const gpu::SyncToken&, bool) {}));
+      }
+
+      request->SendResult(std::make_unique<CopyOutputTextureResult>(
+          rect, texture_mailbox, std::move(release_callback)));
+      return;
     }
 
-    request->SendTextureResult(window_rect.size(), texture_mailbox,
-                               std::move(release_callback));
-    return;
+    case CopyOutputRequest::ResultFormat::RGBA_BITMAP: {
+      std::unique_ptr<PendingAsyncReadPixels> pending_read(
+          new PendingAsyncReadPixels);
+      pending_read->copy_request = std::move(request);
+      pending_read->copy_rect = rect;
+      pending_async_read_pixels_.insert(pending_async_read_pixels_.begin(),
+                                        std::move(pending_read));
+
+      GLuint buffer = 0;
+      gl_->GenBuffers(1, &buffer);
+      gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, buffer);
+      gl_->BufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
+                      4 * window_rect.size().GetArea(), NULL, GL_STREAM_READ);
+
+      GLuint query = 0;
+      gl_->GenQueriesEXT(1, &query);
+      gl_->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, query);
+
+      gl_->ReadPixels(window_rect.x(), window_rect.y(), window_rect.width(),
+                      window_rect.height(), GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+
+      gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
+
+      // Save the buffer to verify the callbacks happen in the expected order.
+      pending_async_read_pixels_.front()->buffer = buffer;
+
+      gl_->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM);
+      context_support_->SignalQuery(
+          query, base::Bind(&GLRenderer::FinishedReadback,
+                            weak_ptr_factory_.GetWeakPtr(), buffer, query,
+                            window_rect.size()));
+      return;
+    }
   }
 
-  DCHECK(request->force_bitmap_result());
-
-  std::unique_ptr<PendingAsyncReadPixels> pending_read(
-      new PendingAsyncReadPixels);
-  pending_read->copy_request = std::move(request);
-  pending_async_read_pixels_.insert(pending_async_read_pixels_.begin(),
-                                    std::move(pending_read));
-
-  GLuint buffer = 0;
-  gl_->GenBuffers(1, &buffer);
-  gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, buffer);
-  gl_->BufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
-                  4 * window_rect.size().GetArea(), NULL, GL_STREAM_READ);
-
-  GLuint query = 0;
-  gl_->GenQueriesEXT(1, &query);
-  gl_->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, query);
-
-  gl_->ReadPixels(window_rect.x(), window_rect.y(), window_rect.width(),
-                  window_rect.height(), GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-
-  gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
-
-  // Save the buffer to verify the callbacks happen in the expected order.
-  pending_async_read_pixels_.front()->buffer = buffer;
-
-  gl_->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM);
-  context_support_->SignalQuery(
-      query,
-      base::Bind(&GLRenderer::FinishedReadback, weak_ptr_factory_.GetWeakPtr(),
-                 buffer, query, window_rect.size()));
+  NOTREACHED();
 }
 
 void GLRenderer::FinishedReadback(unsigned source_buffer,
@@ -2930,7 +2942,7 @@
   PendingAsyncReadPixels* current_read = iter->get();
 
   uint8_t* src_pixels = NULL;
-  std::unique_ptr<SkBitmap> bitmap;
+  SkBitmap bitmap;
 
   if (source_buffer != 0) {
     gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, source_buffer);
@@ -2938,9 +2950,14 @@
         GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY));
 
     if (src_pixels) {
-      bitmap.reset(new SkBitmap);
-      bitmap->allocN32Pixels(size.width(), size.height());
-      uint8_t* dest_pixels = static_cast<uint8_t*>(bitmap->getPixels());
+      // TODO(miu): Provide color space in this allocN32Pixels() call.
+      // http://crbug.com/758057
+      bitmap.allocN32Pixels(size.width(), size.height());
+
+      // TODO(miu): Replace the logic below with a simple SkBitmap.writePixels()
+      // call (to use Skia's optimized swizzle/conversion implementation).
+      // http://crbug.com/754872
+      uint8_t* dest_pixels = static_cast<uint8_t*>(bitmap.getPixels());
 
       size_t row_bytes = size.width() * 4;
       int num_rows = size.height();
@@ -2967,8 +2984,14 @@
     gl_->DeleteBuffers(1, &source_buffer);
   }
 
-  if (bitmap)
-    current_read->copy_request->SendBitmapResult(std::move(bitmap));
+  if (bitmap.readyToDraw()) {
+    current_read->copy_request->SendResult(
+        std::make_unique<CopyOutputSkBitmapResult>(current_read->copy_rect,
+                                                   bitmap));
+  } else {
+    // The CopyOutputRequest will auto-send an empty result on out-of-scope
+    // (below).
+  }
 
   // Conversion from reverse iterator to iterator:
   // Iterator |iter.base() - 1| points to the same element with reverse iterator
@@ -3529,7 +3552,7 @@
     params.contents_device_transform.FlattenTo2d();
     gfx::QuadF device_layer_quad = cc::MathUtil::MapQuad(
         params.contents_device_transform, SharedGeometryQuad(), &clipped);
-    cc::LayerQuad device_layer_edges(device_layer_quad);
+    LayerQuad device_layer_edges(device_layer_quad);
     InflateAntiAliasingDistances(device_layer_quad, &device_layer_edges,
                                  params.edge);
   }
diff --git a/components/viz/service/display/gl_renderer.h b/components/viz/service/display/gl_renderer.h
index 34e73cb..4cd1b602 100644
--- a/components/viz/service/display/gl_renderer.h
+++ b/components/viz/service/display/gl_renderer.h
@@ -103,8 +103,7 @@
   bool FlippedRootFramebuffer() const;
   void EnsureScissorTestEnabled() override;
   void EnsureScissorTestDisabled() override;
-  void CopyCurrentRenderPassToBitmap(
-      std::unique_ptr<CopyOutputRequest> request) override;
+  void CopyDrawnRenderPass(std::unique_ptr<CopyOutputRequest> request) override;
   void SetEnableDCLayers(bool enable) override;
   void FinishDrawingQuadList() override;
 
diff --git a/components/viz/service/display/gl_renderer_unittest.cc b/components/viz/service/display/gl_renderer_unittest.cc
index ebbf55bd..2f3a199 100644
--- a/components/viz/service/display/gl_renderer_unittest.cc
+++ b/components/viz/service/display/gl_renderer_unittest.cc
@@ -1915,8 +1915,6 @@
                      bool lost_resource,
                      cc::BlockingTaskRunner* main_thread_task_runner) {}
 
-void IgnoreCopyResult(std::unique_ptr<CopyOutputResult> result) {}
-
 TEST_F(GLRendererTest, DontOverlayWithCopyRequests) {
   cc::FakeOutputSurfaceClient output_surface_client;
   std::unique_ptr<cc::FakeOutputSurface> output_surface(
@@ -1950,8 +1948,7 @@
       AddRenderPass(&render_passes_in_draw_order_, 1, gfx::Rect(viewport_size),
                     gfx::Transform(), cc::FilterOperations());
   root_pass->has_transparent_background = false;
-  root_pass->copy_requests.push_back(
-      CopyOutputRequest::CreateRequest(base::BindOnce(&IgnoreCopyResult)));
+  root_pass->copy_requests.push_back(CopyOutputRequest::CreateStubForTesting());
 
   TextureMailbox mailbox =
       TextureMailbox(gpu::Mailbox::Generate(), gpu::SyncToken(), GL_TEXTURE_2D,
diff --git a/cc/output/layer_quad.cc b/components/viz/service/display/layer_quad.cc
similarity index 90%
rename from cc/output/layer_quad.cc
rename to components/viz/service/display/layer_quad.cc
index 450bfbcf..0385b5d 100644
--- a/cc/output/layer_quad.cc
+++ b/components/viz/service/display/layer_quad.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "cc/output/layer_quad.h"
+#include "components/viz/service/display/layer_quad.h"
 
 #include <stddef.h>
 
 #include "base/logging.h"
 #include "ui/gfx/geometry/quad_f.h"
 
-namespace cc {
+namespace viz {
 
 LayerQuad::Edge::Edge(const gfx::PointF& p, const gfx::PointF& q) {
   if (p == q) {
@@ -50,10 +50,7 @@
                      const Edge& top,
                      const Edge& right,
                      const Edge& bottom)
-    : left_(left),
-      top_(top),
-      right_(right),
-      bottom_(bottom) {}
+    : left_(left), top_(top), right_(right), bottom_(bottom) {}
 
 gfx::QuadF LayerQuad::ToQuadF() const {
   size_t num_degenerate_edges = left_.degenerate() + right_.degenerate() +
@@ -78,10 +75,8 @@
     return gfx::QuadF(left_.Intersect(top_), top_.Intersect(right_),
                       right_.Intersect(left_), left_.Intersect(right_));
   }
-  return gfx::QuadF(left_.Intersect(top_),
-                    top_.Intersect(right_),
-                    right_.Intersect(bottom_),
-                    bottom_.Intersect(left_));
+  return gfx::QuadF(left_.Intersect(top_), top_.Intersect(right_),
+                    right_.Intersect(bottom_), bottom_.Intersect(left_));
 }
 
 void LayerQuad::ToFloatArray(float flattened[12]) const {
@@ -123,4 +118,4 @@
   }
 }
 
-}  // namespace cc
+}  // namespace viz
diff --git a/cc/output/layer_quad.h b/components/viz/service/display/layer_quad.h
similarity index 81%
rename from cc/output/layer_quad.h
rename to components/viz/service/display/layer_quad.h
index 8b10988..f240471 100644
--- a/cc/output/layer_quad.h
+++ b/components/viz/service/display/layer_quad.h
@@ -2,25 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-
-#ifndef CC_OUTPUT_LAYER_QUAD_H_
-#define CC_OUTPUT_LAYER_QUAD_H_
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_LAYER_QUAD_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_LAYER_QUAD_H_
 
 #include "base/macros.h"
-#include "cc/cc_export.h"
+#include "components/viz/service/viz_service_export.h"
 #include "ui/gfx/geometry/point_f.h"
 
 namespace gfx {
 class QuadF;
 }
 
-static const float kAntiAliasingInflateDistance = 0.5f;
+namespace viz {
 
-namespace cc {
+constexpr float kAntiAliasingInflateDistance = 0.5f;
 
-class CC_EXPORT LayerQuad {
+class VIZ_SERVICE_EXPORT LayerQuad {
  public:
-  class CC_EXPORT Edge {
+  class VIZ_SERVICE_EXPORT Edge {
    public:
     Edge() : x_(0), y_(0), z_(0), degenerate_(false) {}
     Edge(const gfx::PointF& p, const gfx::PointF& q);
@@ -91,9 +90,7 @@
     InflateX(d);
     InflateY(d);
   }
-  void InflateAntiAliasingDistance() {
-    Inflate(kAntiAliasingInflateDistance);
-  }
+  void InflateAntiAliasingDistance() { Inflate(kAntiAliasingInflateDistance); }
 
   gfx::QuadF ToQuadF() const;
 
@@ -108,6 +105,6 @@
   DISALLOW_COPY_AND_ASSIGN(LayerQuad);
 };
 
-}  // namespace cc
+}  // namespace viz
 
-#endif  // CC_OUTPUT_LAYER_QUAD_H_
+#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_LAYER_QUAD_H_
diff --git a/cc/output/layer_quad_unittest.cc b/components/viz/service/display/layer_quad_unittest.cc
similarity index 94%
rename from cc/output/layer_quad_unittest.cc
rename to components/viz/service/display/layer_quad_unittest.cc
index e910705..6de10f5 100644
--- a/cc/output/layer_quad_unittest.cc
+++ b/components/viz/service/display/layer_quad_unittest.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "cc/output/layer_quad.h"
+#include "components/viz/service/display/layer_quad.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/quad_f.h"
 
-namespace cc {
+namespace viz {
 namespace {
 
 TEST(LayerQuadTest, QuadFConversion) {
@@ -66,4 +66,4 @@
 }
 
 }  // namespace
-}  // namespace cc
+}  // namespace viz
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index 4ab0fc9..82a8409 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -617,7 +617,7 @@
                             current_paint_);
 }
 
-void SkiaRenderer::CopyCurrentRenderPassToBitmap(
+void SkiaRenderer::CopyDrawnRenderPass(
     std::unique_ptr<CopyOutputRequest> request) {
   // TODO(weiliangc): Make copy request work. (crbug.com/644851)
   NOTIMPLEMENTED();
diff --git a/components/viz/service/display/skia_renderer.h b/components/viz/service/display/skia_renderer.h
index fa37d20..f4795aa 100644
--- a/components/viz/service/display/skia_renderer.h
+++ b/components/viz/service/display/skia_renderer.h
@@ -54,8 +54,7 @@
   bool FlippedFramebuffer() const override;
   void EnsureScissorTestEnabled() override;
   void EnsureScissorTestDisabled() override;
-  void CopyCurrentRenderPassToBitmap(
-      std::unique_ptr<CopyOutputRequest> request) override;
+  void CopyDrawnRenderPass(std::unique_ptr<CopyOutputRequest> request) override;
   void SetEnableDCLayers(bool enable) override;
   void DidChangeVisibility() override;
   void FinishDrawingQuadList() override;
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index 746bd99..c3819e4 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -236,8 +236,8 @@
 
   const cc::RenderPassList& render_pass_list = frame.render_pass_list;
   if (!valid_surfaces_.count(surface->surface_id())) {
-    for (auto& request : copy_requests)
-      request.second->SendEmptyResult();
+    // As |copy_requests| goes out-of-scope, all copy requests in that container
+    // will auto-send an empty result upon destruction.
     return;
   }
 
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index 1f2bd50..7945154b3 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -689,7 +689,7 @@
 
   SubmitCompositorFrame(embedded_support.get(), embedded_passes,
                         arraysize(embedded_passes), embedded_local_surface_id);
-  auto copy_request = CopyOutputRequest::CreateEmptyRequest();
+  auto copy_request = CopyOutputRequest::CreateStubForTesting();
   auto* copy_request_ptr = copy_request.get();
   embedded_support->RequestCopyOfSurface(std::move(copy_request));
 
@@ -744,9 +744,9 @@
 
   SubmitCompositorFrame(embedded_support.get(), embedded_passes,
                         arraysize(embedded_passes), embedded_local_surface_id);
-  auto copy_request(CopyOutputRequest::CreateEmptyRequest());
+  auto copy_request(CopyOutputRequest::CreateStubForTesting());
   auto* copy_request_ptr = copy_request.get();
-  auto copy_request2(CopyOutputRequest::CreateEmptyRequest());
+  auto copy_request2(CopyOutputRequest::CreateStubForTesting());
   auto* copy_request2_ptr = copy_request2.get();
 
   Quad root_quads[] = {
@@ -825,7 +825,7 @@
 
   SubmitCompositorFrame(embedded_support.get(), embedded_passes,
                         arraysize(embedded_passes), embedded_local_surface_id);
-  auto copy_request(CopyOutputRequest::CreateEmptyRequest());
+  auto copy_request(CopyOutputRequest::CreateStubForTesting());
   auto* copy_request_ptr = copy_request.get();
   embedded_support->RequestCopyOfSurface(std::move(copy_request));
 
@@ -1942,7 +1942,7 @@
     auto* child_root_pass = child_pass_list[1].get();
 
     child_root_pass->copy_requests.push_back(
-        CopyOutputRequest::CreateEmptyRequest());
+        CopyOutputRequest::CreateStubForTesting());
     child_root_pass->damage_rect = gfx::Rect();
     SubmitPassListAsFrame(child_support_.get(), child_local_surface_id,
                           &child_pass_list);
@@ -2431,7 +2431,7 @@
     surface_quad->SetNew(sqs, gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1),
                          surface1_id, cc::SurfaceDrawQuadType::PRIMARY,
                          nullptr);
-    pass->copy_requests.push_back(CopyOutputRequest::CreateEmptyRequest());
+    pass->copy_requests.push_back(CopyOutputRequest::CreateStubForTesting());
 
     cc::CompositorFrame frame = test::MakeEmptyCompositorFrame();
     frame.render_pass_list.push_back(std::move(pass));
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
index a4bb235..73b0b0d 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -622,7 +622,8 @@
   }
 
   bool called1 = false;
-  auto request = CopyOutputRequest::CreateRequest(
+  auto request = std::make_unique<CopyOutputRequest>(
+      CopyOutputRequest::ResultFormat::RGBA_BITMAP,
       base::BindOnce(&CopyRequestTestCallback, &called1));
   request->set_source(kArbitrarySourceId1);
 
@@ -630,7 +631,8 @@
   EXPECT_FALSE(called1);
 
   bool called2 = false;
-  request = CopyOutputRequest::CreateRequest(
+  request = std::make_unique<CopyOutputRequest>(
+      CopyOutputRequest::ResultFormat::RGBA_BITMAP,
       base::BindOnce(&CopyRequestTestCallback, &called2));
   request->set_source(kArbitrarySourceId2);
 
@@ -640,7 +642,8 @@
   EXPECT_FALSE(called2);
 
   bool called3 = false;
-  request = CopyOutputRequest::CreateRequest(
+  request = std::make_unique<CopyOutputRequest>(
+      CopyOutputRequest::ResultFormat::RGBA_BITMAP,
       base::BindOnce(&CopyRequestTestCallback, &called3));
   request->set_source(kArbitrarySourceId1);
 
diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc
index 5266601..429b115 100644
--- a/components/viz/service/surfaces/surface.cc
+++ b/components/viz/service/surfaces/surface.cc
@@ -155,10 +155,8 @@
 
 void Surface::RequestCopyOfOutput(
     std::unique_ptr<CopyOutputRequest> copy_request) {
-  if (!active_frame_data_) {
-    copy_request->SendEmptyResult();
-    return;
-  }
+  if (!active_frame_data_)
+    return;  // |copy_request| auto-sends empty result on out-of-scope.
 
   std::vector<std::unique_ptr<CopyOutputRequest>>& copy_requests =
       active_frame_data_->frame.render_pass_list.back()->copy_requests;
@@ -388,8 +386,9 @@
 void Surface::ClearCopyRequests() {
   if (active_frame_data_) {
     for (const auto& render_pass : active_frame_data_->frame.render_pass_list) {
-      for (const auto& copy_request : render_pass->copy_requests)
-        copy_request->SendEmptyResult();
+      // When the container is cleared, all copy requests within it will
+      // auto-send an empty result as they are being destroyed.
+      render_pass->copy_requests.clear();
     }
   }
 }
diff --git a/components/viz/service/surfaces/surface_unittest.cc b/components/viz/service/surfaces/surface_unittest.cc
index c332b2a..9124ba6 100644
--- a/components/viz/service/surfaces/surface_unittest.cc
+++ b/components/viz/service/surfaces/surface_unittest.cc
@@ -71,7 +71,8 @@
   ASSERT_TRUE(!!surface);
 
   bool copy_called = false;
-  support->RequestCopyOfSurface(CopyOutputRequest::CreateRequest(
+  support->RequestCopyOfSurface(std::make_unique<CopyOutputRequest>(
+      CopyOutputRequest::ResultFormat::RGBA_BITMAP,
       base::BindOnce(&TestCopyResultCallback, &copy_called)));
   EXPECT_TRUE(surface_manager->GetSurfaceForId(surface_id));
   EXPECT_FALSE(copy_called);
@@ -104,7 +105,7 @@
   // Last (root) pass should receive copy request.
   ASSERT_EQ(1u, copy_requests.count(last_pass_id));
   EXPECT_FALSE(copy_called);
-  copy_requests.find(last_pass_id)->second->SendEmptyResult();
+  copy_requests.clear();  // Deleted requests will auto-send an empty result.
   EXPECT_TRUE(copy_called);
 
   support->EvictCurrentSurface();
diff --git a/components/webdata/DEPS b/components/webdata/DEPS
index 99ab708..c4d2a9a13e 100644
--- a/components/webdata/DEPS
+++ b/components/webdata/DEPS
@@ -4,8 +4,6 @@
 ]
 
 specific_include_rules = {
-  # TODO(caitkp): Extract unit tests from //chrome, at lower priority
-  # than production code.
   r'(.*_unittest|.*_test_util)\.(cc|h)': [
     "+components/autofill/core/browser",
     "+components/autofill/core/common",
diff --git a/components/webdata/common/web_data_request_manager.cc b/components/webdata/common/web_data_request_manager.cc
index f48d7abd..991ff741 100644
--- a/components/webdata/common/web_data_request_manager.cc
+++ b/components/webdata/common/web_data_request_manager.cc
@@ -9,7 +9,6 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/stl_util.h"
 #include "base/task_scheduler/post_task.h"
 #include "base/threading/sequenced_task_runner_handle.h"
@@ -133,27 +132,11 @@
 
   // Stop tracking the request. The request is already finished, so "stop
   // tracking" is the same as post-facto cancellation.
-  {
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "422460 "
-            "WebDataRequestManager::RequestCompletedOnThread::UpdateMap"));
-
-    CancelRequest(request->GetHandle());
-  }
+  CancelRequest(request->GetHandle());
 
   // Notify the consumer if needed.
   WebDataServiceConsumer* const consumer = request->GetConsumer();
   if (consumer) {
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "422460 "
-            "WebDataRequestManager::RequestCompletedOnThread::NotifyConsumer"));
-
     consumer->OnWebDataServiceRequestDone(request->GetHandle(),
                                           std::move(result));
   }
diff --git a/content/app/content_main_runner.cc b/content/app/content_main_runner.cc
index 14ae19e0..1aeaf64 100644
--- a/content/app/content_main_runner.cc
+++ b/content/app/content_main_runner.cc
@@ -35,7 +35,6 @@
 #include "base/process/memory.h"
 #include "base/process/process.h"
 #include "base/process/process_handle.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -521,11 +520,6 @@
     }
 #endif  // !OS_ANDROID
 
-#if !defined(OS_ANDROID)
-    if (delegate_ && delegate_->ShouldEnableProfilerRecording())
-      tracked_objects::ScopedTracker::Enable();
-#endif  // !OS_ANDROID
-
     int exit_code = 0;
     if (delegate_ && delegate_->BasicStartupComplete(&exit_code))
       return exit_code;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 0d32a38f..3def6aa 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -58,6 +58,7 @@
     "//content:resources",
     "//content/app/resources",
     "//content/app/strings",
+    "//content/browser/background_fetch:background_fetch_proto",
     "//content/browser/background_sync:background_sync_proto",
     "//content/browser/cache_storage:cache_storage_proto",
     "//content/browser/devtools:devtools_resources",
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index 556bed4..4b31641 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -447,6 +447,10 @@
   return description;
 }
 
+std::string BrowserAccessibilityAndroid::GetRoleString() const {
+  return ToString(GetRole());
+}
+
 base::string16 BrowserAccessibilityAndroid::GetRoleDescription() const {
   content::ContentClient* content_client = content::GetContentClient();
 
diff --git a/content/browser/accessibility/browser_accessibility_android.h b/content/browser/accessibility/browser_accessibility_android.h
index b8f6e56b..12a2c5ba 100644
--- a/content/browser/accessibility/browser_accessibility_android.h
+++ b/content/browser/accessibility/browser_accessibility_android.h
@@ -77,6 +77,8 @@
   base::string16 GetText() const override;
   base::string16 GetHint() const;
 
+  std::string GetRoleString() const;
+
   base::string16 GetRoleDescription() const;
 
   int GetItemIndex() const;
diff --git a/content/browser/accessibility/web_contents_accessibility_android.cc b/content/browser/accessibility/web_contents_accessibility_android.cc
index 1c40b9a2..7b44f98 100644
--- a/content/browser/accessibility/web_contents_accessibility_android.cc
+++ b/content/browser/accessibility/web_contents_accessibility_android.cc
@@ -698,6 +698,7 @@
 
   Java_WebContentsAccessibility_setAccessibilityNodeInfoKitKatAttributes(
       env, obj, info, is_root, node->IsEditableText(),
+      base::android::ConvertUTF8ToJavaString(env, node->GetRoleString()),
       base::android::ConvertUTF16ToJavaString(env, node->GetRoleDescription()),
       base::android::ConvertUTF16ToJavaString(env, node->GetHint()),
       node->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START),
@@ -886,9 +887,11 @@
     return false;
 
   // To behave similarly to an Android SeekBar, move by an increment of
-  // approximately 20%.
+  // approximately 5%.
   float original_value = value;
-  float delta = (max - min) / 5.0f;
+  float delta = (max - min) / 20.0f;
+  // Slider does not move if the delta value is less than 1.
+  delta = ((delta < 1) ? 1 : delta);
   value += (increment ? delta : -delta);
   value = std::max(std::min(value, max), min);
   if (value != original_value) {
diff --git a/content/browser/android/content_view_core.cc b/content/browser/android/content_view_core.cc
index 4ec9060..d855115e 100644
--- a/content/browser/android/content_view_core.cc
+++ b/content/browser/android/content_view_core.cc
@@ -1029,7 +1029,6 @@
       << "A ContentViewCore should be created with a valid WebContents.";
   ui::ViewAndroid* view_android = web_contents->GetView()->GetNativeView();
   view_android->SetDelegate(jview_android_delegate);
-  view_android->SetLayout(ui::ViewAndroid::LayoutParams::MatchParent());
 
   ui::WindowAndroid* window_android =
       reinterpret_cast<ui::WindowAndroid*>(jwindow_android);
diff --git a/content/browser/appcache/appcache_storage_impl.cc b/content/browser/appcache/appcache_storage_impl.cc
index 6cac373..f3df837 100644
--- a/content/browser/appcache/appcache_storage_impl.cc
+++ b/content/browser/appcache/appcache_storage_impl.cc
@@ -17,7 +17,6 @@
 #include "base/files/file_util.h"
 #include "base/location.h"
 #include "base/logging.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
@@ -291,8 +290,6 @@
 };
 
 void AppCacheStorageImpl::InitTask::Run() {
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("AppCacheStorageImpl::InitTask"));
   // If there is no sql database, ensure there is no disk cache either.
   if (!db_file_path_.empty() &&
       !base::PathExists(db_file_path_) &&
@@ -524,8 +521,6 @@
 };
 
 void AppCacheStorageImpl::CacheLoadTask::Run() {
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("AppCacheStorageImpl::CacheLoadTask"));
   success_ =
       database_->FindCache(cache_id_, &cache_record_) &&
       database_->FindGroup(cache_record_.group_id, &group_record_) &&
@@ -569,8 +564,6 @@
 };
 
 void AppCacheStorageImpl::GroupLoadTask::Run() {
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("AppCacheStorageImpl::GroupLoadTask"));
   success_ =
       database_->FindGroupForManifestUrl(manifest_url_, &group_record_) &&
       database_->FindCacheForGroup(group_record_.group_id, &cache_record_) &&
@@ -958,9 +951,6 @@
 };
 
 void AppCacheStorageImpl::FindMainResponseTask::Run() {
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "AppCacheStorageImpl::FindMainResponseTask"));
   // NOTE: The heuristics around choosing amoungst multiple candidates
   // is underspecified, and just plain not fully understood. This needs
   // to be refined.
@@ -1341,9 +1331,6 @@
 };
 
 void AppCacheStorageImpl::LazyUpdateLastAccessTimeTask::Run() {
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "AppCacheStorageImpl::LazyUpdateLastAccessTimeTask"));
   database_->LazyUpdateLastAccessTime(group_id_, last_access_time_);
 }
 
@@ -1361,9 +1348,6 @@
 
   // DatabaseTask:
   void Run() override {
-    tracked_objects::ScopedTracker tracking_profile(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "AppCacheStorageImpl::CommitLastAccessTimesTask"));
     database_->CommitLazyLastAccessTimes();
   }
 
@@ -1396,9 +1380,6 @@
 };
 
 void AppCacheStorageImpl::UpdateEvictionTimesTask::Run() {
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "AppCacheStorageImpl::UpdateEvictionTimes"));
   database_->UpdateEvictionTimes(group_id_,
                                  last_full_update_check_time_,
                                  first_evictable_error_time_);
diff --git a/content/browser/appcache/chrome_appcache_service.cc b/content/browser/appcache/chrome_appcache_service.cc
index 7d2fab2..e6d557a 100644
--- a/content/browser/appcache/chrome_appcache_service.cc
+++ b/content/browser/appcache/chrome_appcache_service.cc
@@ -5,7 +5,6 @@
 #include "content/browser/appcache/chrome_appcache_service.h"
 
 #include "base/files/file_path.h"
-#include "base/profiler/scoped_tracker.h"
 #include "content/browser/appcache/appcache_storage_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
@@ -26,10 +25,6 @@
     ResourceContext* resource_context,
     net::URLRequestContextGetter* request_context_getter,
     scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "477117 ChromeAppCacheService::InitializeOnIOThread"));
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   cache_path_ = cache_path;
diff --git a/content/browser/background_fetch/BUILD.gn b/content/browser/background_fetch/BUILD.gn
new file mode 100644
index 0000000..07c7108
--- /dev/null
+++ b/content/browser/background_fetch/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("background_fetch_proto") {
+  sources = [
+    "background_fetch.proto",
+  ]
+}
diff --git a/content/browser/background_fetch/background_fetch.proto b/content/browser/background_fetch/background_fetch.proto
new file mode 100644
index 0000000..0725408
--- /dev/null
+++ b/content/browser/background_fetch/background_fetch.proto
@@ -0,0 +1,16 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package content.proto;
+
+// Stores per-registration (as opposed to per-request) data.
+//
+// Next tag: 2
+message BackgroundFetchRegistration {
+  optional int64 creation_microseconds_since_unix_epoch = 1;
+}
\ No newline at end of file
diff --git a/content/browser/background_fetch/background_fetch_data_manager.cc b/content/browser/background_fetch/background_fetch_data_manager.cc
index ae3a6f0..ede6a3b 100644
--- a/content/browser/background_fetch/background_fetch_data_manager.cc
+++ b/content/browser/background_fetch/background_fetch_data_manager.cc
@@ -11,6 +11,8 @@
 #include "base/memory/ptr_util.h"
 #include "base/numerics/checked_math.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "content/browser/background_fetch/background_fetch.pb.h"
 #include "content/browser/background_fetch/background_fetch_constants.h"
 #include "content/browser/background_fetch/background_fetch_context.h"
 #include "content/browser/background_fetch/background_fetch_cross_origin_filter.h"
@@ -26,17 +28,26 @@
 
 // Service Worker DB UserData schema
 // =================================
+// Design doc:
+// https://docs.google.com/document/d/1-WPPTP909Gb5PnaBOKP58tPVLw2Fq0Ln-u1EBviIBns/edit
+//
 // - Each key will be stored twice by the Service Worker DB, once as a
 //   "REG_HAS_USER_DATA:", and once as a "REG_USER_DATA:" - see
 //   content/browser/service_worker/service_worker_database.cc for details.
 // - Integer values are serialized as a string by base::Int*ToString().
 //
 // key: "bgfetch_registration_" + <std::string 'registration_id'>
-// value: <TODO: BackgroundFetchOptions serialized as a string>
+// value: <std::string 'serialized content::proto::BackgroundFetchRegistration'>
 //
 // key: "bgfetch_request_" + <std::string 'registration_id'>
 //          + "_" + <int 'request_index'>
 // value: <TODO: FetchAPIRequest serialized as a string>
+//
+// key: "bgfetch_pending_request_"
+//          + <int64_t 'registration_creation_microseconds_since_unix_epoch'>
+//          + "_" + <std::string 'registration_id'>
+//          + "_" + <int 'request_index'>
+// value: ""
 
 namespace content {
 
@@ -45,6 +56,7 @@
 const char kSeparator[] = "_";  // Warning: registration IDs may contain these.
 const char kRegistrationKeyPrefix[] = "bgfetch_registration_";
 const char kRequestKeyPrefix[] = "bgfetch_request_";
+const char kPendingRequestKeyPrefix[] = "bgfetch_pending_request_";
 
 std::string RegistrationKey(
     const BackgroundFetchRegistrationId& registration_id) {
@@ -64,6 +76,40 @@
   return RequestKeyPrefix(registration_id) + base::IntToString(request_index);
 }
 
+std::string PendingRequestKeyPrefix(
+    int64_t registration_creation_microseconds_since_unix_epoch,
+    const BackgroundFetchRegistrationId& registration_id) {
+  // These keys are ordered by creation time rather than by registration_id, so
+  // the highest priority pending requests can be looked up by fetching the
+  // lexicographically smallest keys.
+  //
+  // Currently (pending crbug.com/741609) registrations within each
+  // StoragePartition are prioritised in simple FIFO order by creation time.
+  // Since the ordering must survive restarts, wall clock time is used, but that
+  // is not monotonically increasing, so the ordering is not exact, and the
+  // registration ID is appended to break ties in case the wall clock returns
+  // the same values more than once.
+  //
+  // On Nov 20 2286 17:46:39 the microseconds will transition from 9999999999999
+  // to 10000000000000 and pending requests will briefly sort incorrectly.
+  return kPendingRequestKeyPrefix +
+         base::Int64ToString(
+             registration_creation_microseconds_since_unix_epoch) +
+         kSeparator + registration_id.id() + kSeparator;
+}
+
+std::string PendingRequestKey(
+    int64_t registration_creation_microseconds_since_unix_epoch,
+    const BackgroundFetchRegistrationId& registration_id,
+    int request_index) {
+  // In addition to the ordering from PendingRequestKeyPrefix, the requests
+  // within each registration should be prioritized according to their index.
+  return PendingRequestKeyPrefix(
+             registration_creation_microseconds_since_unix_epoch,
+             registration_id) +
+         base::IntToString(request_index);
+}
+
 enum class DatabaseStatus { kOk, kFailed, kNotFound };
 
 DatabaseStatus ToDatabaseStatus(ServiceWorkerStatusCode status) {
@@ -211,15 +257,37 @@
   }
 
   void StoreRegistration() {
-    // TODO(crbug.com/757760): Serialize actual values for these entries.
+    int64_t registration_creation_microseconds_since_unix_epoch =
+        (base::Time::Now() - base::Time::UnixEpoch()).InMicroseconds();
+
     std::vector<std::pair<std::string, std::string>> entries;
-    entries.reserve(requests_.size() + 1);
+    entries.reserve(requests_.size() * 2 + 1);
+
+    // First serialize per-registration (as opposed to per-request) data.
+    // TODO(crbug.com/757760): Serialize BackgroundFetchOptions as part of this.
+    proto::BackgroundFetchRegistration registration_proto;
+    registration_proto.set_creation_microseconds_since_unix_epoch(
+        registration_creation_microseconds_since_unix_epoch);
+    std::string serialized_registration_proto;
+    if (!registration_proto.SerializeToString(&serialized_registration_proto)) {
+      // TODO(johnme): Log failures to UMA.
+      std::move(callback_).Run(
+          blink::mojom::BackgroundFetchError::STORAGE_ERROR);
+      Finished();  // Destroys |this|.
+      return;
+    }
     entries.emplace_back(RegistrationKey(registration_id_),
-                         "TODO: Serialize BackgroundFetchOptions as value");
+                         std::move(serialized_registration_proto));
+
     // Signed integers are used for request indexes to avoid unsigned gotchas.
     for (int i = 0; i < base::checked_cast<int>(requests_.size()); i++) {
+      // TODO(crbug.com/757760): Serialize actual values for these entries.
       entries.emplace_back(RequestKey(registration_id_, i),
                            "TODO: Serialize FetchAPIRequest as value");
+      entries.emplace_back(
+          PendingRequestKey(registration_creation_microseconds_since_unix_epoch,
+                            registration_id_, i),
+          std::string());
     }
 
     service_worker_context()->StoreRegistrationUserData(
@@ -265,15 +333,40 @@
         callback_(std::move(callback)),
         weak_factory_(this) {}
 
+  ~DeleteRegistrationTask() override = default;
+
   void Start() override {
-    service_worker_context()->ClearRegistrationUserDataByKeyPrefixes(
+    // Get the registration creation time so we can delete any pending requests.
+    service_worker_context()->GetRegistrationUserData(
         registration_id_.service_worker_registration_id(),
-        {RegistrationKey(registration_id_), RequestKeyPrefix(registration_id_)},
-        base::Bind(&DeleteRegistrationTask::DidDeleteRegistration,
+        {RegistrationKey(registration_id_)},
+        base::Bind(&DeleteRegistrationTask::DidGetRegistration,
                    weak_factory_.GetWeakPtr()));
   }
 
  private:
+  void DidGetRegistration(const std::vector<std::string>& data,
+                          ServiceWorkerStatusCode status) {
+    std::vector<std::string> prefixes_to_clear = {
+        RegistrationKey(registration_id_), RequestKeyPrefix(registration_id_)};
+
+    if (status == SERVICE_WORKER_OK) {
+      DCHECK_EQ(1u, data.size());
+      proto::BackgroundFetchRegistration registration_proto;
+      if (registration_proto.ParseFromString(data[0]) &&
+          registration_proto.has_creation_microseconds_since_unix_epoch()) {
+        prefixes_to_clear.emplace_back(PendingRequestKeyPrefix(
+            registration_proto.creation_microseconds_since_unix_epoch(),
+            registration_id_));
+      }
+    }
+
+    service_worker_context()->ClearRegistrationUserDataByKeyPrefixes(
+        registration_id_.service_worker_registration_id(), prefixes_to_clear,
+        base::Bind(&DeleteRegistrationTask::DidDeleteRegistration,
+                   weak_factory_.GetWeakPtr()));
+  }
+
   void DidDeleteRegistration(ServiceWorkerStatusCode status) {
     switch (ToDatabaseStatus(status)) {
       case DatabaseStatus::kOk:
diff --git a/content/browser/background_fetch/background_fetch_data_manager.h b/content/browser/background_fetch/background_fetch_data_manager.h
index 8e9892c7..09e33f7 100644
--- a/content/browser/background_fetch/background_fetch_data_manager.h
+++ b/content/browser/background_fetch/background_fetch_data_manager.h
@@ -38,8 +38,7 @@
 // Service Worker database (except for deletions, e.g. it's safe for the Service
 // Worker code to remove a ServiceWorkerRegistration and all its keys).
 //
-// Schema design doc:
-// https://docs.google.com/document/d/1-WPPTP909Gb5PnaBOKP58tPVLw2Fq0Ln-u1EBviIBns/edit
+// Storage schema is documented in the .cc file.
 class CONTENT_EXPORT BackgroundFetchDataManager {
  public:
   using CreateRegistrationCallback =
diff --git a/content/browser/background_fetch/background_fetch_data_manager_unittest.cc b/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
index 61420e5..aa6b75a4 100644
--- a/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
@@ -24,8 +24,11 @@
 
 const char kExampleId[] = "my-example-id";
 
-const size_t kUserDataKeysPerRegistration = 1u;  // Not including requests.
-const size_t kUserDataKeysPerRequest = 1u;
+// A "bgfetch_registration_" per registration (not including keys for requests).
+const size_t kUserDataKeysPerRegistration = 1u;
+// A "bgfetch_request_" and "bgfetch_pending_request_" per request. See schema
+// documentation in background_fetch_data_manager.cc.
+const size_t kUserDataKeysPerRequest = 2u;
 
 }  // namespace
 
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index e1eabbf..2951fb7 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -1459,6 +1459,8 @@
   if (IsUsingMus()) {
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kIsRunningInMash);
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        switches::kEnableSurfaceSynchronization);
   }
 #endif
 
diff --git a/content/browser/browser_thread_impl.cc b/content/browser/browser_thread_impl.cc
index 8756c06c..8bb84ee 100644
--- a/content/browser/browser_thread_impl.cc
+++ b/content/browser/browser_thread_impl.cc
@@ -14,7 +14,6 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/run_loop.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/platform_thread.h"
@@ -657,7 +656,6 @@
   // Profiler to track potential contention on |globals.lock|. This only does
   // real work on canary and local dev builds, so the cost of having this here
   // should be minimal.
-  tracked_objects::ScopedTracker tracking_profile(FROM_HERE);
   base::AutoLock lock(globals.lock);
   for (int i = 0; i < ID_COUNT; ++i) {
     if (globals.task_runners[i] &&
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index 98ac1d2..e5d440f 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -12,7 +12,6 @@
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/simple_thread.h"
 #include "base/threading/thread.h"
@@ -460,13 +459,6 @@
             gpu_channel_host, gpu::kNullSurfaceHandle, need_alpha_channel,
             false /* support_stencil */, support_locking, nullptr,
             ui::command_buffer_metrics::BROWSER_WORKER_CONTEXT);
-        // TODO(vadimt): Remove ScopedTracker below once crbug.com/125248 is
-        // fixed. Tracking time in BindToCurrentThread.
-        tracked_objects::ScopedTracker tracking_profile(
-            FROM_HERE_WITH_EXPLICIT_FUNCTION(
-                "125248"
-                " GpuProcessTransportFactory::EstablishedGpuChannel"
-                "::Worker"));
         if (!shared_worker_context_provider_->BindToCurrentThread())
           shared_worker_context_provider_ = nullptr;
       }
@@ -487,13 +479,6 @@
             support_stencil, support_locking,
             shared_worker_context_provider_.get(),
             ui::command_buffer_metrics::DISPLAY_COMPOSITOR_ONSCREEN_CONTEXT);
-        // TODO(vadimt): Remove ScopedTracker below once crbug.com/125248 is
-        // fixed. Tracking time in BindToCurrentThread.
-        tracked_objects::ScopedTracker tracking_profile(
-            FROM_HERE_WITH_EXPLICIT_FUNCTION(
-                "125248"
-                " GpuProcessTransportFactory::EstablishedGpuChannel"
-                "::Compositor"));
         // On Mac, GpuCommandBufferMsg_SwapBuffersCompleted must be handled in
         // a nested run loop during resize.
         context_provider->SetDefaultTaskRunner(resize_task_runner_);
@@ -949,12 +934,6 @@
   shared_main_thread_contexts_->SetLostContextCallback(base::Bind(
       &GpuProcessTransportFactory::OnLostMainThreadSharedContextInsideCallback,
       callback_factory_.GetWeakPtr()));
-  // TODO(vadimt): Remove ScopedTracker below once crbug.com/125248 is
-  // fixed. Tracking time in BindToCurrentThread.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "125248"
-          " GpuProcessTransportFactory::SharedMainThreadContextProvider"));
   if (!shared_main_thread_contexts_->BindToCurrentThread())
     shared_main_thread_contexts_ = nullptr;
   return shared_main_thread_contexts_;
diff --git a/content/browser/compositor/surface_utils.cc b/content/browser/compositor/surface_utils.cc
index ace5a181..1ff3c1c 100644
--- a/content/browser/compositor/surface_utils.cc
+++ b/content/browser/compositor/surface_utils.cc
@@ -65,7 +65,6 @@
   // implement it here.
   callback.Run(SkBitmap(), content::READBACK_FAILED);
 #else
-  DCHECK(result->HasTexture());
   base::ScopedClosureRunner scoped_callback_runner(
       base::BindOnce(callback, SkBitmap(), content::READBACK_FAILED));
 
@@ -91,8 +90,12 @@
 
   viz::TextureMailbox texture_mailbox;
   std::unique_ptr<viz::SingleReleaseCallback> release_callback;
-  result->TakeTexture(&texture_mailbox, &release_callback);
-  DCHECK(texture_mailbox.IsTexture());
+  if (auto* mailbox = result->GetTextureMailbox()) {
+    texture_mailbox = *mailbox;
+    release_callback = result->TakeTextureOwnership();
+  }
+  if (!texture_mailbox.IsTexture())
+    return;
 
   ignore_result(scoped_callback_runner.Release());
 
@@ -115,17 +118,21 @@
     // Switch back to default colortype if format not supported.
     color_type = kN32_SkColorType;
   }
-  DCHECK(result->HasBitmap());
-  std::unique_ptr<SkBitmap> source = result->TakeBitmap();
-  DCHECK(source);
+  const SkBitmap source = result->AsSkBitmap();
+  if (!source.readyToDraw()) {
+    callback.Run(source, content::READBACK_FAILED);
+    return;
+  }
   SkBitmap scaled_bitmap;
-  if (source->width() != dst_size_in_pixel.width() ||
-      source->height() != dst_size_in_pixel.height()) {
+  if (source.width() != dst_size_in_pixel.width() ||
+      source.height() != dst_size_in_pixel.height()) {
+    // TODO(miu): Delete this logic here and use the new
+    // CopyOutputRequest::SetScaleRatio() API. http://crbug.com/760348
     scaled_bitmap = skia::ImageOperations::Resize(
-        *source, skia::ImageOperations::RESIZE_BEST, dst_size_in_pixel.width(),
+        source, skia::ImageOperations::RESIZE_BEST, dst_size_in_pixel.width(),
         dst_size_in_pixel.height());
   } else {
-    scaled_bitmap = *source;
+    scaled_bitmap = source;
   }
   if (color_type == kN32_SkColorType) {
     DCHECK_EQ(scaled_bitmap.colorType(), kN32_SkColorType);
@@ -191,7 +198,7 @@
     const SkColorType color_type,
     const ReadbackRequestCallback& callback,
     std::unique_ptr<viz::CopyOutputResult> result) {
-  if (result->IsEmpty() || result->size().IsEmpty()) {
+  if (result->IsEmpty()) {
     callback.Run(SkBitmap(), READBACK_FAILED);
     return;
   }
@@ -202,17 +209,21 @@
   else
     output_size_in_pixel = dst_size_in_pixel;
 
-  if (result->HasTexture()) {
-    // GPU-accelerated path
-    PrepareTextureCopyOutputResult(output_size_in_pixel, color_type, callback,
-                                   std::move(result));
-    return;
-  }
+  switch (result->format()) {
+    case viz::CopyOutputResult::Format::RGBA_TEXTURE:
+      // TODO(miu): Delete this code path. All callers want a SkBitmap result;
+      // so all requests should be changed to RGBA_BITMAP, and then not bother
+      // with the extra GLHelper readback infrastructure client-side.
+      // http://crbug.com/759310
+      PrepareTextureCopyOutputResult(output_size_in_pixel, color_type, callback,
+                                     std::move(result));
+      break;
 
-  DCHECK(result->HasBitmap());
-  // Software path
-  PrepareBitmapCopyOutputResult(output_size_in_pixel, color_type, callback,
-                                std::move(result));
+    case viz::CopyOutputResult::Format::RGBA_BITMAP:
+      PrepareBitmapCopyOutputResult(output_size_in_pixel, color_type, callback,
+                                    std::move(result));
+      break;
+  }
 }
 
 namespace surface_utils {
diff --git a/content/browser/dom_storage/dom_storage_area.cc b/content/browser/dom_storage/dom_storage_area.cc
index 10a1c2df..92cc299e 100644
--- a/content/browser/dom_storage/dom_storage_area.cc
+++ b/content/browser/dom_storage/dom_storage_area.cc
@@ -345,8 +345,10 @@
 void DOMStorageArea::ClearShallowCopiedCommitBatches() {
   if (is_shutdown_)
     return;
-  while (commit_batches_.back().type == CommitBatchHolder::TYPE_CLONE)
+  while (!commit_batches_.empty() &&
+         commit_batches_.back().type == CommitBatchHolder::TYPE_CLONE) {
     commit_batches_.pop_back();
+  }
   original_persistent_namespace_ids_ = nullptr;
 }
 
diff --git a/content/browser/dom_storage/dom_storage_area_unittest.cc b/content/browser/dom_storage/dom_storage_area_unittest.cc
index cd3aacd7..4fd7d350 100644
--- a/content/browser/dom_storage/dom_storage_area_unittest.cc
+++ b/content/browser/dom_storage/dom_storage_area_unittest.cc
@@ -132,6 +132,7 @@
     EXPECT_EQ(area->GetItem(kKey).string(), copy->GetItem(kKey).string());
   EXPECT_EQ(area->Key(0).string(), copy->Key(0).string());
   EXPECT_EQ(copy->map_.get(), area->map_.get());
+  copy->ClearShallowCopiedCommitBatches();
 
   // But will deep copy-on-write as needed.
   old_nullable_value = base::NullableString16(kValue, false);
@@ -249,6 +250,11 @@
   const bool values_cached = GetParam();
   area->SetCacheOnlyKeys(!values_cached);
 
+  scoped_refptr<DOMStorageArea> temp_copy;
+  temp_copy = area->ShallowCopy(2, std::string());
+  EXPECT_TRUE(temp_copy->commit_batches_.empty());
+  temp_copy->ClearShallowCopiedCommitBatches();
+
   // Check if shallow copy is consistent.
   base::string16 old_value;
   base::NullableString16 old_nullable_value;
@@ -258,7 +264,7 @@
   EXPECT_TRUE(area->HasUncommittedChanges());
   EXPECT_EQ(DOMStorageArea::CommitBatchHolder::TYPE_CURRENT_BATCH,
             area->commit_batches_.front().type);
-  copy = area->ShallowCopy(2, std::string());
+  copy = area->ShallowCopy(3, std::string());
   EXPECT_EQ(copy->map_.get(), area->map_.get());
   EXPECT_EQ(1u, copy->original_persistent_namespace_ids_->size());
   EXPECT_EQ(kNamespaceId, (*copy->original_persistent_namespace_ids_)[0]);
diff --git a/content/browser/dom_storage/local_storage_context_mojo.cc b/content/browser/dom_storage/local_storage_context_mojo.cc
index 0ee9799..80ceb4f 100644
--- a/content/browser/dom_storage/local_storage_context_mojo.cc
+++ b/content/browser/dom_storage/local_storage_context_mojo.cc
@@ -605,13 +605,13 @@
   filesystem::mojom::DirectoryPtr directory_clone;
   directory_->Clone(MakeRequest(&directory_clone));
 
-  auto options = leveldb::mojom::OpenOptions::New();
-  options->create_if_missing = true;
-  options->max_open_files = 0;  // use minimum
+  leveldb_env::Options options;
+  options.create_if_missing = true;
+  options.max_open_files = 0;  // use minimum
   // Default write_buffer_size is 4 MB but that might leave a 3.999
   // memory allocation in RAM from a log file recovery.
-  options->write_buffer_size = 64 * 1024;
-  options->shared_block_read_cache = leveldb::mojom::SharedReadCache::Web;
+  options.write_buffer_size = 64 * 1024;
+  options.block_cache = leveldb_env::SharedWebBlockCache();
   leveldb_service_->OpenWithOptions(
       std::move(options), std::move(directory_clone), "leveldb",
       memory_dump_id_, MakeRequest(&database_),
diff --git a/content/browser/dom_storage/local_storage_context_mojo_unittest.cc b/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
index b829354e4..82aa362 100644
--- a/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
+++ b/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
@@ -1084,7 +1084,7 @@
   }
 
   void OpenWithOptions(
-      leveldb::mojom::OpenOptionsPtr options,
+      const leveldb_env::Options& options,
       filesystem::mojom::DirectoryPtr,
       const std::string& dbname,
       const base::Optional<base::trace_event::MemoryAllocatorDumpGuid>&
diff --git a/content/browser/dom_storage/session_storage_database.cc b/content/browser/dom_storage/session_storage_database.cc
index 5615b5a0..7d9413f0 100644
--- a/content/browser/dom_storage/session_storage_database.cc
+++ b/content/browser/dom_storage/session_storage_database.cc
@@ -382,8 +382,10 @@
 
   // All leveldb databases are already dumped by leveldb_env::DBTracker. Add
   // an edge to avoid double counting.
-  pmd->AddSuballocation(mad->guid(),
-                        leveldb_env::DBTracker::GetMemoryDumpName(db_.get()));
+  auto* tracker_dump =
+      leveldb_env::DBTracker::GetOrCreateAllocatorDump(pmd, db_.get());
+  if (tracker_dump)
+    pmd->AddOwnershipEdge(mad->guid(), tracker_dump->guid());
 }
 
 bool SessionStorageDatabase::LazyOpen(bool create_if_needed) {
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 4539339..f7d0bbe 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -1155,8 +1155,7 @@
       // this case.
       new_entry->GetSSL() = SSLStatus();
 
-      if (new_entry->GetURL().SchemeIs(url::kHttpsScheme) &&
-          !rfh->GetParent()) {
+      if (params.url.SchemeIs(url::kHttpsScheme) && !rfh->GetParent()) {
         UMA_HISTOGRAM_BOOLEAN(
             "Navigation.SecureSchemeHasSSLStatus.NewPageInPageOriginMismatch",
             !!new_entry->GetSSL().certificate);
@@ -1169,7 +1168,7 @@
 
     update_virtual_url = new_entry->update_virtual_url_with_url();
 
-    if (new_entry->GetURL().SchemeIs(url::kHttpsScheme) && !rfh->GetParent()) {
+    if (params.url.SchemeIs(url::kHttpsScheme) && !rfh->GetParent()) {
       UMA_HISTOGRAM_BOOLEAN("Navigation.SecureSchemeHasSSLStatus.NewPageInPage",
                             !!new_entry->GetSSL().certificate);
     }
@@ -1193,7 +1192,7 @@
     update_virtual_url = new_entry->update_virtual_url_with_url();
     new_entry->GetSSL() = handle->ssl_status();
 
-    if (new_entry->GetURL().SchemeIs(url::kHttpsScheme) && !rfh->GetParent()) {
+    if (params.url.SchemeIs(url::kHttpsScheme) && !rfh->GetParent()) {
       UMA_HISTOGRAM_BOOLEAN(
           "Navigation.SecureSchemeHasSSLStatus.NewPagePendingEntryMatches",
           !!new_entry->GetSSL().certificate);
@@ -1220,7 +1219,7 @@
     update_virtual_url = needs_update;
     new_entry->GetSSL() = handle->ssl_status();
 
-    if (new_entry->GetURL().SchemeIs(url::kHttpsScheme) && !rfh->GetParent()) {
+    if (params.url.SchemeIs(url::kHttpsScheme) && !rfh->GetParent()) {
       UMA_HISTOGRAM_BOOLEAN(
           "Navigation.SecureSchemeHasSSLStatus.NewPageNoMatchingEntry",
           !!new_entry->GetSSL().certificate);
@@ -1298,7 +1297,7 @@
     if (!is_same_document)
       entry->GetSSL() = handle->ssl_status();
 
-    if (entry->GetURL().SchemeIs(url::kHttpsScheme) && !rfh->GetParent()) {
+    if (params.url.SchemeIs(url::kHttpsScheme) && !rfh->GetParent()) {
       bool has_cert = !!entry->GetSSL().certificate;
       if (is_same_document) {
         UMA_HISTOGRAM_BOOLEAN(
@@ -1337,7 +1336,7 @@
         entry->GetSSL() = handle->ssl_status();
     }
 
-    if (entry->GetURL().SchemeIs(url::kHttpsScheme) && !rfh->GetParent()) {
+    if (params.url.SchemeIs(url::kHttpsScheme) && !rfh->GetParent()) {
       bool has_cert = !!entry->GetSSL().certificate;
       if (is_same_document && was_restored) {
         UMA_HISTOGRAM_BOOLEAN(
@@ -1371,7 +1370,7 @@
     if (!is_same_document)
       entry->GetSSL() = handle->ssl_status();
 
-    if (entry->GetURL().SchemeIs(url::kHttpsScheme) && !rfh->GetParent()) {
+    if (params.url.SchemeIs(url::kHttpsScheme) && !rfh->GetParent()) {
       bool has_cert = !!entry->GetSSL().certificate;
       if (is_same_document) {
         UMA_HISTOGRAM_BOOLEAN(
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.cc b/content/browser/gpu/browser_gpu_channel_host_factory.cc
index 9f9759d..1aaee74 100644
--- a/content/browser/gpu/browser_gpu_channel_host_factory.cc
+++ b/content/browser/gpu/browser_gpu_channel_host_factory.cc
@@ -7,7 +7,6 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/location.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread_restrictions.h"
@@ -98,11 +97,6 @@
       main_task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
 
 void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "477117 "
-          "BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO"));
   GpuProcessHost* host = GpuProcessHost::Get();
   if (!host) {
     LOG(ERROR) << "Failed to launch GPU process.";
@@ -157,11 +151,6 @@
 void BrowserGpuChannelHostFactory::EstablishRequest::Wait() {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   {
-    // TODO(vadimt): Remove ScopedTracker below once crbug.com/125248 is fixed.
-    tracked_objects::ScopedTracker tracking_profile(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "125248 BrowserGpuChannelHostFactory::EstablishRequest::Wait"));
-
     // We're blocking the UI thread, which is generally undesirable.
     // In this case we need to wait for this before we can show any UI
     // /anyway/, so it won't cause additional jank.
@@ -316,11 +305,6 @@
   if (!pending_request_->channel_handle().mojo_handle.is_valid()) {
     DCHECK(!gpu_channel_.get());
   } else {
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/466866
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile1(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "466866 BrowserGpuChannelHostFactory::GpuChannelEstablished1"));
     GetContentClient()->SetGpuInfo(pending_request_->gpu_info());
     gpu_channel_ = gpu::GpuChannelHost::Create(
         this, gpu_client_id_, pending_request_->gpu_info(),
@@ -329,12 +313,6 @@
   }
   pending_request_ = NULL;
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/466866 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "466866 BrowserGpuChannelHostFactory::GpuChannelEstablished2"));
-
   std::vector<gpu::GpuChannelEstablishedCallback> established_callbacks;
   established_callbacks_.swap(established_callbacks);
   for (auto& callback : established_callbacks)
diff --git a/content/browser/image_capture/image_capture_impl.cc b/content/browser/image_capture/image_capture_impl.cc
index c3f8651..91865a78 100644
--- a/content/browser/image_capture/image_capture_impl.cc
+++ b/content/browser/image_capture/image_capture_impl.cc
@@ -11,9 +11,9 @@
 #include "content/browser/browser_main_loop.h"
 #include "content/browser/renderer_host/media/media_stream_manager.h"
 #include "content/browser/renderer_host/media/video_capture_manager.h"
-#include "content/common/media/media_stream_options.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_features.h"
+#include "content/public/common/media_stream_request.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/scoped_callback_runner.h"
 #include "media/capture/video/video_capture_device.h"
@@ -50,7 +50,7 @@
   const int session_id =
       media_stream_manager->VideoDeviceIdToSessionId(source_id);
 
-  if (session_id == StreamDeviceInfo::kNoId)
+  if (session_id == MediaStreamDevice::kNoId)
     return;
   media_stream_manager->video_capture_manager()->GetPhotoState(
       session_id, std::move(callback));
@@ -66,7 +66,7 @@
   const int session_id =
       media_stream_manager->VideoDeviceIdToSessionId(source_id);
 
-  if (session_id == StreamDeviceInfo::kNoId)
+  if (session_id == MediaStreamDevice::kNoId)
     return;
   media_stream_manager->video_capture_manager()->SetPhotoOptions(
       session_id, std::move(settings), std::move(callback));
@@ -80,7 +80,7 @@
   const int session_id =
       media_stream_manager->VideoDeviceIdToSessionId(source_id);
 
-  if (session_id == StreamDeviceInfo::kNoId)
+  if (session_id == MediaStreamDevice::kNoId)
     return;
   media_stream_manager->video_capture_manager()->TakePhoto(session_id,
                                                            std::move(callback));
diff --git a/content/browser/indexed_db/leveldb/leveldb_database.cc b/content/browser/indexed_db/leveldb/leveldb_database.cc
index ff185b09..bba3967 100644
--- a/content/browser/indexed_db/leveldb/leveldb_database.cc
+++ b/content/browser/indexed_db/leveldb/leveldb_database.cc
@@ -496,8 +496,10 @@
 
   // All leveldb databases are already dumped by leveldb_env::DBTracker. Add
   // an edge to avoid double counting.
-  pmd->AddSuballocation(dump->guid(),
-                        leveldb_env::DBTracker::GetMemoryDumpName(db_.get()));
+  auto* tracker_dump =
+      leveldb_env::DBTracker::GetOrCreateAllocatorDump(pmd, db_.get());
+  if (tracker_dump)
+    pmd->AddOwnershipEdge(dump->guid(), tracker_dump->guid());
 
   return true;
 }
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index 2f36a89..df2bd686 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -29,7 +29,6 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/task_scheduler/post_task.h"
@@ -695,11 +694,6 @@
     UMA_HISTOGRAM_SPARSE_SLOWLY(
         "Net.ErrorCodesForMainFrame3",
         -loader->request()->status().error());
-
-    // Record time to success and error for the most common errors, and for
-    // the aggregate remainder errors.
-    base::TimeDelta request_loading_time(
-        base::TimeTicks::Now() - loader->request()->creation_time());
     if (loader->request()->status().error() == net::ERR_ABORTED) {
       UMA_HISTOGRAM_CUSTOM_COUNTS("Net.ErrAborted.SentBytes",
                                   loader->request()->GetTotalSentBytes(), 1,
@@ -707,11 +701,6 @@
       UMA_HISTOGRAM_CUSTOM_COUNTS("Net.ErrAborted.ReceivedBytes",
                                   loader->request()->GetTotalReceivedBytes(), 1,
                                   50000000, 50);
-
-      if (delegate_) {
-        delegate_->OnAbortedFrameLoad(loader->request()->url(),
-                                      request_loading_time);
-      }
     }
 
     if (loader->request()->url().SchemeIsCryptographic()) {
@@ -850,10 +839,6 @@
     mojom::URLLoaderClientPtr url_loader_client,
     const net::NetworkTrafficAnnotationTag& traffic_annotation) {
   DCHECK(requester_info->IsRenderer() || requester_info->IsNavigationPreload());
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "477117 ResourceDispatcherHostImpl::OnRequestResource"));
   // When logging time-to-network only care about main frame and non-transfer
   // navigations.
   // PlzNavigate: this log happens from NavigationRequest::OnRequestStarted
@@ -1455,10 +1440,6 @@
     mojom::URLLoaderRequest mojo_request,
     mojom::URLLoaderClientPtr url_loader_client) {
   DCHECK(requester_info->IsRenderer() || requester_info->IsNavigationPreload());
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456331 ResourceDispatcherHostImpl::CreateResourceHandler"));
   // Construct the IPC resource handler.
   std::unique_ptr<ResourceHandler> handler;
   if (sync_result_handler) {
@@ -2414,11 +2395,6 @@
 void ResourceDispatcherHostImpl::StartLoading(
     ResourceRequestInfoImpl* info,
     std::unique_ptr<ResourceLoader> loader) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456331 ResourceDispatcherHostImpl::StartLoading"));
-
   ResourceLoader* loader_ptr = loader.get();
   DCHECK(pending_loaders_[info->GetGlobalRequestID()] == nullptr);
   pending_loaders_[info->GetGlobalRequestID()] = std::move(loader);
diff --git a/content/browser/media/capture/aura_window_capture_machine.cc b/content/browser/media/capture/aura_window_capture_machine.cc
index eaa46f90..22a4fba8 100644
--- a/content/browser/media/capture/aura_window_capture_machine.cc
+++ b/content/browser/media/capture/aura_window_capture_machine.cc
@@ -24,7 +24,6 @@
 #include "services/device/public/interfaces/wake_lock_provider.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "skia/ext/image_operations.h"
-#include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
@@ -237,7 +236,8 @@
   if (oracle_proxy_->ObserveEventAndDecideCapture(
           event, gfx::Rect(), event_time, &frame, &capture_frame_cb)) {
     std::unique_ptr<viz::CopyOutputRequest> request =
-        viz::CopyOutputRequest::CreateRequest(
+        std::make_unique<viz::CopyOutputRequest>(
+            viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
             base::BindOnce(&AuraWindowCaptureMachine::DidCopyOutput,
                            weak_factory_.GetWeakPtr(), std::move(frame),
                            event_time, start_time, capture_frame_cb));
@@ -301,11 +301,7 @@
     return false;
   }
   if (result->IsEmpty()) {
-    VLOG(1) << "CopyOutputRequest failed: No texture or bitmap in result.";
-    return false;
-  }
-  if (result->size().IsEmpty()) {
-    VLOG(1) << "CopyOutputRequest failed: Zero-area texture/bitmap result.";
+    VLOG(1) << "CopyOutputRequest failed: Empty result.";
     return false;
   }
   DCHECK(video_frame);
@@ -336,8 +332,10 @@
 
   viz::TextureMailbox texture_mailbox;
   std::unique_ptr<viz::SingleReleaseCallback> release_callback;
-  result->TakeTexture(&texture_mailbox, &release_callback);
-  DCHECK(texture_mailbox.IsTexture());
+  if (auto* mailbox = result->GetTextureMailbox()) {
+    texture_mailbox = *mailbox;
+    release_callback = result->TakeTextureOwnership();
+  }
   if (!texture_mailbox.IsTexture()) {
     VLOG(1) << "Aborting capture: Failed to take texture from mailbox.";
     return false;
diff --git a/content/browser/media/capture/desktop_capture_device.cc b/content/browser/media/capture/desktop_capture_device.cc
index 7e27e9a..2c733c7 100644
--- a/content/browser/media/capture/desktop_capture_device.cc
+++ b/content/browser/media/capture/desktop_capture_device.cc
@@ -128,7 +128,7 @@
   webrtc::DesktopSize previous_frame_size_;
 
   // Determines the size of frames to deliver to the |client_|.
-  std::unique_ptr<media::CaptureResolutionChooser> resolution_chooser_;
+  media::CaptureResolutionChooser resolution_chooser_;
 
   // DesktopFrame into which captured frames are down-scaled and/or letterboxed,
   // depending upon the caller's requested capture capabilities. If frames can
@@ -193,9 +193,13 @@
 
   client_ = std::move(client);
   requested_frame_rate_ = params.requested_format.frame_rate;
-  resolution_chooser_.reset(new media::CaptureResolutionChooser(
-      params.requested_format.frame_size,
-      params.resolution_change_policy));
+
+  // Pass the min/max resolution and fixed aspect ratio settings from |params|
+  // to the CaptureResolutionChooser.
+  const auto constraints = params.SuggestConstraints();
+  resolution_chooser_.SetConstraints(constraints.min_frame_size,
+                                     constraints.max_frame_size,
+                                     constraints.fixed_aspect_ratio);
 
   DCHECK(!wake_lock_);
   // Gets a service_manager::Connector first, then request a wake lock.
@@ -261,15 +265,15 @@
   // determine the new output size.
   if (!previous_frame_size_.equals(frame->size())) {
     output_frame_.reset();
-    resolution_chooser_->SetSourceSize(gfx::Size(frame->size().width(),
-                                                 frame->size().height()));
+    resolution_chooser_.SetSourceSize(
+        gfx::Size(frame->size().width(), frame->size().height()));
     previous_frame_size_ = frame->size();
   }
   // Align to 2x2 pixel boundaries, as required by OnIncomingCapturedData() so
   // it can convert the frame to I420 format.
   webrtc::DesktopSize output_size(
-      resolution_chooser_->capture_size().width() & ~1,
-      resolution_chooser_->capture_size().height() & ~1);
+      resolution_chooser_.capture_size().width() & ~1,
+      resolution_chooser_.capture_size().height() & ~1);
   if (output_size.is_empty()) {
     // Even RESOLUTION_POLICY_ANY_WITHIN_LIMIT is used, a non-empty size should
     // be guaranteed.
diff --git a/content/browser/net/quota_policy_cookie_store.cc b/content/browser/net/quota_policy_cookie_store.cc
index b404b05..ec7b4b8 100644
--- a/content/browser/net/quota_policy_cookie_store.cc
+++ b/content/browser/net/quota_policy_cookie_store.cc
@@ -12,7 +12,6 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/memory/ref_counted.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/sequenced_task_runner.h"
 #include "base/task_scheduler/post_task.h"
 #include "content/public/browser/browser_thread.h"
@@ -138,10 +137,6 @@
 
 std::unique_ptr<net::CookieStore> CreateCookieStore(
     const CookieStoreConfig& config) {
-  // TODO(bcwhite): Remove ScopedTracker below once crbug.com/483686 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("483686 content::CreateCookieStore"));
-
   std::unique_ptr<net::CookieMonster> cookie_monster;
 
   if (config.path.empty()) {
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index 700109a..c35725e9 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -125,7 +125,8 @@
   }
 
   std::unique_ptr<viz::CopyOutputRequest> request =
-      viz::CopyOutputRequest::CreateRequest(
+      std::make_unique<viz::CopyOutputRequest>(
+          viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
           base::BindOnce(&CopyFromCompositingSurfaceHasResult, output_size,
                          preferred_color_type, callback));
   if (!src_subrect.IsEmpty())
@@ -142,8 +143,10 @@
     return;
   }
 
-  std::unique_ptr<viz::CopyOutputRequest> request =
-      viz::CopyOutputRequest::CreateRequest(base::BindOnce(
+  std::unique_ptr<viz::CopyOutputRequest> request = std::make_unique<
+      viz::CopyOutputRequest>(
+      viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
+      base::BindOnce(
           &DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo,
           AsWeakPtr(),  // For caching the ReadbackYUVInterface on this class.
           nullptr, std::move(target), callback));
@@ -347,10 +350,12 @@
   }
 
   std::unique_ptr<viz::CopyOutputRequest> request =
-      viz::CopyOutputRequest::CreateRequest(base::BindOnce(
-          &DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo,
-          AsWeakPtr(), subscriber_texture, frame,
-          base::Bind(callback, present_time)));
+      std::make_unique<viz::CopyOutputRequest>(
+          viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
+          base::BindOnce(
+              &DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo,
+              AsWeakPtr(), subscriber_texture, frame,
+              base::Bind(callback, present_time)));
   // Setting the source in this copy request asks that the layer abort any prior
   // uncommitted copy requests made on behalf of the same frame subscriber.
   // This will not affect any of the copy requests spawned elsewhere from
@@ -579,9 +584,6 @@
     gl_helper->GenerateSyncToken(&sync_token);
   }
   if (release_callback) {
-    // A release callback means the texture came from the compositor, so there
-    // should be no |subscriber_texture|.
-    DCHECK(!subscriber_texture.get());
     const bool lost_resource = !sync_token.HasData();
     release_callback->Run(sync_token, lost_resource);
   }
@@ -604,8 +606,6 @@
     return;
   if (result->IsEmpty())
     return;
-  if (result->size().IsEmpty())
-    return;
 
   // Compute the dest size we want after the letterboxing resize. Make the
   // coordinates and sizes even because we letterbox in YUV space
@@ -621,19 +621,17 @@
   if (region_in_frame.IsEmpty())
     return;
 
-  if (!result->HasTexture()) {
-    DCHECK(result->HasBitmap());
-    std::unique_ptr<SkBitmap> bitmap = result->TakeBitmap();
+  if (result->format() == viz::CopyOutputResult::Format::RGBA_BITMAP) {
+    SkBitmap bitmap = result->AsSkBitmap();
     // Scale the bitmap to the required size, if necessary.
     SkBitmap scaled_bitmap;
     if (result->size() != region_in_frame.size()) {
       skia::ImageOperations::ResizeMethod method =
           skia::ImageOperations::RESIZE_GOOD;
-      scaled_bitmap = skia::ImageOperations::Resize(*bitmap.get(), method,
-                                                    region_in_frame.width(),
-                                                    region_in_frame.height());
+      scaled_bitmap = skia::ImageOperations::Resize(
+          bitmap, method, region_in_frame.width(), region_in_frame.height());
     } else {
-      scaled_bitmap = *bitmap.get();
+      scaled_bitmap = bitmap;
     }
 
     media::CopyRGBToVideoFrame(
@@ -654,8 +652,12 @@
 
   viz::TextureMailbox texture_mailbox;
   std::unique_ptr<viz::SingleReleaseCallback> release_callback;
-  result->TakeTexture(&texture_mailbox, &release_callback);
-  DCHECK(texture_mailbox.IsTexture());
+  if (auto* mailbox = result->GetTextureMailbox()) {
+    texture_mailbox = *mailbox;
+    release_callback = result->TakeTextureOwnership();
+  }
+  if (!texture_mailbox.IsTexture())
+    return;
 
   gfx::Rect result_rect(result->size());
 
diff --git a/content/browser/renderer_host/input/input_router.h b/content/browser/renderer_host/input/input_router.h
index e1f30c9..07d8fe7 100644
--- a/content/browser/renderer_host/input/input_router.h
+++ b/content/browser/renderer_host/input/input_router.h
@@ -10,6 +10,7 @@
 #include "content/browser/renderer_host/input/gesture_event_queue.h"
 #include "content/browser/renderer_host/input/touch_event_queue.h"
 #include "content/common/input/input_event_ack_state.h"
+#include "content/common/widget.mojom.h"
 #include "content/public/browser/native_web_keyboard_event.h"
 #include "ipc/ipc_listener.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
@@ -61,6 +62,9 @@
   virtual cc::TouchAction AllowedTouchAction() = 0;
 
   virtual void SetForceEnableZoom(bool enabled) = 0;
+
+  // Associate this InputRouter with a remote host channel.
+  virtual void BindHost(mojom::WidgetInputHandlerHostRequest request) = 0;
 };
 
 }  // namespace content
diff --git a/content/browser/renderer_host/input/input_router_impl.cc b/content/browser/renderer_host/input/input_router_impl.cc
index 7c51daa..57653f0 100644
--- a/content/browser/renderer_host/input/input_router_impl.cc
+++ b/content/browser/renderer_host/input/input_router_impl.cc
@@ -110,6 +110,7 @@
       wheel_event_queue_(this, wheel_scroll_latching_enabled_),
       gesture_event_queue_(this, this, config.gesture_config),
       device_scale_factor_(1.f),
+      host_binding_(this),
       weak_ptr_factory_(this) {
   weak_this_ = weak_ptr_factory_.GetWeakPtr();
 
@@ -221,17 +222,54 @@
   return touch_action_filter_.allowed_touch_action();
 }
 
+void InputRouterImpl::BindHost(mojom::WidgetInputHandlerHostRequest request) {
+  host_binding_.Close();
+  host_binding_.Bind(std::move(request));
+}
+
+void InputRouterImpl::CancelTouchTimeout() {
+  touch_event_queue_->SetAckTimeoutEnabled(false);
+}
+
+void InputRouterImpl::SetWhiteListedTouchAction(cc::TouchAction touch_action,
+                                                uint32_t unique_touch_event_id,
+                                                InputEventAckState state) {
+  // TODO(hayleyferr): Catch the cases that we have filtered out sending the
+  // touchstart.
+
+  touch_action_filter_.OnSetWhiteListedTouchAction(touch_action);
+  client_->OnSetWhiteListedTouchAction(touch_action);
+}
+
+void InputRouterImpl::DidOverscroll(const ui::DidOverscrollParams& params) {
+  client_->DidOverscroll(params);
+}
+
+void InputRouterImpl::DidStopFlinging() {
+  DCHECK_GT(active_renderer_fling_count_, 0);
+  // Note that we're only guaranteed to get a fling end notification from the
+  // renderer, not from any other consumers. Consequently, the GestureEventQueue
+  // cannot use this bookkeeping for logic like tap suppression.
+  --active_renderer_fling_count_;
+  client_->DidStopFlinging();
+}
+
+void InputRouterImpl::ImeCancelComposition() {
+  client_->OnImeCancelComposition();
+}
+
+void InputRouterImpl::ImeCompositionRangeChanged(
+    const gfx::Range& range,
+    const std::vector<gfx::Rect>& bounds) {
+  client_->OnImeCompositionRangeChanged(range, bounds);
+}
+
 bool InputRouterImpl::OnMessageReceived(const IPC::Message& message) {
   // TODO(dtapuska): Move these to mojo
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(InputRouterImpl, message)
-    IPC_MESSAGE_HANDLER(InputHostMsg_DidOverscroll, OnDidOverscroll)
     IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers,
                         OnHasTouchEventHandlers)
-    IPC_MESSAGE_HANDLER(InputHostMsg_SetTouchAction, OnSetTouchAction)
-    IPC_MESSAGE_HANDLER(InputHostMsg_SetWhiteListedTouchAction,
-                        OnSetWhiteListedTouchAction)
-    IPC_MESSAGE_HANDLER(InputHostMsg_DidStopFlinging, OnDidStopFlinging)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
 
@@ -473,7 +511,7 @@
   if (overscroll) {
     DCHECK_EQ(WebInputEvent::kGestureScrollUpdate,
               gesture_event.event.GetType());
-    OnDidOverscroll(overscroll.value());
+    DidOverscroll(overscroll.value());
   }
 
   // |gesture_event_queue_| will forward to OnGestureEventAck when appropriate.
@@ -496,15 +534,11 @@
   event.latency.AddNewLatencyFrom(latency);
 
   if (overscroll)
-    OnDidOverscroll(overscroll.value());
+    DidOverscroll(overscroll.value());
 
   wheel_event_queue_.ProcessMouseWheelAck(state, event.latency);
 }
 
-void InputRouterImpl::OnDidOverscroll(const ui::DidOverscrollParams& params) {
-  client_->DidOverscroll(params);
-}
-
 void InputRouterImpl::OnHasTouchEventHandlers(bool has_handlers) {
   TRACE_EVENT1("input", "InputRouterImpl::OnHasTouchEventHandlers",
                "has_handlers", has_handlers);
@@ -532,26 +566,6 @@
   UpdateTouchAckTimeoutEnabled();
 }
 
-void InputRouterImpl::OnSetWhiteListedTouchAction(
-    cc::TouchAction white_listed_touch_action,
-    uint32_t unique_touch_event_id,
-    InputEventAckState ack_result) {
-  // TODO(hayleyferr): Catch the cases that we have filtered out sending the
-  // touchstart.
-
-  touch_action_filter_.OnSetWhiteListedTouchAction(white_listed_touch_action);
-  client_->OnSetWhiteListedTouchAction(white_listed_touch_action);
-}
-
-void InputRouterImpl::OnDidStopFlinging() {
-  DCHECK_GT(active_renderer_fling_count_, 0);
-  // Note that we're only guaranteed to get a fling end notification from the
-  // renderer, not from any other consumers. Consequently, the GestureEventQueue
-  // cannot use this bookkeeping for logic like tap suppression.
-  --active_renderer_fling_count_;
-  client_->DidStopFlinging();
-}
-
 void InputRouterImpl::UpdateTouchAckTimeoutEnabled() {
   // kTouchActionNone will prevent scrolling, in which case the timeout serves
   // little purpose. It's also a strong signal that touch handling is critical
@@ -561,6 +575,4 @@
   touch_event_queue_->SetAckTimeoutEnabled(touch_ack_timeout_enabled);
 }
 
-
-
 }  // namespace content
diff --git a/content/browser/renderer_host/input/input_router_impl.h b/content/browser/renderer_host/input/input_router_impl.h
index f9c987f..0cff7da 100644
--- a/content/browser/renderer_host/input/input_router_impl.h
+++ b/content/browser/renderer_host/input/input_router_impl.h
@@ -25,7 +25,9 @@
 #include "content/common/input/input_event_ack_source.h"
 #include "content/common/input/input_event_stream_validator.h"
 #include "content/common/input/input_handler.mojom.h"
+#include "content/common/widget.mojom.h"
 #include "content/public/browser/native_web_keyboard_event.h"
+#include "mojo/public/cpp/bindings/binding.h"
 
 namespace ui {
 class LatencyInfo;
@@ -39,6 +41,10 @@
 class CONTENT_EXPORT InputRouterImplClient : public InputRouterClient {
  public:
   virtual mojom::WidgetInputHandler* GetWidgetInputHandler() = 0;
+  virtual void OnImeCancelComposition() = 0;
+  virtual void OnImeCompositionRangeChanged(
+      const gfx::Range& range,
+      const std::vector<gfx::Rect>& bounds) = 0;
 };
 
 // A default implementation for browser input event routing.
@@ -47,7 +53,8 @@
       public GestureEventQueueClient,
       public MouseWheelEventQueueClient,
       public TouchEventQueueClient,
-      public TouchpadTapSuppressionControllerClient {
+      public TouchpadTapSuppressionControllerClient,
+      public mojom::WidgetInputHandlerHost {
  public:
   InputRouterImpl(InputRouterImplClient* client,
                   InputDispositionHandler* disposition_handler,
@@ -69,6 +76,19 @@
   void SetFrameTreeNodeId(int frame_tree_node_id) override;
   void SetForceEnableZoom(bool enabled) override;
   cc::TouchAction AllowedTouchAction() override;
+  void BindHost(mojom::WidgetInputHandlerHostRequest request) override;
+
+  // InputHandlerHost impl
+  void CancelTouchTimeout() override;
+  void SetWhiteListedTouchAction(cc::TouchAction touch_action,
+                                 uint32_t unique_touch_event_id,
+                                 InputEventAckState state) override;
+  void DidOverscroll(const ui::DidOverscrollParams& params) override;
+  void DidStopFlinging() override;
+  void ImeCancelComposition() override;
+  void ImeCompositionRangeChanged(
+      const gfx::Range& range,
+      const std::vector<gfx::Rect>& bounds) override;
 
   // IPC::Listener
   bool OnMessageReceived(const IPC::Message& message) override;
@@ -147,13 +167,8 @@
       const base::Optional<cc::TouchAction>& touch_action);
 
   // IPC message handlers
-  void OnDidOverscroll(const ui::DidOverscrollParams& params);
   void OnHasTouchEventHandlers(bool has_handlers);
   void OnSetTouchAction(cc::TouchAction touch_action);
-  void OnSetWhiteListedTouchAction(cc::TouchAction white_listed_touch_action,
-                                   uint32_t unique_touch_event_id,
-                                   InputEventAckState ack_result);
-  void OnDidStopFlinging();
 
   // Called when a touch timeout-affecting bit has changed, in turn toggling the
   // touch ack timeout feature of the |touch_event_queue_| as appropriate. Input
@@ -189,6 +204,8 @@
   // Last touch position relative to screen. Used to compute movementX/Y.
   base::flat_map<int, gfx::Point> global_touch_position_;
 
+  mojo::Binding<mojom::WidgetInputHandlerHost> host_binding_;
+
   base::WeakPtr<InputRouterImpl> weak_this_;
   base::WeakPtrFactory<InputRouterImpl> weak_ptr_factory_;
 
diff --git a/content/browser/renderer_host/input/input_router_impl_unittest.cc b/content/browser/renderer_host/input/input_router_impl_unittest.cc
index 449c070b..5bf522a 100644
--- a/content/browser/renderer_host/input/input_router_impl_unittest.cc
+++ b/content/browser/renderer_host/input/input_router_impl_unittest.cc
@@ -127,6 +127,12 @@
     return &widget_input_handler_;
   }
 
+  void OnImeCompositionRangeChanged(
+      const gfx::Range& range,
+      const std::vector<gfx::Rect>& character_bounds) override {}
+
+  void OnImeCancelComposition() override {}
+
   std::vector<MockWidgetInputHandler::DispatchedEvent>
   GetAndResetDispatchedEvents() {
     return widget_input_handler_.GetAndResetDispatchedEvents();
@@ -436,16 +442,13 @@
         ViewHostMsg_HasTouchEventHandlers(0, has_handlers));
   }
 
-  void OnSetTouchAction(cc::TouchAction touch_action) {
-    input_router_->OnMessageReceived(
-        InputHostMsg_SetTouchAction(0, touch_action));
-  }
+  void CancelTouchTimeout() { input_router_->CancelTouchTimeout(); }
 
   void OnSetWhiteListedTouchAction(cc::TouchAction white_listed_touch_action,
                                    uint32_t unique_touch_event_id,
                                    InputEventAckState ack_result) {
-    input_router_->OnMessageReceived(InputHostMsg_SetWhiteListedTouchAction(
-        0, white_listed_touch_action, unique_touch_event_id, ack_result));
+    input_router_->SetWhiteListedTouchAction(white_listed_touch_action,
+                                             unique_touch_event_id, ack_result);
   }
 
   DispatchedEvents GetAndResetDispatchedEvents() {
@@ -1279,7 +1282,6 @@
   SendTouchEvent();
   DispatchedEvents touch_press_event2 = GetAndResetDispatchedEvents();
   EXPECT_EQ(1U, touch_press_event2.size());
-  OnSetTouchAction(cc::kTouchActionPanY);
   EXPECT_TRUE(TouchEventTimeoutEnabled());
   ReleaseTouchPoint(0);
   SendTouchEvent();
@@ -1293,7 +1295,7 @@
   PressTouchPoint(1, 1);
   SendTouchEvent();
   DispatchedEvents touch_press_event3 = GetAndResetDispatchedEvents();
-  OnSetTouchAction(cc::kTouchActionNone);
+  CancelTouchTimeout();
   EXPECT_FALSE(TouchEventTimeoutEnabled());
   ReleaseTouchPoint(0);
   SendTouchEvent();
@@ -1327,9 +1329,10 @@
   EXPECT_EQ(1U, dispatched_events.size());
 
   // kTouchActionNone should disable the timeout.
-  OnSetTouchAction(cc::kTouchActionNone);
-  CallCallback(std::move(dispatched_events.at(0).callback_),
-               INPUT_EVENT_ACK_STATE_CONSUMED);
+  CancelTouchTimeout();
+  CallCallbackWithTouchAction(std::move(dispatched_events.at(0).callback_),
+                              INPUT_EVENT_ACK_STATE_CONSUMED,
+                              cc::kTouchActionNone);
   EXPECT_EQ(1U, disposition_handler_->GetAndResetAckCount());
   EXPECT_FALSE(TouchEventTimeoutEnabled());
 
@@ -1380,7 +1383,7 @@
   SendTouchEvent();
   DispatchedEvents touch_press_event1 = GetAndResetDispatchedEvents();
   EXPECT_EQ(1U, touch_press_event1.size());
-  OnSetTouchAction(cc::kTouchActionNone);
+  CancelTouchTimeout();
   MoveTouchPoint(0, 50, 50);
   SendTouchEvent();
   DispatchedEvents touch_move_event1 = GetAndResetDispatchedEvents();
@@ -1404,8 +1407,9 @@
   DispatchedEvents touch_release_event2 = GetAndResetDispatchedEvents();
   EXPECT_EQ(1U, touch_release_event2.size());
 
-  CallCallback(std::move(touch_press_event1.at(0).callback_),
-               INPUT_EVENT_ACK_STATE_CONSUMED);
+  CallCallbackWithTouchAction(std::move(touch_press_event1.at(0).callback_),
+                              INPUT_EVENT_ACK_STATE_CONSUMED,
+                              cc::kTouchActionNone);
   CallCallback(std::move(touch_move_event1.at(0).callback_),
                INPUT_EVENT_ACK_STATE_CONSUMED);
 
@@ -1456,9 +1460,10 @@
   SendTouchEvent();
   DispatchedEvents touch_move_event1 = GetAndResetDispatchedEvents();
   EXPECT_EQ(1U, touch_move_event1.size());
-  OnSetTouchAction(cc::kTouchActionNone);
-  CallCallback(std::move(touch_press_event1.at(0).callback_),
-               INPUT_EVENT_ACK_STATE_CONSUMED);
+  CancelTouchTimeout();
+  CallCallbackWithTouchAction(std::move(touch_press_event1.at(0).callback_),
+                              INPUT_EVENT_ACK_STATE_CONSUMED,
+                              cc::kTouchActionNone);
   CallCallback(std::move(touch_move_event1.at(0).callback_),
                INPUT_EVENT_ACK_STATE_CONSUMED);
 
@@ -1514,15 +1519,16 @@
   SendTouchEvent();
   MoveTouchPoint(0, 50, 50);
   SendTouchEvent();
-  OnSetTouchAction(cc::kTouchActionNone);
+  CancelTouchTimeout();
   ReleaseTouchPoint(0);
   SendTouchEvent();
   DispatchedEvents dispatched_events = GetAndResetDispatchedEvents();
   EXPECT_EQ(3U, dispatched_events.size());
 
   // Ensure we have touch-action:none, suppressing scroll events.
-  CallCallback(std::move(dispatched_events.at(0).callback_),
-               INPUT_EVENT_ACK_STATE_CONSUMED);
+  CallCallbackWithTouchAction(std::move(dispatched_events.at(0).callback_),
+                              INPUT_EVENT_ACK_STATE_CONSUMED,
+                              cc::kTouchActionNone);
   EXPECT_EQ(0U, GetAndResetDispatchedEvents().size());
   CallCallback(std::move(dispatched_events.at(1).callback_),
                INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
@@ -1596,11 +1602,12 @@
   // Sequence 1.
   PressTouchPoint(1, 1);
   SendTouchEvent();
-  OnSetTouchAction(cc::kTouchActionNone);
+  CancelTouchTimeout();
   DispatchedEvents dispatched_events = GetAndResetDispatchedEvents();
   EXPECT_EQ(1U, dispatched_events.size());
-  CallCallback(std::move(dispatched_events.at(0).callback_),
-               INPUT_EVENT_ACK_STATE_CONSUMED);
+  CallCallbackWithTouchAction(std::move(dispatched_events.at(0).callback_),
+                              INPUT_EVENT_ACK_STATE_CONSUMED,
+                              cc::kTouchActionNone);
 
   ReleaseTouchPoint(0);
   SendTouchEvent();
@@ -1670,11 +1677,12 @@
   // Sequence 1.
   PressTouchPoint(1, 1);
   SendTouchEvent();
-  OnSetTouchAction(cc::kTouchActionNone);
+  CancelTouchTimeout();
   DispatchedEvents dispatched_events = GetAndResetDispatchedEvents();
   EXPECT_EQ(1U, dispatched_events.size());
-  CallCallback(std::move(dispatched_events.at(0).callback_),
-               INPUT_EVENT_ACK_STATE_CONSUMED);
+  CallCallbackWithTouchAction(std::move(dispatched_events.at(0).callback_),
+                              INPUT_EVENT_ACK_STATE_CONSUMED,
+                              cc::kTouchActionNone);
 
   ReleaseTouchPoint(0);
   SendTouchEvent();
@@ -1881,7 +1889,7 @@
   overscroll.latest_overscroll_delta = gfx::Vector2dF(-7, 0);
   overscroll.current_fling_velocity = gfx::Vector2dF(-1, 0);
 
-  input_router_->OnMessageReceived(InputHostMsg_DidOverscroll(0, overscroll));
+  input_router_->DidOverscroll(overscroll);
   DidOverscrollParams client_overscroll = client_->GetAndResetOverscroll();
   EXPECT_EQ(overscroll.accumulated_overscroll,
             client_overscroll.accumulated_overscroll);
diff --git a/content/browser/renderer_host/input/legacy_input_router_impl.cc b/content/browser/renderer_host/input/legacy_input_router_impl.cc
index ff807e38..869f2a7 100644
--- a/content/browser/renderer_host/input/legacy_input_router_impl.cc
+++ b/content/browser/renderer_host/input/legacy_input_router_impl.cc
@@ -238,6 +238,11 @@
   device_scale_factor_ = device_scale_factor;
 }
 
+void LegacyInputRouterImpl::BindHost(
+    mojom::WidgetInputHandlerHostRequest request) {
+  NOTREACHED();
+}
+
 bool LegacyInputRouterImpl::OnMessageReceived(const IPC::Message& message) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(LegacyInputRouterImpl, message)
diff --git a/content/browser/renderer_host/input/legacy_input_router_impl.h b/content/browser/renderer_host/input/legacy_input_router_impl.h
index a48ea9a..4152ec49 100644
--- a/content/browser/renderer_host/input/legacy_input_router_impl.h
+++ b/content/browser/renderer_host/input/legacy_input_router_impl.h
@@ -72,6 +72,7 @@
   void NotifySiteIsMobileOptimized(bool is_mobile_optimized) override;
   bool HasPendingEvents() const override;
   void SetDeviceScaleFactor(float device_scale_factor) override;
+  void BindHost(mojom::WidgetInputHandlerHostRequest request) override;
 
   // IPC::Listener
   bool OnMessageReceived(const IPC::Message& message) override;
diff --git a/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc b/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
index 17a745b..441aa8d 100644
--- a/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
+++ b/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
@@ -40,8 +40,6 @@
 
   MOCK_METHOD2(Opened, void(MediaStreamType, const int));
   MOCK_METHOD2(Closed, void(MediaStreamType, const int));
-  MOCK_METHOD2(DevicesEnumerated, void(MediaStreamType,
-                                       const StreamDeviceInfoArray&));
   MOCK_METHOD2(Aborted, void(MediaStreamType, int));
 
  private:
@@ -285,7 +283,7 @@
   WaitForOpenCompletion();
 
   // Access a non-opened device.
-  // This should fail and return an empty StreamDeviceInfo.
+  // This should fail and return an empty MediaStreamDevice.
   int invalid_session_id = session_id + 1;
   const MediaStreamDevice* device =
       manager_->GetOpenedDeviceById(invalid_session_id);
diff --git a/content/browser/renderer_host/media/media_devices_manager.cc b/content/browser/renderer_host/media/media_devices_manager.cc
index e6690a4..5426c18 100644
--- a/content/browser/renderer_host/media/media_devices_manager.cc
+++ b/content/browser/renderer_host/media/media_devices_manager.cc
@@ -27,7 +27,6 @@
 
 #if defined(OS_MACOSX)
 #include "base/bind_helpers.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "content/browser/browser_main_loop.h"
 #include "media/device_monitors/device_monitor_mac.h"
@@ -263,19 +262,9 @@
 #if defined(OS_MACOSX)
 void MediaDevicesManager::StartMonitoringOnUIThread() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  // TODO(erikchen): Remove ScopedTracker below once crbug.com/458404 is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "458404 MediaDevicesManager::GetBrowserMainLoop"));
   BrowserMainLoop* browser_main_loop = content::BrowserMainLoop::GetInstance();
   if (!browser_main_loop)
     return;
-
-  // TODO(erikchen): Remove ScopedTracker below once crbug.com/458404 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile3(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "458404 MediaDevicesManager::DeviceMonitorMac::StartMonitoring"));
   browser_main_loop->device_monitor_mac()->StartMonitoring();
 }
 #endif
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index 07d63d6..5d523aa 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -17,7 +17,6 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/power_monitor/power_monitor.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -1272,35 +1271,13 @@
   // callback threads that we don't own and don't want to attach.
   g_media_stream_manager_tls_ptr.Pointer()->Set(this);
 
-  // TODO(dalecurtis): Remove ScopedTracker below once crbug.com/457525 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "457525 MediaStreamManager::InitializeDeviceManagersOnIOThread 1"));
-
-  // TODO(dalecurtis): Remove ScopedTracker below once crbug.com/457525 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "457525 MediaStreamManager::InitializeDeviceManagersOnIOThread 2"));
   audio_input_device_manager_ = new AudioInputDeviceManager(audio_system_);
   audio_input_device_manager_->RegisterListener(this);
 
-  // TODO(dalecurtis): Remove ScopedTracker below once crbug.com/457525 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile3(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "457525 MediaStreamManager::InitializeDeviceManagersOnIOThread 3"));
   // We want to be notified of IO message loop destruction to delete the thread
   // and the device managers.
   base::MessageLoop::current()->AddDestructionObserver(this);
 
-  // TODO(dalecurtis): Remove ScopedTracker below once crbug.com/457525 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile4(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "457525 MediaStreamManager::InitializeDeviceManagersOnIOThread 4"));
-
   video_capture_manager_ =
       new VideoCaptureManager(std::move(video_capture_provider),
                               base::BindRepeating(&SendVideoCaptureLogMessage));
diff --git a/content/browser/renderer_host/media/media_stream_manager.h b/content/browser/renderer_host/media/media_stream_manager.h
index e546930..fdaa16f 100644
--- a/content/browser/renderer_host/media/media_stream_manager.h
+++ b/content/browser/renderer_host/media/media_stream_manager.h
@@ -45,6 +45,7 @@
 #include "content/common/media/media_devices.h"
 #include "content/common/media/media_stream_options.h"
 #include "content/public/browser/media_request_state.h"
+#include "content/public/common/media_stream_request.h"
 #include "media/base/video_facing.h"
 
 namespace media {
diff --git a/content/browser/renderer_host/media/media_stream_provider.h b/content/browser/renderer_host/media/media_stream_provider.h
index 6131c9c..324234e 100644
--- a/content/browser/renderer_host/media/media_stream_provider.h
+++ b/content/browser/renderer_host/media/media_stream_provider.h
@@ -17,7 +17,7 @@
 
 #include "base/memory/ref_counted.h"
 #include "content/common/content_export.h"
-#include "content/common/media/media_stream_options.h"
+#include "content/public/common/media_stream_request.h"
 
 namespace content {
 
diff --git a/content/browser/renderer_host/media/media_stream_ui_controller_unittest.cc b/content/browser/renderer_host/media/media_stream_ui_controller_unittest.cc
deleted file mode 100644
index 5805279..0000000
--- a/content/browser/renderer_host/media/media_stream_ui_controller_unittest.cc
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string>
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "base/run_loop.h"
-#include "content/browser/renderer_host/media/media_stream_settings_requester.h"
-#include "content/browser/renderer_host/media/media_stream_ui_controller.h"
-#include "content/common/media/media_stream_options.h"
-#include "content/public/common/media_stream_request.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::_;
-
-namespace content {
-
-class MediaStreamDeviceUIControllerTest
-    : public ::testing::Test,
-      public SettingsRequester {
- public:
-  MediaStreamDeviceUIControllerTest() {}
-
-  // Mock implementation of SettingsRequester.
-  // TODO(sergeyu): Move mock SettingsRequester to a separate class.
-  MOCK_METHOD2(DevicesAccepted, void(
-      const std::string&, const StreamDeviceInfoArray&));
-  MOCK_METHOD1(SettingsError, void(const std::string&));
-  MOCK_METHOD1(StopStreamFromUI, void(const std::string&));
-  void GetAvailableDevices(MediaStreamDevices* devices) override {
-    devices->push_back(MediaStreamDevice(MEDIA_DEVICE_AUDIO_CAPTURE,
-                                         "mic",
-                                         "mic_id",
-                                         0,
-                                         0));
-    devices->push_back(MediaStreamDevice(MEDIA_DEVICE_VIDEO_CAPTURE,
-                                         "camera",
-                                         "camera_id"));
-  }
-
- protected:
-  virtual void SetUp() {
-    ui_controller_.reset(new MediaStreamUIController(this));
-  }
-
-  virtual void TearDown() { base::RunLoop().RunUntilIdle(); }
-
-  void CreateDummyRequest(const std::string& label, bool audio, bool video) {
-    int dummy_render_process_id = 1;
-    int dummy_render_view_id = 1;
-    StreamOptions components(audio, video );
-    GURL security_origin;
-    ui_controller_->MakeUIRequest(label,
-                                  dummy_render_process_id,
-                                  dummy_render_view_id,
-                                  components,
-                                  security_origin,
-                                  MEDIA_GENERATE_STREAM,
-                                  std::string());
-  }
-
-  TestBrowserThreadBundle thread_bundle_;
-  std::unique_ptr<MediaStreamUIController> ui_controller_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MediaStreamDeviceUIControllerTest);
-};
-
-TEST_F(MediaStreamDeviceUIControllerTest, GenerateRequest) {
-  const std::string label = "dummy_label";
-  CreateDummyRequest(label, true, false);
-
-  // Expecting an error callback triggered by the non-existing
-  // RenderViewHostImpl.
-  EXPECT_CALL(*this, SettingsError(label));
-}
-
-TEST_F(MediaStreamDeviceUIControllerTest, GenerateAndRemoveRequest) {
-  const std::string label = "label";
-  CreateDummyRequest(label, true, false);
-
-  // Remove the current request, it should not crash.
-  ui_controller_->CancelUIRequest(label);
-}
-
-TEST_F(MediaStreamDeviceUIControllerTest, HandleRequestUsingFakeUI) {
-  ui_controller_->UseFakeUI(std::unique_ptr<MediaStreamUI>());
-
-  const std::string label = "label";
-  CreateDummyRequest(label, true, true);
-
-  // Remove the current request, it should not crash.
-  EXPECT_CALL(*this, DevicesAccepted(label, _));
-
-  base::RunLoop().RunUntilIdle();
-
-  ui_controller_->NotifyUIIndicatorDevicesClosed(label);
-}
-
-TEST_F(MediaStreamDeviceUIControllerTest, CreateRequestsAndCancelTheFirst) {
-  ui_controller_->UseFakeUI(std::unique_ptr<MediaStreamUI>());
-
-  // Create the first audio request.
-  const std::string label_1 = "label_1";
-  CreateDummyRequest(label_1, true, false);
-
-  // Create the second video request.
-  const std::string label_2 = "label_2";
-  CreateDummyRequest(label_2, false, true);
-
-  // Create the third audio and video request.
-  const std::string label_3 = "label_3";
-  CreateDummyRequest(label_3, true, true);
-
-  // Remove the first request which has been brought to the UI.
-  ui_controller_->CancelUIRequest(label_1);
-
-  // We should get callbacks from the rest of the requests.
-  EXPECT_CALL(*this, DevicesAccepted(label_2, _));
-  EXPECT_CALL(*this, DevicesAccepted(label_3, _));
-
-  base::RunLoop().RunUntilIdle();
-
-  ui_controller_->NotifyUIIndicatorDevicesClosed(label_2);
-  ui_controller_->NotifyUIIndicatorDevicesClosed(label_3);
-}
-
-TEST_F(MediaStreamDeviceUIControllerTest, CreateRequestsAndCancelTheLast) {
-  ui_controller_->UseFakeUI(std::unique_ptr<MediaStreamUI>());
-
-  // Create the first audio request.
-  const std::string label_1 = "label_1";
-  CreateDummyRequest(label_1, true, false);
-
-  // Create the second video request.
-  const std::string label_2 = "label_2";
-  CreateDummyRequest(label_2, false, true);
-
-  // Create the third audio and video request.
-  const std::string label_3 = "label_3";
-  CreateDummyRequest(label_3, true, true);
-
-  // Remove the last request which is pending in the queue.
-  ui_controller_->CancelUIRequest(label_3);
-
-  // We should get callbacks from the rest of the requests.
-  EXPECT_CALL(*this, DevicesAccepted(label_1, _));
-  EXPECT_CALL(*this, DevicesAccepted(label_2, _));
-
-  base::RunLoop().RunUntilIdle();
-
-  ui_controller_->NotifyUIIndicatorDevicesClosed(label_1);
-  ui_controller_->NotifyUIIndicatorDevicesClosed(label_2);
-}
-
-}  // namespace content
diff --git a/content/browser/renderer_host/media/video_capture_manager_unittest.cc b/content/browser/renderer_host/media/video_capture_manager_unittest.cc
index c62a3bd..582b388 100644
--- a/content/browser/renderer_host/media/video_capture_manager_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_manager_unittest.cc
@@ -22,7 +22,7 @@
 #include "content/browser/renderer_host/media/in_process_video_capture_provider.h"
 #include "content/browser/renderer_host/media/media_stream_provider.h"
 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
-#include "content/common/media/media_stream_options.h"
+#include "content/public/common/media_stream_request.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "media/capture/video/fake_video_capture_device_factory.h"
 #include "media/capture/video/video_capture_system_impl.h"
@@ -188,12 +188,12 @@
   void HandleEnumerationResult(
       const base::Closure& quit_closure,
       const media::VideoCaptureDeviceDescriptors& descriptors) {
-    StreamDeviceInfoArray devices;
+    MediaStreamDevices devices;
     for (const auto& descriptor : descriptors) {
-      devices.emplace_back(MEDIA_DEVICE_VIDEO_CAPTURE,
-                           descriptor.GetNameAndModel(), descriptor.device_id);
+      devices.emplace_back(MEDIA_DEVICE_VIDEO_CAPTURE, descriptor.device_id,
+                           descriptor.GetNameAndModel());
     }
-    devices_ = std::move(devices);
+    devices_ = devices;
     quit_closure.Run();
   }
 
@@ -298,7 +298,7 @@
   TestBrowserThreadBundle thread_bundle_;
   std::unique_ptr<MockFrameObserver> frame_observer_;
   WrappedDeviceFactory* video_capture_device_factory_;
-  StreamDeviceInfoArray devices_;
+  MediaStreamDevices devices_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(VideoCaptureManagerTest);
@@ -313,7 +313,7 @@
   EXPECT_CALL(*frame_observer_, OnStarted(_));
   EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _));
 
-  int video_session_id = vcm_->Open(devices_.front().device);
+  int video_session_id = vcm_->Open(devices_.front());
   VideoCaptureControllerID client_id = StartClient(video_session_id, true);
 
   StopClient(client_id);
@@ -330,7 +330,7 @@
     EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, i));
     EXPECT_CALL(*frame_observer_, OnStarted(_));
     EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, i));
-    int video_session_id = vcm_->Open(devices_.front().device);
+    int video_session_id = vcm_->Open(devices_.front());
     VideoCaptureControllerID client_id = StartClient(video_session_id, true);
 
     StopClient(client_id);
@@ -349,7 +349,7 @@
   EXPECT_CALL(*frame_observer_, OnStarted(_));
   EXPECT_CALL(*listener_, Aborted(MEDIA_DEVICE_VIDEO_CAPTURE, _));
 
-  int video_session_id = vcm_->Open(devices_.front().device);
+  int video_session_id = vcm_->Open(devices_.front());
   VideoCaptureControllerID client_id = StartClient(video_session_id, true);
 
   // Wait for device opened.
@@ -373,7 +373,7 @@
   EXPECT_CALL(observer,
               OnVideoCaptureStopped(WrappedDeviceFactory::DEFAULT_FACING));
 
-  int video_session_id = vcm_->Open(devices_.front().device);
+  int video_session_id = vcm_->Open(devices_.front());
   VideoCaptureControllerID client_id = StartClient(video_session_id, true);
 
   StopClient(client_id);
@@ -390,11 +390,11 @@
   EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(2);
   EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(2);
 
-  int video_session_id_first = vcm_->Open(devices_.front().device);
+  int video_session_id_first = vcm_->Open(devices_.front());
 
   // This should trigger an error callback with error code
   // 'kDeviceAlreadyInUse'.
-  int video_session_id_second = vcm_->Open(devices_.front().device);
+  int video_session_id_second = vcm_->Open(devices_.front());
   EXPECT_NE(video_session_id_first, video_session_id_second);
 
   vcm_->Close(video_session_id_first);
@@ -446,7 +446,7 @@
 
   InSequence s;
   EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _));
-  video_session_id = vcm_->Open(devices_.front().device);
+  video_session_id = vcm_->Open(devices_.front());
   base::RunLoop().RunUntilIdle();
 
   // Right after opening the device, we should see all its formats.
@@ -500,7 +500,7 @@
 TEST_F(VideoCaptureManagerTest,
        ManipulateDeviceAndCheckCapabilitiesWithDeviceId) {
   // Requesting formats should work even before enumerating/opening devices.
-  std::string device_id = devices_.front().device.id;
+  std::string device_id = devices_.front().id;
   media::VideoCaptureFormats supported_formats;
   supported_formats.clear();
   EXPECT_TRUE(vcm_->GetDeviceSupportedFormats(device_id, &supported_formats));
@@ -514,7 +514,7 @@
 
   InSequence s;
   EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _));
-  int video_session_id = vcm_->Open(devices_.front().device);
+  int video_session_id = vcm_->Open(devices_.front());
   base::RunLoop().RunUntilIdle();
 
   // Right after opening the device, we should see all its formats.
@@ -567,7 +567,7 @@
 TEST_F(VideoCaptureManagerTest, StartDeviceAndGetDeviceFormatInUse) {
   InSequence s;
   EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _));
-  int video_session_id = vcm_->Open(devices_.front().device);
+  int video_session_id = vcm_->Open(devices_.front());
   base::RunLoop().RunUntilIdle();
 
   // Right after opening the device, we should see no format in use.
@@ -609,10 +609,10 @@
 // use is an empty vector.
 TEST_F(VideoCaptureManagerTest,
        StartDeviceAndGetDeviceFormatInUseWithDeviceId) {
-  std::string device_id = devices_.front().device.id;
+  std::string device_id = devices_.front().id;
   InSequence s;
   EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _));
-  int video_session_id = vcm_->Open(devices_.front().device);
+  int video_session_id = vcm_->Open(devices_.front());
   base::RunLoop().RunUntilIdle();
 
   // Right after opening the device, we should see no format in use.
@@ -649,11 +649,11 @@
   EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(2);
   EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _)).Times(2);
 
-  StreamDeviceInfoArray::iterator it = devices_.begin();
+  MediaStreamDevices::iterator it = devices_.begin();
 
-  int video_session_id_first = vcm_->Open(it->device);
+  int video_session_id_first = vcm_->Open(*it);
   ++it;
-  int video_session_id_second = vcm_->Open(it->device);
+  int video_session_id_second = vcm_->Open(*it);
 
   vcm_->Close(video_session_id_first);
   vcm_->Close(video_session_id_second);
@@ -703,7 +703,7 @@
   EXPECT_CALL(*frame_observer_, OnStarted(_));
   EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _));
 
-  int video_session_id = vcm_->Open(devices_.front().device);
+  int video_session_id = vcm_->Open(devices_.front());
 
   VideoCaptureControllerID client_id = StartClient(video_session_id, true);
 
@@ -724,7 +724,7 @@
   EXPECT_CALL(*listener_, Opened(MEDIA_DEVICE_VIDEO_CAPTURE, _));
   EXPECT_CALL(*frame_observer_, OnStarted(_));
 
-  const int video_session_id = vcm_->Open(devices_.front().device);
+  const int video_session_id = vcm_->Open(devices_.front());
   const VideoCaptureControllerID client_id =
       StartClient(video_session_id, true);
 
@@ -766,7 +766,7 @@
   EXPECT_CALL(*frame_observer_, OnStarted(_));
   EXPECT_CALL(*listener_, Closed(MEDIA_DEVICE_VIDEO_CAPTURE, _));
 
-  int video_session_id = vcm_->Open(devices_.front().device);
+  int video_session_id = vcm_->Open(devices_.front());
   VideoCaptureControllerID client_id = StartClient(video_session_id, true);
 
   // Release/ResumeDevices according to ApplicationStatus. Should cause no
diff --git a/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc b/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc
index 827b3cb..22de859 100644
--- a/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc
+++ b/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc
@@ -13,7 +13,6 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "build/build_config.h"
 #include "content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h"
 #include "content/browser/renderer_host/pepper/pepper_socket_utils.h"
@@ -799,11 +798,6 @@
     int net_result) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  // TODO(rvargas): Remove ScopedTracker below once crbug.com/462784 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "462784 PepperTCPSocketMessageFilter::OnConnectCompleted"));
-
   if (!state_.IsPending(TCPSocketState::CONNECT)) {
     DCHECK(state_.state() == TCPSocketState::CLOSED);
     SendConnectError(context, PP_ERROR_FAILED);
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 272d0542..51284904f8 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -188,6 +188,8 @@
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "ppapi/features/features.h"
+#include "services/device/public/interfaces/battery_monitor.mojom.h"
+#include "services/device/public/interfaces/constants.mojom.h"
 #include "services/resource_coordinator/public/cpp/resource_coordinator_features.h"
 #include "services/resource_coordinator/public/cpp/resource_coordinator_interface.h"
 #include "services/service_manager/embedder/switches.h"
@@ -690,13 +692,13 @@
 
 // Forwards service requests to Service Manager since the renderer cannot launch
 // out-of-process services on is own.
-template <typename R>
-void ForwardShapeDetectionRequest(R request) {
+template <typename Interface>
+void ForwardRequest(const char* service_name,
+                    mojo::InterfaceRequest<Interface> request) {
   // TODO(beng): This should really be using the per-profile connector.
   service_manager::Connector* connector =
       ServiceManagerConnection::GetForProcess()->GetConnector();
-  connector->BindInterface(shape_detection::mojom::kServiceName,
-                           std::move(request));
+  connector->BindInterface(service_name, std::move(request));
 }
 
 class RenderProcessHostIsReadyObserver : public RenderProcessHostObserver {
@@ -1792,16 +1794,20 @@
 
   AddUIThreadInterface(
       registry.get(),
-      base::Bind(&ForwardShapeDetectionRequest<
-                 shape_detection::mojom::BarcodeDetectionRequest>));
+      base::Bind(&ForwardRequest<shape_detection::mojom::BarcodeDetection>,
+                 shape_detection::mojom::kServiceName));
   AddUIThreadInterface(
       registry.get(),
-      base::Bind(&ForwardShapeDetectionRequest<
-                 shape_detection::mojom::FaceDetectionProviderRequest>));
+      base::Bind(&ForwardRequest<shape_detection::mojom::FaceDetectionProvider>,
+                 shape_detection::mojom::kServiceName));
   AddUIThreadInterface(
       registry.get(),
-      base::Bind(&ForwardShapeDetectionRequest<
-                 shape_detection::mojom::TextDetectionRequest>));
+      base::Bind(&ForwardRequest<shape_detection::mojom::TextDetection>,
+                 shape_detection::mojom::kServiceName));
+
+  AddUIThreadInterface(
+      registry.get(), base::Bind(&ForwardRequest<device::mojom::BatteryMonitor>,
+                                 device::mojom::kServiceName));
 
   AddUIThreadInterface(
       registry.get(),
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index a633920..14c8c0f 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -308,11 +308,6 @@
   }
 };
 
-bool IsRunningInMash() {
-  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-  return cmdline->HasSwitch(switches::kIsRunningInMash);
-}
-
 }  // namespace
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -378,8 +373,6 @@
   }
 #endif
 
-  SetWidget(std::move(widget));
-
   std::pair<RoutingIDWidgetMap::iterator, bool> result =
       g_routing_id_widget_map.Get().insert(std::make_pair(
           RenderWidgetHostID(process->GetID(), routing_id_), this));
@@ -397,6 +390,7 @@
 
   SetupInputRouter();
   touch_emulator_.reset();
+  SetWidget(std::move(widget));
 
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kDisableHangMonitor)) {
@@ -412,7 +406,6 @@
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
   enable_surface_synchronization_ =
-      IsRunningInMash() ||
       command_line.HasSwitch(switches::kEnableSurfaceSynchronization);
 
   delegate_->RenderWidgetCreated(this);
@@ -2816,7 +2809,13 @@
 void RenderWidgetHostImpl::SetWidget(mojom::WidgetPtr widget) {
   if (widget && base::FeatureList::IsEnabled(features::kMojoInputMessages)) {
     widget_input_handler_.reset();
-    widget->GetWidgetInputHandler(mojo::MakeRequest(&widget_input_handler_));
+
+    mojom::WidgetInputHandlerHostPtr host;
+    mojom::WidgetInputHandlerHostRequest host_request =
+        mojo::MakeRequest(&host);
+    widget->SetupWidgetInputHandler(mojo::MakeRequest(&widget_input_handler_),
+                                    std::move(host));
+    input_router_->BindHost(std::move(host_request));
   }
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 2ed5e1c..4391225652 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -607,9 +607,15 @@
   // with the interface calls processed on the WidgetInputHandler.
   void SetWidgetInputHandler(
       mojom::WidgetInputHandlerAssociatedPtr widget_input_handler);
-  mojom::WidgetInputHandler* GetWidgetInputHandler() override;
   void SetWidget(mojom::WidgetPtr widget);
 
+  // InputRouterImplClient overrides.
+  mojom::WidgetInputHandler* GetWidgetInputHandler() override;
+  void OnImeCompositionRangeChanged(
+      const gfx::Range& range,
+      const std::vector<gfx::Rect>& character_bounds) override;
+  void OnImeCancelComposition() override;
+
  protected:
   // ---------------------------------------------------------------------------
   // The following method is overridden by RenderViewHost to send upwards to
@@ -676,10 +682,6 @@
   void OnAutoscrollEnd();
   void OnTextInputStateChanged(const TextInputState& params);
 
-  void OnImeCompositionRangeChanged(
-      const gfx::Range& range,
-      const std::vector<gfx::Rect>& character_bounds);
-  void OnImeCancelComposition();
   void OnLockMouse(bool user_gesture,
                    bool privileged);
   void OnUnlockMouse();
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index 7c4664f7..fa6a10a 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -119,6 +119,7 @@
   void SetFrameTreeNodeId(int frameTreeNodeId) override {}
   cc::TouchAction AllowedTouchAction() override { return cc::kTouchActionAuto; }
   void SetForceEnableZoom(bool enabled) override {}
+  void BindHost(mojom::WidgetInputHandlerHostRequest request) override {}
 
   // IPC::Listener
   bool OnMessageReceived(const IPC::Message& message) override {
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 2b7274f..0997df88 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -363,14 +363,18 @@
       base::Bind(callback, SkBitmap(), READBACK_FAILED));
   TRACE_EVENT0("cc",
                "RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult");
-  if (!result->HasTexture() || result->IsEmpty() || result->size().IsEmpty())
+  if (result->IsEmpty())
     return;
+
   viz::TextureMailbox texture_mailbox;
   std::unique_ptr<viz::SingleReleaseCallback> release_callback;
-  result->TakeTexture(&texture_mailbox, &release_callback);
-  DCHECK(texture_mailbox.IsTexture());
+  if (auto* mailbox = result->GetTextureMailbox()) {
+    texture_mailbox = *mailbox;
+    release_callback = result->TakeTextureOwnership();
+  }
   if (!texture_mailbox.IsTexture())
     return;
+
   viz::GLHelper* gl_helper = GetPostReadbackGLHelper();
   if (!gl_helper)
     return;
@@ -472,7 +476,6 @@
   // Set the layer which will hold the content layer for this view. The content
   // layer is managed by the DelegatedFrameHost.
   view_.SetLayer(cc::Layer::Create());
-  view_.SetLayout(ui::ViewAndroid::LayoutParams::MatchParent());
 
   if (using_browser_compositor_) {
     viz::FrameSinkId frame_sink_id =
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 7d67e5f..629ab52 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -3527,8 +3527,10 @@
   void ReleaseSwappedFrame() {
     std::unique_ptr<viz::CopyOutputRequest> request =
         std::move(view_->last_copy_request_);
-    request->SendTextureResult(view_rect_.size(), request->texture_mailbox(),
-                               std::unique_ptr<viz::SingleReleaseCallback>());
+    request->SendResult(std::make_unique<viz::CopyOutputTextureResult>(
+        view_rect_, request->texture_mailbox(),
+        viz::SingleReleaseCallback::Create(
+            base::Bind([](const gpu::SyncToken&, bool) {}))));
     RunLoopUntilCallback();
   }
 
@@ -3645,9 +3647,9 @@
   TearDownEnvironment();
 
   // Send the result after-the-fact.  It goes nowhere since DelegatedFrameHost
-  // has been destroyed.
-  request->SendTextureResult(view_rect_.size(), request->texture_mailbox(),
-                             std::unique_ptr<viz::SingleReleaseCallback>());
+  // has been destroyed.  CopyOutputRequest auto-sends an empty result upon
+  // destruction.
+  request.reset();
 
   // Because the copy request callback may be holding state within it, that
   // state must handle the RWHVA and ImageTransportFactory going away before the
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.cc b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
index 913526ad..8ce4d00 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
@@ -797,7 +797,8 @@
   DCHECK(support_);
 
   std::unique_ptr<viz::CopyOutputRequest> request =
-      viz::CopyOutputRequest::CreateRequest(
+      std::make_unique<viz::CopyOutputRequest>(
+          viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
           base::BindOnce(&CopyFromCompositingSurfaceHasResult, output_size,
                          preferred_color_type, callback));
   if (!src_subrect.IsEmpty())
diff --git a/content/browser/service_worker/embedded_worker_instance_unittest.cc b/content/browser/service_worker/embedded_worker_instance_unittest.cc
index 4b875bf..9754a45 100644
--- a/content/browser/service_worker/embedded_worker_instance_unittest.cc
+++ b/content/browser/service_worker/embedded_worker_instance_unittest.cc
@@ -68,6 +68,13 @@
                 RegisterCallback callback) override {
     NOTIMPLEMENTED();
   }
+  void GetRegistration(const GURL& client_url,
+                       GetRegistrationCallback callback) override {
+    NOTIMPLEMENTED();
+  }
+  void GetRegistrations(GetRegistrationsCallback callback) override {
+    NOTIMPLEMENTED();
+  }
 
   mojom::ServiceWorkerContainerAssociatedPtr client_;
   mojo::AssociatedBinding<mojom::ServiceWorkerContainerHost> binding_;
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index b80e421..35d033e 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -16,7 +16,6 @@
 #include "base/lazy_instance.h"
 #include "base/location.h"
 #include "base/logging.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/task_scheduler/post_task.h"
 #include "base/threading/sequenced_worker_pool.h"
@@ -148,10 +147,6 @@
 // static
 void ServiceWorkerContext::AddExcludedHeadersForFetchEvent(
     const std::set<std::string>& header_names) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "477117 ServiceWorkerContext::AddExcludedHeadersForFetchEvent"));
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   g_excluded_header_name_set.Get().insert(header_names.begin(),
                                           header_names.end());
@@ -816,10 +811,6 @@
                        base::RetainedRef(loader_factory_getter)));
     return;
   }
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "477117 ServiceWorkerContextWrapper::InitInternal"));
   DCHECK(!context_core_);
   if (quota_manager_proxy) {
     quota_manager_proxy->RegisterClient(new ServiceWorkerQuotaClient(this));
diff --git a/content/browser/service_worker/service_worker_dispatcher_host.cc b/content/browser/service_worker/service_worker_dispatcher_host.cc
index 727d8c9f..dd60a1d3 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host.cc
+++ b/content/browser/service_worker/service_worker_dispatcher_host.cc
@@ -9,7 +9,6 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -164,10 +163,6 @@
                         OnUpdateServiceWorker)
     IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_UnregisterServiceWorker,
                         OnUnregisterServiceWorker)
-    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GetRegistration,
-                        OnGetRegistration)
-    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GetRegistrations,
-                        OnGetRegistrations)
     IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GetRegistrationForReady,
                         OnGetRegistrationForReady)
     IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PostMessageToWorker,
@@ -414,136 +409,6 @@
                  thread_id, request_id));
 }
 
-void ServiceWorkerDispatcherHost::OnGetRegistration(
-    int thread_id,
-    int request_id,
-    int provider_id,
-    const GURL& document_url) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  TRACE_EVENT0("ServiceWorker",
-               "ServiceWorkerDispatcherHost::OnGetRegistration");
-
-  ProviderStatus provider_status;
-  ServiceWorkerProviderHost* provider_host =
-      GetProviderHostForRequest(&provider_status, provider_id);
-  switch (provider_status) {
-    case ProviderStatus::NO_CONTEXT:  // fallthrough
-    case ProviderStatus::DEAD_HOST:
-      Send(new ServiceWorkerMsg_ServiceWorkerGetRegistrationError(
-          thread_id, request_id, blink::mojom::ServiceWorkerErrorType::kAbort,
-          base::ASCIIToUTF16(kServiceWorkerGetRegistrationErrorPrefix) +
-              base::ASCIIToUTF16(kShutdownErrorMessage)));
-      return;
-    case ProviderStatus::NO_HOST:
-      bad_message::ReceivedBadMessage(
-          this, bad_message::SWDH_GET_REGISTRATION_NO_HOST);
-      return;
-    case ProviderStatus::NO_URL:
-      Send(new ServiceWorkerMsg_ServiceWorkerGetRegistrationError(
-          thread_id, request_id,
-          blink::mojom::ServiceWorkerErrorType::kSecurity,
-          base::ASCIIToUTF16(kServiceWorkerGetRegistrationErrorPrefix) +
-              base::ASCIIToUTF16(kNoDocumentURLErrorMessage)));
-      return;
-    case ProviderStatus::OK:
-      break;
-  }
-
-  if (!document_url.is_valid()) {
-    bad_message::ReceivedBadMessage(this,
-                                    bad_message::SWDH_GET_REGISTRATION_BAD_URL);
-    return;
-  }
-
-  std::vector<GURL> urls = {provider_host->document_url(), document_url};
-  if (!ServiceWorkerUtils::AllOriginsMatchAndCanAccessServiceWorkers(urls)) {
-    bad_message::ReceivedBadMessage(this,
-                                    bad_message::SWDH_GET_REGISTRATION_CANNOT);
-    return;
-  }
-
-  if (!GetContentClient()->browser()->AllowServiceWorker(
-          provider_host->document_url(), provider_host->topmost_frame_url(),
-          resource_context_, base::Bind(&GetWebContents, render_process_id_,
-                                        provider_host->frame_id()))) {
-    Send(new ServiceWorkerMsg_ServiceWorkerGetRegistrationError(
-        thread_id, request_id, blink::mojom::ServiceWorkerErrorType::kDisabled,
-        base::ASCIIToUTF16(kServiceWorkerGetRegistrationErrorPrefix) +
-            base::ASCIIToUTF16(kUserDeniedPermissionMessage)));
-    return;
-  }
-
-  TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
-                           "ServiceWorkerDispatcherHost::GetRegistration",
-                           request_id, "Document URL", document_url.spec());
-  GetContext()->storage()->FindRegistrationForDocument(
-      document_url,
-      base::Bind(&ServiceWorkerDispatcherHost::GetRegistrationComplete,
-                 this,
-                 thread_id,
-                 provider_id,
-                 request_id));
-}
-
-void ServiceWorkerDispatcherHost::OnGetRegistrations(int thread_id,
-                                                     int request_id,
-                                                     int provider_id) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  TRACE_EVENT0("ServiceWorker",
-               "ServiceWorkerDispatcherHost::OnGetRegistrations");
-
-  ProviderStatus provider_status;
-  ServiceWorkerProviderHost* provider_host =
-      GetProviderHostForRequest(&provider_status, provider_id);
-  switch (provider_status) {
-    case ProviderStatus::NO_CONTEXT:  // fallthrough
-    case ProviderStatus::DEAD_HOST:
-      Send(new ServiceWorkerMsg_ServiceWorkerGetRegistrationsError(
-          thread_id, request_id, blink::mojom::ServiceWorkerErrorType::kAbort,
-          base::ASCIIToUTF16(kServiceWorkerGetRegistrationsErrorPrefix) +
-              base::ASCIIToUTF16(kShutdownErrorMessage)));
-      return;
-    case ProviderStatus::NO_HOST:
-      bad_message::ReceivedBadMessage(
-          this, bad_message::SWDH_GET_REGISTRATIONS_NO_HOST);
-      return;
-    case ProviderStatus::NO_URL:
-      Send(new ServiceWorkerMsg_ServiceWorkerGetRegistrationsError(
-          thread_id, request_id,
-          blink::mojom::ServiceWorkerErrorType::kSecurity,
-          base::ASCIIToUTF16(kServiceWorkerGetRegistrationsErrorPrefix) +
-              base::ASCIIToUTF16(kNoDocumentURLErrorMessage)));
-      return;
-    case ProviderStatus::OK:
-      break;
-  }
-
-  if (!OriginCanAccessServiceWorkers(provider_host->document_url())) {
-    bad_message::ReceivedBadMessage(
-        this, bad_message::SWDH_GET_REGISTRATIONS_INVALID_ORIGIN);
-    return;
-  }
-
-  if (!GetContentClient()->browser()->AllowServiceWorker(
-          provider_host->document_url(), provider_host->topmost_frame_url(),
-          resource_context_, base::Bind(&GetWebContents, render_process_id_,
-                                        provider_host->frame_id()))) {
-    Send(new ServiceWorkerMsg_ServiceWorkerGetRegistrationsError(
-        thread_id, request_id, blink::mojom::ServiceWorkerErrorType::kDisabled,
-        base::ASCIIToUTF16(kServiceWorkerGetRegistrationsErrorPrefix) +
-            base::ASCIIToUTF16(kUserDeniedPermissionMessage)));
-    return;
-  }
-
-  TRACE_EVENT_ASYNC_BEGIN0("ServiceWorker",
-                           "ServiceWorkerDispatcherHost::GetRegistrations",
-                           request_id);
-  GetContext()->storage()->GetRegistrationsForOrigin(
-      provider_host->document_url().GetOrigin(),
-      base::Bind(&ServiceWorkerDispatcherHost::GetRegistrationsComplete, this,
-                 thread_id, provider_id, request_id));
-}
-
 void ServiceWorkerDispatcherHost::OnGetRegistrationForReady(
     int thread_id,
     int request_id,
@@ -869,10 +734,6 @@
 
 void ServiceWorkerDispatcherHost::OnProviderCreated(
     ServiceWorkerProviderHostInfo info) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "477117 ServiceWorkerDispatcherHost::OnProviderCreated"));
   TRACE_EVENT0("ServiceWorker",
                "ServiceWorkerDispatcherHost::OnProviderCreated");
   if (!GetContext())
@@ -1193,101 +1054,6 @@
                                                       is_success));
 }
 
-void ServiceWorkerDispatcherHost::GetRegistrationComplete(
-    int thread_id,
-    int provider_id,
-    int request_id,
-    ServiceWorkerStatusCode status,
-    scoped_refptr<ServiceWorkerRegistration> registration) {
-  TRACE_EVENT_ASYNC_END2(
-      "ServiceWorker", "ServiceWorkerDispatcherHost::GetRegistration",
-      request_id, "Status", status, "Registration ID",
-      registration ? registration->id() : kInvalidServiceWorkerRegistrationId);
-  if (!GetContext())
-    return;
-
-  ServiceWorkerProviderHost* provider_host =
-      GetContext()->GetProviderHost(render_process_id_, provider_id);
-  if (!provider_host)
-    return;  // The provider has already been destroyed.
-
-  if (status != SERVICE_WORKER_OK && status != SERVICE_WORKER_ERROR_NOT_FOUND) {
-    base::string16 error_message;
-    blink::mojom::ServiceWorkerErrorType error_type;
-    GetServiceWorkerRegistrationStatusResponse(status, std::string(),
-                                               &error_type, &error_message);
-    Send(new ServiceWorkerMsg_ServiceWorkerGetRegistrationError(
-        thread_id, request_id, error_type,
-        base::ASCIIToUTF16(kServiceWorkerGetRegistrationErrorPrefix) +
-            error_message));
-
-    return;
-  }
-
-  ServiceWorkerRegistrationObjectInfo info;
-  ServiceWorkerVersionAttributes attrs;
-  if (status == SERVICE_WORKER_OK) {
-    DCHECK(registration.get());
-    if (!registration->is_uninstalling()) {
-      GetRegistrationObjectInfoAndVersionAttributes(
-          provider_host->AsWeakPtr(), registration.get(), &info, &attrs);
-    }
-  }
-
-  Send(new ServiceWorkerMsg_DidGetRegistration(
-      thread_id, request_id, info, attrs));
-}
-
-void ServiceWorkerDispatcherHost::GetRegistrationsComplete(
-    int thread_id,
-    int provider_id,
-    int request_id,
-    ServiceWorkerStatusCode status,
-    const std::vector<scoped_refptr<ServiceWorkerRegistration>>&
-        registrations) {
-  TRACE_EVENT_ASYNC_END1("ServiceWorker",
-                         "ServiceWorkerDispatcherHost::GetRegistrations",
-                         request_id, "Status", status);
-  if (!GetContext())
-    return;
-
-  ServiceWorkerProviderHost* provider_host =
-      GetContext()->GetProviderHost(render_process_id_, provider_id);
-  if (!provider_host)
-    return;  // The provider has already been destroyed.
-
-  if (status != SERVICE_WORKER_OK) {
-    base::string16 error_message;
-    blink::mojom::ServiceWorkerErrorType error_type;
-    GetServiceWorkerRegistrationStatusResponse(status, std::string(),
-                                               &error_type, &error_message);
-    Send(new ServiceWorkerMsg_ServiceWorkerGetRegistrationsError(
-        thread_id, request_id, error_type,
-        base::ASCIIToUTF16(kServiceWorkerGetRegistrationErrorPrefix) +
-            error_message));
-    return;
-  }
-
-  std::vector<ServiceWorkerRegistrationObjectInfo> object_infos;
-  std::vector<ServiceWorkerVersionAttributes> version_attrs;
-
-  for (const auto& registration : registrations) {
-    DCHECK(registration.get());
-    if (!registration->is_uninstalling()) {
-      ServiceWorkerRegistrationObjectInfo object_info;
-      ServiceWorkerVersionAttributes version_attr;
-      GetRegistrationObjectInfoAndVersionAttributes(
-          provider_host->AsWeakPtr(), registration.get(), &object_info,
-          &version_attr);
-      object_infos.push_back(object_info);
-      version_attrs.push_back(version_attr);
-    }
-  }
-
-  Send(new ServiceWorkerMsg_DidGetRegistrations(thread_id, request_id,
-                                                object_infos, version_attrs));
-}
-
 void ServiceWorkerDispatcherHost::GetRegistrationForReadyComplete(
     int thread_id,
     int request_id,
diff --git a/content/browser/service_worker/service_worker_dispatcher_host.h b/content/browser/service_worker/service_worker_dispatcher_host.h
index 2f3f705..05407f3 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host.h
+++ b/content/browser/service_worker/service_worker_dispatcher_host.h
@@ -22,8 +22,6 @@
 #include "content/public/browser/browser_message_filter.h"
 #include "mojo/public/cpp/bindings/strong_associated_binding_set.h"
 
-class GURL;
-
 namespace url {
 class Origin;
 }  // namespace url
@@ -151,11 +149,6 @@
                                  int request_id,
                                  int provider_id,
                                  int64_t registration_id);
-  void OnGetRegistration(int thread_id,
-                         int request_id,
-                         int provider_id,
-                         const GURL& document_url);
-  void OnGetRegistrations(int thread_id, int request_id, int provider_id);
   void OnGetRegistrationForReady(int thread_id,
                                  int request_id,
                                  int provider_id);
@@ -234,19 +227,6 @@
   void UnregistrationComplete(int thread_id,
                               int request_id,
                               ServiceWorkerStatusCode status);
-  void GetRegistrationComplete(
-      int thread_id,
-      int provider_id,
-      int request_id,
-      ServiceWorkerStatusCode status,
-      scoped_refptr<ServiceWorkerRegistration> registration);
-  void GetRegistrationsComplete(
-      int thread_id,
-      int provider_id,
-      int request_id,
-      ServiceWorkerStatusCode status,
-      const std::vector<scoped_refptr<ServiceWorkerRegistration>>&
-          registrations);
   void GetRegistrationForReadyComplete(
       int thread_id,
       int request_id,
diff --git a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
index d2c06369..7ae60f02 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
@@ -251,21 +251,21 @@
                 GURL pattern,
                 GURL worker_url,
                 blink::mojom::ServiceWorkerErrorType expected) {
-    blink::mojom::ServiceWorkerErrorType result;
+    blink::mojom::ServiceWorkerErrorType error;
     ServiceWorkerRegistrationOptions options(pattern);
     container_host->Register(
         worker_url, options,
         base::BindOnce(
-            [](blink::mojom::ServiceWorkerErrorType* result,
+            [](blink::mojom::ServiceWorkerErrorType* out_error,
                blink::mojom::ServiceWorkerErrorType error,
                const base::Optional<std::string>& error_msg,
                const base::Optional<ServiceWorkerRegistrationObjectInfo>&
                    registration,
                const base::Optional<ServiceWorkerVersionAttributes>&
-                   attributes) { *result = error; },
-            &result));
+                   attributes) { *out_error = error; },
+            &error));
     base::RunLoop().RunUntilIdle();
-    EXPECT_EQ(expected, result);
+    EXPECT_EQ(expected, error);
   }
 
   void SendUnregister(int64_t provider_id, int64_t registration_id) {
@@ -284,33 +284,64 @@
     dispatcher_host_->ipc_sink()->ClearMessages();
   }
 
-  void SendGetRegistration(int64_t provider_id, GURL document_url) {
-    dispatcher_host_->OnMessageReceived(
-        ServiceWorkerHostMsg_GetRegistration(
-            -1, -1, provider_id, document_url));
+  void SendGetRegistration(mojom::ServiceWorkerContainerHost* container_host,
+                           GURL document_url) {
+    container_host->GetRegistration(
+        document_url,
+        base::BindOnce(
+            [](blink::mojom::ServiceWorkerErrorType error,
+               const base::Optional<std::string>& error_msg,
+               const base::Optional<ServiceWorkerRegistrationObjectInfo>&
+                   registration,
+               const base::Optional<ServiceWorkerVersionAttributes>&
+                   attributes) {}));
     base::RunLoop().RunUntilIdle();
   }
 
-  void GetRegistration(int64_t provider_id,
+  void GetRegistration(mojom::ServiceWorkerContainerHost* container_host,
                        GURL document_url,
-                       uint32_t expected_message) {
-    SendGetRegistration(provider_id, document_url);
-    EXPECT_TRUE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
-        expected_message));
-    dispatcher_host_->ipc_sink()->ClearMessages();
+                       blink::mojom::ServiceWorkerErrorType expected) {
+    blink::mojom::ServiceWorkerErrorType error;
+    container_host->GetRegistration(
+        document_url,
+        base::BindOnce(
+            [](blink::mojom::ServiceWorkerErrorType* out_error,
+               blink::mojom::ServiceWorkerErrorType error,
+               const base::Optional<std::string>& error_msg,
+               const base::Optional<ServiceWorkerRegistrationObjectInfo>&
+                   registration,
+               const base::Optional<ServiceWorkerVersionAttributes>&
+                   attributes) { *out_error = error; },
+            &error));
+    base::RunLoop().RunUntilIdle();
+    EXPECT_EQ(expected, error);
   }
 
-  void SendGetRegistrations(int64_t provider_id) {
-    dispatcher_host_->OnMessageReceived(
-        ServiceWorkerHostMsg_GetRegistrations(-1, -1, provider_id));
+  void SendGetRegistrations(mojom::ServiceWorkerContainerHost* container_host) {
+    container_host->GetRegistrations(base::BindOnce(
+        [](blink::mojom::ServiceWorkerErrorType error,
+           const base::Optional<std::string>& error_msg,
+           const base::Optional<
+               std::vector<ServiceWorkerRegistrationObjectInfo>>& infos,
+           const base::Optional<std::vector<ServiceWorkerVersionAttributes>>&
+               attrs) {}));
     base::RunLoop().RunUntilIdle();
   }
 
-  void GetRegistrations(int64_t provider_id, uint32_t expected_message) {
-    SendGetRegistrations(provider_id);
-    EXPECT_TRUE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
-        expected_message));
-    dispatcher_host_->ipc_sink()->ClearMessages();
+  void GetRegistrations(mojom::ServiceWorkerContainerHost* container_host,
+                        blink::mojom::ServiceWorkerErrorType expected) {
+    blink::mojom::ServiceWorkerErrorType error;
+    container_host->GetRegistrations(base::BindOnce(
+        [](blink::mojom::ServiceWorkerErrorType* out_error,
+           blink::mojom::ServiceWorkerErrorType error,
+           const base::Optional<std::string>& error_msg,
+           const base::Optional<
+               std::vector<ServiceWorkerRegistrationObjectInfo>>& infos,
+           const base::Optional<std::vector<ServiceWorkerVersionAttributes>>&
+               attrs) { *out_error = error; },
+        &error));
+    base::RunLoop().RunUntilIdle();
+    EXPECT_EQ(expected, error);
   }
 
   void DispatchExtendableMessageEvent(
@@ -385,11 +416,11 @@
   Register(remote_endpoint.host_ptr()->get(), GURL("https://www.example.com/"),
            GURL("https://www.example.com/bar"),
            blink::mojom::ServiceWorkerErrorType::kDisabled);
-  GetRegistration(kProviderId,
+  GetRegistration(remote_endpoint.host_ptr()->get(),
                   GURL("https://www.example.com/"),
-                  ServiceWorkerMsg_ServiceWorkerGetRegistrationError::ID);
-  GetRegistrations(kProviderId,
-                   ServiceWorkerMsg_ServiceWorkerGetRegistrationsError::ID);
+                  blink::mojom::ServiceWorkerErrorType::kDisabled);
+  GetRegistrations(remote_endpoint.host_ptr()->get(),
+                   blink::mojom::ServiceWorkerErrorType::kDisabled);
 
   // Add a registration into a live registration map so that Unregister() can
   // find it.
@@ -679,91 +710,72 @@
 
 TEST_F(ServiceWorkerDispatcherHostTest, GetRegistration_SameOrigin) {
   const int64_t kProviderId = 99;  // Dummy value
-  std::unique_ptr<ServiceWorkerProviderHost> host(
-      CreateServiceWorkerProviderHost(kProviderId));
-  host->SetDocumentUrl(GURL("https://www.example.com/foo"));
-  context()->AddProviderHost(std::move(host));
+  ServiceWorkerRemoteProviderEndpoint remote_endpoint =
+      PrepareServiceWorkerProviderHost(kProviderId,
+                                       GURL("https://www.example.com/foo"));
 
-  GetRegistration(kProviderId,
+  GetRegistration(remote_endpoint.host_ptr()->get(),
                   GURL("https://www.example.com/"),
-                  ServiceWorkerMsg_DidGetRegistration::ID);
+                  blink::mojom::ServiceWorkerErrorType::kNone);
 }
 
 TEST_F(ServiceWorkerDispatcherHostTest, GetRegistration_CrossOriginShouldFail) {
   const int64_t kProviderId = 99;  // Dummy value
-  std::unique_ptr<ServiceWorkerProviderHost> host(
-      CreateServiceWorkerProviderHost(kProviderId));
-  host->SetDocumentUrl(GURL("https://www.example.com/foo"));
-  context()->AddProviderHost(std::move(host));
+  ServiceWorkerRemoteProviderEndpoint remote_endpoint =
+      PrepareServiceWorkerProviderHost(kProviderId,
+                                       GURL("https://www.example.com/foo"));
 
-  SendGetRegistration(kProviderId, GURL("https://foo.example.com/"));
-  EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
+  ASSERT_TRUE(bad_messages_.empty());
+  SendGetRegistration(remote_endpoint.host_ptr()->get(),
+                      GURL("https://foo.example.com/"));
+  EXPECT_EQ(1u, bad_messages_.size());
 }
 
 TEST_F(ServiceWorkerDispatcherHostTest,
        GetRegistration_InvalidScopeShouldFail) {
   const int64_t kProviderId = 99;  // Dummy value
-  std::unique_ptr<ServiceWorkerProviderHost> host(
-      CreateServiceWorkerProviderHost(kProviderId));
-  host->SetDocumentUrl(GURL("https://www.example.com/foo"));
-  context()->AddProviderHost(std::move(host));
+  ServiceWorkerRemoteProviderEndpoint remote_endpoint =
+      PrepareServiceWorkerProviderHost(kProviderId,
+                                       GURL("https://www.example.com/foo"));
 
-  SendGetRegistration(kProviderId, GURL(""));
-  EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
+  ASSERT_TRUE(bad_messages_.empty());
+  SendGetRegistration(remote_endpoint.host_ptr()->get(), GURL(""));
+  EXPECT_EQ(1u, bad_messages_.size());
 }
 
 TEST_F(ServiceWorkerDispatcherHostTest,
        GetRegistration_NonSecureOriginShouldFail) {
   const int64_t kProviderId = 99;  // Dummy value
-  std::unique_ptr<ServiceWorkerProviderHost> host(
-      CreateServiceWorkerProviderHost(kProviderId));
-  host->SetDocumentUrl(GURL("http://www.example.com/foo"));
-  context()->AddProviderHost(std::move(host));
+  ServiceWorkerRemoteProviderEndpoint remote_endpoint =
+      PrepareServiceWorkerProviderHost(kProviderId,
+                                       GURL("http://www.example.com/foo"));
 
-  SendGetRegistration(kProviderId, GURL("http://www.example.com/"));
-  EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
-}
-
-TEST_F(ServiceWorkerDispatcherHostTest, GetRegistration_EarlyContextDeletion) {
-  helper_->ShutdownContext();
-
-  // Let the shutdown reach the simulated IO thread.
-  base::RunLoop().RunUntilIdle();
-
-  GetRegistration(-1,
-                  GURL(),
-                  ServiceWorkerMsg_ServiceWorkerGetRegistrationError::ID);
+  ASSERT_TRUE(bad_messages_.empty());
+  SendGetRegistration(remote_endpoint.host_ptr()->get(),
+                      GURL("http://www.example.com/"));
+  EXPECT_EQ(1u, bad_messages_.size());
 }
 
 TEST_F(ServiceWorkerDispatcherHostTest, GetRegistrations_SecureOrigin) {
   const int64_t kProviderId = 99;  // Dummy value
-  std::unique_ptr<ServiceWorkerProviderHost> host(
-      CreateServiceWorkerProviderHost(kProviderId));
-  host->SetDocumentUrl(GURL("https://www.example.com/foo"));
-  context()->AddProviderHost(std::move(host));
+  ServiceWorkerRemoteProviderEndpoint remote_endpoint =
+      PrepareServiceWorkerProviderHost(kProviderId,
+                                       GURL("https://www.example.com/foo"));
 
-  GetRegistrations(kProviderId, ServiceWorkerMsg_DidGetRegistrations::ID);
+  GetRegistrations(remote_endpoint.host_ptr()->get(),
+                   blink::mojom::ServiceWorkerErrorType::kNone);
 }
 
 TEST_F(ServiceWorkerDispatcherHostTest,
        GetRegistrations_NonSecureOriginShouldFail) {
   const int64_t kProviderId = 99;  // Dummy value
-  std::unique_ptr<ServiceWorkerProviderHost> host(
-      CreateServiceWorkerProviderHost(kProviderId));
-  host->SetDocumentUrl(GURL("http://www.example.com/foo"));
-  context()->AddProviderHost(std::move(host));
+  ServiceWorkerRemoteProviderEndpoint remote_endpoint =
+      PrepareServiceWorkerProviderHost(kProviderId,
+                                       GURL("http://www.example.com/foo"));
 
-  SendGetRegistrations(kProviderId);
-  EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
-}
-
-TEST_F(ServiceWorkerDispatcherHostTest, GetRegistrations_EarlyContextDeletion) {
-  helper_->ShutdownContext();
-
-  // Let the shutdown reach the simulated IO thread.
-  base::RunLoop().RunUntilIdle();
-
-  GetRegistrations(-1, ServiceWorkerMsg_ServiceWorkerGetRegistrationsError::ID);
+  ASSERT_TRUE(bad_messages_.empty());
+  SendGetRegistrations(remote_endpoint.host_ptr()->get());
+  EXPECT_EQ(1u, bad_messages_.size());
 }
 
 TEST_F(ServiceWorkerDispatcherHostTest, CleanupOnRendererCrash) {
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index 23ee40c..d592ede 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -52,8 +52,8 @@
 const char kBadMessageInvalidURL[] = "Some URLs are invalid.";
 const char kBadMessageInproperOrigins[] =
     "Origins are not matching, or some cannot access service worker.";
-const char kBadMessageRegisterFromNonWindow[] =
-    "The register message should not come from a non-window client.";
+const char kBadMessageFromNonWindow[] =
+    "The request message should not come from a non-window client.";
 
 // Provider host for navigation with PlzNavigate or when service worker's
 // context is created on the browser side. This function provides the next
@@ -943,23 +943,25 @@
     return;
   }
 
+  int64_t trace_id = base::TimeTicks::Now().since_origin().InMicroseconds();
   TRACE_EVENT_ASYNC_BEGIN2(
-      "ServiceWorker", "ServiceWorkerProviderHost::Register", this, "Scope",
+      "ServiceWorker", "ServiceWorkerProviderHost::Register", trace_id, "Scope",
       options.scope.spec(), "Script URL", script_url.spec());
   context_->RegisterServiceWorker(
       script_url, options, this,
       base::AdaptCallbackForRepeating(
           base::BindOnce(&ServiceWorkerProviderHost::RegistrationComplete,
-                         AsWeakPtr(), std::move(callback))));
+                         AsWeakPtr(), std::move(callback), trace_id)));
 }
 
 void ServiceWorkerProviderHost::RegistrationComplete(
     RegisterCallback callback,
+    int64_t trace_id,
     ServiceWorkerStatusCode status,
     const std::string& status_message,
     int64_t registration_id) {
   TRACE_EVENT_ASYNC_END2("ServiceWorker", "ServiceWorkerProviderHost::Register",
-                         this, "Status", status, "Registration ID",
+                         trace_id, "Status", status, "Registration ID",
                          registration_id);
   if (!dispatcher_host_ || !IsContextAlive()) {
     std::move(callback).Run(blink::mojom::ServiceWorkerErrorType::kAbort,
@@ -970,14 +972,13 @@
   }
 
   if (status != SERVICE_WORKER_OK) {
-    base::string16 error_message;
+    std::string error_message;
     blink::mojom::ServiceWorkerErrorType error_type;
-    GetServiceWorkerRegistrationStatusResponse(status, status_message,
-                                               &error_type, &error_message);
-    std::move(callback).Run(
-        error_type,
-        kServiceWorkerRegisterErrorPrefix + base::UTF16ToASCII(error_message),
-        base::nullopt, base::nullopt);
+    GetServiceWorkerErrorTypeForRegistration(status, status_message,
+                                             &error_type, &error_message);
+    std::move(callback).Run(error_type,
+                            kServiceWorkerRegisterErrorPrefix + error_message,
+                            base::nullopt, base::nullopt);
     return;
   }
 
@@ -996,12 +997,208 @@
                           base::nullopt, info, attrs);
 }
 
+void ServiceWorkerProviderHost::GetRegistration(
+    const GURL& client_url,
+    GetRegistrationCallback callback) {
+  if (!dispatcher_host_ || !IsContextAlive()) {
+    std::move(callback).Run(
+        blink::mojom::ServiceWorkerErrorType::kAbort,
+        std::string(kServiceWorkerGetRegistrationErrorPrefix) +
+            std::string(kShutdownErrorMessage),
+        base::nullopt, base::nullopt);
+    return;
+  }
+  // TODO(falken): This check can be removed once crbug.com/439697 is fixed.
+  if (document_url().is_empty()) {
+    std::move(callback).Run(
+        blink::mojom::ServiceWorkerErrorType::kSecurity,
+        std::string(kServiceWorkerGetRegistrationErrorPrefix) +
+            std::string(kNoDocumentURLErrorMessage),
+        base::nullopt, base::nullopt);
+    return;
+  }
+
+  std::string error_message;
+  if (!IsValidGetRegistrationMessage(client_url, &error_message)) {
+    mojo::ReportBadMessage(error_message);
+    // ReportBadMessage() will kill the renderer process, but Mojo complains if
+    // the callback is not run. Just run it with nonsense arguments.
+    std::move(callback).Run(blink::mojom::ServiceWorkerErrorType::kUnknown,
+                            std::string(), base::nullopt, base::nullopt);
+    return;
+  }
+
+  if (!GetContentClient()->browser()->AllowServiceWorker(
+          document_url(), topmost_frame_url(),
+          dispatcher_host_->resource_context(),
+          base::Bind(&GetWebContents, render_process_id_, frame_id()))) {
+    std::move(callback).Run(
+        blink::mojom::ServiceWorkerErrorType::kDisabled,
+        std::string(kServiceWorkerGetRegistrationErrorPrefix) +
+            std::string(kUserDeniedPermissionMessage),
+        base::nullopt, base::nullopt);
+    return;
+  }
+
+  int64_t trace_id = base::TimeTicks::Now().since_origin().InMicroseconds();
+  TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
+                           "ServiceWorkerProviderHost::GetRegistration",
+                           trace_id, "Client URL", client_url.spec());
+  context_->storage()->FindRegistrationForDocument(
+      client_url, base::AdaptCallbackForRepeating(base::BindOnce(
+                      &ServiceWorkerProviderHost::GetRegistrationComplete,
+                      AsWeakPtr(), std::move(callback), trace_id)));
+}
+
+void ServiceWorkerProviderHost::GetRegistrations(
+    GetRegistrationsCallback callback) {
+  if (!dispatcher_host_ || !IsContextAlive()) {
+    std::move(callback).Run(
+        blink::mojom::ServiceWorkerErrorType::kAbort,
+        std::string(kServiceWorkerGetRegistrationsErrorPrefix) +
+            std::string(kShutdownErrorMessage),
+        base::nullopt, base::nullopt);
+    return;
+  }
+  // TODO(falken): This check can be removed once crbug.com/439697 is fixed.
+  if (document_url().is_empty()) {
+    std::move(callback).Run(
+        blink::mojom::ServiceWorkerErrorType::kSecurity,
+        std::string(kServiceWorkerGetRegistrationsErrorPrefix) +
+            std::string(kNoDocumentURLErrorMessage),
+        base::nullopt, base::nullopt);
+    return;
+  }
+
+  std::string error_message;
+  if (!IsValidGetRegistrationsMessage(&error_message)) {
+    mojo::ReportBadMessage(error_message);
+    // ReportBadMessage() will kill the renderer process, but Mojo complains if
+    // the callback is not run. Just run it with nonsense arguments.
+    std::move(callback).Run(blink::mojom::ServiceWorkerErrorType::kUnknown,
+                            std::string(), base::nullopt, base::nullopt);
+    return;
+  }
+
+  if (!GetContentClient()->browser()->AllowServiceWorker(
+          document_url(), topmost_frame_url(),
+          dispatcher_host_->resource_context(),
+          base::Bind(&GetWebContents, render_process_id_, frame_id()))) {
+    std::move(callback).Run(
+        blink::mojom::ServiceWorkerErrorType::kDisabled,
+        std::string(kServiceWorkerGetRegistrationsErrorPrefix) +
+            std::string(kUserDeniedPermissionMessage),
+        base::nullopt, base::nullopt);
+    return;
+  }
+
+  int64_t trace_id = base::TimeTicks::Now().since_origin().InMicroseconds();
+  TRACE_EVENT_ASYNC_BEGIN0(
+      "ServiceWorker", "ServiceWorkerProviderHost::GetRegistrations", trace_id);
+  context_->storage()->GetRegistrationsForOrigin(
+      document_url().GetOrigin(),
+      base::AdaptCallbackForRepeating(
+          base::BindOnce(&ServiceWorkerProviderHost::GetRegistrationsComplete,
+                         AsWeakPtr(), std::move(callback), trace_id)));
+}
+
+void ServiceWorkerProviderHost::GetRegistrationComplete(
+    GetRegistrationCallback callback,
+    int64_t trace_id,
+    ServiceWorkerStatusCode status,
+    scoped_refptr<ServiceWorkerRegistration> registration) {
+  TRACE_EVENT_ASYNC_END2(
+      "ServiceWorker", "ServiceWorkerProviderHost::GetRegistration", trace_id,
+      "Status", status, "Registration ID",
+      registration ? registration->id() : kInvalidServiceWorkerRegistrationId);
+  if (!dispatcher_host_ || !IsContextAlive()) {
+    std::move(callback).Run(
+        blink::mojom::ServiceWorkerErrorType::kAbort,
+        std::string(kServiceWorkerGetRegistrationErrorPrefix) +
+            std::string(kShutdownErrorMessage),
+        base::nullopt, base::nullopt);
+    return;
+  }
+
+  if (status != SERVICE_WORKER_OK && status != SERVICE_WORKER_ERROR_NOT_FOUND) {
+    std::string error_message;
+    blink::mojom::ServiceWorkerErrorType error_type;
+    GetServiceWorkerErrorTypeForRegistration(status, std::string(), &error_type,
+                                             &error_message);
+    std::move(callback).Run(
+        error_type, kServiceWorkerGetRegistrationErrorPrefix + error_message,
+        base::nullopt, base::nullopt);
+    return;
+  }
+
+  ServiceWorkerRegistrationObjectInfo info;
+  ServiceWorkerVersionAttributes attrs;
+  if (status == SERVICE_WORKER_OK) {
+    DCHECK(registration.get());
+    if (!registration->is_uninstalling()) {
+      dispatcher_host_->GetRegistrationObjectInfoAndVersionAttributes(
+          AsWeakPtr(), registration.get(), &info, &attrs);
+    }
+  }
+
+  std::move(callback).Run(blink::mojom::ServiceWorkerErrorType::kNone,
+                          base::nullopt, info, attrs);
+}
+
+void ServiceWorkerProviderHost::GetRegistrationsComplete(
+    GetRegistrationsCallback callback,
+    int64_t trace_id,
+    ServiceWorkerStatusCode status,
+    const std::vector<scoped_refptr<ServiceWorkerRegistration>>&
+        registrations) {
+  TRACE_EVENT_ASYNC_END1("ServiceWorker",
+                         "ServiceWorkerProviderHost::GetRegistrations",
+                         trace_id, "Status", status);
+  if (!dispatcher_host_ || !IsContextAlive()) {
+    std::move(callback).Run(
+        blink::mojom::ServiceWorkerErrorType::kAbort,
+        std::string(kServiceWorkerGetRegistrationsErrorPrefix) +
+            std::string(kShutdownErrorMessage),
+        base::nullopt, base::nullopt);
+    return;
+  }
+
+  if (status != SERVICE_WORKER_OK) {
+    std::string error_message;
+    blink::mojom::ServiceWorkerErrorType error_type;
+    GetServiceWorkerErrorTypeForRegistration(status, std::string(), &error_type,
+                                             &error_message);
+    std::move(callback).Run(
+        error_type, kServiceWorkerGetRegistrationsErrorPrefix + error_message,
+        base::nullopt, base::nullopt);
+    return;
+  }
+
+  std::vector<ServiceWorkerRegistrationObjectInfo> object_infos;
+  std::vector<ServiceWorkerVersionAttributes> version_attrs;
+
+  for (const auto& registration : registrations) {
+    DCHECK(registration.get());
+    if (!registration->is_uninstalling()) {
+      ServiceWorkerRegistrationObjectInfo object_info;
+      ServiceWorkerVersionAttributes version_attr;
+      dispatcher_host_->GetRegistrationObjectInfoAndVersionAttributes(
+          AsWeakPtr(), registration.get(), &object_info, &version_attr);
+      object_infos.push_back(object_info);
+      version_attrs.push_back(version_attr);
+    }
+  }
+
+  std::move(callback).Run(blink::mojom::ServiceWorkerErrorType::kNone,
+                          base::nullopt, object_infos, version_attrs);
+}
+
 bool ServiceWorkerProviderHost::IsValidRegisterMessage(
     const GURL& script_url,
     const ServiceWorkerRegistrationOptions& options,
     std::string* out_error) const {
   if (client_type() != blink::kWebServiceWorkerClientTypeWindow) {
-    *out_error = kBadMessageRegisterFromNonWindow;
+    *out_error = kBadMessageFromNonWindow;
     return false;
   }
   if (!options.scope.is_valid() || !script_url.is_valid()) {
@@ -1021,4 +1218,38 @@
   return true;
 }
 
+bool ServiceWorkerProviderHost::IsValidGetRegistrationMessage(
+    const GURL& client_url,
+    std::string* out_error) const {
+  if (client_type() != blink::kWebServiceWorkerClientTypeWindow) {
+    *out_error = kBadMessageFromNonWindow;
+    return false;
+  }
+  if (!client_url.is_valid()) {
+    *out_error = kBadMessageInvalidURL;
+    return false;
+  }
+  std::vector<GURL> urls = {document_url(), client_url};
+  if (!ServiceWorkerUtils::AllOriginsMatchAndCanAccessServiceWorkers(urls)) {
+    *out_error = kBadMessageInproperOrigins;
+    return false;
+  }
+
+  return true;
+}
+
+bool ServiceWorkerProviderHost::IsValidGetRegistrationsMessage(
+    std::string* out_error) const {
+  if (client_type() != blink::kWebServiceWorkerClientTypeWindow) {
+    *out_error = kBadMessageFromNonWindow;
+    return false;
+  }
+  if (!OriginCanAccessServiceWorkers(document_url())) {
+    *out_error = kBadMessageInproperOrigins;
+    return false;
+  }
+
+  return true;
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index d00d87d..2d68042 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -406,16 +406,36 @@
   void Register(const GURL& script_url,
                 const ServiceWorkerRegistrationOptions& options,
                 RegisterCallback callback) override;
+  void GetRegistration(const GURL& client_url,
+                       GetRegistrationCallback callback) override;
+  void GetRegistrations(GetRegistrationsCallback callback) override;
 
   // Callback for ServiceWorkerContextCore::RegisterServiceWorker().
   void RegistrationComplete(RegisterCallback callback,
+                            int64_t trace_id,
                             ServiceWorkerStatusCode status,
                             const std::string& status_message,
                             int64_t registration_id);
+  // Callback for ServiceWorkerStorage::FindRegistrationForDocument().
+  void GetRegistrationComplete(
+      GetRegistrationCallback callback,
+      int64_t trace_id,
+      ServiceWorkerStatusCode status,
+      scoped_refptr<ServiceWorkerRegistration> registration);
+  // Callback for ServiceWorkerStorage::GetRegistrationsForOrigin().
+  void GetRegistrationsComplete(
+      GetRegistrationsCallback callback,
+      int64_t trace_id,
+      ServiceWorkerStatusCode status,
+      const std::vector<scoped_refptr<ServiceWorkerRegistration>>&
+          registrations);
 
   bool IsValidRegisterMessage(const GURL& script_url,
                               const ServiceWorkerRegistrationOptions& options,
                               std::string* out_error) const;
+  bool IsValidGetRegistrationMessage(const GURL& client_url,
+                                     std::string* out_error) const;
+  bool IsValidGetRegistrationsMessage(std::string* out_error) const;
 
   const std::string client_uuid_;
   const base::TimeTicks create_time_;
diff --git a/content/browser/service_worker/service_worker_registration_status.cc b/content/browser/service_worker/service_worker_registration_status.cc
index ed6bc74..0031b75 100644
--- a/content/browser/service_worker/service_worker_registration_status.cc
+++ b/content/browser/service_worker/service_worker_registration_status.cc
@@ -77,4 +77,67 @@
                << status << " " << ServiceWorkerStatusToString(status);
 }
 
+void GetServiceWorkerErrorTypeForRegistration(
+    ServiceWorkerStatusCode status,
+    const std::string& status_message,
+    blink::mojom::ServiceWorkerErrorType* out_error,
+    std::string* out_message) {
+  *out_error = blink::mojom::ServiceWorkerErrorType::kUnknown;
+  if (!status_message.empty())
+    *out_message = status_message;
+  else
+    *out_message = ServiceWorkerStatusToString(status);
+  switch (status) {
+    case SERVICE_WORKER_OK:
+      NOTREACHED() << "Calling this when status == OK is not allowed";
+      return;
+
+    case SERVICE_WORKER_ERROR_START_WORKER_FAILED:
+    case SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED:
+    case SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND:
+    case SERVICE_WORKER_ERROR_REDUNDANT:
+    case SERVICE_WORKER_ERROR_DISALLOWED:
+      *out_error = blink::mojom::ServiceWorkerErrorType::kInstall;
+      return;
+
+    case SERVICE_WORKER_ERROR_NOT_FOUND:
+      *out_error = blink::mojom::ServiceWorkerErrorType::kNotFound;
+      return;
+
+    case SERVICE_WORKER_ERROR_NETWORK:
+      *out_error = blink::mojom::ServiceWorkerErrorType::kNetwork;
+      return;
+
+    case SERVICE_WORKER_ERROR_SCRIPT_EVALUATE_FAILED:
+      *out_error = blink::mojom::ServiceWorkerErrorType::kScriptEvaluateFailed;
+      return;
+
+    case SERVICE_WORKER_ERROR_SECURITY:
+      *out_error = blink::mojom::ServiceWorkerErrorType::kSecurity;
+      return;
+
+    case SERVICE_WORKER_ERROR_TIMEOUT:
+      *out_error = blink::mojom::ServiceWorkerErrorType::kTimeout;
+      return;
+
+    case SERVICE_WORKER_ERROR_ABORT:
+      *out_error = blink::mojom::ServiceWorkerErrorType::kAbort;
+      return;
+
+    case SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED:
+    case SERVICE_WORKER_ERROR_IPC_FAILED:
+    case SERVICE_WORKER_ERROR_FAILED:
+    case SERVICE_WORKER_ERROR_EXISTS:
+    case SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED:
+    case SERVICE_WORKER_ERROR_STATE:
+    case SERVICE_WORKER_ERROR_DISK_CACHE:
+    case SERVICE_WORKER_ERROR_MAX_VALUE:
+      // Unexpected, or should have bailed out before calling this, or we don't
+      // have a corresponding blink error code yet.
+      break;  // Fall through to NOTREACHED().
+  }
+  NOTREACHED() << "Got unexpected error code: " << status << " "
+               << ServiceWorkerStatusToString(status);
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_registration_status.h b/content/browser/service_worker/service_worker_registration_status.h
index 7f94b26..fe584a5c 100644
--- a/content/browser/service_worker/service_worker_registration_status.h
+++ b/content/browser/service_worker/service_worker_registration_status.h
@@ -11,13 +11,24 @@
 
 namespace content {
 
-// This should only be called for errors, where status != OK.
+// TODO(leonhsl): Eliminate this function once all its users changed to use
+// GetServiceWorkerErrorTypeForRegistration() which gets out a std::string
+// |message| rather than a base::string16. The legacy IPC used base::string16 as
+// an old WebKit convention. This should only be called for errors, where status
+// != OK.
 void GetServiceWorkerRegistrationStatusResponse(
     ServiceWorkerStatusCode status,
     const std::string& status_message,
     blink::mojom::ServiceWorkerErrorType* error_type,
     base::string16* message);
 
+// This should only be called for errors, where status != OK.
+void GetServiceWorkerErrorTypeForRegistration(
+    ServiceWorkerStatusCode status,
+    const std::string& status_message,
+    blink::mojom::ServiceWorkerErrorType* out_error,
+    std::string* out_message);
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_REGISTRATION_STATUS_H_
diff --git a/content/browser/web_contents/web_contents_android.cc b/content/browser/web_contents/web_contents_android.cc
index f45cf9a7..66742a9 100644
--- a/content/browser/web_contents/web_contents_android.cc
+++ b/content/browser/web_contents/web_contents_android.cc
@@ -676,6 +676,14 @@
   return Java_WebContentsImpl_createSize(env, size.width(), size.height());
 }
 
+void WebContentsAndroid::SetSize(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj,
+    jint width,
+    jint height) {
+  web_contents_->GetNativeView()->OnSizeChanged(width, height);
+}
+
 ScopedJavaLocalRef<jobject> WebContentsAndroid::GetOrCreateEventForwarder(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj) {
diff --git a/content/browser/web_contents/web_contents_android.h b/content/browser/web_contents/web_contents_android.h
index b206ce4..ff9c7043 100644
--- a/content/browser/web_contents/web_contents_android.h
+++ b/content/browser/web_contents/web_contents_android.h
@@ -212,6 +212,10 @@
   base::android::ScopedJavaLocalRef<jobject> GetFullscreenVideoSize(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
+  void SetSize(JNIEnv* env,
+               const base::android::JavaParamRef<jobject>& obj,
+               jint width,
+               jint height);
 
   base::android::ScopedJavaLocalRef<jobject> GetOrCreateEventForwarder(
       JNIEnv* env,
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 4e1699b..657746b 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -23,7 +23,6 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/process/process.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_number_conversions.h"
@@ -1461,11 +1460,6 @@
 
 void WebContentsImpl::NotifyNavigationStateChanged(
     InvalidateTypes changed_flags) {
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466285
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "466285 WebContentsImpl::NotifyNavigationStateChanged"));
   // Notify the media observer of potential audibility changes.
   if (changed_flags & INVALIDATE_TYPE_TAB) {
     media_web_contents_observer_->MaybeUpdateAudibleState();
@@ -2098,6 +2092,13 @@
                focused_web_contents->interstitial_page_->GetMainFrame())
         ->GetRenderWidgetHost();
   }
+  if (!GuestMode::IsCrossProcessFrameGuest(focused_web_contents) &&
+      focused_web_contents->browser_plugin_guest_) {
+    // If this is a guest, we need to be controlled by our embedder.
+    return focused_web_contents->GetOuterWebContents()
+        ->GetMainFrame()
+        ->GetRenderWidgetHost();
+  }
 
   return focused_web_contents->GetMainFrame()->GetRenderWidgetHost();
 }
@@ -3435,11 +3436,6 @@
     const net::LoadStateWithParam& load_state,
     uint64_t upload_position,
     uint64_t upload_size) {
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466285
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "466285 WebContentsImpl::LoadStateChanged::Start"));
   load_state_ = load_state;
   upload_position_ = upload_position;
   upload_size_ = upload_size;
diff --git a/content/browser/webui/url_data_manager_backend.cc b/content/browser/webui/url_data_manager_backend.cc
index 2d6949d..66e52a3 100644
--- a/content/browser/webui/url_data_manager_backend.cc
+++ b/content/browser/webui/url_data_manager_backend.cc
@@ -18,7 +18,6 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/memory/weak_ptr.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -89,11 +88,6 @@
               int buf_size,
               const scoped_refptr<base::RefCountedMemory>& data,
               int64_t data_offset) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455423 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455423 URLRequestChromeJob::CompleteRead memcpy"));
   memcpy(buf->data(), data->front() + data_offset, buf_size);
 }
 
diff --git a/content/child/BUILD.gn b/content/child/BUILD.gn
index bc8e5ea..b662baa 100644
--- a/content/child/BUILD.gn
+++ b/content/child/BUILD.gn
@@ -148,8 +148,6 @@
     "service_factory.h",
     "service_worker/service_worker_dispatcher.cc",
     "service_worker/service_worker_dispatcher.h",
-    "service_worker/service_worker_event_dispatcher_holder.cc",
-    "service_worker/service_worker_event_dispatcher_holder.h",
     "service_worker/service_worker_handle_reference.cc",
     "service_worker/service_worker_handle_reference.h",
     "service_worker/service_worker_message_filter.cc",
diff --git a/content/child/service_worker/service_worker_dispatcher.cc b/content/child/service_worker/service_worker_dispatcher.cc
index 29b64bcc..f6920ad2 100644
--- a/content/child/service_worker/service_worker_dispatcher.cc
+++ b/content/child/service_worker/service_worker_dispatcher.cc
@@ -72,10 +72,6 @@
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerUpdated, OnUpdated)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerUnregistered,
                         OnUnregistered)
-    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidGetRegistration,
-                        OnDidGetRegistration)
-    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidGetRegistrations,
-                        OnDidGetRegistrations)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidGetRegistrationForReady,
                         OnDidGetRegistrationForReady)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidEnableNavigationPreload,
@@ -88,10 +84,6 @@
                         OnUpdateError)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerUnregistrationError,
                         OnUnregistrationError)
-    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerGetRegistrationError,
-                        OnGetRegistrationError)
-    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerGetRegistrationsError,
-                        OnGetRegistrationsError)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_EnableNavigationPreloadError,
                         OnEnableNavigationPreloadError)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_GetNavigationPreloadStateError,
@@ -137,46 +129,6 @@
       CurrentWorkerId(), request_id, provider_id, registration_id));
 }
 
-void ServiceWorkerDispatcher::GetRegistration(
-    int provider_id,
-    const GURL& document_url,
-    std::unique_ptr<WebServiceWorkerGetRegistrationCallbacks> callbacks) {
-  DCHECK(callbacks);
-
-  if (document_url.possibly_invalid_spec().size() > url::kMaxURLChars) {
-    std::string error_message(kServiceWorkerGetRegistrationErrorPrefix);
-    error_message += "The provided documentURL is too long.";
-    callbacks->OnError(
-        WebServiceWorkerError(blink::mojom::ServiceWorkerErrorType::kSecurity,
-                              blink::WebString::FromASCII(error_message)));
-    return;
-  }
-
-  int request_id =
-      pending_get_registration_callbacks_.Add(std::move(callbacks));
-  TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
-                           "ServiceWorkerDispatcher::GetRegistration",
-                           request_id,
-                           "Document URL", document_url.spec());
-  thread_safe_sender_->Send(new ServiceWorkerHostMsg_GetRegistration(
-      CurrentWorkerId(), request_id, provider_id, document_url));
-}
-
-void ServiceWorkerDispatcher::GetRegistrations(
-    int provider_id,
-    std::unique_ptr<WebServiceWorkerGetRegistrationsCallbacks> callbacks) {
-  DCHECK(callbacks);
-
-  int request_id =
-      pending_get_registrations_callbacks_.Add(std::move(callbacks));
-
-  TRACE_EVENT_ASYNC_BEGIN0("ServiceWorker",
-                           "ServiceWorkerDispatcher::GetRegistrations",
-                           request_id);
-  thread_safe_sender_->Send(new ServiceWorkerHostMsg_GetRegistrations(
-      CurrentWorkerId(), request_id, provider_id));
-}
-
 void ServiceWorkerDispatcher::GetRegistrationForReady(
     int provider_id,
     std::unique_ptr<WebServiceWorkerGetRegistrationForReadyCallbacks>
@@ -392,69 +344,6 @@
   pending_unregistration_callbacks_.Remove(request_id);
 }
 
-void ServiceWorkerDispatcher::OnDidGetRegistration(
-    int thread_id,
-    int request_id,
-    const ServiceWorkerRegistrationObjectInfo& info,
-    const ServiceWorkerVersionAttributes& attrs) {
-  TRACE_EVENT_ASYNC_STEP_INTO0(
-      "ServiceWorker",
-      "ServiceWorkerDispatcher::GetRegistration",
-      request_id,
-      "OnDidGetRegistration");
-  TRACE_EVENT_ASYNC_END0("ServiceWorker",
-                         "ServiceWorkerDispatcher::GetRegistration",
-                         request_id);
-  WebServiceWorkerGetRegistrationCallbacks* callbacks =
-      pending_get_registration_callbacks_.Lookup(request_id);
-  DCHECK(callbacks);
-  if (!callbacks)
-    return;
-
-  scoped_refptr<WebServiceWorkerRegistrationImpl> registration;
-  if (info.handle_id != kInvalidServiceWorkerRegistrationHandleId)
-    registration = GetOrAdoptRegistration(info, attrs);
-
-  callbacks->OnSuccess(
-      WebServiceWorkerRegistrationImpl::CreateHandle(registration));
-  pending_get_registration_callbacks_.Remove(request_id);
-}
-
-void ServiceWorkerDispatcher::OnDidGetRegistrations(
-    int thread_id,
-    int request_id,
-    const std::vector<ServiceWorkerRegistrationObjectInfo>& infos,
-    const std::vector<ServiceWorkerVersionAttributes>& attrs) {
-  TRACE_EVENT_ASYNC_STEP_INTO0(
-      "ServiceWorker",
-      "ServiceWorkerDispatcher::GetRegistrations",
-      request_id,
-      "OnDidGetRegistrations");
-  TRACE_EVENT_ASYNC_END0("ServiceWorker",
-                         "ServiceWorkerDispatcher::GetRegistrations",
-                         request_id);
-
-  WebServiceWorkerGetRegistrationsCallbacks* callbacks =
-      pending_get_registrations_callbacks_.Lookup(request_id);
-  DCHECK(callbacks);
-  if (!callbacks)
-    return;
-
-  using WebServiceWorkerRegistrationHandles =
-      WebServiceWorkerProvider::WebServiceWorkerRegistrationHandles;
-  std::unique_ptr<WebServiceWorkerRegistrationHandles> registrations =
-      base::MakeUnique<WebServiceWorkerRegistrationHandles>(infos.size());
-  for (size_t i = 0; i < infos.size(); ++i) {
-    if (infos[i].handle_id == kInvalidServiceWorkerHandleId)
-      continue;
-    (*registrations)[i] = WebServiceWorkerRegistrationImpl::CreateHandle(
-        GetOrAdoptRegistration(infos[i], attrs[i]));
-  }
-
-  callbacks->OnSuccess(std::move(registrations));
-  pending_get_registrations_callbacks_.Remove(request_id);
-}
-
 void ServiceWorkerDispatcher::OnDidGetRegistrationForReady(
     int thread_id,
     int request_id,
@@ -561,54 +450,6 @@
   pending_unregistration_callbacks_.Remove(request_id);
 }
 
-void ServiceWorkerDispatcher::OnGetRegistrationError(
-    int thread_id,
-    int request_id,
-    blink::mojom::ServiceWorkerErrorType error_type,
-    const base::string16& message) {
-  TRACE_EVENT_ASYNC_STEP_INTO0(
-      "ServiceWorker",
-      "ServiceWorkerDispatcher::GetRegistration",
-      request_id,
-      "OnGetRegistrationError");
-  TRACE_EVENT_ASYNC_END0("ServiceWorker",
-                         "ServiceWorkerDispatcher::GetRegistration",
-                         request_id);
-  WebServiceWorkerGetRegistrationCallbacks* callbacks =
-      pending_get_registration_callbacks_.Lookup(request_id);
-  DCHECK(callbacks);
-  if (!callbacks)
-    return;
-
-  callbacks->OnError(
-      WebServiceWorkerError(error_type, blink::WebString::FromUTF16(message)));
-  pending_get_registration_callbacks_.Remove(request_id);
-}
-
-void ServiceWorkerDispatcher::OnGetRegistrationsError(
-    int thread_id,
-    int request_id,
-    blink::mojom::ServiceWorkerErrorType error_type,
-    const base::string16& message) {
-  TRACE_EVENT_ASYNC_STEP_INTO0(
-      "ServiceWorker",
-      "ServiceWorkerDispatcher::GetRegistrations",
-      request_id,
-      "OnGetRegistrationsError");
-  TRACE_EVENT_ASYNC_END0("ServiceWorker",
-                         "ServiceWorkerDispatcher::GetRegistrations",
-                         request_id);
-  WebServiceWorkerGetRegistrationsCallbacks* callbacks =
-      pending_get_registrations_callbacks_.Lookup(request_id);
-  DCHECK(callbacks);
-  if (!callbacks)
-    return;
-
-  callbacks->OnError(
-      WebServiceWorkerError(error_type, blink::WebString::FromUTF16(message)));
-  pending_get_registrations_callbacks_.Remove(request_id);
-}
-
 void ServiceWorkerDispatcher::OnEnableNavigationPreloadError(
     int thread_id,
     int request_id,
diff --git a/content/child/service_worker/service_worker_dispatcher.h b/content/child/service_worker/service_worker_dispatcher.h
index 175aaf8..844c59be 100644
--- a/content/child/service_worker/service_worker_dispatcher.h
+++ b/content/child/service_worker/service_worker_dispatcher.h
@@ -24,8 +24,6 @@
 #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerRegistration.h"
 #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerState.h"
 
-class GURL;
-
 namespace base {
 class SingleThreadTaskRunner;
 }
@@ -59,12 +57,6 @@
   typedef blink::WebServiceWorkerRegistration::
       WebServiceWorkerUnregistrationCallbacks
           WebServiceWorkerUnregistrationCallbacks;
-  typedef
-      blink::WebServiceWorkerProvider::WebServiceWorkerGetRegistrationCallbacks
-      WebServiceWorkerGetRegistrationCallbacks;
-  typedef
-      blink::WebServiceWorkerProvider::WebServiceWorkerGetRegistrationsCallbacks
-      WebServiceWorkerGetRegistrationsCallbacks;
   typedef blink::WebServiceWorkerProvider::
       WebServiceWorkerGetRegistrationForReadyCallbacks
           WebServiceWorkerGetRegistrationForReadyCallbacks;
@@ -92,15 +84,6 @@
       int provider_id,
       int64_t registration_id,
       std::unique_ptr<WebServiceWorkerUnregistrationCallbacks> callbacks);
-  // Corresponds to navigator.serviceWorker.getRegistration().
-  void GetRegistration(
-      int provider_id,
-      const GURL& document_url,
-      std::unique_ptr<WebServiceWorkerGetRegistrationCallbacks> callbacks);
-  // Corresponds to navigator.serviceWorker.getRegistrations().
-  void GetRegistrations(
-      int provider_id,
-      std::unique_ptr<WebServiceWorkerGetRegistrationsCallbacks> callbacks);
 
   void GetRegistrationForReady(
       int provider_id,
@@ -174,10 +157,6 @@
       base::IDMap<std::unique_ptr<WebServiceWorkerUpdateCallbacks>>;
   using UnregistrationCallbackMap =
       base::IDMap<std::unique_ptr<WebServiceWorkerUnregistrationCallbacks>>;
-  using GetRegistrationCallbackMap =
-      base::IDMap<std::unique_ptr<WebServiceWorkerGetRegistrationCallbacks>>;
-  using GetRegistrationsCallbackMap =
-      base::IDMap<std::unique_ptr<WebServiceWorkerGetRegistrationsCallbacks>>;
   using GetRegistrationForReadyCallbackMap = base::IDMap<
       std::unique_ptr<WebServiceWorkerGetRegistrationForReadyCallbacks>>;
   using EnableNavigationPreloadCallbackMap =
@@ -206,15 +185,6 @@
   void OnUnregistered(int thread_id,
                       int request_id,
                       bool is_success);
-  void OnDidGetRegistration(int thread_id,
-                            int request_id,
-                            const ServiceWorkerRegistrationObjectInfo& info,
-                            const ServiceWorkerVersionAttributes& attrs);
-  void OnDidGetRegistrations(
-      int thread_id,
-      int request_id,
-      const std::vector<ServiceWorkerRegistrationObjectInfo>& infos,
-      const std::vector<ServiceWorkerVersionAttributes>& attrs);
   void OnDidGetRegistrationForReady(
       int thread_id,
       int request_id,
@@ -233,14 +203,6 @@
                              int request_id,
                              blink::mojom::ServiceWorkerErrorType error_type,
                              const base::string16& message);
-  void OnGetRegistrationError(int thread_id,
-                              int request_id,
-                              blink::mojom::ServiceWorkerErrorType error_type,
-                              const base::string16& message);
-  void OnGetRegistrationsError(int thread_id,
-                               int request_id,
-                               blink::mojom::ServiceWorkerErrorType error_type,
-                               const base::string16& message);
   void OnEnableNavigationPreloadError(
       int thread_id,
       int request_id,
@@ -290,8 +252,6 @@
 
   UpdateCallbackMap pending_update_callbacks_;
   UnregistrationCallbackMap pending_unregistration_callbacks_;
-  GetRegistrationCallbackMap pending_get_registration_callbacks_;
-  GetRegistrationsCallbackMap pending_get_registrations_callbacks_;
   GetRegistrationForReadyCallbackMap get_for_ready_callbacks_;
   EnableNavigationPreloadCallbackMap enable_navigation_preload_callbacks_;
   GetNavigationPreloadStateCallbackMap get_navigation_preload_state_callbacks_;
diff --git a/content/child/service_worker/service_worker_event_dispatcher_holder.cc b/content/child/service_worker/service_worker_event_dispatcher_holder.cc
deleted file mode 100644
index 6b63bcaae..0000000
--- a/content/child/service_worker/service_worker_event_dispatcher_holder.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/child/service_worker/service_worker_event_dispatcher_holder.h"
-
-namespace content {
-
-ServiceWorkerEventDispatcherHolder::ServiceWorkerEventDispatcherHolder(
-    mojom::ServiceWorkerEventDispatcherPtrInfo ptr_info) {
-  event_dispatcher_.Bind(std::move(ptr_info));
-}
-
-ServiceWorkerEventDispatcherHolder::ServiceWorkerEventDispatcherHolder(
-    mojom::ServiceWorkerEventDispatcher* raw_ptr)
-    : event_dispatcher_raw_ptr_for_testing_(raw_ptr) {}
-
-ServiceWorkerEventDispatcherHolder::~ServiceWorkerEventDispatcherHolder() =
-    default;
-
-}  // namespace content
diff --git a/content/child/service_worker/service_worker_event_dispatcher_holder.h b/content/child/service_worker/service_worker_event_dispatcher_holder.h
deleted file mode 100644
index 22a9a14..0000000
--- a/content/child/service_worker/service_worker_event_dispatcher_holder.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_CHILD_SERVICE_WORKER_SERVICE_WORKER_EVENT_DISPATCHER_HOLDER_H_
-#define CONTENT_CHILD_SERVICE_WORKER_SERVICE_WORKER_EVENT_DISPATCHER_HOLDER_H_
-
-#include "base/memory/ref_counted.h"
-#include "content/common/content_export.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
-
-namespace content {
-
-// Allows mojo::ServiceWorkerEventDispatcherPtr to be shared by
-// multiple owners.
-class CONTENT_EXPORT ServiceWorkerEventDispatcherHolder
-    : public base::RefCounted<ServiceWorkerEventDispatcherHolder> {
- public:
-  explicit ServiceWorkerEventDispatcherHolder(
-      mojom::ServiceWorkerEventDispatcherPtrInfo ptr_info);
-
-  mojom::ServiceWorkerEventDispatcher* get() {
-    if (event_dispatcher_raw_ptr_for_testing_)
-      return event_dispatcher_raw_ptr_for_testing_;
-    return event_dispatcher_.get();
-  }
-
- private:
-  friend class ServiceWorkerSubresourceLoaderTest;
-  friend class base::RefCounted<ServiceWorkerEventDispatcherHolder>;
-  ~ServiceWorkerEventDispatcherHolder();
-
-  static scoped_refptr<ServiceWorkerEventDispatcherHolder> CreateForTesting(
-      mojom::ServiceWorkerEventDispatcher* raw_ptr) {
-    return make_scoped_refptr(new ServiceWorkerEventDispatcherHolder(raw_ptr));
-  }
-  explicit ServiceWorkerEventDispatcherHolder(
-      mojom::ServiceWorkerEventDispatcher* raw_ptr);
-
-  mojom::ServiceWorkerEventDispatcher* event_dispatcher_raw_ptr_for_testing_ =
-      nullptr;
-  mojom::ServiceWorkerEventDispatcherPtr event_dispatcher_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_CHILD_SERVICE_WORKER_SERVICE_WORKER_EVENT_DISPATCHER_HOLDER_H_
diff --git a/content/child/service_worker/service_worker_message_filter.cc b/content/child/service_worker/service_worker_message_filter.cc
index e8a4a97..6e019d89 100644
--- a/content/child/service_worker/service_worker_message_filter.cc
+++ b/content/child/service_worker/service_worker_message_filter.cc
@@ -67,10 +67,6 @@
   // Specifically handle some messages in case we failed to post task
   // to the thread (meaning that the context on the thread is now gone).
   IPC_BEGIN_MESSAGE_MAP(ServiceWorkerMessageFilter, msg)
-    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidGetRegistration,
-                        OnStaleGetRegistration)
-    IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidGetRegistrations,
-                        OnStaleGetRegistrations)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_DidGetRegistrationForReady,
                         OnStaleGetRegistration)
     IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetVersionAttributes,
@@ -96,16 +92,6 @@
   SendRegistrationObjectDestroyed(thread_safe_sender(), info.handle_id);
 }
 
-void ServiceWorkerMessageFilter::OnStaleGetRegistrations(
-    int thread_id,
-    int request_id,
-    const std::vector<ServiceWorkerRegistrationObjectInfo>& infos,
-    const std::vector<ServiceWorkerVersionAttributes>& attrs) {
-  DCHECK_EQ(infos.size(), attrs.size());
-  for (size_t i = 0; i < infos.size(); ++i)
-    OnStaleGetRegistration(thread_id, request_id, infos[i], attrs[i]);
-}
-
 void ServiceWorkerMessageFilter::OnStaleSetVersionAttributes(
     int thread_id,
     int registration_handle_id,
diff --git a/content/child/service_worker/service_worker_message_filter.h b/content/child/service_worker/service_worker_message_filter.h
index dc55284a..0c41d4b 100644
--- a/content/child/service_worker/service_worker_message_filter.h
+++ b/content/child/service_worker/service_worker_message_filter.h
@@ -43,11 +43,6 @@
                               int request_id,
                               const ServiceWorkerRegistrationObjectInfo& info,
                               const ServiceWorkerVersionAttributes& attrs);
-  void OnStaleGetRegistrations(
-      int thread_id,
-      int request_id,
-      const std::vector<ServiceWorkerRegistrationObjectInfo>& info,
-      const std::vector<ServiceWorkerVersionAttributes>& attrs);
   void OnStaleSetVersionAttributes(
       int thread_id,
       int registration_handle_id,
diff --git a/content/child/service_worker/service_worker_provider_context.cc b/content/child/service_worker/service_worker_provider_context.cc
index f99ae5c..f46d4b0 100644
--- a/content/child/service_worker/service_worker_provider_context.cc
+++ b/content/child/service_worker/service_worker_provider_context.cc
@@ -8,11 +8,11 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/stl_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/child/child_thread_impl.h"
 #include "content/child/service_worker/service_worker_dispatcher.h"
-#include "content/child/service_worker/service_worker_event_dispatcher_holder.h"
 #include "content/child/service_worker/service_worker_handle_reference.h"
 #include "content/child/service_worker/service_worker_registration_handle_reference.h"
 #include "content/child/service_worker/service_worker_subresource_loader.h"
@@ -43,7 +43,6 @@
 
   // S13nServiceWorker:
   // Used when we create |subresource_loader_factory|.
-  scoped_refptr<ServiceWorkerEventDispatcherHolder> event_dispatcher;
   scoped_refptr<ChildURLLoaderFactoryGetter> default_loader_factory_getter;
 
   // Tracks feature usage for UseCounter.
@@ -155,12 +154,14 @@
   state->used_features = used_features;
   if (event_dispatcher_ptr_info.is_valid()) {
     CHECK(ServiceWorkerUtils::IsServicificationEnabled());
-    state->event_dispatcher =
-        base::MakeRefCounted<ServiceWorkerEventDispatcherHolder>(
-            std::move(event_dispatcher_ptr_info));
+    mojom::ServiceWorkerEventDispatcherPtr event_dispatcher_ptr;
+    event_dispatcher_ptr.Bind(std::move(event_dispatcher_ptr_info));
+    auto event_dispatcher = base::MakeRefCounted<
+        base::RefCountedData<mojom::ServiceWorkerEventDispatcherPtr>>();
+    event_dispatcher->data = std::move(event_dispatcher_ptr);
     mojo::MakeStrongBinding(
         base::MakeUnique<ServiceWorkerSubresourceLoaderFactory>(
-            state->event_dispatcher, state->default_loader_factory_getter,
+            event_dispatcher, state->default_loader_factory_getter,
             state->controller->url().GetOrigin()),
         mojo::MakeRequest(&state->subresource_loader_factory));
   }
diff --git a/content/child/service_worker/service_worker_subresource_loader.cc b/content/child/service_worker/service_worker_subresource_loader.cc
index 997a4cc..4f5f5563 100644
--- a/content/child/service_worker/service_worker_subresource_loader.cc
+++ b/content/child/service_worker/service_worker_subresource_loader.cc
@@ -7,7 +7,6 @@
 #include "base/atomic_sequence_num.h"
 #include "base/callback.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "content/child/service_worker/service_worker_event_dispatcher_holder.h"
 #include "content/common/service_worker/service_worker_loader_helpers.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/child/child_url_loader_factory_getter.h"
@@ -37,7 +36,8 @@
     const ResourceRequest& resource_request,
     mojom::URLLoaderClientPtr client,
     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
-    scoped_refptr<ServiceWorkerEventDispatcherHolder> event_dispatcher,
+    scoped_refptr<base::RefCountedData<mojom::ServiceWorkerEventDispatcherPtr>>
+        event_dispatcher,
     scoped_refptr<ChildURLLoaderFactoryGetter> default_loader_factory_getter,
     const GURL& controller_origin)
     : url_loader_client_(std::move(client)),
@@ -53,7 +53,7 @@
       blob_client_binding_(this),
       default_loader_factory_getter_(std::move(default_loader_factory_getter)),
       weak_factory_(this) {
-  DCHECK(event_dispatcher_ && event_dispatcher_->get());
+  DCHECK(event_dispatcher_ && event_dispatcher_->data);
   url_loader_binding_.set_connection_error_handler(base::BindOnce(
       &ServiceWorkerSubresourceLoader::DeleteSoon, weak_factory_.GetWeakPtr()));
   StartRequest(resource_request);
@@ -80,7 +80,7 @@
   mojom::ServiceWorkerFetchResponseCallbackPtr response_callback_ptr;
   response_callback_binding_.Bind(mojo::MakeRequest(&response_callback_ptr));
 
-  event_dispatcher_->get()->DispatchFetchEvent(
+  event_dispatcher_->data->DispatchFetchEvent(
       GetNextFetchEventID(), *request, mojom::FetchEventPreloadHandlePtr(),
       std::move(response_callback_ptr),
       base::Bind(&ServiceWorkerSubresourceLoader::OnFetchEventFinished,
@@ -298,7 +298,8 @@
 // ServiceWorkerSubresourceLoaderFactory ------------------------------------
 
 ServiceWorkerSubresourceLoaderFactory::ServiceWorkerSubresourceLoaderFactory(
-    scoped_refptr<ServiceWorkerEventDispatcherHolder> event_dispatcher,
+    scoped_refptr<base::RefCountedData<mojom::ServiceWorkerEventDispatcherPtr>>
+        event_dispatcher,
     scoped_refptr<ChildURLLoaderFactoryGetter> default_loader_factory_getter,
     const GURL& controller_origin)
     : event_dispatcher_(std::move(event_dispatcher)),
diff --git a/content/child/service_worker/service_worker_subresource_loader.h b/content/child/service_worker/service_worker_subresource_loader.h
index c313b6b..3e01642c 100644
--- a/content/child/service_worker/service_worker_subresource_loader.h
+++ b/content/child/service_worker/service_worker_subresource_loader.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_CHILD_SERVICE_WORKER_SERVICE_WORKER_SUBRESOURCE_LOADER_H_
 #define CONTENT_CHILD_SERVICE_WORKER_SERVICE_WORKER_SUBRESOURCE_LOADER_H_
 
+#include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "content/common/content_export.h"
@@ -17,7 +18,6 @@
 
 namespace content {
 
-class ServiceWorkerEventDispatcherHolder;
 class ChildURLLoaderFactoryGetter;
 
 // S13nServiceWorker:
@@ -40,7 +40,9 @@
       const ResourceRequest& resource_request,
       mojom::URLLoaderClientPtr client,
       const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
-      scoped_refptr<ServiceWorkerEventDispatcherHolder> event_dispatcher,
+      scoped_refptr<
+          base::RefCountedData<mojom::ServiceWorkerEventDispatcherPtr>>
+          event_dispatcher,
       scoped_refptr<ChildURLLoaderFactoryGetter> default_loader_factory_getter,
       const GURL& controller_origin);
 
@@ -108,7 +110,8 @@
   // For handling FetchEvent response.
   mojo::Binding<ServiceWorkerFetchResponseCallback> response_callback_binding_;
 
-  scoped_refptr<ServiceWorkerEventDispatcherHolder> event_dispatcher_;
+  scoped_refptr<base::RefCountedData<mojom::ServiceWorkerEventDispatcherPtr>>
+      event_dispatcher_;
 
   // These are given by the constructor (as the params for
   // URLLoaderFactory::CreateLoaderAndStart).
@@ -155,7 +158,8 @@
   // network fallback. |controller_origin| is used to create a new Blob public
   // URL (this will become unnecessary once we switch over to MojoBlobs).
   ServiceWorkerSubresourceLoaderFactory(
-      scoped_refptr<ServiceWorkerEventDispatcherHolder> event_dispatcher,
+      scoped_refptr<base::RefCountedData<
+          mojom::ServiceWorkerEventDispatcherPtr>> event_dispatcher,
       scoped_refptr<ChildURLLoaderFactoryGetter> default_loader_factory_getter,
       const GURL& controller_origin);
 
@@ -173,7 +177,8 @@
   void Clone(mojom::URLLoaderFactoryRequest request) override;
 
  private:
-  scoped_refptr<ServiceWorkerEventDispatcherHolder> event_dispatcher_;
+  scoped_refptr<base::RefCountedData<mojom::ServiceWorkerEventDispatcherPtr>>
+      event_dispatcher_;
 
   // Contains a set of default loader factories for the associated loading
   // context. Used to load a blob, and for network fallback.
diff --git a/content/child/service_worker/service_worker_subresource_loader_unittest.cc b/content/child/service_worker/service_worker_subresource_loader_unittest.cc
index 7cc760c..11d0f64 100644
--- a/content/child/service_worker/service_worker_subresource_loader_unittest.cc
+++ b/content/child/service_worker/service_worker_subresource_loader_unittest.cc
@@ -6,9 +6,9 @@
 
 #include "base/run_loop.h"
 #include "content/child/child_url_loader_factory_getter_impl.h"
-#include "content/child/service_worker/service_worker_event_dispatcher_holder.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_url_loader_client.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -22,6 +22,10 @@
     MockServiceWorkerEventDispatcher() = default;
     ~MockServiceWorkerEventDispatcher() override = default;
 
+    void AddBinding(mojom::ServiceWorkerEventDispatcherRequest request) {
+      bindings_.AddBinding(this, std::move(request));
+    }
+
     // mojom::ServiceWorkerEventDispatcher:
     void DispatchInstallEvent(
         mojom::ServiceWorkerInstallEventMethodsAssociatedPtrInfo client,
@@ -124,6 +128,7 @@
    private:
     int fetch_event_count_ = 0;
     ServiceWorkerFetchRequest fetch_event_request_;
+    mojo::BindingSet<mojom::ServiceWorkerEventDispatcher> bindings_;
     DISALLOW_COPY_AND_ASSIGN(MockServiceWorkerEventDispatcher);
   };
 
@@ -143,10 +148,15 @@
 
     EXPECT_EQ(0, event_dispatcher_.fetch_event_count());
 
+    mojom::ServiceWorkerEventDispatcherPtr event_dispatcher_ptr;
+    event_dispatcher_.AddBinding(mojo::MakeRequest(&event_dispatcher_ptr));
+    auto shared_event_dispatcher = base::MakeRefCounted<
+        base::RefCountedData<mojom::ServiceWorkerEventDispatcherPtr>>();
+    shared_event_dispatcher->data = std::move(event_dispatcher_ptr);
+
     ServiceWorkerSubresourceLoaderFactory loader_factory(
-        ServiceWorkerEventDispatcherHolder::CreateForTesting(
-            &event_dispatcher_),
-        loader_factory_getter, request.url.GetOrigin());
+        std::move(shared_event_dispatcher), loader_factory_getter,
+        request.url.GetOrigin());
     loader_factory.CreateLoaderAndStart(
         mojo::MakeRequest(&url_loader), 0, 0, mojom::kURLLoadOptionNone,
         request, url_loader_client.CreateInterfacePtr(),
diff --git a/content/child/service_worker/web_service_worker_provider_impl.cc b/content/child/service_worker/web_service_worker_provider_impl.cc
index 6a07648..d0c5231 100644
--- a/content/child/service_worker/web_service_worker_provider_impl.cc
+++ b/content/child/service_worker/web_service_worker_provider_impl.cc
@@ -24,6 +24,13 @@
 
 namespace content {
 
+namespace {
+
+const char kLostConnectionErrorMessage[] =
+    "Lost connection to the service worker system.";
+
+}  // anonymous namespace
+
 WebServiceWorkerProviderImpl::WebServiceWorkerProviderImpl(
     ThreadSafeSender* thread_safe_sender,
     ServiceWorkerProviderContext* context)
@@ -86,7 +93,7 @@
 
   if (!context_->container_host()) {
     std::string error_message(kServiceWorkerRegisterErrorPrefix);
-    error_message += "Lost connection to the service worker system.";
+    error_message += kLostConnectionErrorMessage;
     callbacks->OnError(blink::WebServiceWorkerError(
         blink::mojom::ServiceWorkerErrorType::kAbort,
         blink::WebString::FromASCII(error_message)));
@@ -107,16 +114,60 @@
 }
 
 void WebServiceWorkerProviderImpl::GetRegistration(
-    const blink::WebURL& document_url,
+    const blink::WebURL& web_document_url,
     std::unique_ptr<WebServiceWorkerGetRegistrationCallbacks> callbacks) {
-  GetDispatcher()->GetRegistration(context_->provider_id(), document_url,
-                                   std::move(callbacks));
+  DCHECK(callbacks);
+  GURL document_url(web_document_url);
+  if (document_url.possibly_invalid_spec().size() > url::kMaxURLChars) {
+    std::string error_message(kServiceWorkerGetRegistrationErrorPrefix);
+    error_message += "The provided documentURL is too long.";
+    callbacks->OnError(blink::WebServiceWorkerError(
+        blink::mojom::ServiceWorkerErrorType::kSecurity,
+        blink::WebString::FromASCII(error_message)));
+    return;
+  }
+
+  if (!context_->container_host()) {
+    std::string error_message(kServiceWorkerGetRegistrationErrorPrefix);
+    error_message += kLostConnectionErrorMessage;
+    callbacks->OnError(blink::WebServiceWorkerError(
+        blink::mojom::ServiceWorkerErrorType::kAbort,
+        blink::WebString::FromASCII(error_message)));
+    return;
+  }
+
+  TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
+                           "WebServiceWorkerProviderImpl::GetRegistration",
+                           this, "Document URL", document_url.spec());
+  // As |this| owns |context_| which owns the
+  // mojom::ServiceWorkerContainerHostAssociatedPtr, using Unretained() here
+  // should be guaranteed safe.
+  context_->container_host()->GetRegistration(
+      document_url,
+      base::BindOnce(&WebServiceWorkerProviderImpl::OnDidGetRegistration,
+                     base::Unretained(this), std::move(callbacks)));
 }
 
 void WebServiceWorkerProviderImpl::GetRegistrations(
     std::unique_ptr<WebServiceWorkerGetRegistrationsCallbacks> callbacks) {
-  GetDispatcher()->GetRegistrations(context_->provider_id(),
-                                    std::move(callbacks));
+  DCHECK(callbacks);
+  if (!context_->container_host()) {
+    std::string error_message(kServiceWorkerGetRegistrationsErrorPrefix);
+    error_message += kLostConnectionErrorMessage;
+    callbacks->OnError(blink::WebServiceWorkerError(
+        blink::mojom::ServiceWorkerErrorType::kAbort,
+        blink::WebString::FromASCII(error_message)));
+    return;
+  }
+
+  TRACE_EVENT_ASYNC_BEGIN0(
+      "ServiceWorker", "WebServiceWorkerProviderImpl::GetRegistrations", this);
+  // As |this| owns |context_| which owns the
+  // mojom::ServiceWorkerContainerHostAssociatedPtr, using Unretained() here
+  // should be guaranteed safe.
+  context_->container_host()->GetRegistrations(
+      base::BindOnce(&WebServiceWorkerProviderImpl::OnDidGetRegistrations,
+                     base::Unretained(this), std::move(callbacks)));
 }
 
 void WebServiceWorkerProviderImpl::GetRegistrationForReady(
@@ -165,19 +216,87 @@
       "ServiceWorker", "WebServiceWorkerProviderImpl::RegisterServiceWorker",
       this, "Error", ServiceWorkerUtils::ErrorTypeToString(error), "Message",
       error_msg ? *error_msg : "Success");
-  if (error == blink::mojom::ServiceWorkerErrorType::kNone) {
-    DCHECK(!error_msg);
-    DCHECK(registration);
-    DCHECK(attributes);
-    callbacks->OnSuccess(WebServiceWorkerRegistrationImpl::CreateHandle(
-        GetDispatcher()->GetOrAdoptRegistration(*registration, *attributes)));
-  } else {
+  if (error != blink::mojom::ServiceWorkerErrorType::kNone) {
     DCHECK(error_msg);
     DCHECK(!registration);
     DCHECK(!attributes);
     callbacks->OnError(blink::WebServiceWorkerError(
         error, blink::WebString::FromASCII(*error_msg)));
+    return;
   }
+
+  DCHECK(!error_msg);
+  DCHECK(registration);
+  DCHECK(attributes);
+  DCHECK_NE(kInvalidServiceWorkerRegistrationHandleId, registration->handle_id);
+  callbacks->OnSuccess(WebServiceWorkerRegistrationImpl::CreateHandle(
+      GetDispatcher()->GetOrAdoptRegistration(*registration, *attributes)));
+}
+
+void WebServiceWorkerProviderImpl::OnDidGetRegistration(
+    std::unique_ptr<WebServiceWorkerGetRegistrationCallbacks> callbacks,
+    blink::mojom::ServiceWorkerErrorType error,
+    const base::Optional<std::string>& error_msg,
+    const base::Optional<ServiceWorkerRegistrationObjectInfo>& registration,
+    const base::Optional<ServiceWorkerVersionAttributes>& attributes) {
+  TRACE_EVENT_ASYNC_END2("ServiceWorker",
+                         "WebServiceWorkerProviderImpl::GetRegistration", this,
+                         "Error", ServiceWorkerUtils::ErrorTypeToString(error),
+                         "Message", error_msg ? *error_msg : "Success");
+  if (error != blink::mojom::ServiceWorkerErrorType::kNone) {
+    DCHECK(error_msg);
+    DCHECK(!registration);
+    DCHECK(!attributes);
+    callbacks->OnError(blink::WebServiceWorkerError(
+        error, blink::WebString::FromASCII(*error_msg)));
+    return;
+  }
+
+  DCHECK(!error_msg);
+  DCHECK(registration);
+  DCHECK(attributes);
+  scoped_refptr<WebServiceWorkerRegistrationImpl> impl;
+  // The handle id is invalid if no corresponding registration has been found
+  // or the found one is uninstalling.
+  if (registration->handle_id != kInvalidServiceWorkerRegistrationHandleId) {
+    impl = GetDispatcher()->GetOrAdoptRegistration(*registration, *attributes);
+  }
+  callbacks->OnSuccess(WebServiceWorkerRegistrationImpl::CreateHandle(impl));
+}
+
+void WebServiceWorkerProviderImpl::OnDidGetRegistrations(
+    std::unique_ptr<WebServiceWorkerGetRegistrationsCallbacks> callbacks,
+    blink::mojom::ServiceWorkerErrorType error,
+    const base::Optional<std::string>& error_msg,
+    const base::Optional<std::vector<ServiceWorkerRegistrationObjectInfo>>&
+        infos,
+    const base::Optional<std::vector<ServiceWorkerVersionAttributes>>& attrs) {
+  TRACE_EVENT_ASYNC_END2("ServiceWorker",
+                         "WebServiceWorkerProviderImpl::GetRegistrations", this,
+                         "Error", ServiceWorkerUtils::ErrorTypeToString(error),
+                         "Message", error_msg ? *error_msg : "Success");
+  if (error != blink::mojom::ServiceWorkerErrorType::kNone) {
+    DCHECK(error_msg);
+    DCHECK(!infos);
+    DCHECK(!attrs);
+    callbacks->OnError(blink::WebServiceWorkerError(
+        error, blink::WebString::FromASCII(*error_msg)));
+    return;
+  }
+
+  DCHECK(!error_msg);
+  DCHECK(infos);
+  DCHECK(attrs);
+  using WebServiceWorkerRegistrationHandles =
+      WebServiceWorkerProvider::WebServiceWorkerRegistrationHandles;
+  std::unique_ptr<WebServiceWorkerRegistrationHandles> registrations =
+      base::MakeUnique<WebServiceWorkerRegistrationHandles>(infos->size());
+  for (size_t i = 0; i < infos->size(); ++i) {
+    DCHECK_NE(kInvalidServiceWorkerRegistrationHandleId, (*infos)[i].handle_id);
+    (*registrations)[i] = WebServiceWorkerRegistrationImpl::CreateHandle(
+        GetDispatcher()->GetOrAdoptRegistration((*infos)[i], (*attrs)[i]));
+  }
+  callbacks->OnSuccess(std::move(registrations));
 }
 
 }  // namespace content
diff --git a/content/child/service_worker/web_service_worker_provider_impl.h b/content/child/service_worker/web_service_worker_provider_impl.h
index b549621..ef6c301 100644
--- a/content/child/service_worker/web_service_worker_provider_impl.h
+++ b/content/child/service_worker/web_service_worker_provider_impl.h
@@ -37,13 +37,13 @@
 
   void SetClient(blink::WebServiceWorkerProviderClient* client) override;
 
-  // Corresponds to navigator.serviceWorker.register().
+  // blink::WebServiceWorkerProvider implementation.
   void RegisterServiceWorker(
-      const blink::WebURL& pattern,
-      const blink::WebURL& script_url,
+      const blink::WebURL& web_pattern,
+      const blink::WebURL& web_script_url,
       std::unique_ptr<WebServiceWorkerRegistrationCallbacks>) override;
   void GetRegistration(
-      const blink::WebURL& document_url,
+      const blink::WebURL& web_document_url,
       std::unique_ptr<WebServiceWorkerGetRegistrationCallbacks>) override;
   void GetRegistrations(
       std::unique_ptr<WebServiceWorkerGetRegistrationsCallbacks>) override;
@@ -67,6 +67,21 @@
       const base::Optional<ServiceWorkerRegistrationObjectInfo>& registration,
       const base::Optional<ServiceWorkerVersionAttributes>& attributes);
 
+  void OnDidGetRegistration(
+      std::unique_ptr<WebServiceWorkerGetRegistrationCallbacks> callbacks,
+      blink::mojom::ServiceWorkerErrorType error,
+      const base::Optional<std::string>& error_msg,
+      const base::Optional<ServiceWorkerRegistrationObjectInfo>& registration,
+      const base::Optional<ServiceWorkerVersionAttributes>& attributes);
+
+  void OnDidGetRegistrations(
+      std::unique_ptr<WebServiceWorkerGetRegistrationsCallbacks> callbacks,
+      blink::mojom::ServiceWorkerErrorType error,
+      const base::Optional<std::string>& error_msg,
+      const base::Optional<std::vector<ServiceWorkerRegistrationObjectInfo>>&
+          infos,
+      const base::Optional<std::vector<ServiceWorkerVersionAttributes>>& attrs);
+
   scoped_refptr<ThreadSafeSender> thread_safe_sender_;
   scoped_refptr<ServiceWorkerProviderContext> context_;
 
diff --git a/content/common/font_cache_dispatcher_win.cc b/content/common/font_cache_dispatcher_win.cc
index 09da8f7..a4c897c 100644
--- a/content/common/font_cache_dispatcher_win.cc
+++ b/content/common/font_cache_dispatcher_win.cc
@@ -9,7 +9,6 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string16.h"
 #include "content/common/child_process_messages.h"
 
@@ -23,10 +22,6 @@
   static FontCache* GetInstance() { return base::Singleton<FontCache>::get(); }
 
   void PreCacheFont(const LOGFONT& font, FontCacheDispatcher* dispatcher) {
-    // TODO(ananta): Remove ScopedTracker below once crbug.com/90127 is fixed.
-    tracked_objects::ScopedTracker tracking_profile(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION("90127 FontCache::PreCacheFont"));
-
     base::AutoLock lock(mutex_);
 
     // Fetch the font into memory.
diff --git a/content/common/input/input_handler.mojom b/content/common/input/input_handler.mojom
index 86aabcc..3383167 100644
--- a/content/common/input/input_handler.mojom
+++ b/content/common/input/input_handler.mojom
@@ -147,6 +147,44 @@
   TouchAction touch_action;
 };
 
+// Interface exposed by the browser to the renderer. The messages
+// contained in here are state changes that the renderer has taken
+// that the browser must be aware of.
+interface WidgetInputHandlerHost {
+  // Cancel the active touch timeout. Occurs when the renderer
+  // has processed a touch-action: none but not yet dispatched
+  // the event to javascript.
+  CancelTouchTimeout();
+
+  // The whitelisted touch action and the associated unique touch event id
+  // for a new touch point sent by the compositor. The unique touch event id is
+  // only needed to verify that the whitelisted touch action is being associated
+  // with the correct touch event. The input event ack state is needed when
+  // the touchstart message was not sent to the renderer and the touch
+  // actions need to be reset and the touch ack timeout needs to be started.
+  SetWhiteListedTouchAction(TouchAction touch_action,
+                            uint32 unique_touch_event_id,
+                            InputEventAckState state);
+
+  // Sent by the compositor when input scroll events are dropped due to bounds
+  // restrictions on the root scroll offset.
+  DidOverscroll(DidOverscrollParams params);
+
+  // Sent by the compositor when a fling animation is stopped.
+  DidStopFlinging();
+
+  // Required for cancelling an ongoing input method composition.
+  ImeCancelComposition();
+
+  // Sends the character bounds after every composition change
+  // to always have correct bound info.
+  ImeCompositionRangeChanged(gfx.mojom.Range range,
+                             array<gfx.mojom.Rect> bounds);
+};
+
+// Interface exposed by the renderer to the browser. This class represents
+// an input interface for an associated Widget object. See FrameInputHandler
+// for an interface at the frame level.
 interface WidgetInputHandler {
   // Tells widget focus has been changed.
   SetFocus(bool focused);
diff --git a/content/common/media/media_stream_options.cc b/content/common/media/media_stream_options.cc
index 7c906c9..4d177a8 100644
--- a/content/common/media/media_stream_options.cc
+++ b/content/common/media/media_stream_options.cc
@@ -4,8 +4,6 @@
 
 #include "content/common/media/media_stream_options.h"
 
-#include "base/logging.h"
-
 namespace content {
 
 const char kMediaStreamSourceTab[] = "tab";
@@ -37,39 +35,4 @@
 
 StreamControls::~StreamControls() {}
 
-// static
-const int StreamDeviceInfo::kNoId = -1;
-
-StreamDeviceInfo::StreamDeviceInfo()
-    : session_id(kNoId) {}
-
-StreamDeviceInfo::StreamDeviceInfo(MediaStreamType service_param,
-                                   const std::string& name_param,
-                                   const std::string& device_param)
-    : device(service_param, device_param, name_param), session_id(kNoId) {}
-
-StreamDeviceInfo::StreamDeviceInfo(MediaStreamType service_param,
-                                   const std::string& name_param,
-                                   const std::string& device_param,
-                                   int sample_rate,
-                                   int channel_layout,
-                                   int frames_per_buffer)
-    : device(service_param,
-             device_param,
-             name_param,
-             sample_rate,
-             channel_layout,
-             frames_per_buffer),
-      session_id(kNoId) {}
-
-StreamDeviceInfo::StreamDeviceInfo(MediaStreamDevice media_stream_device)
-    : device(media_stream_device), session_id(media_stream_device.session_id) {}
-
-// static
-bool StreamDeviceInfo::IsEqual(const StreamDeviceInfo& first,
-                               const StreamDeviceInfo& second) {
-  return first.device.IsEqual(second.device) &&
-      first.session_id == second.session_id;
-}
-
 }  // namespace content
diff --git a/content/common/media/media_stream_options.h b/content/common/media/media_stream_options.h
index 1d8183be..0b6d19e9 100644
--- a/content/common/media/media_stream_options.h
+++ b/content/common/media/media_stream_options.h
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "content/common/content_export.h"
-#include "content/public/common/media_stream_request.h"
 
 namespace content {
 
@@ -56,32 +55,6 @@
   bool disable_local_echo;
 };
 
-// StreamDeviceInfo describes information about a device.
-struct CONTENT_EXPORT StreamDeviceInfo {
-  static const int kNoId;
-
-  StreamDeviceInfo();
-  StreamDeviceInfo(MediaStreamType service_param,
-                   const std::string& name_param,
-                   const std::string& device_param);
-  StreamDeviceInfo(MediaStreamType service_param,
-                   const std::string& name_param,
-                   const std::string& device_param,
-                   int sample_rate,
-                   int channel_layout,
-                   int frames_per_buffer);
-  explicit StreamDeviceInfo(MediaStreamDevice media_stream_device);
-  static bool IsEqual(const StreamDeviceInfo& first,
-                      const StreamDeviceInfo& second);
-
-  MediaStreamDevice device;
-
-  // Id for this capture session. Unique for all sessions of the same type.
-  int session_id;
-};
-
-typedef std::vector<StreamDeviceInfo> StreamDeviceInfoArray;
-
 }  // namespace content
 
 #endif  // CONTENT_COMMON_MEDIA_MEDIA_STREAM_OPTIONS_H_
diff --git a/content/common/native_types.typemap b/content/common/native_types.typemap
index a8e0c0cd..57f7b897 100644
--- a/content/common/native_types.typemap
+++ b/content/common/native_types.typemap
@@ -85,6 +85,7 @@
   "content.mojom.SyntheticPinch=content::SyntheticPinchGestureParams",
   "content.mojom.SyntheticTap=content::SyntheticTapGestureParams",
   "content.mojom.SyntheticPointerAction=content::SyntheticPointerActionListParams",
+  "content.mojom.TouchAction=cc::TouchAction",
   "content.mojom.TouchActionOptional=cc::TouchAction",
   "content.mojom.TouchState=blink::WebTouchPoint::State",
   "content.mojom.WebPopupType=blink::WebPopupType",
diff --git a/content/common/service_worker/service_worker_container.mojom b/content/common/service_worker/service_worker_container.mojom
index 0916bde..0adf263d 100644
--- a/content/common/service_worker/service_worker_container.mojom
+++ b/content/common/service_worker/service_worker_container.mojom
@@ -21,9 +21,29 @@
         string? error_msg,
         ServiceWorkerRegistrationObjectInfo? registration,
         ServiceWorkerVersionAttributes? attributes);
+
+  // Corresponds to navigator.serviceWorker.getRegistration().
+  // Gets the service worker registration for the |client_url|.
+  // On success, |error| is kNone with |registration| and |attributes| set.
+  // Otherwise, |error| and |error_msg| describe the failure.
+  GetRegistration(url.mojom.Url client_url)
+    => (blink.mojom.ServiceWorkerErrorType error,
+        string? error_msg,
+        ServiceWorkerRegistrationObjectInfo? registration,
+        ServiceWorkerVersionAttributes? attributes);
+
+  // Corresponds to navigator.serviceWorker.getRegistrations().
+  // Gets all service worker registrations which have the same origin with
+  // the ServiceWorkerContainer that this interface hosts.
+  // On success, |error| is kNone with |infos| and |attrs| set. Otherwise,
+  // |error| and |error_msg| describe the failure.
+  GetRegistrations()
+    => (blink.mojom.ServiceWorkerErrorType error,
+        string? error_msg,
+        array<ServiceWorkerRegistrationObjectInfo>? infos,
+        array<ServiceWorkerVersionAttributes>? attrs);
+
   // TODO(leonhsl): implement this interface.
-  // GetRegistation();
-  // GetRegistrations();
   // GetRegistrationForReady();
   // GetControllerServiceWorker();
 };
diff --git a/content/common/service_worker/service_worker_messages.h b/content/common/service_worker/service_worker_messages.h
index 2826878..818617cb 100644
--- a/content/common/service_worker/service_worker_messages.h
+++ b/content/common/service_worker/service_worker_messages.h
@@ -183,17 +183,6 @@
                      int /* provider_id */,
                      int64_t /* registration_id */)
 
-IPC_MESSAGE_CONTROL4(ServiceWorkerHostMsg_GetRegistration,
-                     int /* thread_id */,
-                     int /* request_id */,
-                     int /* provider_id */,
-                     GURL /* document_url */)
-
-IPC_MESSAGE_CONTROL3(ServiceWorkerHostMsg_GetRegistrations,
-                     int /* thread_id */,
-                     int /* request_id */,
-                     int /* provider_id */)
-
 IPC_MESSAGE_CONTROL3(ServiceWorkerHostMsg_GetRegistrationForReady,
                      int /* thread_id */,
                      int /* request_id */,
@@ -328,20 +317,6 @@
                      int /* request_id */,
                      bool /* is_success */)
 
-// Response to ServiceWorkerHostMsg_GetRegistration.
-IPC_MESSAGE_CONTROL4(ServiceWorkerMsg_DidGetRegistration,
-                     int /* thread_id */,
-                     int /* request_id */,
-                     content::ServiceWorkerRegistrationObjectInfo,
-                     content::ServiceWorkerVersionAttributes)
-
-// Response to ServiceWorkerHostMsg_GetRegistrations.
-IPC_MESSAGE_CONTROL4(ServiceWorkerMsg_DidGetRegistrations,
-                     int /* thread_id */,
-                     int /* request_id */,
-                     std::vector<content::ServiceWorkerRegistrationObjectInfo>,
-                     std::vector<content::ServiceWorkerVersionAttributes>)
-
 // Response to ServiceWorkerHostMsg_GetRegistrationForReady.
 IPC_MESSAGE_CONTROL4(ServiceWorkerMsg_DidGetRegistrationForReady,
                      int /* thread_id */,
@@ -365,22 +340,6 @@
                      blink::mojom::ServiceWorkerErrorType,
                      base::string16 /* message */)
 
-// Sent when any kind of registration error occurs during a
-// GetRegistration handler above.
-IPC_MESSAGE_CONTROL4(ServiceWorkerMsg_ServiceWorkerGetRegistrationError,
-                     int /* thread_id */,
-                     int /* request_id */,
-                     blink::mojom::ServiceWorkerErrorType,
-                     base::string16 /* message */)
-
-// Sent when any kind of registration error occurs during a
-// GetRegistrations handler above.
-IPC_MESSAGE_CONTROL4(ServiceWorkerMsg_ServiceWorkerGetRegistrationsError,
-                     int /* thread_id */,
-                     int /* request_id */,
-                     blink::mojom::ServiceWorkerErrorType,
-                     base::string16 /* message */)
-
 // Informs the child process that the ServiceWorker's state has changed.
 IPC_MESSAGE_CONTROL3(ServiceWorkerMsg_ServiceWorkerStateChanged,
                      int /* thread_id */,
diff --git a/content/common/widget.mojom b/content/common/widget.mojom
index 342a7f3..a002e4f 100644
--- a/content/common/widget.mojom
+++ b/content/common/widget.mojom
@@ -6,7 +6,7 @@
 
 import "content/common/input/input_handler.mojom";
 
-// Interface representing the Widget.
+// Interface exposed by the renderer for Widgets.
 interface Widget {
-  GetWidgetInputHandler(WidgetInputHandler& request);
+  SetupWidgetInputHandler(WidgetInputHandler& request, WidgetInputHandlerHost host);
 };
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java
index ee29872..043cfef9 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java
@@ -32,6 +32,9 @@
     private final SurfaceView mSurfaceView;
     protected ContentViewCore mContentViewCore;
 
+    private int mWidth;
+    private int mHeight;
+
     /**
      * Constructs a new ContentViewRenderView.
      * This should be called and the {@link ContentViewRenderView} should be added to the view
@@ -101,6 +104,15 @@
         mSurfaceView.setVisibility(VISIBLE);
     }
 
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        mWidth = w;
+        mHeight = h;
+        WebContents webContents =
+                mContentViewCore != null ? mContentViewCore.getWebContents() : null;
+        if (webContents != null) webContents.setSize(w, h);
+    }
+
     /**
      * Sets the background color of the surface view.  This method is necessary because the
      * background color of ContentViewRenderView itself is covered by the background of
@@ -137,7 +149,7 @@
         WebContents webContents = contentViewCore != null ? contentViewCore.getWebContents() : null;
         if (webContents != null) {
             nativeOnPhysicalBackingSizeChanged(
-                    mNativeContentViewRenderView, webContents, getWidth(), getHeight());
+                    mNativeContentViewRenderView, webContents, mWidth, mHeight);
         }
         nativeSetCurrentWebContents(mNativeContentViewRenderView, webContents);
     }
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/KitKatWebContentsAccessibility.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/KitKatWebContentsAccessibility.java
index 6b6233c6..d1eb5d3 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/KitKatWebContentsAccessibility.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/KitKatWebContentsAccessibility.java
@@ -33,9 +33,10 @@
 
     @Override
     protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInfo node,
-            boolean isRoot, boolean isEditableText, String roleDescription, String hint,
-            int selectionStartIndex, int selectionEndIndex) {
+            boolean isRoot, boolean isEditableText, String role, String roleDescription,
+            String hint, int selectionStartIndex, int selectionEndIndex) {
         Bundle bundle = node.getExtras();
+        bundle.putCharSequence("AccessibilityNodeInfo.chromeRole", role);
         bundle.putCharSequence("AccessibilityNodeInfo.roleDescription", roleDescription);
         bundle.putCharSequence("AccessibilityNodeInfo.hint", hint);
         if (isRoot) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/OWebContentsAccessibility.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/OWebContentsAccessibility.java
index 1fb6e67..9a7e0c6 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/OWebContentsAccessibility.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/OWebContentsAccessibility.java
@@ -44,9 +44,9 @@
 
     @Override
     protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInfo node,
-            boolean isRoot, boolean isEditableText, String roleDescription, String hint,
-            int selectionStartIndex, int selectionEndIndex) {
-        super.setAccessibilityNodeInfoKitKatAttributes(node, isRoot, isEditableText,
+            boolean isRoot, boolean isEditableText, String role, String roleDescription,
+            String hint, int selectionStartIndex, int selectionEndIndex) {
+        super.setAccessibilityNodeInfoKitKatAttributes(node, isRoot, isEditableText, role,
                 roleDescription, hint, selectionStartIndex, selectionEndIndex);
         node.setHintText(hint);
     }
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibility.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibility.java
index f8dd8268ec..71723c7 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibility.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibility.java
@@ -971,8 +971,8 @@
 
     @CalledByNative
     protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInfo node,
-            boolean isRoot, boolean isEditableText, String roleDescription, String hint,
-            int selectionStartIndex, int selectionEndIndex) {
+            boolean isRoot, boolean isEditableText, String role, String roleDescription,
+            String hint, int selectionStartIndex, int selectionEndIndex) {
         // Requires KitKat or higher.
     }
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
index 2114811..3bc17e2 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
@@ -604,6 +604,11 @@
         return nativeGetFullscreenVideoSize(mNativeWebContentsAndroid);
     }
 
+    @Override
+    public void setSize(int width, int height) {
+        nativeSetSize(mNativeWebContentsAndroid, width, height);
+    }
+
     @CalledByNative
     private final void setMediaSession(MediaSessionImpl mediaSession) {
         mMediaSession = mediaSession;
@@ -703,5 +708,6 @@
     private native void nativeSetHasPersistentVideo(long nativeWebContentsAndroid, boolean value);
     private native boolean nativeHasActiveEffectivelyFullscreenVideo(long nativeWebContentsAndroid);
     private native Rect nativeGetFullscreenVideoSize(long nativeWebContentsAndroid);
+    private native void nativeSetSize(long nativeWebContentsAndroid, int width, int height);
     private native EventForwarder nativeGetOrCreateEventForwarder(long nativeWebContentsAndroid);
 }
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java b/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
index b8dc2cb..a1915f9e 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java
@@ -449,4 +449,12 @@
      * @param value Whether there is a persistent video associated with this WebContents.
      */
     public void setHasPersistentVideo(boolean value);
+
+    /**
+     * Set the view size of WebContents. The size is in physical pixel.
+     *
+     * @param width The width of the view.
+     * @param height The height of the view.
+     */
+    void setSize(int width, int height);
 }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java
index a004867a..60268e2e 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java
@@ -111,6 +111,10 @@
         Assert.assertEquals(result[0], result[2]);
         Assert.assertEquals(result[0], result[3]);
 
+        // The role string should be a camel cased programmatic identifier.
+        CharSequence roleString = extras.getCharSequence("AccessibilityNodeInfo.chromeRole");
+        Assert.assertEquals("staticText", roleString.toString());
+
         // Wait for inline text boxes to load. Unfortunately we don't have a signal for this,
         // we have to keep sleeping until it loads. In practice it only takes a few milliseconds.
         do {
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json
index da47ee2..9b7de47 100644
--- a/content/public/app/mojo/content_browser_manifest.json
+++ b/content/public/app/mojo/content_browser_manifest.json
@@ -38,6 +38,7 @@
           "content::mojom::URLLoaderFactory",
           "content::mojom::VideoCaptureHost",
           "content::mojom::WorkerURLLoaderFactoryProvider",
+          "device::mojom::BatteryMonitor",
           "device::mojom::GamepadMonitor",
           "discardable_memory::mojom::DiscardableSharedMemoryManager",
           "memory_coordinator::mojom::MemoryCoordinatorHandle",
@@ -74,6 +75,7 @@
         "content_utility": [ "browser" ],
         "data_decoder": [ "image_decoder" ],
         "device": [
+          "device:battery_monitor",
           "device:generic_sensor",
           "device:nfc",
           "device:serial",
diff --git a/content/public/app/mojo/content_renderer_manifest.json b/content/public/app/mojo/content_renderer_manifest.json
index cfb7f9e9..7d77959 100644
--- a/content/public/app/mojo/content_renderer_manifest.json
+++ b/content/public/app/mojo/content_renderer_manifest.json
@@ -23,7 +23,6 @@
         "*": [ "app" ],
         "content_browser": [ "renderer" ],
         "device": [
-          "device:battery_monitor",
           "device:power_monitor",
           "device:screen_orientation",
           "device:sensors",
diff --git a/content/public/browser/resource_dispatcher_host_delegate.cc b/content/public/browser/resource_dispatcher_host_delegate.cc
index 94b7755..ff8f78a 100644
--- a/content/public/browser/resource_dispatcher_host_delegate.cc
+++ b/content/public/browser/resource_dispatcher_host_delegate.cc
@@ -104,11 +104,6 @@
   return std::unique_ptr<net::ClientCertStore>();
 }
 
-void ResourceDispatcherHostDelegate::OnAbortedFrameLoad(
-    const GURL& url,
-    base::TimeDelta request_loading_time) {
-}
-
 ResourceDispatcherHostDelegate::~ResourceDispatcherHostDelegate() {
 }
 
diff --git a/content/public/browser/resource_dispatcher_host_delegate.h b/content/public/browser/resource_dispatcher_host_delegate.h
index 8a5a133..997ff17 100644
--- a/content/public/browser/resource_dispatcher_host_delegate.h
+++ b/content/public/browser/resource_dispatcher_host_delegate.h
@@ -142,12 +142,6 @@
   virtual std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
       ResourceContext* resource_context);
 
-  // Notification that a main frame load was aborted. The |request_loading_time|
-  // parameter contains the time between the load request start and abort.
-  // Called on the IO thread.
-  virtual void OnAbortedFrameLoad(const GURL& url,
-                                  base::TimeDelta request_loading_time);
-
  protected:
   virtual ~ResourceDispatcherHostDelegate();
 };
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index a82bca1..eced3b0 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -263,6 +263,11 @@
     "SendBeaconThrowForBlobWithNonSimpleType",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Service worker based payment apps as defined by w3c here:
+// https://w3c.github.io/webpayments-payment-apps-api/
+const base::Feature kServiceWorkerPaymentApps{
+    "ServiceWorkerPaymentApps", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Streaming installed scripts on starting service workers.
 const base::Feature kServiceWorkerScriptStreaming{
     "ServiceWorkerScriptStreaming", base::FEATURE_DISABLED_BY_DEFAULT};
@@ -419,12 +424,6 @@
 const base::Feature kHideIncorrectlySizedFullscreenFrames{
     "HideIncorrectlySizedFullscreenFrames", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Service worker based payment apps as defined by w3c here:
-// https://w3c.github.io/webpayments-payment-apps-api/
-const base::Feature kServiceWorkerPaymentApps{
-    "ServiceWorkerPaymentApps",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Controls whether the WebNFC API is enabled:
 // https://w3c.github.io/web-nfc/
 const base::Feature kWebNfc{"WebNFC", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index a34e0c7..1599ad7 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -70,6 +70,7 @@
 CONTENT_EXPORT extern const base::Feature kScrollAnchoring;
 CONTENT_EXPORT
 extern const base::Feature kSendBeaconThrowForBlobWithNonSimpleType;
+CONTENT_EXPORT extern const base::Feature kServiceWorkerPaymentApps;
 CONTENT_EXPORT extern const base::Feature kServiceWorkerScriptStreaming;
 CONTENT_EXPORT extern const base::Feature kSharedArrayBuffer;
 CONTENT_EXPORT extern const base::Feature kSignInProcessIsolation;
@@ -104,7 +105,6 @@
 CONTENT_EXPORT extern const base::Feature kAndroidAutofillAccessibility;
 CONTENT_EXPORT extern const base::Feature kHideIncorrectlySizedFullscreenFrames;
 CONTENT_EXPORT extern const base::Feature kImeThread;
-CONTENT_EXPORT extern const base::Feature kServiceWorkerPaymentApps;
 CONTENT_EXPORT extern const base::Feature kWebNfc;
 CONTENT_EXPORT extern const base::Feature kWebVrVsyncAlign;
 #endif  // defined(OS_ANDROID)
diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc
index 167d10462..0ab1619 100644
--- a/content/renderer/browser_plugin/browser_plugin.cc
+++ b/content/renderer/browser_plugin/browser_plugin.cc
@@ -59,11 +59,6 @@
 static base::LazyInstance<PluginContainerMap>::DestructorAtExit
     g_plugin_container_map = LAZY_INSTANCE_INITIALIZER;
 
-bool IsRunningInMash() {
-  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-  return cmdline->HasSwitch(switches::kIsRunningInMash);
-}
-
 }  // namespace
 
 namespace content {
@@ -102,7 +97,6 @@
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
   enable_surface_synchronization_ =
-      IsRunningInMash() ||
       command_line.HasSwitch(switches::kEnableSurfaceSynchronization);
 }
 
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc
index 6413419..89d0576 100644
--- a/content/renderer/gpu/render_widget_compositor.cc
+++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -251,11 +251,6 @@
   return gfx::Size(default_tile_size, default_tile_size);
 }
 
-bool IsRunningInMash() {
-  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-  return cmdline->HasSwitch(switches::kIsRunningInMash);
-}
-
 // Check cc::BrowserControlsState, and blink::WebBrowserControlsState
 // are kept in sync.
 static_assert(int(blink::kWebBrowserControlsBoth) == int(cc::BOTH),
@@ -462,7 +457,6 @@
   settings.initial_debug_state.SetRecordRenderingStats(
       cmd.HasSwitch(cc::switches::kEnableGpuBenchmarking));
   settings.enable_surface_synchronization =
-      IsRunningInMash() ||
       cmd.HasSwitch(switches::kEnableSurfaceSynchronization);
 
   if (cmd.HasSwitch(cc::switches::kSlowDownRasterScaleFactor)) {
@@ -1002,17 +996,6 @@
   return layer_tree_host_->have_scroll_event_handlers();
 }
 
-void CompositeAndReadbackAsyncCallback(
-    blink::WebCompositeAndReadbackAsyncCallback* callback,
-    std::unique_ptr<viz::CopyOutputResult> result) {
-  if (result->HasBitmap()) {
-    std::unique_ptr<SkBitmap> result_bitmap = result->TakeBitmap();
-    callback->DidCompositeAndReadback(*result_bitmap);
-  } else {
-    callback->DidCompositeAndReadback(SkBitmap());
-  }
-}
-
 bool RenderWidgetCompositor::CompositeIsSynchronous() const {
   if (!threaded_) {
     DCHECK(!layer_tree_host_->GetSettings().single_thread_proxy_scheduler);
@@ -1063,15 +1046,20 @@
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner =
       layer_tree_host_->GetTaskRunnerProvider()->MainThreadTaskRunner();
   std::unique_ptr<viz::CopyOutputRequest> request =
-      viz::CopyOutputRequest::CreateBitmapRequest(base::BindOnce(
-          [](blink::WebCompositeAndReadbackAsyncCallback* callback,
-             scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-             std::unique_ptr<viz::CopyOutputResult> result) {
-            task_runner->PostTask(FROM_HERE,
-                                  base::Bind(&CompositeAndReadbackAsyncCallback,
-                                             callback, base::Passed(&result)));
-          },
-          callback, base::Passed(&main_thread_task_runner)));
+      std::make_unique<viz::CopyOutputRequest>(
+          viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+          base::BindOnce(
+              [](blink::WebCompositeAndReadbackAsyncCallback* callback,
+                 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+                 std::unique_ptr<viz::CopyOutputResult> result) {
+                task_runner->PostTask(
+                    FROM_HERE,
+                    base::Bind(&blink::WebCompositeAndReadbackAsyncCallback::
+                                   DidCompositeAndReadback,
+                               base::Unretained(callback),
+                               result->AsSkBitmap()));
+              },
+              callback, base::Passed(&main_thread_task_runner)));
   // Force a redraw to ensure that the copy swap promise isn't cancelled due to
   // no damage.
   SetNeedsForcedRedraw();
diff --git a/content/renderer/input/widget_input_handler_manager.cc b/content/renderer/input/widget_input_handler_manager.cc
index 03c5dd6..9822f226 100644
--- a/content/renderer/input/widget_input_handler_manager.cc
+++ b/content/renderer/input/widget_input_handler_manager.cc
@@ -59,31 +59,25 @@
 
 scoped_refptr<WidgetInputHandlerManager> WidgetInputHandlerManager::Create(
     base::WeakPtr<RenderWidget> render_widget,
-    IPC::Sender* legacy_host_channel,
     scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
     blink::scheduler::RendererScheduler* renderer_scheduler) {
   scoped_refptr<WidgetInputHandlerManager> manager =
-      new WidgetInputHandlerManager(
-          std::move(render_widget), legacy_host_channel,
-          std::move(compositor_task_runner), renderer_scheduler);
+      new WidgetInputHandlerManager(std::move(render_widget),
+                                    std::move(compositor_task_runner),
+                                    renderer_scheduler);
   manager->Init();
   return manager;
 }
 
 WidgetInputHandlerManager::WidgetInputHandlerManager(
     base::WeakPtr<RenderWidget> render_widget,
-    IPC::Sender* legacy_host_channel,
     scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
     blink::scheduler::RendererScheduler* renderer_scheduler)
     : render_widget_(render_widget),
       renderer_scheduler_(renderer_scheduler),
       input_event_queue_(render_widget->GetInputEventQueue()),
       main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
-      legacy_host_message_sender_(legacy_host_channel),
       compositor_task_runner_(compositor_task_runner) {
-  // TODO(dtapuska): Define a mojo channel for back to the host. Currently
-  // we use legacy IPC.
-  legacy_host_message_routing_id_ = render_widget->routing_id();
 }
 
 void WidgetInputHandlerManager::Init() {
@@ -113,6 +107,17 @@
   }
 }
 
+void WidgetInputHandlerManager::SetWidgetInputHandlerHost(
+    mojom::WidgetInputHandlerHostPtr host) {
+  if (compositor_task_runner_) {
+    host_ = mojo::ThreadSafeInterfacePtr<mojom::WidgetInputHandlerHost>::Create(
+        host.PassInterface(), compositor_task_runner_);
+  } else {
+    host_ = mojo::ThreadSafeInterfacePtr<mojom::WidgetInputHandlerHost>::Create(
+        std::move(host));
+  }
+}
+
 void WidgetInputHandlerManager::AddInterface(
     mojom::WidgetInputHandlerRequest request) {
   if (compositor_task_runner_) {
@@ -166,17 +171,13 @@
   params.current_fling_velocity = current_fling_velocity;
   params.causal_event_viewport_point = causal_event_viewport_point;
   params.scroll_boundary_behavior = scroll_boundary_behavior;
-  if (legacy_host_message_sender_) {
-    legacy_host_message_sender_->Send(new InputHostMsg_DidOverscroll(
-        legacy_host_message_routing_id_, params));
-  }
+  DCHECK(host_);
+  (*host_)->DidOverscroll(params);
 }
 
 void WidgetInputHandlerManager::DidStopFlinging() {
-  if (legacy_host_message_sender_) {
-    legacy_host_message_sender_->Send(
-        new InputHostMsg_DidStopFlinging(legacy_host_message_routing_id_));
-  }
+  DCHECK(host_);
+  (*host_)->DidStopFlinging();
 }
 
 void WidgetInputHandlerManager::DidAnimateForInput() {
@@ -206,9 +207,24 @@
     uint32_t unique_touch_event_id,
     ui::InputHandlerProxy::EventDisposition event_disposition) {
   InputEventAckState ack_state = InputEventDispositionToAck(event_disposition);
-  legacy_host_message_sender_->Send(new InputHostMsg_SetWhiteListedTouchAction(
-      legacy_host_message_routing_id_, touch_action, unique_touch_event_id,
-      ack_state));
+  DCHECK(host_);
+  (*host_)->SetWhiteListedTouchAction(touch_action, unique_touch_event_id,
+                                      ack_state);
+}
+
+void WidgetInputHandlerManager::ProcessTouchAction(
+    cc::TouchAction touch_action) {
+  DCHECK(host_);
+  // Cancel the touch timeout on TouchActionNone since it is a good hint
+  // that author doesn't want scrolling.
+  if (touch_action == cc::TouchAction::kTouchActionNone)
+    (*host_)->CancelTouchTimeout();
+}
+
+const WidgetInputHandlerManager::WidgetInputHandlerHost&
+WidgetInputHandlerManager::GetWidgetInputHandlerHost() {
+  DCHECK(host_);
+  return host_;
 }
 
 void WidgetInputHandlerManager::ObserveGestureEventOnMainThread(
diff --git a/content/renderer/input/widget_input_handler_manager.h b/content/renderer/input/widget_input_handler_manager.h
index 1774869..b15b6cd 100644
--- a/content/renderer/input/widget_input_handler_manager.h
+++ b/content/renderer/input/widget_input_handler_manager.h
@@ -8,6 +8,7 @@
 #include "content/common/input/input_handler.mojom.h"
 #include "content/renderer/render_frame_impl.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/thread_safe_interface_ptr.h"
 #include "ui/events/blink/input_handler_proxy.h"
 #include "ui/events/blink/input_handler_proxy_client.h"
 
@@ -29,12 +30,12 @@
  public:
   static scoped_refptr<WidgetInputHandlerManager> Create(
       base::WeakPtr<RenderWidget> render_widget,
-      IPC::Sender* legacy_host_channel,
       scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
       blink::scheduler::RendererScheduler* renderer_scheduler);
   void AddAssociatedInterface(
       mojom::WidgetInputHandlerAssociatedRequest interface_request);
 
+  void SetWidgetInputHandlerHost(mojom::WidgetInputHandlerHostPtr host);
   void AddInterface(mojom::WidgetInputHandlerRequest interface_request);
 
   // InputHandlerProxyClient overrides.
@@ -71,6 +72,12 @@
   void DispatchEvent(std::unique_ptr<content::InputEvent> event,
                      mojom::WidgetInputHandler::DispatchEventCallback callback);
 
+  void ProcessTouchAction(cc::TouchAction touch_action);
+
+  using WidgetInputHandlerHost = scoped_refptr<
+      mojo::ThreadSafeInterfacePtr<mojom::WidgetInputHandlerHost>>;
+  const WidgetInputHandlerHost& GetWidgetInputHandlerHost();
+
  protected:
   friend class base::RefCountedThreadSafe<WidgetInputHandlerManager>;
   ~WidgetInputHandlerManager() override;
@@ -78,7 +85,6 @@
  private:
   WidgetInputHandlerManager(
       base::WeakPtr<RenderWidget> render_widget,
-      IPC::Sender* legacy_host_channel,
       scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
       blink::scheduler::RendererScheduler* renderer_scheduler);
   void Init();
@@ -108,14 +114,21 @@
       const blink::WebGestureEvent& gesture_event,
       const cc::InputHandlerScrollResult& scroll_result);
 
+  // Only valid to be called on the main thread.
   base::WeakPtr<RenderWidget> render_widget_;
   blink::scheduler::RendererScheduler* renderer_scheduler_;
+
+  // InputHandlerProxy is only interacted with on the compositor
+  // thread.
+  std::unique_ptr<ui::InputHandlerProxy> input_handler_proxy_;
+
+  // The WidgetInputHandlerHost is bound on the compositor task runner
+  // but class can be called on the compositor and main thread.
+  WidgetInputHandlerHost host_;
+
+  // Any thread can access these variables.
   scoped_refptr<MainThreadEventQueue> input_event_queue_;
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
-
-  std::unique_ptr<ui::InputHandlerProxy> input_handler_proxy_;
-  IPC::Sender* legacy_host_message_sender_;
-  int legacy_host_message_routing_id_;
   scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(WidgetInputHandlerManager);
diff --git a/content/renderer/media/webmediaplayer_ms.cc b/content/renderer/media/webmediaplayer_ms.cc
index e275d9b..af6988b 100644
--- a/content/renderer/media/webmediaplayer_ms.cc
+++ b/content/renderer/media/webmediaplayer_ms.cc
@@ -623,7 +623,9 @@
 
 void WebMediaPlayerMS::Paint(blink::WebCanvas* canvas,
                              const blink::WebRect& rect,
-                             cc::PaintFlags& flags) {
+                             cc::PaintFlags& flags,
+                             int already_uploaded_id,
+                             VideoFrameUploadMetadata* out_metadata) {
   DVLOG(3) << __func__;
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -764,7 +766,9 @@
     unsigned type,
     int level,
     bool premultiply_alpha,
-    bool flip_y) {
+    bool flip_y,
+    int already_uploaded_id,
+    VideoFrameUploadMetadata* out_metadata) {
   TRACE_EVENT0("webmediaplayerms", "copyVideoTextureToPlatformTexture");
   DCHECK(thread_checker_.CalledOnValidThread());
 
diff --git a/content/renderer/media/webmediaplayer_ms.h b/content/renderer/media/webmediaplayer_ms.h
index dd4b3b9..c182571 100644
--- a/content/renderer/media/webmediaplayer_ms.h
+++ b/content/renderer/media/webmediaplayer_ms.h
@@ -108,7 +108,9 @@
   // Methods for painting.
   void Paint(blink::WebCanvas* canvas,
              const blink::WebRect& rect,
-             cc::PaintFlags& flags) override;
+             cc::PaintFlags& flags,
+             int already_uploaded_id,
+             VideoFrameUploadMetadata* out_metadata) override;
   media::SkCanvasVideoRenderer* GetSkCanvasVideoRenderer();
   void ResetCanvasCache();
 
@@ -157,15 +159,18 @@
   void OnVolumeMultiplierUpdate(double multiplier) override;
   void OnBecamePersistentVideo(bool value) override;
 
-  bool CopyVideoTextureToPlatformTexture(gpu::gles2::GLES2Interface* gl,
-                                         unsigned target,
-                                         unsigned int texture,
-                                         unsigned internal_format,
-                                         unsigned format,
-                                         unsigned type,
-                                         int level,
-                                         bool premultiply_alpha,
-                                         bool flip_y) override;
+  bool CopyVideoTextureToPlatformTexture(
+      gpu::gles2::GLES2Interface* gl,
+      unsigned target,
+      unsigned int texture,
+      unsigned internal_format,
+      unsigned format,
+      unsigned type,
+      int level,
+      bool premultiply_alpha,
+      bool flip_y,
+      int already_uploaded_id,
+      VideoFrameUploadMetadata* out_metadata) override;
 
   bool TexImageImpl(TexImageFunctionID functionID,
                     unsigned target,
diff --git a/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc b/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc
index 6b70133..696527e 100644
--- a/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc
+++ b/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc
@@ -74,7 +74,9 @@
 
   void Paint(blink::WebCanvas* canvas,
              const blink::WebRect& paint_rectangle,
-             cc::PaintFlags&) override {
+             cc::PaintFlags&,
+             int already_uploaded_id,
+             VideoFrameUploadMetadata* out_metadata) override {
     // We could fill in |canvas| with a meaningful pattern in ARGB and verify
     // that is correctly captured (as I420) by HTMLVideoElementCapturerSource
     // but I don't think that'll be easy/useful/robust, so just let go here.
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index 71a64ac0..a5d43cb 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -54,11 +54,6 @@
 base::LazyInstance<FrameMap>::DestructorAtExit g_frame_map =
     LAZY_INSTANCE_INITIALIZER;
 
-bool IsRunningInMash() {
-  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-  return cmdline->HasSwitch(switches::kIsRunningInMash);
-}
-
 }  // namespace
 
 // static
@@ -222,7 +217,6 @@
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
   enable_surface_synchronization_ =
-      IsRunningInMash() ||
       command_line.HasSwitch(switches::kEnableSurfaceSynchronization);
 }
 
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 4bbfbf6..2c2c70a 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1949,7 +1949,6 @@
     use_software = true;
 
   bool enable_surface_synchronization =
-      IsRunningInMash() ||
       command_line.HasSwitch(switches::kEnableSurfaceSynchronization);
 
   // In disable gpu vsync mode, also let the renderer tick as fast as it
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 26e1f69..358ec4b 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -544,7 +544,7 @@
     RenderThreadImpl* render_thread_impl = RenderThreadImpl::current();
 
     widget_input_handler_manager_ = WidgetInputHandlerManager::Create(
-        weak_ptr_factory_.GetWeakPtr(), RenderThread::Get()->GetChannel(),
+        weak_ptr_factory_.GetWeakPtr(),
         render_thread_impl && compositor_
             ? render_thread_impl->compositor_task_runner()
             : nullptr,
@@ -1097,7 +1097,13 @@
 }
 
 void RenderWidget::OnDidOverscroll(const ui::DidOverscrollParams& params) {
-  Send(new InputHostMsg_DidOverscroll(routing_id_, params));
+  if (widget_input_handler_manager_) {
+    WidgetInputHandlerManager::WidgetInputHandlerHost host =
+        widget_input_handler_manager_->GetWidgetInputHandlerHost();
+    (*host)->DidOverscroll(params);
+  } else {
+    Send(new InputHostMsg_DidOverscroll(routing_id_, params));
+  }
 }
 
 void RenderWidget::SetInputHandler(RenderWidgetInputHandler* input_handler) {
@@ -1677,7 +1683,13 @@
     // If we failed to set the composition text, then we need to let the browser
     // process to cancel the input method's ongoing composition session, to make
     // sure we are in a consistent state.
-    Send(new InputHostMsg_ImeCancelComposition(routing_id()));
+    if (widget_input_handler_manager_) {
+      WidgetInputHandlerManager::WidgetInputHandlerHost host =
+          widget_input_handler_manager_->GetWidgetInputHandlerHost();
+      (*host)->ImeCancelComposition();
+    } else {
+      Send(new InputHostMsg_ImeCancelComposition(routing_id()));
+    }
   }
   UpdateCompositionInfo(false /* not an immediate request */);
 }
@@ -1926,8 +1938,15 @@
   }
   composition_character_bounds_ = character_bounds;
   composition_range_ = range;
-  Send(new InputHostMsg_ImeCompositionRangeChanged(
-      routing_id(), composition_range_, composition_character_bounds_));
+  if (widget_input_handler_manager_) {
+    WidgetInputHandlerManager::WidgetInputHandlerHost host =
+        widget_input_handler_manager_->GetWidgetInputHandlerHost();
+    (*host)->ImeCompositionRangeChanged(composition_range_,
+                                        composition_character_bounds_);
+  } else {
+    Send(new InputHostMsg_ImeCompositionRangeChanged(
+        routing_id(), composition_range_, composition_character_bounds_));
+  }
 }
 
 void RenderWidget::ConvertViewportToWindow(blink::WebRect* rect) {
@@ -2330,7 +2349,11 @@
   if (!input_handler_->ProcessTouchAction(touch_action))
     return;
 
-  Send(new InputHostMsg_SetTouchAction(routing_id_, touch_action));
+  if (widget_input_handler_manager_) {
+    widget_input_handler_manager_->ProcessTouchAction(touch_action);
+  } else {
+    Send(new InputHostMsg_SetTouchAction(routing_id_, touch_action));
+  }
 }
 
 void RenderWidget::RegisterRenderFrameProxy(RenderFrameProxy* proxy) {
@@ -2423,9 +2446,11 @@
       ->GetActiveWebInputMethodController();
 }
 
-void RenderWidget::GetWidgetInputHandler(
-    mojom::WidgetInputHandlerRequest request) {
+void RenderWidget::SetupWidgetInputHandler(
+    mojom::WidgetInputHandlerRequest request,
+    mojom::WidgetInputHandlerHostPtr host) {
   widget_input_handler_manager_->AddInterface(std::move(request));
+  widget_input_handler_manager_->SetWidgetInputHandlerHost(std::move(host));
 }
 
 void RenderWidget::SetWidgetBinding(mojom::WidgetRequest request) {
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 737c9b94..7f5f1556 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -416,7 +416,9 @@
   void HandleInputEvent(const blink::WebCoalescedInputEvent& input_event,
                         const ui::LatencyInfo& latency_info,
                         HandledEventCallback callback) override;
-  void GetWidgetInputHandler(mojom::WidgetInputHandlerRequest request) override;
+
+  void SetupWidgetInputHandler(mojom::WidgetInputHandlerRequest request,
+                               mojom::WidgetInputHandlerHostPtr host) override;
 
   scoped_refptr<MainThreadEventQueue> GetInputEventQueue();
 
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index 96a79c7..6b2e59d 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -187,29 +187,12 @@
         ['win', 'intel', 'd3d11'], bug=666392)
 
     # Win 10 / Intel
-    self.Fail('conformance/rendering/clear-after-copyTexImage2D.html',
-        ['win10', 'intel'], bug=737002)
     self.Fail('deqp/functional/gles3/fbocolorbuffer/clear.html',
         ['win10', 'intel', 'd3d11', 'no_passthrough'], bug=483282)
 
     # Intel HD 530
     self.Fail('conformance2/textures/misc/angle-stuck-depth-textures.html',
         ['win', 'intel', 'no_passthrough', 'd3d11'], bug=680797)
-    self.Fail('deqp/functional/gles3/fboinvalidate/format_00.html',
-        ['win', 'intel', 'd3d11'], bug=680797)
-    self.Fail('deqp/functional/gles3/fboinvalidate/format_01.html',
-        ['win', 'intel', 'd3d11'], bug=680797)
-    self.Fail('deqp/functional/gles3/fboinvalidate/format_02.html',
-        ['win', 'intel', 'd3d11'], bug=680797)
-    self.Fail('deqp/functional/gles3/framebufferblit/' +
-        'default_framebuffer_03.html',
-        ['win', 'intel', 'd3d11'], bug=680797)
-    self.Fail('deqp/functional/gles3/framebufferblit/' +
-        'default_framebuffer_04.html',
-        ['win', 'intel', 'd3d11'], bug=680797)
-    self.Fail('deqp/functional/gles3/framebufferblit/' +
-        'default_framebuffer_06.html',
-        ['win', 'intel', 'd3d11'], bug=680797)
 
     # It's unfortunate that these suppressions need to be so broad, but it
     # looks like the D3D11 device can be lost spontaneously on this
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index b49d5e9..7e3119d 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -253,10 +253,6 @@
         'tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html',
         ['win10', ('nvidia', 0x1cb3)], bug=728670)
 
-    # Win10 / Intel failures
-    self.Fail('conformance/rendering/clear-after-copyTexImage2D.html',
-        ['win10', 'intel'], bug=737002)
-
     # Win7 / Intel failures
     self.Fail('conformance/textures/misc/' +
               'copy-tex-image-and-sub-image-2d.html',
diff --git a/content/test/mock_widget_impl.cc b/content/test/mock_widget_impl.cc
index ba2dd35..ab741df 100644
--- a/content/test/mock_widget_impl.cc
+++ b/content/test/mock_widget_impl.cc
@@ -11,7 +11,8 @@
 
 MockWidgetImpl::~MockWidgetImpl() {}
 
-void MockWidgetImpl::GetWidgetInputHandler(
-    mojom::WidgetInputHandlerRequest request) {}
+void MockWidgetImpl::SetupWidgetInputHandler(
+    mojom::WidgetInputHandlerRequest request,
+    mojom::WidgetInputHandlerHostPtr host) {}
 
 }  // namespace content
diff --git a/content/test/mock_widget_impl.h b/content/test/mock_widget_impl.h
index 48f2c911..9dccf15 100644
--- a/content/test/mock_widget_impl.h
+++ b/content/test/mock_widget_impl.h
@@ -16,7 +16,8 @@
   explicit MockWidgetImpl(mojo::InterfaceRequest<mojom::Widget> request);
   ~MockWidgetImpl() override;
 
-  void GetWidgetInputHandler(mojom::WidgetInputHandlerRequest request) override;
+  void SetupWidgetInputHandler(mojom::WidgetInputHandlerRequest request,
+                               mojom::WidgetInputHandlerHostPtr host) override;
 
  private:
   mojo::Binding<mojom::Widget> binding_;
diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm
index e163647a..b01cc27c 100644
--- a/device/bluetooth/bluetooth_adapter_mac.mm
+++ b/device/bluetooth/bluetooth_adapter_mac.mm
@@ -18,7 +18,6 @@
 #include "base/mac/mac_util.h"
 #include "base/mac/sdk_forward_declarations.h"
 #include "base/memory/ptr_util.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/sys_string_conversions.h"
@@ -442,22 +441,12 @@
 }
 
 void BluetoothAdapterMac::PollAdapter() {
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "461181 BluetoothAdapterMac::PollAdapter::Start"));
   bool was_present = IsPresent();
   std::string address;
   bool classic_powered = false;
   IOBluetoothHostController* controller =
       [IOBluetoothHostController defaultController];
 
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "461181 BluetoothAdapterMac::PollAdapter::GetControllerStats"));
   if (controller != nil) {
     address = BluetoothDevice::CanonicalizeAddress(
         base::SysNSStringToUTF8([controller addressAsString]));
@@ -470,39 +459,18 @@
   bool is_present = !address.empty();
   address_ = address;
 
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile3(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "461181 BluetoothAdapterMac::PollAdapter::AdapterPresentChanged"));
   if (was_present != is_present) {
     for (auto& observer : observers_)
       observer.AdapterPresentChanged(this, is_present);
   }
 
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile4(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "461181 BluetoothAdapterMac::PollAdapter::AdapterPowerChanged"));
   if (classic_powered_ != classic_powered) {
     classic_powered_ = classic_powered;
     for (auto& observer : observers_)
       observer.AdapterPoweredChanged(this, classic_powered_);
   }
 
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile5(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "461181 BluetoothAdapterMac::PollAdapter::RemoveTimedOutDevices"));
   RemoveTimedOutDevices();
-
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
-  // is fixed.
-  tracked_objects::ScopedTracker tracking_profile6(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "461181 BluetoothAdapterMac::PollAdapter::AddPairedDevices"));
   AddPairedDevices();
 
   ui_task_runner_->PostDelayedTask(
diff --git a/docs/ios/objects.md b/docs/ios/objects.md
index 43c1b7b7..11738698 100644
--- a/docs/ios/objects.md
+++ b/docs/ios/objects.md
@@ -54,6 +54,9 @@
 ChromeBrowserState and each ChromeBrowserState has one associated
 BrowserList.
 
+The BrowserList owns the WebStateListDelegate that is passed to all the
+created Browsers (and then forwarded to their WebStateList).
+
 The corresponding object on desktop is BrowserList but the API is
 different. On desktop, it is a singleton and it points to all the
 Browsers instances whereas on iOS there is one per ChromeBrowserState.
@@ -65,7 +68,9 @@
 a single Browser per BrowserList.
 
 The Browser owns a WebStateList and thus indirectly owns all the tabs
-(aka WebState and their associated tab helpers).
+(aka WebState and their associated tab helpers). The Browser also owns
+the CommandDispatcher and ChromeBroadcaster used for dispatching UI
+commands and property synchronisation.
 
 The corresponding object on desktop is Browser.
 
@@ -87,6 +92,20 @@
 
 The corresponding object on desktop is TabStripModel.
 
+# WebStateListDelegate
+
+WebStateListDelegate is the delegate for WebStateList. It is invoked
+before a WebState is inserted to or after a WebState is removed from
+the WebStateList.
+
+Each WebStateList points to a WebStateListDelegate but does not own
+it to allow sharing the same delegate for multiple WebStateList. In
+general, the WebStateListDelegate role is to attach tab helpers to
+the WebState when it is added to the WebStateList (and optionally to
+shut them down).
+
+The corresponding object on desktop is TabStripModelDelegate.
+
 # WebState
 
 WebState wraps a WKWebView and allows navigation. A WebState can have
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index 34a0faf..b9728849 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -84,6 +84,8 @@
     "browser/mock_external_provider.h",
     "browser/scoped_ignore_content_verifier_for_test.cc",
     "browser/scoped_ignore_content_verifier_for_test.h",
+    "browser/test_event_router.cc",
+    "browser/test_event_router.h",
     "browser/test_extension_registry_observer.cc",
     "browser/test_extension_registry_observer.h",
     "browser/test_extensions_browser_client.cc",
diff --git a/extensions/browser/api/networking_private/networking_private_api.cc b/extensions/browser/api/networking_private/networking_private_api.cc
index c9725e96..f9fa173 100644
--- a/extensions/browser/api/networking_private/networking_private_api.cc
+++ b/extensions/browser/api/networking_private/networking_private_api.cc
@@ -566,7 +566,11 @@
 
 ExtensionFunction::ResponseAction
 NetworkingPrivateRequestNetworkScanFunction::Run() {
-  if (!GetDelegate(browser_context())->RequestScan())
+  std::unique_ptr<private_api::RequestNetworkScan::Params> params =
+      private_api::RequestNetworkScan::Params::Create(*args_);
+  EXTENSION_FUNCTION_VALIDATE(params);
+  std::string network_type = private_api::ToString(params->network_type);
+  if (!GetDelegate(browser_context())->RequestScan(network_type))
     return RespondNow(Error(networking_private::kErrorNotSupported));
   return RespondNow(NoArguments());
 }
diff --git a/extensions/browser/api/networking_private/networking_private_chromeos.cc b/extensions/browser/api/networking_private/networking_private_chromeos.cc
index 59dcbc9..8e85486 100644
--- a/extensions/browser/api/networking_private/networking_private_chromeos.cc
+++ b/extensions/browser/api/networking_private/networking_private_chromeos.cc
@@ -806,8 +806,10 @@
   return true;
 }
 
-bool NetworkingPrivateChromeOS::RequestScan() {
-  GetStateHandler()->RequestScan();
+bool NetworkingPrivateChromeOS::RequestScan(const std::string& type) {
+  NetworkTypePattern pattern = chromeos::onc::NetworkTypePatternFromOncType(
+      type.empty() ? ::onc::network_type::kAllTypes : type);
+  GetStateHandler()->RequestScan(pattern);
   return true;
 }
 
diff --git a/extensions/browser/api/networking_private/networking_private_chromeos.h b/extensions/browser/api/networking_private/networking_private_chromeos.h
index 1686b2c..cf66941 100644
--- a/extensions/browser/api/networking_private/networking_private_chromeos.h
+++ b/extensions/browser/api/networking_private/networking_private_chromeos.h
@@ -97,7 +97,7 @@
   std::unique_ptr<base::DictionaryValue> GetCertificateLists() override;
   bool EnableNetworkType(const std::string& type) override;
   bool DisableNetworkType(const std::string& type) override;
-  bool RequestScan() override;
+  bool RequestScan(const std::string& type) override;
 
  private:
   // Callback for both GetProperties and GetManagedProperties. Copies
diff --git a/extensions/browser/api/networking_private/networking_private_delegate.h b/extensions/browser/api/networking_private/networking_private_delegate.h
index bb5f774..9cd7c90 100644
--- a/extensions/browser/api/networking_private/networking_private_delegate.h
+++ b/extensions/browser/api/networking_private/networking_private_delegate.h
@@ -152,7 +152,10 @@
 
   // Returns true if a scan was requested. It may take many seconds for a scan
   // to complete. The scan may or may not trigger API events when complete.
-  virtual bool RequestScan() = 0;
+  // |type| is the type of network to request a scan for; if empty, scans for
+  // all supported network types except Cellular, which must be requested
+  // explicitly.
+  virtual bool RequestScan(const std::string& type) = 0;
 
   // Optional methods for adding a NetworkingPrivateDelegateObserver for
   // implementations that require it (non-chromeos).
diff --git a/extensions/browser/api/networking_private/networking_private_linux.cc b/extensions/browser/api/networking_private/networking_private_linux.cc
index f4d3909..03e8f0f 100644
--- a/extensions/browser/api/networking_private/networking_private_linux.cc
+++ b/extensions/browser/api/networking_private/networking_private_linux.cc
@@ -607,7 +607,7 @@
   return false;
 }
 
-bool NetworkingPrivateLinux::RequestScan() {
+bool NetworkingPrivateLinux::RequestScan(const std::string& /* type */) {
   return GetNetworksForScanRequest();
 }
 
diff --git a/extensions/browser/api/networking_private/networking_private_linux.h b/extensions/browser/api/networking_private/networking_private_linux.h
index ff3c5e52..8f7b83bc 100644
--- a/extensions/browser/api/networking_private/networking_private_linux.h
+++ b/extensions/browser/api/networking_private/networking_private_linux.h
@@ -98,7 +98,7 @@
   std::unique_ptr<base::DictionaryValue> GetCertificateLists() override;
   bool EnableNetworkType(const std::string& type) override;
   bool DisableNetworkType(const std::string& type) override;
-  bool RequestScan() override;
+  bool RequestScan(const std::string& type) override;
   void AddObserver(NetworkingPrivateDelegateObserver* observer) override;
   void RemoveObserver(NetworkingPrivateDelegateObserver* observer) override;
 
diff --git a/extensions/browser/api/networking_private/networking_private_service_client.cc b/extensions/browser/api/networking_private/networking_private_service_client.cc
index 513658c5..511baa1 100644
--- a/extensions/browser/api/networking_private/networking_private_service_client.cc
+++ b/extensions/browser/api/networking_private/networking_private_service_client.cc
@@ -375,7 +375,8 @@
   return false;
 }
 
-bool NetworkingPrivateServiceClient::RequestScan() {
+bool NetworkingPrivateServiceClient::RequestScan(
+    const std::string& /* type */) {
   task_runner_->PostTask(FROM_HERE,
                          base::Bind(&WiFiService::RequestNetworkScan,
                                     base::Unretained(wifi_service_.get())));
diff --git a/extensions/browser/api/networking_private/networking_private_service_client.h b/extensions/browser/api/networking_private/networking_private_service_client.h
index ee960aa..a67490c 100644
--- a/extensions/browser/api/networking_private/networking_private_service_client.h
+++ b/extensions/browser/api/networking_private/networking_private_service_client.h
@@ -111,7 +111,7 @@
   std::unique_ptr<base::DictionaryValue> GetCertificateLists() override;
   bool EnableNetworkType(const std::string& type) override;
   bool DisableNetworkType(const std::string& type) override;
-  bool RequestScan() override;
+  bool RequestScan(const std::string& type) override;
   void AddObserver(NetworkingPrivateDelegateObserver* observer) override;
   void RemoveObserver(NetworkingPrivateDelegateObserver* observer) override;
 
diff --git a/extensions/browser/extension_host.cc b/extensions/browser/extension_host.cc
index 4ec655a..65dbedd 100644
--- a/extensions/browser/extension_host.cc
+++ b/extensions/browser/extension_host.cc
@@ -7,7 +7,6 @@
 #include "base/logging.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "content/public/browser/browser_context.h"
@@ -144,10 +143,6 @@
 }
 
 void ExtensionHost::CreateRenderViewNow() {
-  // TODO(robliao): Remove ScopedTracker below once crbug.com/464206 is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "464206 ExtensionHost::CreateRenderViewNow1"));
   if (!ExtensionRegistry::Get(browser_context_)
            ->ready_extensions()
            .Contains(extension_->id())) {
@@ -157,10 +152,6 @@
   is_render_view_creation_pending_ = false;
   LoadInitialURL();
   if (IsBackgroundPage()) {
-    // TODO(robliao): Remove ScopedTracker below once crbug.com/464206 is fixed.
-    tracked_objects::ScopedTracker tracking_profile2(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "464206 ExtensionHost::CreateRenderViewNow2"));
     DCHECK(IsRenderViewLive());
     if (extension_) {
       std::string group_name = base::FieldTrialList::FindFullName(
@@ -172,10 +163,6 @@
         host_contents_->WasHidden();
       }
     }
-    // TODO(robliao): Remove ScopedTracker below once crbug.com/464206 is fixed.
-    tracked_objects::ScopedTracker tracking_profile3(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "464206 ExtensionHost::CreateRenderViewNow3"));
     // Connect orphaned dev-tools instances.
     delegate_->OnRenderViewCreatedForBackgroundPage(this);
   }
diff --git a/extensions/browser/test_event_router.cc b/extensions/browser/test_event_router.cc
new file mode 100644
index 0000000..7bf58e113
--- /dev/null
+++ b/extensions/browser/test_event_router.cc
@@ -0,0 +1,63 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/test_event_router.h"
+#include "base/logging.h"
+#include "content/public/browser/browser_context.h"
+#include "extensions/browser/extension_prefs.h"
+
+namespace extensions {
+
+TestEventRouter::EventObserver::~EventObserver() {}
+
+void TestEventRouter::EventObserver::OnDispatchEventToExtension(
+    const std::string& extension_id,
+    const Event& event) {}
+
+void TestEventRouter::EventObserver::OnBroadcastEvent(const Event& event) {}
+
+TestEventRouter::TestEventRouter(content::BrowserContext* context)
+    : EventRouter(context, ExtensionPrefs::Get(context)) {}
+
+TestEventRouter::~TestEventRouter() {}
+
+int TestEventRouter::GetEventCount(std::string event_name) const {
+  if (seen_events_.count(event_name) == 0)
+    return 0;
+  return seen_events_.find(event_name)->second;
+}
+
+void TestEventRouter::AddEventObserver(EventObserver* obs) {
+  observers_.AddObserver(obs);
+}
+
+void TestEventRouter::RemoveEventObserver(EventObserver* obs) {
+  observers_.RemoveObserver(obs);
+}
+
+void TestEventRouter::BroadcastEvent(std::unique_ptr<Event> event) {
+  IncrementEventCount(event->event_name);
+
+  for (auto& observer : observers_)
+    observer.OnBroadcastEvent(*event);
+}
+
+void TestEventRouter::DispatchEventToExtension(const std::string& extension_id,
+                                               std::unique_ptr<Event> event) {
+  if (!expected_extension_id_.empty())
+    DCHECK_EQ(expected_extension_id_, extension_id);
+
+  IncrementEventCount(event->event_name);
+
+  for (auto& observer : observers_)
+    observer.OnDispatchEventToExtension(extension_id, *event);
+}
+
+void TestEventRouter::IncrementEventCount(const std::string& event_name) {
+  if (seen_events_.count(event_name) == 0)
+    seen_events_[event_name] = 0;
+  seen_events_[event_name]++;
+}
+
+}  // namespace extensions
diff --git a/extensions/browser/test_event_router.h b/extensions/browser/test_event_router.h
new file mode 100644
index 0000000..a914de1
--- /dev/null
+++ b/extensions/browser/test_event_router.h
@@ -0,0 +1,88 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_TEST_EVENT_ROUTER_H_
+#define EXTENSIONS_BROWSER_TEST_EVENT_ROUTER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/event_router_factory.h"
+#include "extensions/common/extension_id.h"
+
+namespace extensions {
+
+// An EventRouter that tests can use to observe, await, or verify events
+// dispatched to an extension.
+class TestEventRouter : public EventRouter {
+ public:
+  // Allows observing events dispatched to the event router.
+  class EventObserver {
+   public:
+    // These functions correspond to the ones in EventRouter.
+    virtual void OnBroadcastEvent(const Event& event);
+    virtual void OnDispatchEventToExtension(const std::string& extension_id,
+                                            const Event& event);
+
+   protected:
+    virtual ~EventObserver();
+  };
+
+  explicit TestEventRouter(content::BrowserContext* context);
+  ~TestEventRouter() override;
+
+  // Returns the number of times an event has been broadcast or dispatched.
+  int GetEventCount(std::string event_name) const;
+
+  void AddEventObserver(EventObserver* obs);
+  void RemoveEventObserver(EventObserver* obs);
+
+  // Sets the extension ID all dispatched events will be expected to be sent to.
+  void set_expected_extension_id(const ExtensionId& extension_id) {
+    expected_extension_id_ = extension_id;
+  }
+
+  // EventRouter:
+  void BroadcastEvent(std::unique_ptr<Event> event) override;
+  void DispatchEventToExtension(const std::string& extension_id,
+                                std::unique_ptr<Event> event) override;
+
+ private:
+  // Increments the count of dispatched events seen with the given name.
+  void IncrementEventCount(const std::string& event_name);
+
+  ExtensionId expected_extension_id_;
+
+  // Count of dispatched and broadcasted events by event name.
+  std::map<std::string, int> seen_events_;
+
+  base::ObserverList<EventObserver, false> observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestEventRouter);
+};
+
+// Creates and enables a TestEventRouter for testing. Callers can override T to
+// provide a derived event router.
+template <typename T = TestEventRouter>
+T* CreateAndUseTestEventRouter(content::BrowserContext* context) {
+  // The factory function only requires that T be a KeyedService. Ensure it is
+  // actually derived from EventRouter to avoid undefined behavior.
+  static_assert(std::is_base_of<EventRouter, T>(),
+                "T must be derived from EventRouter");
+  return static_cast<T*>(
+      extensions::EventRouterFactory::GetInstance()->SetTestingFactoryAndUse(
+          context, [](content::BrowserContext* context) {
+            return static_cast<std::unique_ptr<KeyedService>>(
+                std::make_unique<T>(context));
+          }));
+}
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_BROWSER_TEST_EVENT_ROUTER_H_
diff --git a/extensions/browser/value_store/leveldb_value_store.cc b/extensions/browser/value_store/leveldb_value_store.cc
index c0e133bf..fc08825a 100644
--- a/extensions/browser/value_store/leveldb_value_store.cc
+++ b/extensions/browser/value_store/leveldb_value_store.cc
@@ -261,8 +261,10 @@
 
   // All leveldb databases are already dumped by leveldb_env::DBTracker. Add
   // an edge to avoid double counting.
-  pmd->AddSuballocation(dump->guid(),
-                        leveldb_env::DBTracker::GetMemoryDumpName(db()));
+  auto* tracker_db =
+      leveldb_env::DBTracker::GetOrCreateAllocatorDump(pmd, db());
+  if (tracker_db)
+    pmd->AddOwnershipEdge(dump->guid(), tracker_db->guid());
 
   return true;
 }
diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json
index 46836b5..c21e608 100644
--- a/extensions/common/api/_permission_features.json
+++ b/extensions/common/api/_permission_features.json
@@ -368,7 +368,6 @@
       "12E618C3C6E97495AAECF2AC12DEB082353241C6",  // QO component extension
       "3727DD3E564B6055387425027AD74C58784ACC15",  // Editor
       "C41AD9DCD670210295614257EF8C9945AD68D86E",  // Google Now
-      "6EEEA7775E79C735E4BA1F24DBB191BDACB1262C",  // RU-NTP
       "7AE714FFD394E073F0294CFA134C9F91DB5FBAA4",  // CCD Development
       "C7DA3A55C2355F994D3FDDAD120B426A0DF63843",  // CCD Testing
       "75E3CFFFC530582C583E4690EF97C70B9C8423B7",  // CCD Release
diff --git a/extensions/common/api/networking_onc.idl b/extensions/common/api/networking_onc.idl
index 024d4d4..740f197 100644
--- a/extensions/common/api/networking_onc.idl
+++ b/extensions/common/api/networking_onc.idl
@@ -969,10 +969,12 @@
     static void disableNetworkType(NetworkType networkType);
 
     // Requests that the networking subsystem scan for new networks and
-    // update the list returned by $(ref:getNetworks). This is only a
+    // update the list returned by $(ref:getVisibleNetworks). This is only a
     // request: the network subsystem can choose to ignore it.  If the list
     // is updated, then the $(ref:onNetworkListChanged) event will be fired.
-    static void requestNetworkScan();
+    // |networkType|: If provided, requests a scan specific to the type.
+    //     For Cellular a mobile network scan will be requested if supported.
+    static void requestNetworkScan(optional NetworkType networkType);
 
     // Starts a connection to the network with networkGuid.
     // |networkGuid|: The GUID of the network to connect to.
diff --git a/extensions/common/api/networking_private.idl b/extensions/common/api/networking_private.idl
index 00afb36..c029d340 100644
--- a/extensions/common/api/networking_private.idl
+++ b/extensions/common/api/networking_private.idl
@@ -977,7 +977,9 @@
     // update the list returned by $(ref:getVisibleNetworks). This is only a
     // request: the network subsystem can choose to ignore it.  If the list
     // is updated, then the $(ref:onNetworkListChanged) event will be fired.
-    static void requestNetworkScan();
+    // |networkType|: If provided, requests a scan specific to the type.
+    //     For Cellular a mobile network scan will be requested if supported.
+    static void requestNetworkScan(optional NetworkType networkType);
 
     // Starts a connection to the network with networkGuid.
     // |networkGuid|: The GUID of the network to connect to.
diff --git a/extensions/shell/browser/shell_network_controller_chromeos.cc b/extensions/shell/browser/shell_network_controller_chromeos.cc
index ab3cdab..bde9c32 100644
--- a/extensions/shell/browser/shell_network_controller_chromeos.cc
+++ b/extensions/shell/browser/shell_network_controller_chromeos.cc
@@ -15,6 +15,7 @@
 #include "chromeos/network/network_handler_callbacks.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
+#include "chromeos/network/network_type_pattern.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
 namespace extensions {
@@ -139,7 +140,8 @@
 
 void ShellNetworkController::RequestScan() {
   VLOG(1) << "Requesting scan";
-  chromeos::NetworkHandler::Get()->network_state_handler()->RequestScan();
+  chromeos::NetworkHandler::Get()->network_state_handler()->RequestScan(
+      chromeos::NetworkTypePattern::Default());
 }
 
 void ShellNetworkController::ConnectIfUnconnected() {
diff --git a/google_apis/gaia/account_tracker.cc b/google_apis/gaia/account_tracker.cc
index 978fb93..bfab026 100644
--- a/google_apis/gaia/account_tracker.cc
+++ b/google_apis/gaia/account_tracker.cc
@@ -6,7 +6,6 @@
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/stl_util.h"
 #include "base/trace_event/trace_event.h"
 #include "net/url_request/url_request_context_getter.h"
@@ -72,12 +71,6 @@
 }
 
 void AccountTracker::OnRefreshTokenAvailable(const std::string& account_id) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AccountTracker::OnRefreshTokenAvailable"));
-
   TRACE_EVENT1("identity",
                "AccountTracker::OnRefreshTokenAvailable",
                "account_key",
@@ -140,12 +133,6 @@
 }
 
 void AccountTracker::NotifySignInChanged(const AccountState& account) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AccountTracker::NotifySignInChanged"));
-
   DCHECK(!account.ids.gaia.empty());
   for (auto& observer : observer_list_)
     observer.OnAccountSignInChanged(account.ids, account.is_signed_in);
@@ -153,12 +140,6 @@
 
 void AccountTracker::UpdateSignInState(const std::string& account_key,
                                        bool is_signed_in) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AccountTracker::UpdateSignInState"));
-
   StartTrackingAccount(account_key);
   AccountState& account = accounts_[account_key];
   bool needs_gaia_id = account.ids.gaia.empty();
@@ -173,12 +154,6 @@
 }
 
 void AccountTracker::StartTrackingAccount(const std::string& account_key) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AccountTracker::StartTrackingAccount"));
-
   if (!base::ContainsKey(accounts_, account_key)) {
     DVLOG(1) << "StartTracking " << account_key;
     AccountState account_state;
@@ -209,42 +184,17 @@
 }
 
 void AccountTracker::StartFetchingUserInfo(const std::string& account_key) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AccountTracker::StartFetchingUserInfo"));
-
   if (base::ContainsKey(user_info_requests_, account_key)) {
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile1(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "422460 AccountTracker::StartFetchingUserInfo 1"));
-
     DeleteFetcher(user_info_requests_[account_key].get());
   }
 
   DVLOG(1) << "StartFetching " << account_key;
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AccountTracker::StartFetchingUserInfo 2"));
-
   AccountIdFetcher* fetcher =
       new AccountIdFetcher(identity_provider_->GetTokenService(),
                            request_context_getter_.get(),
                            this,
                            account_key);
   user_info_requests_[account_key] = base::WrapUnique(fetcher);
-
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile3(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 AccountTracker::StartFetchingUserInfo 3"));
-
   fetcher->Start();
 }
 
diff --git a/google_apis/gaia/identity_provider.cc b/google_apis/gaia/identity_provider.cc
index 77a1480..6a899e1 100644
--- a/google_apis/gaia/identity_provider.cc
+++ b/google_apis/gaia/identity_provider.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/profiler/scoped_tracker.h"
 #include "google_apis/gaia/identity_provider.h"
 
 IdentityProvider::Observer::~Observer() {}
@@ -40,12 +39,6 @@
 }
 
 void IdentityProvider::OnRefreshTokenAvailable(const std::string& account_id) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 IdentityProvider::OnRefreshTokenAvailable"));
-
   if (account_id != GetActiveAccountId())
     return;
   for (auto& observer : token_service_observers_)
diff --git a/google_apis/gaia/oauth2_token_service.cc b/google_apis/gaia/oauth2_token_service.cc
index 0f038579..49170bda 100644
--- a/google_apis/gaia/oauth2_token_service.cc
+++ b/google_apis/gaia/oauth2_token_service.cc
@@ -14,7 +14,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/rand_util.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -212,12 +211,6 @@
       new Fetcher(oauth2_token_service, account_id, getter, client_id,
                   client_secret, scopes, waiting_request));
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 OAuth2TokenService::Fetcher::CreateAndStart"));
-
   fetcher->Start();
   return fetcher;
 }
@@ -483,22 +476,11 @@
     Consumer* consumer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 OAuth2TokenService::StartRequestForClientWithContext 1"));
   std::unique_ptr<RequestImpl> request(new RequestImpl(account_id, consumer));
   for (auto& observer : diagnostics_observer_list_)
     observer.OnAccessTokenRequested(account_id, consumer->id(), scopes);
 
   if (!RefreshTokenIsAvailable(account_id)) {
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile2(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "422460 OAuth2TokenService::StartRequestForClientWithContext 2"));
-
     GoogleServiceAuthError error(GoogleServiceAuthError::USER_NOT_SIGNED_UP);
 
     for (auto& observer : diagnostics_observer_list_) {
@@ -517,12 +499,6 @@
                                        account_id,
                                        scopes);
   if (HasCacheEntry(request_parameters)) {
-    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile3(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "422460 OAuth2TokenService::StartRequestForClientWithContext 3"));
-
     StartCacheLookupRequest(request.get(), request_parameters, consumer);
   } else {
     FetchOAuth2Token(request.get(),
@@ -541,12 +517,6 @@
                                           const std::string& client_id,
                                           const std::string& client_secret,
                                           const ScopeSet& scopes) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 OAuth2TokenService::FetchOAuth2Token"));
-
   // If there is already a pending fetcher for |scopes| and |account_id|,
   // simply register this |request| for those results rather than starting
   // a new fetcher.
diff --git a/google_apis/gaia/oauth2_token_service_delegate.cc b/google_apis/gaia/oauth2_token_service_delegate.cc
index a59c9e8..ce2f37e 100644
--- a/google_apis/gaia/oauth2_token_service_delegate.cc
+++ b/google_apis/gaia/oauth2_token_service_delegate.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/profiler/scoped_tracker.h"
-#include "google_apis/gaia/oauth2_token_service.h"
 #include "google_apis/gaia/oauth2_token_service_delegate.h"
 
+#include "google_apis/gaia/oauth2_token_service.h"
+
 OAuth2TokenServiceDelegate::ScopedBatchChange::ScopedBatchChange(
     OAuth2TokenServiceDelegate* delegate)
     : delegate_(delegate) {
@@ -75,12 +75,6 @@
 
 void OAuth2TokenServiceDelegate::FireRefreshTokenAvailable(
     const std::string& account_id) {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 OAuth2TokenService::FireRefreshTokenAvailable"));
-
   for (auto& observer : observer_list_)
     observer.OnRefreshTokenAvailable(account_id);
 }
@@ -92,12 +86,6 @@
 }
 
 void OAuth2TokenServiceDelegate::FireRefreshTokensLoaded() {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 OAuth2TokenService::FireRefreshTokensLoaded"));
-
   for (auto& observer : observer_list_)
     observer.OnRefreshTokensLoaded();
 }
diff --git a/google_apis/gcm/engine/connection_factory_impl.cc b/google_apis/gcm/engine/connection_factory_impl.cc
index 1eba5809..ff9abd8 100644
--- a/google_apis/gcm/engine/connection_factory_impl.cc
+++ b/google_apis/gcm/engine/connection_factory_impl.cc
@@ -10,7 +10,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "google_apis/gcm/engine/connection_handler_impl.h"
 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
@@ -370,10 +369,6 @@
 }
 
 void ConnectionFactoryImpl::OnConnectDone(int result) {
-  // TODO(zea): Remove ScopedTracker below once crbug.com/455884 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455884 ConnectionFactoryImpl::OnConnectDone"));
   if (result != net::OK) {
     // If the connection fails, try another proxy.
     result = ReconsiderProxyAfterError(result);
diff --git a/google_apis/gcm/engine/gcm_store_impl.cc b/google_apis/gcm/engine/gcm_store_impl.cc
index 6ac63d4a..f4e5f3d5 100644
--- a/google_apis/gcm/engine/gcm_store_impl.cc
+++ b/google_apis/gcm/engine/gcm_store_impl.cc
@@ -13,7 +13,6 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/sequenced_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
@@ -1429,10 +1428,6 @@
 
 void GCMStoreImpl::LoadContinuation(const LoadCallback& callback,
                                     std::unique_ptr<LoadResult> result) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "477117 GCMStoreImpl::LoadContinuation"));
   if (!result->success) {
     callback.Run(std::move(result));
     return;
diff --git a/gpu/ipc/client/command_buffer_proxy_impl.cc b/gpu/ipc/client/command_buffer_proxy_impl.cc
index e6db37e..26d241c 100644
--- a/gpu/ipc/client/command_buffer_proxy_impl.cc
+++ b/gpu/ipc/client/command_buffer_proxy_impl.cc
@@ -13,7 +13,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/shared_memory.h"
 #include "base/optional.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/stl_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
@@ -209,11 +208,6 @@
   if (!base::SharedMemory::IsHandleValid(handle))
     return false;
 
-  // TODO(vadimt): Remove ScopedTracker below once crbug.com/125248 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "125248 CommandBufferProxyImpl::Initialize"));
-
   // Route must be added before sending the message, otherwise messages sent
   // from the GPU process could race against adding ourselves to the filter.
   channel->AddRouteWithTaskRunner(route_id_, weak_ptr_factory_.GetWeakPtr(),
diff --git a/gpu/ipc/service/gpu_vsync_provider_win.cc b/gpu/ipc/service/gpu_vsync_provider_win.cc
index 47cf27f..9adfece 100644
--- a/gpu/ipc/service/gpu_vsync_provider_win.cc
+++ b/gpu/ipc/service/gpu_vsync_provider_win.cc
@@ -257,7 +257,8 @@
 
   NTSTATUS wait_result = WaitForVBlankEvent();
   if (wait_result != STATUS_SUCCESS) {
-    if (wait_result == STATUS_GRAPHICS_PRESENT_OCCLUDED) {
+    if (wait_result == STATUS_GRAPHICS_PRESENT_OCCLUDED ||
+        wait_result == WAIT_TIMEOUT) {
       // This may be triggered by the monitor going into sleep.
       UseDelayBasedVSyncOnError(WaitForVBlankErrorCode::kWaitForVBlankEvent);
       return;
diff --git a/ios/chrome/browser/autofill/BUILD.gn b/ios/chrome/browser/autofill/BUILD.gn
index b274d00b..efcdaf6 100644
--- a/ios/chrome/browser/autofill/BUILD.gn
+++ b/ios/chrome/browser/autofill/BUILD.gn
@@ -15,6 +15,8 @@
     "form_suggestion_label.h",
     "form_suggestion_label.mm",
     "form_suggestion_provider.h",
+    "form_suggestion_tab_helper.h",
+    "form_suggestion_tab_helper.mm",
     "form_suggestion_view.h",
     "form_suggestion_view.mm",
     "form_suggestion_view_client.h",
diff --git a/ios/chrome/browser/autofill/form_suggestion_tab_helper.h b/ios/chrome/browser/autofill/form_suggestion_tab_helper.h
new file mode 100644
index 0000000..b45e293
--- /dev/null
+++ b/ios/chrome/browser/autofill/form_suggestion_tab_helper.h
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_AUTOFILL_FORM_SUGGESTION_TAB_HELPER_H_
+#define IOS_CHROME_BROWSER_AUTOFILL_FORM_SUGGESTION_TAB_HELPER_H_
+
+#import <Foundation/Foundation.h>
+
+#include "base/macros.h"
+#import "ios/web/public/web_state/web_state_observer.h"
+#import "ios/web/public/web_state/web_state_user_data.h"
+
+@protocol FormInputAccessoryViewProvider;
+@protocol FormSuggestionProvider;
+@class FormSuggestionController;
+
+// Class binding a FormSuggestionController to a WebState.
+class FormSuggestionTabHelper
+    : public web::WebStateObserver,
+      public web::WebStateUserData<FormSuggestionTabHelper> {
+ public:
+  ~FormSuggestionTabHelper() override;
+
+  // Creates a FormSuggestionTabHelper and attaches it to the given |web_state|.
+  static void CreateForWebState(web::WebState* web_state,
+                                NSArray<id<FormSuggestionProvider>>* providers);
+
+  // Returns an object that can provide an input accessory view from the
+  // FormSuggestionController.
+  id<FormInputAccessoryViewProvider> GetAccessoryViewProvider();
+
+ private:
+  FormSuggestionTabHelper(web::WebState* web_state,
+                          NSArray<id<FormSuggestionProvider>>* providers);
+
+  // web::WebStateObserver implementation.
+  void WebStateDestroyed() override;
+
+  // The Objective-C form suggestion controller instance.
+  __strong FormSuggestionController* controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(FormSuggestionTabHelper);
+};
+
+#endif  // IOS_CHROME_BROWSER_AUTOFILL_FORM_SUGGESTION_TAB_HELPER_H_
diff --git a/ios/chrome/browser/autofill/form_suggestion_tab_helper.mm b/ios/chrome/browser/autofill/form_suggestion_tab_helper.mm
new file mode 100644
index 0000000..160672f
--- /dev/null
+++ b/ios/chrome/browser/autofill/form_suggestion_tab_helper.mm
@@ -0,0 +1,48 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/autofill/form_suggestion_tab_helper.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#import "ios/chrome/browser/autofill/form_suggestion_controller.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+DEFINE_WEB_STATE_USER_DATA_KEY(FormSuggestionTabHelper);
+
+FormSuggestionTabHelper::~FormSuggestionTabHelper() = default;
+
+// static
+void FormSuggestionTabHelper::CreateForWebState(
+    web::WebState* web_state,
+    NSArray<id<FormSuggestionProvider>>* providers) {
+  DCHECK(web_state);
+  DCHECK(!FromWebState(web_state));
+  web_state->SetUserData(
+      UserDataKey(),
+      base::WrapUnique(new FormSuggestionTabHelper(web_state, providers)));
+}
+
+id<FormInputAccessoryViewProvider>
+FormSuggestionTabHelper::GetAccessoryViewProvider() {
+  return controller_.accessoryViewProvider;
+}
+
+FormSuggestionTabHelper::FormSuggestionTabHelper(
+    web::WebState* web_state,
+    NSArray<id<FormSuggestionProvider>>* providers)
+    : web::WebStateObserver(web_state),
+      controller_([[FormSuggestionController alloc]
+          initWithWebState:web_state
+                 providers:providers]) {
+  DCHECK(web::WebStateObserver::web_state());
+}
+
+void FormSuggestionTabHelper::WebStateDestroyed() {
+  [controller_ detachFromWebState];
+  controller_ = nil;
+}
diff --git a/ios/chrome/browser/browser_state/BUILD.gn b/ios/chrome/browser/browser_state/BUILD.gn
index 168122a..a78e474 100644
--- a/ios/chrome/browser/browser_state/BUILD.gn
+++ b/ios/chrome/browser/browser_state/BUILD.gn
@@ -106,6 +106,7 @@
     "//ios/chrome/browser/sync/glue",
     "//ios/chrome/browser/translate",
     "//ios/chrome/browser/ui/browser_list",
+    "//ios/chrome/browser/ui/browser_list:browser_list_impl",
     "//ios/chrome/browser/undo",
     "//ios/clean/chrome/browser/ui/overlays",
     "//ios/net",
diff --git a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
index 818714e..5c949ca8 100644
--- a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
+++ b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
@@ -47,6 +47,7 @@
 #include "ios/chrome/browser/sync/ios_user_event_service_factory.h"
 #include "ios/chrome/browser/sync/sync_setup_service_factory.h"
 #include "ios/chrome/browser/translate/translate_accept_languages_factory.h"
+#include "ios/chrome/browser/ui/browser_list/browser_list_factory.h"
 #include "ios/chrome/browser/ui/browser_list/browser_list_session_service_factory.h"
 #include "ios/chrome/browser/undo/bookmark_undo_service_factory.h"
 #include "ios/chrome/browser/web_data_service_factory.h"
@@ -91,6 +92,7 @@
   ios::WebDataServiceFactory::GetInstance();
   ios::WebHistoryServiceFactory::GetInstance();
   AuthenticationServiceFactory::GetInstance();
+  BrowserListFactory::GetInstance();
   BrowserListSessionServiceFactory::GetInstance();
   DesktopPromotionSyncServiceFactory::GetInstance();
   feature_engagement::TrackerFactory::GetInstance();
diff --git a/ios/chrome/browser/net/ios_chrome_network_delegate.cc b/ios/chrome/browser/net/ios_chrome_network_delegate.cc
index 42f111a1..2537e87 100644
--- a/ios/chrome/browser/net/ios_chrome_network_delegate.cc
+++ b/ios/chrome/browser/net/ios_chrome_network_delegate.cc
@@ -15,7 +15,6 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/path_service.h"
-#include "base/profiler/scoped_tracker.h"
 #include "components/prefs/pref_member.h"
 #include "components/prefs/pref_service.h"
 #include "ios/chrome/browser/pref_names.h"
@@ -86,24 +85,8 @@
     net::URLRequest* request,
     const net::CompletionCallback& callback,
     GURL* new_url) {
-  // TODO(mmenke): Remove ScopedTracker below once crbug.com/456327 is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456327 URLRequest::IOSChromeNetworkDelegate::OnBeforeURLRequest"));
-
-  // TODO(mmenke): Remove ScopedTracker below once crbug.com/456327 is fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456327 URLRequest::IOSChromeNetworkDelegate::OnBeforeURLRequest 2"));
-
   if (enable_do_not_track_ && enable_do_not_track_->GetValue())
     request->SetExtraRequestHeaderByName(kDNTHeader, "1", true /* override */);
-
-  // TODO(mmenke): Remove ScopedTracker below once crbug.com/456327 is fixed.
-  tracked_objects::ScopedTracker tracking_profile4(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456327 URLRequest::IOSChromeNetworkDelegate::OnBeforeURLRequest 4"));
-
   return net::OK;
 }
 
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm
index 6869c9c..eaa106e 100644
--- a/ios/chrome/browser/tabs/tab.mm
+++ b/ios/chrome/browser/tabs/tab.mm
@@ -45,9 +45,8 @@
 #include "components/strings/grit/components_strings.h"
 #include "components/url_formatter/url_formatter.h"
 #include "ios/chrome/browser/application_context.h"
-#import "ios/chrome/browser/autofill/autofill_tab_helper.h"
 #import "ios/chrome/browser/autofill/form_input_accessory_view_controller.h"
-#import "ios/chrome/browser/autofill/form_suggestion_controller.h"
+#import "ios/chrome/browser/autofill/form_suggestion_tab_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
 #import "ios/chrome/browser/crash_loop_detection_util.h"
@@ -213,9 +212,6 @@
   // opening a URL in an external application.
   ExternalAppLauncher* _externalAppLauncher;
 
-  // Handles suggestions for form entry.
-  FormSuggestionController* _suggestionController;
-
   // Manages the input accessory view during form input.
   FormInputAccessoryViewController* _inputAccessoryViewController;
 
@@ -255,10 +251,6 @@
 // Handles caching and retrieving of snapshots.
 @property(nonatomic, strong) SnapshotManager* snapshotManager;
 
-// Returns a list of FormSuggestionProviders to be queried for suggestions
-// in order of priority.
-- (NSArray*)suggestionProviders;
-
 // Returns a list of FormInputAccessoryViewProviders to be queried for an input
 // accessory view in order of priority.
 - (NSArray*)accessoryViewProviders;
@@ -434,9 +426,6 @@
   if (experimental_flags::IsAutoReloadEnabled())
     _autoReloadBridge = [[AutoReloadBridge alloc] initWithTab:self];
 
-  _suggestionController = [[FormSuggestionController alloc]
-      initWithWebState:self.webState
-             providers:[self suggestionProviders]];
   _inputAccessoryViewController = [[FormInputAccessoryViewController alloc]
       initWithWebState:self.webState
              providers:[self accessoryViewProviders]];
@@ -459,16 +448,8 @@
           ->GetAccessoryViewProvider();
   if (provider)
     [providers addObject:provider];
-  [providers addObject:[_suggestionController accessoryViewProvider]];
-  return providers;
-}
-
-- (NSArray*)suggestionProviders {
-  NSMutableArray* providers = [NSMutableArray array];
-  [providers addObject:PasswordTabHelper::FromWebState(self.webState)
-                           ->GetSuggestionProvider()];
-  [providers addObject:AutofillTabHelper::FromWebState(self.webState)
-                           ->GetSuggestionProvider()];
+  [providers addObject:FormSuggestionTabHelper::FromWebState(self.webState)
+                           ->GetAccessoryViewProvider()];
   return providers;
 }
 
@@ -860,8 +841,6 @@
   _faviconDriverObserverBridge.reset();
   [_openInController detachFromWebController];
   _openInController = nil;
-  [_suggestionController detachFromWebState];
-  _suggestionController = nil;
   if (_fullScreenController)
     [self.webController removeObserver:_fullScreenController];
   [_fullScreenController invalidate];
diff --git a/ios/chrome/browser/tabs/tab_helper_util.mm b/ios/chrome/browser/tabs/tab_helper_util.mm
index 2f27c9f..949fb418 100644
--- a/ios/chrome/browser/tabs/tab_helper_util.mm
+++ b/ios/chrome/browser/tabs/tab_helper_util.mm
@@ -14,6 +14,7 @@
 #include "components/keyed_service/core/service_access_type.h"
 #import "components/signin/ios/browser/account_consistency_service.h"
 #import "ios/chrome/browser/autofill/autofill_tab_helper.h"
+#import "ios/chrome/browser/autofill/form_suggestion_tab_helper.h"
 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/favicon/favicon_service_factory.h"
@@ -100,6 +101,11 @@
       web_state, PasswordTabHelper::FromWebState(web_state)
                      ->GetPasswordGenerationManager());
 
+  FormSuggestionTabHelper::CreateForWebState(web_state, @[
+    PasswordTabHelper::FromWebState(web_state)->GetSuggestionProvider(),
+    AutofillTabHelper::FromWebState(web_state)->GetSuggestionProvider(),
+  ]);
+
   // Allow the embedder to attach tab helpers.
   ios::GetChromeBrowserProvider()->AttachTabHelpers(web_state, tab);
 
diff --git a/ios/chrome/browser/ui/browser_list/BUILD.gn b/ios/chrome/browser/ui/browser_list/BUILD.gn
index f5ecaaf..3fff254 100644
--- a/ios/chrome/browser/ui/browser_list/BUILD.gn
+++ b/ios/chrome/browser/ui/browser_list/BUILD.gn
@@ -8,18 +8,44 @@
     "browser.mm",
     "browser_list.h",
     "browser_list.mm",
+    "browser_list_factory.h",
     "browser_list_observer.h",
     "browser_list_observer.mm",
     "browser_list_session_service.h",
     "browser_list_session_service_factory.h",
+    "browser_user_data.h",
+  ]
+  deps = [
+    "//base",
+    "//components/keyed_service/core",
+    "//components/keyed_service/ios",
+    "//ios/chrome/browser/ui/broadcaster",
+    "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/web_state_list",
+    "//ios/web",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+# This is a separate target to break circular dependencies. The "browser_list"
+# target must not depends on this one.
+source_set("browser_list_impl") {
+  visibility = [
+    ":unit_tests",
+    "//ios/chrome/browser/browser_state:browser_state_impl",
+  ]
+  sources = [
+    "browser_list_factory.mm",
+    "browser_list_impl.h",
+    "browser_list_impl.mm",
     "browser_list_session_service_factory.mm",
     "browser_list_session_service_impl.h",
     "browser_list_session_service_impl.mm",
-    "browser_user_data.h",
     "browser_web_state_list_delegate.h",
     "browser_web_state_list_delegate.mm",
   ]
   deps = [
+    ":browser_list",
     "//base",
     "//components/keyed_service/core",
     "//components/keyed_service/ios",
@@ -29,8 +55,6 @@
     "//ios/chrome/browser/sessions",
     "//ios/chrome/browser/sessions:serialisation",
     "//ios/chrome/browser/ssl",
-    "//ios/chrome/browser/ui/broadcaster",
-    "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/web",
     "//ios/chrome/browser/web_state_list",
     "//ios/web",
@@ -41,13 +65,16 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "browser_list_unittest.mm",
+    "browser_list_impl_unittest.mm",
   ]
   deps = [
     ":browser_list",
+    ":browser_list_impl",
     "//base",
     "//base/test:test_support",
     "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/web_state_list",
+    "//ios/chrome/browser/web_state_list:test_support",
     "//testing/gtest",
   ]
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/chrome/browser/ui/browser_list/browser.h b/ios/chrome/browser/ui/browser_list/browser.h
index 665e031..d27d77d 100644
--- a/ios/chrome/browser/ui/browser_list/browser.h
+++ b/ios/chrome/browser/ui/browser_list/browser.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "base/supports_user_data.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
 
 class WebStateList;
 class WebStateListDelegate;
@@ -20,28 +21,36 @@
 class ChromeBrowserState;
 }
 
-// Browser holds the state backing a collection of Tabs and the attached
-// UI elements (Tab strip, ...).
+// Browser is the model for a window containing multiple tabs. Instances
+// are owned by a BrowserList to allow multiple windows for a single user
+// session.
+//
+// See src/docs/ios/objects.md for more information.
 class Browser : public base::SupportsUserData {
  public:
-  explicit Browser(ios::ChromeBrowserState* browser_state);
+  // Constructs a Browser attached to |browser_state|. The |delegate| is
+  // passed to WebStateList constructor. It must be non-null and outlive
+  // the Browser.
+  Browser(ios::ChromeBrowserState* browser_state,
+          WebStateListDelegate* delegate);
   ~Browser() override;
 
-  WebStateList& web_state_list() { return *web_state_list_.get(); }
-  const WebStateList& web_state_list() const { return *web_state_list_.get(); }
-
+  // Accessors for the ChromeBroadcaster and CommandDispatcher.
+  ChromeBroadcaster* broadcaster() { return broadcaster_; }
   CommandDispatcher* dispatcher() { return dispatcher_; }
 
+  // Accessor for the owning ChromeBrowserState.
   ios::ChromeBrowserState* browser_state() const { return browser_state_; }
 
-  ChromeBroadcaster* broadcaster() { return broadcaster_; }
+  // Accessors for the WebStateList.
+  WebStateList& web_state_list() { return web_state_list_; }
+  const WebStateList& web_state_list() const { return web_state_list_; }
 
  private:
   __strong ChromeBroadcaster* broadcaster_;
   __strong CommandDispatcher* dispatcher_;
   ios::ChromeBrowserState* browser_state_;
-  std::unique_ptr<WebStateListDelegate> web_state_list_delegate_;
-  std::unique_ptr<WebStateList> web_state_list_;
+  WebStateList web_state_list_;
 
   DISALLOW_COPY_AND_ASSIGN(Browser);
 };
diff --git a/ios/chrome/browser/ui/browser_list/browser.mm b/ios/chrome/browser/ui/browser_list/browser.mm
index b705943d..90ab74f 100644
--- a/ios/chrome/browser/ui/browser_list/browser.mm
+++ b/ios/chrome/browser/ui/browser_list/browser.mm
@@ -8,23 +8,19 @@
 #include "base/memory/ptr_util.h"
 
 #import "ios/chrome/browser/ui/broadcaster/chrome_broadcaster.h"
-#import "ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
-#import "ios/chrome/browser/web_state_list/web_state_list.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-Browser::Browser(ios::ChromeBrowserState* browser_state)
+Browser::Browser(ios::ChromeBrowserState* browser_state,
+                 WebStateListDelegate* delegate)
     : broadcaster_([[ChromeBroadcaster alloc] init]),
       dispatcher_([[CommandDispatcher alloc] init]),
-      browser_state_(browser_state) {
+      browser_state_(browser_state),
+      web_state_list_(delegate) {
   DCHECK(browser_state_);
-  web_state_list_delegate_ =
-      base::MakeUnique<BrowserWebStateListDelegate>(this);
-  web_state_list_ =
-      base::MakeUnique<WebStateList>(web_state_list_delegate_.get());
 }
 
 Browser::~Browser() = default;
diff --git a/ios/chrome/browser/ui/browser_list/browser_list.h b/ios/chrome/browser/ui/browser_list/browser_list.h
index a2a71e5..fe6e83b6 100644
--- a/ios/chrome/browser/ui/browser_list/browser_list.h
+++ b/ios/chrome/browser/ui/browser_list/browser_list.h
@@ -5,58 +5,50 @@
 #ifndef IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_H_
 #define IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_H_
 
-#include <memory>
-#include <vector>
-
 #include "base/macros.h"
-#include "base/observer_list.h"
-#include "base/supports_user_data.h"
-#include "ios/chrome/browser/ui/browser_list/browser.h"
+#include "components/keyed_service/core/keyed_service.h"
 
+class Browser;
 class BrowserListObserver;
 
-namespace ios {
-class ChromeBrowserState;
-}
-
-// BrowserList attaches Browsers instance to a ChromeBrowserState.
-class BrowserList : public base::SupportsUserData::Data {
+// BrowserList attaches multiple Browser to a single ChromeBrowserState.
+// It allows listening for the addition or removal of Browsers via the
+// BrowserListObserver interface.
+//
+// See src/docs/ios/objects.md for more information.
+class BrowserList : public KeyedService {
  public:
-  explicit BrowserList(ios::ChromeBrowserState* browser_state);
   ~BrowserList() override;
 
-  static BrowserList* FromBrowserState(ios::ChromeBrowserState* browser_state);
-
   // Returns the number of Browsers in the BrowserList.
-  int count() const { return static_cast<int>(browsers_.size()); }
+  virtual int GetCount() const = 0;
 
   // Returns whether the specified index is valid.
-  int ContainsIndex(int index) const;
+  virtual int ContainsIndex(int index) const = 0;
 
   // Returns the Browser at the specified index.
-  Browser* GetBrowserAtIndex(int index) const;
+  virtual Browser* GetBrowserAtIndex(int index) const = 0;
 
   // Returns the index of the specified Browser, or kInvalidIndex if not found.
-  int GetIndexOfBrowser(const Browser* browser) const;
+  virtual int GetIndexOfBrowser(const Browser* browser) const = 0;
 
   // Creates and returns a new Browser instance.
-  Browser* CreateNewBrowser();
+  virtual Browser* CreateNewBrowser() = 0;
 
   // Closes the Browser at the specified index.
-  void CloseBrowserAtIndex(int index);
+  virtual void CloseBrowserAtIndex(int index) = 0;
 
   // Adds/removes |observer| from the list of observers.
-  void AddObserver(BrowserListObserver* observer);
-  void RemoveObserver(BrowserListObserver* observer);
+  virtual void AddObserver(BrowserListObserver* observer) = 0;
+  virtual void RemoveObserver(BrowserListObserver* observer) = 0;
 
   // Invalid index.
-  static const int kInvalidIndex = -1;
+  static constexpr int kInvalidIndex = -1;
+
+ protected:
+  BrowserList();
 
  private:
-  ios::ChromeBrowserState* browser_state_;
-  std::vector<std::unique_ptr<Browser>> browsers_;
-  base::ObserverList<BrowserListObserver, true> observers_;
-
   DISALLOW_COPY_AND_ASSIGN(BrowserList);
 };
 
diff --git a/ios/chrome/browser/ui/browser_list/browser_list.mm b/ios/chrome/browser/ui/browser_list/browser_list.mm
index 1bac1c2b..a0b38be 100644
--- a/ios/chrome/browser/ui/browser_list/browser_list.mm
+++ b/ios/chrome/browser/ui/browser_list/browser_list.mm
@@ -4,80 +4,13 @@
 
 #import "ios/chrome/browser/ui/browser_list/browser_list.h"
 
-#include <stdint.h>
-
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/ui/browser_list/browser_list_observer.h"
-
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-namespace {
-const char kBrowserListKey = 0;
-}
-
-BrowserList::BrowserList(ios::ChromeBrowserState* browser_state)
-    : browser_state_(browser_state) {
-  DCHECK(browser_state_);
-}
+BrowserList::BrowserList() = default;
 
 BrowserList::~BrowserList() = default;
 
 // static
-BrowserList* BrowserList::FromBrowserState(
-    ios::ChromeBrowserState* browser_state) {
-  base::SupportsUserData::Data* data =
-      browser_state->GetUserData(&kBrowserListKey);
-  if (!data) {
-    browser_state->SetUserData(&kBrowserListKey,
-                               base::MakeUnique<BrowserList>(browser_state));
-    data = browser_state->GetUserData(&kBrowserListKey);
-  }
-  DCHECK(data);
-  return static_cast<BrowserList*>(data);
-}
-
-int BrowserList::ContainsIndex(int index) const {
-  return 0 <= index && index < count();
-}
-
-Browser* BrowserList::GetBrowserAtIndex(int index) const {
-  DCHECK(ContainsIndex(index));
-  return browsers_[index].get();
-}
-
-int BrowserList::GetIndexOfBrowser(const Browser* browser) const {
-  for (int index = 0; index < count(); ++index) {
-    if (browsers_[index].get() == browser)
-      return index;
-  }
-  return kInvalidIndex;
-}
-
-Browser* BrowserList::CreateNewBrowser() {
-  browsers_.push_back(base::MakeUnique<Browser>(browser_state_));
-  Browser* browser_created = browsers_.back().get();
-  for (BrowserListObserver& observer : observers_)
-    observer.OnBrowserCreated(this, browser_created);
-  return browser_created;
-}
-
-void BrowserList::CloseBrowserAtIndex(int index) {
-  Browser* browser_removed = GetBrowserAtIndex(index);
-  for (BrowserListObserver& observer : observers_)
-    observer.OnBrowserRemoved(this, browser_removed);
-  browsers_.erase(browsers_.begin() + index);
-}
-
-void BrowserList::AddObserver(BrowserListObserver* observer) {
-  observers_.AddObserver(observer);
-}
-
-void BrowserList::RemoveObserver(BrowserListObserver* observer) {
-  observers_.RemoveObserver(observer);
-}
-
-const int BrowserList::kInvalidIndex;
+constexpr int BrowserList::kInvalidIndex;
diff --git a/ios/chrome/browser/ui/browser_list/browser_list_factory.h b/ios/chrome/browser/ui/browser_list/browser_list_factory.h
new file mode 100644
index 0000000..0b3e326
--- /dev/null
+++ b/ios/chrome/browser/ui/browser_list/browser_list_factory.h
@@ -0,0 +1,47 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_FACTORY_H_
+#define IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_FACTORY_H_
+
+#include "base/macros.h"
+#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}
+
+namespace ios {
+class ChromeBrowserState;
+}
+
+class BrowserList;
+
+// BrowserListFactory attaches BrowserLists to ChromeBrowserStates.
+class BrowserListFactory : public BrowserStateKeyedServiceFactory {
+ public:
+  // Convenience getter that typecasts the value returned to BrowserList.
+  static BrowserList* GetForBrowserState(
+      ios::ChromeBrowserState* browser_state);
+
+  // Getter for singleton instance.
+  static BrowserListFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<BrowserListFactory>;
+
+  BrowserListFactory();
+  ~BrowserListFactory() override;
+
+  // BrowserStateKeyedServiceFactory:
+  std::unique_ptr<KeyedService> BuildServiceInstanceFor(
+      web::BrowserState* context) const override;
+  web::BrowserState* GetBrowserStateToUse(
+      web::BrowserState* context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserListFactory);
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_FACTORY_H_
diff --git a/ios/chrome/browser/ui/browser_list/browser_list_factory.mm b/ios/chrome/browser/ui/browser_list/browser_list_factory.mm
new file mode 100644
index 0000000..9af0839
--- /dev/null
+++ b/ios/chrome/browser/ui/browser_list/browser_list_factory.mm
@@ -0,0 +1,50 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/browser_list/browser_list_factory.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "components/keyed_service/ios/browser_state_dependency_manager.h"
+#include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_impl.h"
+#import "ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// static
+BrowserList* BrowserListFactory::GetForBrowserState(
+    ios::ChromeBrowserState* browser_state) {
+  return static_cast<BrowserList*>(
+      GetInstance()->GetServiceForBrowserState(browser_state, true));
+}
+
+// static
+BrowserListFactory* BrowserListFactory::GetInstance() {
+  return base::Singleton<BrowserListFactory>::get();
+}
+
+BrowserListFactory::BrowserListFactory()
+    : BrowserStateKeyedServiceFactory(
+          "BrowserList",
+          BrowserStateDependencyManager::GetInstance()) {}
+
+BrowserListFactory::~BrowserListFactory() = default;
+
+std::unique_ptr<KeyedService> BrowserListFactory::BuildServiceInstanceFor(
+    web::BrowserState* context) const {
+  return std::make_unique<BrowserListImpl>(
+      ios::ChromeBrowserState::FromBrowserState(context),
+      std::make_unique<BrowserWebStateListDelegate>());
+}
+
+web::BrowserState* BrowserListFactory::GetBrowserStateToUse(
+    web::BrowserState* context) const {
+  return GetBrowserStateOwnInstanceInIncognito(context);
+}
diff --git a/ios/chrome/browser/ui/browser_list/browser_list_impl.h b/ios/chrome/browser/ui/browser_list/browser_list_impl.h
new file mode 100644
index 0000000..aff97fb
--- /dev/null
+++ b/ios/chrome/browser/ui/browser_list/browser_list_impl.h
@@ -0,0 +1,50 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_IMPL_H_
+#define IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_IMPL_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "ios/chrome/browser/ui/browser_list/browser.h"
+#include "ios/chrome/browser/ui/browser_list/browser_list.h"
+
+class BrowserListObserver;
+class WebStateListDelegate;
+
+namespace ios {
+class ChromeBrowserState;
+}
+
+// Concrete implementation of the BrowserList interface.
+class BrowserListImpl final : public BrowserList {
+ public:
+  BrowserListImpl(ios::ChromeBrowserState* browser_state,
+                  std::unique_ptr<WebStateListDelegate> delegate);
+
+  ~BrowserListImpl() override;
+
+  // BrowserList implementation.
+  int GetCount() const override;
+  int ContainsIndex(int index) const override;
+  Browser* GetBrowserAtIndex(int index) const override;
+  int GetIndexOfBrowser(const Browser* browser) const override;
+  Browser* CreateNewBrowser() override;
+  void CloseBrowserAtIndex(int index) override;
+  void AddObserver(BrowserListObserver* observer) override;
+  void RemoveObserver(BrowserListObserver* observer) override;
+
+ private:
+  ios::ChromeBrowserState* browser_state_;
+  std::unique_ptr<WebStateListDelegate> delegate_;
+  std::vector<std::unique_ptr<Browser>> browsers_;
+  base::ObserverList<BrowserListObserver, true> observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserListImpl);
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_BROWSER_LIST_BROWSER_LIST_IMPL_H_
diff --git a/ios/chrome/browser/ui/browser_list/browser_list_impl.mm b/ios/chrome/browser/ui/browser_list/browser_list_impl.mm
new file mode 100644
index 0000000..27dbb31c
--- /dev/null
+++ b/ios/chrome/browser/ui/browser_list/browser_list_impl.mm
@@ -0,0 +1,70 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/browser_list/browser_list_impl.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_observer.h"
+#import "ios/chrome/browser/web_state_list/web_state_list_delegate.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+BrowserListImpl::BrowserListImpl(ios::ChromeBrowserState* browser_state,
+                                 std::unique_ptr<WebStateListDelegate> delegate)
+    : browser_state_(browser_state), delegate_(std::move(delegate)) {
+  DCHECK(browser_state_);
+  DCHECK(delegate_);
+}
+
+BrowserListImpl::~BrowserListImpl() = default;
+
+int BrowserListImpl::GetCount() const {
+  return static_cast<int>(browsers_.size());
+}
+
+int BrowserListImpl::ContainsIndex(int index) const {
+  return 0 <= index && index < GetCount();
+}
+
+Browser* BrowserListImpl::GetBrowserAtIndex(int index) const {
+  DCHECK(ContainsIndex(index));
+  return browsers_[index].get();
+}
+
+int BrowserListImpl::GetIndexOfBrowser(const Browser* browser) const {
+  for (int index = 0; index < GetCount(); ++index) {
+    if (browsers_[index].get() == browser)
+      return index;
+  }
+  return kInvalidIndex;
+}
+
+Browser* BrowserListImpl::CreateNewBrowser() {
+  DCHECK_LT(browsers_.size(), static_cast<size_t>(INT_MAX));
+  browsers_.push_back(
+      std::make_unique<Browser>(browser_state_, delegate_.get()));
+  Browser* browser_created = browsers_.back().get();
+  for (BrowserListObserver& observer : observers_)
+    observer.OnBrowserCreated(this, browser_created);
+  return browser_created;
+}
+
+void BrowserListImpl::CloseBrowserAtIndex(int index) {
+  Browser* browser_removed = GetBrowserAtIndex(index);
+  for (BrowserListObserver& observer : observers_)
+    observer.OnBrowserRemoved(this, browser_removed);
+  browsers_.erase(browsers_.begin() + index);
+}
+
+void BrowserListImpl::AddObserver(BrowserListObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void BrowserListImpl::RemoveObserver(BrowserListObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
diff --git a/ios/chrome/browser/ui/browser_list/browser_list_impl_unittest.mm b/ios/chrome/browser/ui/browser_list/browser_list_impl_unittest.mm
new file mode 100644
index 0000000..cb9d84f
--- /dev/null
+++ b/ios/chrome/browser/ui/browser_list/browser_list_impl_unittest.mm
@@ -0,0 +1,159 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/browser_list/browser_list_impl.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "base/test/scoped_task_environment.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_observer.h"
+#import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
+#import "ios/chrome/browser/web_state_list/web_state_list_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// Observer for the test BrowserList that record which of the
+// BrowserListObserver methods have been called.
+class BrowserListImplTestObserver : public BrowserListObserver {
+ public:
+  BrowserListImplTestObserver() = default;
+
+  // Reset statistics whether events has been called.
+  void ResetStatistics() {
+    browser_created_called_ = false;
+    browser_removed_called_ = false;
+  }
+
+  // Returns whether OnBrowserCreated() was invoked.
+  bool browser_created_called() const { return browser_created_called_; }
+
+  // Returns whether OnBrowserRemoved() was invoked.
+  bool browser_removed_called() const { return browser_removed_called_; }
+
+  // BrowserListObserver implementation.
+  void OnBrowserCreated(BrowserList* browser_list, Browser* browser) override {
+    browser_created_called_ = true;
+  }
+
+  void OnBrowserRemoved(BrowserList* browser_list, Browser* browser) override {
+    browser_removed_called_ = true;
+  }
+
+ private:
+  bool browser_created_called_ = false;
+  bool browser_removed_called_ = false;
+};
+
+}  // namespace
+
+class BrowserListImplTest : public PlatformTest {
+ public:
+  BrowserListImplTest() {
+    browser_state_ = TestChromeBrowserState::Builder().Build();
+    browser_list_ = std::make_unique<BrowserListImpl>(
+        browser_state_.get(), std::make_unique<FakeWebStateListDelegate>());
+  }
+
+  BrowserListImpl* browser_list() { return browser_list_.get(); }
+
+ private:
+  base::test::ScopedTaskEnvironment task_environment_;
+  std::unique_ptr<ios::ChromeBrowserState> browser_state_;
+  std::unique_ptr<BrowserListImpl> browser_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserListImplTest);
+};
+
+// Tests the creation of a new Browser.
+TEST_F(BrowserListImplTest, CreateBrowser) {
+  EXPECT_EQ(0, browser_list()->GetCount());
+
+  Browser* browser = browser_list()->CreateNewBrowser();
+  ASSERT_EQ(1, browser_list()->GetCount());
+  EXPECT_EQ(browser, browser_list()->GetBrowserAtIndex(0));
+}
+
+// Tests the destruction of a Browser.
+TEST_F(BrowserListImplTest, CloseBrowser) {
+  browser_list()->CreateNewBrowser();
+  EXPECT_EQ(1, browser_list()->GetCount());
+
+  browser_list()->CloseBrowserAtIndex(0);
+  EXPECT_EQ(0, browser_list()->GetCount());
+}
+
+// Tests that ContainsIndex returns the correct Browser and supports invalid
+// indexes.
+TEST_F(BrowserListImplTest, ContainsIndex) {
+  EXPECT_EQ(0, browser_list()->GetCount());
+  EXPECT_FALSE(browser_list()->ContainsIndex(-1));
+  EXPECT_FALSE(browser_list()->ContainsIndex(0));
+  EXPECT_FALSE(browser_list()->ContainsIndex(1));
+
+  browser_list()->CreateNewBrowser();
+  EXPECT_EQ(1, browser_list()->GetCount());
+  EXPECT_FALSE(browser_list()->ContainsIndex(-1));
+  EXPECT_TRUE(browser_list()->ContainsIndex(0));
+  EXPECT_FALSE(browser_list()->ContainsIndex(1));
+
+  browser_list()->CreateNewBrowser();
+  EXPECT_EQ(2, browser_list()->GetCount());
+  EXPECT_FALSE(browser_list()->ContainsIndex(-1));
+  EXPECT_TRUE(browser_list()->ContainsIndex(0));
+  EXPECT_TRUE(browser_list()->ContainsIndex(1));
+}
+
+// Tests that GetIndexOfBrowser returns the correct index for a Browser,
+// including when passed an unknown Browser.
+TEST_F(BrowserListImplTest, GetIndexOfBrowser) {
+  EXPECT_EQ(BrowserList::kInvalidIndex,
+            browser_list()->GetIndexOfBrowser(nullptr));
+
+  Browser* browser_0 = browser_list()->CreateNewBrowser();
+  EXPECT_EQ(0, browser_list()->GetIndexOfBrowser(browser_0));
+  EXPECT_EQ(browser_0, browser_list()->GetBrowserAtIndex(0));
+
+  Browser* browser_1 = browser_list()->CreateNewBrowser();
+  EXPECT_EQ(1, browser_list()->GetIndexOfBrowser(browser_1));
+  EXPECT_EQ(browser_1, browser_list()->GetBrowserAtIndex(1));
+
+  // browser_0 is now invalid.
+  browser_list()->CloseBrowserAtIndex(0);
+  EXPECT_EQ(BrowserList::kInvalidIndex,
+            browser_list()->GetIndexOfBrowser(browser_0));
+  EXPECT_EQ(0, browser_list()->GetIndexOfBrowser(browser_1));
+  EXPECT_EQ(browser_1, browser_list()->GetBrowserAtIndex(0));
+}
+
+// Tests that the BrowserListObserver methods are correctly called.
+TEST_F(BrowserListImplTest, Observer) {
+  BrowserListImplTestObserver observer;
+  ScopedObserver<BrowserList, BrowserListObserver> scoped_observer(&observer);
+  scoped_observer.Add(browser_list());
+
+  observer.ResetStatistics();
+  ASSERT_FALSE(observer.browser_created_called());
+  ASSERT_FALSE(observer.browser_removed_called());
+
+  browser_list()->CreateNewBrowser();
+  EXPECT_TRUE(observer.browser_created_called());
+  EXPECT_FALSE(observer.browser_removed_called());
+
+  observer.ResetStatistics();
+  ASSERT_FALSE(observer.browser_created_called());
+  ASSERT_FALSE(observer.browser_removed_called());
+
+  browser_list()->CloseBrowserAtIndex(0);
+  EXPECT_FALSE(observer.browser_created_called());
+  EXPECT_TRUE(observer.browser_removed_called());
+}
diff --git a/ios/chrome/browser/ui/browser_list/browser_list_session_service_factory.mm b/ios/chrome/browser/ui/browser_list/browser_list_session_service_factory.mm
index 877c95a..0999720 100644
--- a/ios/chrome/browser/ui/browser_list/browser_list_session_service_factory.mm
+++ b/ios/chrome/browser/ui/browser_list/browser_list_session_service_factory.mm
@@ -16,7 +16,7 @@
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/sessions/session_service_ios.h"
-#import "ios/chrome/browser/ui/browser_list/browser_list.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_factory.h"
 #import "ios/chrome/browser/ui/browser_list/browser_list_session_service_impl.h"
 #import "ios/web/public/certificate_policy_cache.h"
 #import "ios/web/public/web_state/session_certificate_policy_cache.h"
@@ -60,7 +60,9 @@
 BrowserListSessionServiceFactory::BrowserListSessionServiceFactory()
     : BrowserStateKeyedServiceFactory(
           "BrowserListSessionService",
-          BrowserStateDependencyManager::GetInstance()) {}
+          BrowserStateDependencyManager::GetInstance()) {
+  DependsOn(BrowserListFactory::GetInstance());
+}
 
 BrowserListSessionServiceFactory::~BrowserListSessionServiceFactory() {}
 
@@ -72,7 +74,7 @@
   // It is safe to use base::Unretained here as BrowserListSessionServiceImpl
   // will be destroyed before the ChromeBrowserState (as it is a KeyedService).
   return base::MakeUnique<BrowserListSessionServiceImpl>(
-      BrowserList::FromBrowserState(browser_state),
+      BrowserListFactory::GetForBrowserState(browser_state),
       base::SysUTF8ToNSString(browser_state->GetStatePath().AsUTF8Unsafe()),
       [SessionServiceIOS sharedService],
       base::BindRepeating(&CreateWebState, base::Unretained(browser_state)));
diff --git a/ios/chrome/browser/ui/browser_list/browser_list_session_service_impl.mm b/ios/chrome/browser/ui/browser_list/browser_list_session_service_impl.mm
index f6841c1..23b7504 100644
--- a/ios/chrome/browser/ui/browser_list/browser_list_session_service_impl.mm
+++ b/ios/chrome/browser/ui/browser_list/browser_list_session_service_impl.mm
@@ -157,7 +157,7 @@
         const base::RepeatingClosure& closure)
     : browser_list_(browser_list), closure_(closure) {
   DCHECK(!closure_.is_null());
-  for (int index = 0; index < browser_list_->count(); ++index)
+  for (int index = 0; index < browser_list_->GetCount(); ++index)
     OnBrowserCreated(browser_list_, browser_list_->GetBrowserAtIndex(index));
   browser_list_->AddObserver(this);
 }
@@ -223,7 +223,7 @@
   DCHECK_LE(session.sessionWindows.count, static_cast<NSUInteger>(INT_MAX));
   for (NSUInteger index = 0; index < session.sessionWindows.count; ++index) {
     Browser* browser =
-        static_cast<int>(index) < browser_list_->count()
+        static_cast<int>(index) < browser_list_->GetCount()
             ? browser_list_->GetBrowserAtIndex(static_cast<int>(index))
             : browser_list_->CreateNewBrowser();
 
@@ -269,7 +269,7 @@
 
 void BrowserListSessionServiceImpl::ScheduleSaveSession(bool immediately) {
   DCHECK(browser_list_) << "ScheduleSaveSession called after Shutdown.";
-  DCHECK_GE(browser_list_->count(), 0);
+  DCHECK_GE(browser_list_->GetCount(), 0);
 
   base::WeakPtr<BrowserListSessionServiceImpl> weak_ptr =
       weak_factory_.GetWeakPtr();
@@ -278,7 +278,7 @@
     if (!weak_ptr)
       return nil;
 
-    const int count = service->browser_list_->count();
+    const int count = service->browser_list_->GetCount();
     NSMutableArray<SessionWindowIOS*>* windows =
         [NSMutableArray arrayWithCapacity:static_cast<NSUInteger>(count)];
     for (int index = 0; index < count; ++index) {
diff --git a/ios/chrome/browser/ui/browser_list/browser_list_unittest.mm b/ios/chrome/browser/ui/browser_list/browser_list_unittest.mm
deleted file mode 100644
index e3316f2..0000000
--- a/ios/chrome/browser/ui/browser_list/browser_list_unittest.mm
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/browser_list/browser_list.h"
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/scoped_observer.h"
-#include "base/test/scoped_task_environment.h"
-#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
-#import "ios/chrome/browser/ui/browser_list/browser_list_observer.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-
-class BrowserListTestObserver : public BrowserListObserver {
- public:
-  BrowserListTestObserver() = default;
-
-  // Reset statistics whether events has been called.
-  void ResetStatistics() {
-    browser_created_called_ = false;
-    browser_removed_called_ = false;
-  }
-
-  // Returns whether OnBrowserCreated() was invoked.
-  bool browser_created_called() const { return browser_created_called_; }
-
-  // Returns whether OnBrowserRemoved() was invoked.
-  bool browser_removed_called() const { return browser_removed_called_; }
-
-  // BrowserListObserver implementation.
-  void OnBrowserCreated(BrowserList* browser_list, Browser* browser) override {
-    browser_created_called_ = true;
-  }
-
-  void OnBrowserRemoved(BrowserList* browser_list, Browser* browser) override {
-    browser_removed_called_ = true;
-  }
-
- private:
-  bool browser_created_called_ = false;
-  bool browser_removed_called_ = false;
-};
-
-}  // namespace
-
-class BrowserListTest : public PlatformTest {
- public:
-  BrowserListTest() {
-    browser_state_ = TestChromeBrowserState::Builder().Build();
-  }
-
-  ios::ChromeBrowserState* browser_state() { return browser_state_.get(); }
-
- private:
-  base::test::ScopedTaskEnvironment task_environment_;
-  std::unique_ptr<ios::ChromeBrowserState> browser_state_;
-
-  DISALLOW_COPY_AND_ASSIGN(BrowserListTest);
-};
-
-TEST_F(BrowserListTest, Initialisation) {
-  EXPECT_TRUE(BrowserList::FromBrowserState(browser_state()));
-}
-
-TEST_F(BrowserListTest, CreateBrowser) {
-  BrowserList* browser_list = BrowserList::FromBrowserState(browser_state());
-  EXPECT_EQ(0, browser_list->count());
-
-  Browser* browser = browser_list->CreateNewBrowser();
-  ASSERT_EQ(1, browser_list->count());
-  EXPECT_EQ(browser, browser_list->GetBrowserAtIndex(0));
-}
-
-TEST_F(BrowserListTest, CloseBrowser) {
-  BrowserList* browser_list = BrowserList::FromBrowserState(browser_state());
-  browser_list->CreateNewBrowser();
-  EXPECT_EQ(1, browser_list->count());
-
-  browser_list->CloseBrowserAtIndex(0);
-  EXPECT_EQ(0, browser_list->count());
-}
-
-TEST_F(BrowserListTest, ContainsIndex) {
-  BrowserList* browser_list = BrowserList::FromBrowserState(browser_state());
-  EXPECT_EQ(0, browser_list->count());
-  EXPECT_FALSE(browser_list->ContainsIndex(-1));
-  EXPECT_FALSE(browser_list->ContainsIndex(0));
-  EXPECT_FALSE(browser_list->ContainsIndex(1));
-
-  browser_list->CreateNewBrowser();
-  EXPECT_EQ(1, browser_list->count());
-  EXPECT_FALSE(browser_list->ContainsIndex(-1));
-  EXPECT_TRUE(browser_list->ContainsIndex(0));
-  EXPECT_FALSE(browser_list->ContainsIndex(1));
-
-  browser_list->CreateNewBrowser();
-  EXPECT_EQ(2, browser_list->count());
-  EXPECT_FALSE(browser_list->ContainsIndex(-1));
-  EXPECT_TRUE(browser_list->ContainsIndex(0));
-  EXPECT_TRUE(browser_list->ContainsIndex(1));
-}
-
-TEST_F(BrowserListTest, GetIndexOfBrowser) {
-  BrowserList* browser_list = BrowserList::FromBrowserState(browser_state());
-  EXPECT_EQ(BrowserList::kInvalidIndex,
-            browser_list->GetIndexOfBrowser(nullptr));
-
-  Browser* browser_0 = browser_list->CreateNewBrowser();
-  EXPECT_EQ(0, browser_list->GetIndexOfBrowser(browser_0));
-  EXPECT_EQ(browser_0, browser_list->GetBrowserAtIndex(0));
-
-  Browser* browser_1 = browser_list->CreateNewBrowser();
-  EXPECT_EQ(1, browser_list->GetIndexOfBrowser(browser_1));
-  EXPECT_EQ(browser_1, browser_list->GetBrowserAtIndex(1));
-
-  // browser_0 is now invalid.
-  browser_list->CloseBrowserAtIndex(0);
-  EXPECT_EQ(BrowserList::kInvalidIndex,
-            browser_list->GetIndexOfBrowser(browser_0));
-  EXPECT_EQ(0, browser_list->GetIndexOfBrowser(browser_1));
-  EXPECT_EQ(browser_1, browser_list->GetBrowserAtIndex(0));
-}
-
-TEST_F(BrowserListTest, Observer) {
-  BrowserList* browser_list = BrowserList::FromBrowserState(browser_state());
-
-  BrowserListTestObserver observer;
-  ScopedObserver<BrowserList, BrowserListObserver> scoped_observer(&observer);
-  scoped_observer.Add(browser_list);
-
-  observer.ResetStatistics();
-  ASSERT_FALSE(observer.browser_created_called());
-  ASSERT_FALSE(observer.browser_removed_called());
-
-  browser_list->CreateNewBrowser();
-  EXPECT_TRUE(observer.browser_created_called());
-  EXPECT_FALSE(observer.browser_removed_called());
-
-  observer.ResetStatistics();
-  ASSERT_FALSE(observer.browser_created_called());
-  ASSERT_FALSE(observer.browser_removed_called());
-
-  browser_list->CloseBrowserAtIndex(0);
-  EXPECT_FALSE(observer.browser_created_called());
-  EXPECT_TRUE(observer.browser_removed_called());
-}
diff --git a/ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.h b/ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.h
index 355468d..1c2179c 100644
--- a/ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.h
+++ b/ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.h
@@ -8,12 +8,14 @@
 #include "base/macros.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_delegate.h"
 
-class Browser;
-
-// WebStateList delegate for the new architecture.
+// BrowserWebStateListDelegate attaches tab helper to the WebStates when
+// they are added to the WebStateList.
+//
+// If a tab helper needs to be present for all WebStates in clean skeleton,
+// then it must be attached to the WebState in this class' WillAddWebState.
 class BrowserWebStateListDelegate : public WebStateListDelegate {
  public:
-  explicit BrowserWebStateListDelegate(Browser* browser);
+  BrowserWebStateListDelegate();
   ~BrowserWebStateListDelegate() override;
 
   // WebStateListDelegate implementation.
@@ -21,8 +23,6 @@
   void WebStateDetached(web::WebState* web_state) override;
 
  private:
-  Browser* browser_;
-
   DISALLOW_COPY_AND_ASSIGN(BrowserWebStateListDelegate);
 };
 
diff --git a/ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.mm b/ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.mm
index 5ebff4d..06ca27d 100644
--- a/ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.mm
+++ b/ios/chrome/browser/ui/browser_list/browser_web_state_list_delegate.mm
@@ -14,10 +14,7 @@
 #error "This file requires ARC support."
 #endif
 
-BrowserWebStateListDelegate::BrowserWebStateListDelegate(Browser* browser)
-    : browser_(browser) {
-  DCHECK(browser_);
-}
+BrowserWebStateListDelegate::BrowserWebStateListDelegate() = default;
 
 BrowserWebStateListDelegate::~BrowserWebStateListDelegate() = default;
 
diff --git a/ios/chrome/browser/ui/coordinators/BUILD.gn b/ios/chrome/browser/ui/coordinators/BUILD.gn
index 422056f4..743ab57 100644
--- a/ios/chrome/browser/ui/coordinators/BUILD.gn
+++ b/ios/chrome/browser/ui/coordinators/BUILD.gn
@@ -34,6 +34,7 @@
     "//base/test:test_support",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/ui/browser_list",
+    "//ios/chrome/browser/web_state_list:test_support",
     "//testing/gtest",
   ]
 }
diff --git a/ios/chrome/browser/ui/coordinators/browser_coordinator_test.h b/ios/chrome/browser/ui/coordinators/browser_coordinator_test.h
index 073872d..8fd07378 100644
--- a/ios/chrome/browser/ui/coordinators/browser_coordinator_test.h
+++ b/ios/chrome/browser/ui/coordinators/browser_coordinator_test.h
@@ -7,12 +7,12 @@
 
 #include <memory>
 
-#include "base/macros.h"
 #include "base/test/scoped_task_environment.h"
 #include "testing/platform_test.h"
 
 class Browser;
 class TestChromeBrowserState;
+class WebStateListDelegate;
 
 class BrowserCoordinatorTest : public PlatformTest {
  protected:
@@ -24,6 +24,7 @@
  private:
   base::test::ScopedTaskEnvironment task_environment_;
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
+  std::unique_ptr<WebStateListDelegate> delegate_;
   std::unique_ptr<Browser> browser_;
 
   DISALLOW_COPY_AND_ASSIGN(BrowserCoordinatorTest);
diff --git a/ios/chrome/browser/ui/coordinators/browser_coordinator_test.mm b/ios/chrome/browser/ui/coordinators/browser_coordinator_test.mm
index 85277aa..99fb0ad 100644
--- a/ios/chrome/browser/ui/coordinators/browser_coordinator_test.mm
+++ b/ios/chrome/browser/ui/coordinators/browser_coordinator_test.mm
@@ -6,6 +6,7 @@
 
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/ui/browser_list/browser.h"
+#import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -14,7 +15,9 @@
 BrowserCoordinatorTest::BrowserCoordinatorTest() {
   // Initialize the browser.
   chrome_browser_state_ = TestChromeBrowserState::Builder().Build();
-  browser_ = std::make_unique<Browser>(chrome_browser_state_.get());
+  delegate_ = std::make_unique<FakeWebStateListDelegate>();
+  browser_ =
+      std::make_unique<Browser>(chrome_browser_state_.get(), delegate_.get());
 }
 
 BrowserCoordinatorTest::~BrowserCoordinatorTest() = default;
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_popup_view_ios.h b/ios/chrome/browser/ui/omnibox/omnibox_popup_view_ios.h
index bdc5428..b191ffaf 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_popup_view_ios.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_popup_view_ios.h
@@ -41,7 +41,7 @@
   void InvalidateLine(size_t line) override {}
   void OnLineSelected(size_t line) override {}
   void UpdatePopupAppearance() override;
-  void SetMatchIcon(size_t match_index, const gfx::Image& icon) override {}
+  void OnMatchIconUpdated(size_t match_index) override {}
   gfx::Rect GetTargetBounds() override;
   void PaintUpdatesNow() override {}
   void OnDragCanceled() override {}
diff --git a/ios/chrome/browser/ui/settings/accounts_collection_egtest.mm b/ios/chrome/browser/ui/settings/accounts_collection_egtest.mm
index aef092b..71e2ed3 100644
--- a/ios/chrome/browser/ui/settings/accounts_collection_egtest.mm
+++ b/ios/chrome/browser/ui/settings/accounts_collection_egtest.mm
@@ -11,7 +11,6 @@
 #include "ios/chrome/browser/signin/signin_manager_factory.h"
 #import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
 #include "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h"
-#import "ios/chrome/browser/ui/tools_menu/tools_popup_controller.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
diff --git a/ios/chrome/browser/ui/settings/block_popups_egtest.mm b/ios/chrome/browser/ui/settings/block_popups_egtest.mm
index 2eedc314..ba4bc19 100644
--- a/ios/chrome/browser/ui/settings/block_popups_egtest.mm
+++ b/ios/chrome/browser/ui/settings/block_popups_egtest.mm
@@ -11,9 +11,7 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
-#import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h"
 #include "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h"
-#import "ios/chrome/browser/ui/tools_menu/tools_popup_controller.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #include "ios/chrome/test/app/navigation_test_util.h"
diff --git a/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm b/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm
index 9908f27..1e15a44 100644
--- a/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/passwords_settings_egtest.mm
@@ -22,13 +22,11 @@
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
-#import "ios/chrome/browser/ui/settings/password_details_collection_view_controller_for_testing.h"
 #import "ios/chrome/browser/ui/settings/reauthentication_module.h"
-#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
 #include "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h"
-#import "ios/chrome/browser/ui/util/top_view_controller.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
+#import "ios/chrome/test/app/password_test_util.h"
 #include "ios/chrome/test/earl_grey/accessibility_util.h"
 #import "ios/chrome/test/earl_grey/chrome_actions.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
@@ -57,6 +55,7 @@
 using chrome_test_util::ButtonWithAccessibilityLabel;
 using chrome_test_util::SettingsMenuBackButton;
 using chrome_test_util::NavigationBarDoneButton;
+using chrome_test_util::SetUpAndReturnMockReauthenticationModule;
 
 namespace {
 
@@ -346,62 +345,6 @@
 
 }  // namespace
 
-@interface MockReauthenticationModule : NSObject<ReauthenticationProtocol>
-
-// Indicates whether the device is capable of reauthenticating the user.
-@property(nonatomic, assign) BOOL canAttempt;
-
-// Indicates whether (mock) authentication should succeed or not. Setting
-// |shouldSucceed| to any value sets |canAttempt| to YES.
-@property(nonatomic, assign) BOOL shouldSucceed;
-
-@end
-
-@implementation MockReauthenticationModule
-
-@synthesize shouldSucceed = _shouldSucceed;
-@synthesize canAttempt = _canAttempt;
-
-- (void)setShouldSucceed:(BOOL)shouldSucceed {
-  _canAttempt = YES;
-  _shouldSucceed = shouldSucceed;
-}
-
-- (BOOL)canAttemptReauth {
-  return _canAttempt;
-}
-
-- (void)attemptReauthWithLocalizedReason:(NSString*)localizedReason
-                                 handler:(void (^)(BOOL success))
-                                             showCopyPasswordsHandler {
-  showCopyPasswordsHandler(_shouldSucceed);
-}
-
-@end
-
-namespace {
-
-// Replace the reauthentication module in
-// PasswordDetailsCollectionViewController with a fake one to avoid being
-// blocked with a reauth prompt, and return the fake reauthentication module.
-MockReauthenticationModule* SetUpAndReturnMockReauthenticationModule() {
-  MockReauthenticationModule* mock_reauthentication_module =
-      [[MockReauthenticationModule alloc] init];
-  // TODO(crbug.com/754642): Stop using TopPresentedViewController();
-  SettingsNavigationController* settings_navigation_controller =
-      base::mac::ObjCCastStrict<SettingsNavigationController>(
-          top_view_controller::TopPresentedViewController());
-  PasswordDetailsCollectionViewController*
-      password_details_collection_view_controller =
-          base::mac::ObjCCastStrict<PasswordDetailsCollectionViewController>(
-              settings_navigation_controller.topViewController);
-  [password_details_collection_view_controller
-      setReauthenticationModule:mock_reauthentication_module];
-  return mock_reauthentication_module;
-}
-
-}  // namespace
-
 // Various tests for the Save Passwords section of the settings.
 @interface PasswordsSettingsTestCase : ChromeTestCase
 @end
diff --git a/ios/chrome/browser/ui/settings/settings_egtest.mm b/ios/chrome/browser/ui/settings/settings_egtest.mm
index 54ad8eb..4856dd5 100644
--- a/ios/chrome/browser/ui/settings/settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/settings_egtest.mm
@@ -23,10 +23,7 @@
 #include "ios/chrome/browser/pref_names.h"
 #import "ios/chrome/browser/ui/authentication/signin_promo_view.h"
 #import "ios/chrome/browser/ui/browser_view_controller.h"
-#import "ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.h"
-#import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h"
 #include "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h"
-#import "ios/chrome/browser/ui/tools_menu/tools_popup_controller.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ios/chrome/grit/ios_theme_resources.h"
@@ -58,10 +55,16 @@
 
 using chrome_test_util::ButtonWithAccessibilityLabelId;
 using chrome_test_util::ClearBrowsingDataCollectionView;
+using chrome_test_util::ClearBrowsingHistoryButton;
+using chrome_test_util::ClearCacheButton;
+using chrome_test_util::ClearCookiesButton;
+using chrome_test_util::ClearSavedPasswordsButton;
 using chrome_test_util::ContentSettingsButton;
 using chrome_test_util::NavigationBarDoneButton;
+using chrome_test_util::SettingsCollectionView;
 using chrome_test_util::SettingsMenuBackButton;
 using chrome_test_util::SettingsMenuPrivacyButton;
+using chrome_test_util::VoiceSearchButton;
 
 namespace {
 
@@ -80,25 +83,6 @@
   kBreakpadFirstLaunch,
 };
 
-// Matcher for the clear browsing history cell on the clear browsing data panel.
-id<GREYMatcher> ClearBrowsingHistoryButton() {
-  return grey_allOf(grey_accessibilityID(kClearBrowsingHistoryCellId),
-                    grey_sufficientlyVisible(), nil);
-}
-// Matcher for the clear cookies cell on the clear browsing data panel.
-id<GREYMatcher> ClearCookiesButton() {
-  return grey_accessibilityID(kClearCookiesCellId);
-}
-// Matcher for the clear cache cell on the clear browsing data panel.
-id<GREYMatcher> ClearCacheButton() {
-  return grey_allOf(grey_accessibilityID(kClearCacheCellId),
-                    grey_sufficientlyVisible(), nil);
-}
-// Matcher for the clear saved passwords cell on the clear browsing data panel.
-id<GREYMatcher> ClearSavedPasswordsButton() {
-  return grey_allOf(grey_accessibilityID(kClearSavedPasswordsCellId),
-                    grey_sufficientlyVisible(), nil);
-}
 // Matcher for the clear browsing data button on the clear browsing data panel.
 id<GREYMatcher> ClearBrowsingDataButton() {
   return ButtonWithAccessibilityLabelId(IDS_IOS_CLEAR_BUTTON);
@@ -127,11 +111,7 @@
 id<GREYMatcher> GoogleChromeButton() {
   return ButtonWithAccessibilityLabelId(IDS_IOS_PRODUCT_NAME);
 }
-// Matcher for the Google Chrome cell on the main Settings screen.
-id<GREYMatcher> VoiceSearchButton() {
-  return grey_allOf(grey_accessibilityID(kSettingsVoiceSearchCellId),
-                    grey_accessibilityTrait(UIAccessibilityTraitButton), nil);
-}
+
 // Matcher for the Preload Webpages button on the bandwidth UI.
 id<GREYMatcher> BandwidthPreloadWebpagesButton() {
   return ButtonWithAccessibilityLabelId(IDS_IOS_OPTIONS_PRELOAD_WEBPAGES);
@@ -631,8 +611,7 @@
   chrome_test_util::OpenNewIncognitoTab();
 
   [ChromeEarlGreyUI openSettingsMenu];
-  [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kSettingsCollectionViewId)]
+  [[EarlGrey selectElementWithMatcher:SettingsCollectionView()]
       assertWithMatcher:grey_notNil()];
 
   [[EarlGrey selectElementWithMatcher:NavigationBarDoneButton()]
@@ -764,8 +743,7 @@
 // not when it itslef presents something.
 - (void)testSettingsKeyboardCommands {
   [ChromeEarlGreyUI openSettingsMenu];
-  [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kSettingsCollectionViewId)]
+  [[EarlGrey selectElementWithMatcher:SettingsCollectionView()]
       assertWithMatcher:grey_notNil()];
 
   // Verify that the Settings register keyboard commands.
diff --git a/ios/chrome/browser/ui/settings/translate_ui_egtest.mm b/ios/chrome/browser/ui/settings/translate_ui_egtest.mm
index 2f64b38..bf5506b 100644
--- a/ios/chrome/browser/ui/settings/translate_ui_egtest.mm
+++ b/ios/chrome/browser/ui/settings/translate_ui_egtest.mm
@@ -5,9 +5,7 @@
 #import <EarlGrey/EarlGrey.h>
 #import <XCTest/XCTest.h>
 
-#import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h"
 #include "ios/chrome/browser/ui/tools_menu/tools_menu_constants.h"
-#import "ios/chrome/browser/ui/tools_menu/tools_popup_controller.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ios/chrome/test/earl_grey/accessibility_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
diff --git a/ios/chrome/browser/web_state_list/web_state_list_delegate.h b/ios/chrome/browser/web_state_list/web_state_list_delegate.h
index 50933e8..99d7d1b 100644
--- a/ios/chrome/browser/web_state_list/web_state_list_delegate.h
+++ b/ios/chrome/browser/web_state_list/web_state_list_delegate.h
@@ -13,6 +13,8 @@
 
 // A delegate interface that the WebStateList uses to perform work that it
 // cannot do itself such as attaching tab helpers, ...
+//
+// See src/docs/ios/objects.md for more information.
 class WebStateListDelegate {
  public:
   WebStateListDelegate() = default;
diff --git a/ios/chrome/common/channel_info.mm b/ios/chrome/common/channel_info.mm
index 1ee7a6b..54404e0 100644
--- a/ios/chrome/common/channel_info.mm
+++ b/ios/chrome/common/channel_info.mm
@@ -8,7 +8,6 @@
 #import <Foundation/Foundation.h>
 
 #import "base/mac/bundle_locations.h"
-#include "base/profiler/scoped_tracker.h"
 #import "base/strings/sys_string_conversions.h"
 #include "components/version_info/version_info.h"
 #include "components/version_info/version_string.h"
@@ -28,12 +27,6 @@
 }  // namespace
 
 std::string GetVersionString() {
-  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "422460 VersionInfo::CreateVersionString"));
-
   return version_info::GetVersionStringWithModifier(GetChannelString());
 }
 
diff --git a/ios/chrome/test/app/BUILD.gn b/ios/chrome/test/app/BUILD.gn
index bdd90add..9420398b 100644
--- a/ios/chrome/test/app/BUILD.gn
+++ b/ios/chrome/test/app/BUILD.gn
@@ -16,6 +16,8 @@
     "history_test_util.mm",
     "navigation_test_util.h",
     "navigation_test_util.mm",
+    "password_test_util.h",
+    "password_test_util.mm",
     "settings_test_util.h",
     "settings_test_util.mm",
     "signin_test_util.h",
@@ -71,10 +73,13 @@
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/main",
     "//ios/chrome/browser/ui/ntp:ntp_controller",
+    "//ios/chrome/browser/ui/settings",
+    "//ios/chrome/browser/ui/settings:test_support",
     "//ios/chrome/browser/ui/stack_view",
     "//ios/chrome/browser/ui/static_content",
     "//ios/chrome/browser/ui/tab_switcher",
     "//ios/chrome/browser/ui/tabs",
+    "//ios/chrome/browser/ui/util",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/signin:test_support",
     "//ios/testing:ios_test_support",
diff --git a/ios/chrome/test/app/password_test_util.h b/ios/chrome/test/app/password_test_util.h
new file mode 100644
index 0000000..1dafd65
--- /dev/null
+++ b/ios/chrome/test/app/password_test_util.h
@@ -0,0 +1,30 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_TEST_APP_PASSWORD_TEST_UTIL_H_
+#define IOS_CHROME_TEST_APP_PASSWORD_TEST_UTIL_H_
+
+#import "ios/chrome/browser/ui/settings/reauthentication_module.h"
+
+@interface MockReauthenticationModule : NSObject<ReauthenticationProtocol>
+
+// Indicates whether the device is capable of reauthenticating the user.
+@property(nonatomic, assign) BOOL canAttempt;
+
+// Indicates whether (mock) authentication should succeed or not. Setting
+// |shouldSucceed| to any value sets |canAttempt| to YES.
+@property(nonatomic, assign) BOOL shouldSucceed;
+
+@end
+
+namespace chrome_test_util {
+
+// Replace the reauthentication module in
+// PasswordDetailsCollectionViewController with a fake one to avoid being
+// blocked with a reauth prompt, and return the fake reauthentication module.
+MockReauthenticationModule* SetUpAndReturnMockReauthenticationModule();
+
+}  // namespace chrome_test_util
+
+#endif  // IOS_CHROME_TEST_APP_PASSWORD_TEST_UTIL_H_
diff --git a/ios/chrome/test/app/password_test_util.mm b/ios/chrome/test/app/password_test_util.mm
new file mode 100644
index 0000000..caf44b2
--- /dev/null
+++ b/ios/chrome/test/app/password_test_util.mm
@@ -0,0 +1,59 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/test/app/password_test_util.h"
+
+#include "base/mac/foundation_util.h"
+#import "ios/chrome/browser/ui/settings/password_details_collection_view_controller_for_testing.h"
+#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
+#import "ios/chrome/browser/ui/util/top_view_controller.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation MockReauthenticationModule
+
+@synthesize shouldSucceed = _shouldSucceed;
+@synthesize canAttempt = _canAttempt;
+
+- (void)setShouldSucceed:(BOOL)shouldSucceed {
+  _canAttempt = YES;
+  _shouldSucceed = shouldSucceed;
+}
+
+- (BOOL)canAttemptReauth {
+  return _canAttempt;
+}
+
+- (void)attemptReauthWithLocalizedReason:(NSString*)localizedReason
+                                 handler:(void (^)(BOOL success))
+                                             showCopyPasswordsHandler {
+  showCopyPasswordsHandler(_shouldSucceed);
+}
+
+@end
+
+namespace chrome_test_util {
+
+// Replace the reauthentication module in
+// PasswordDetailsCollectionViewController with a fake one to avoid being
+// blocked with a reauth prompt, and return the fake reauthentication module.
+MockReauthenticationModule* SetUpAndReturnMockReauthenticationModule() {
+  MockReauthenticationModule* mock_reauthentication_module =
+      [[MockReauthenticationModule alloc] init];
+  // TODO(crbug.com/754642): Stop using TopPresentedViewController();
+  SettingsNavigationController* settings_navigation_controller =
+      base::mac::ObjCCastStrict<SettingsNavigationController>(
+          top_view_controller::TopPresentedViewController());
+  PasswordDetailsCollectionViewController*
+      password_details_collection_view_controller =
+          base::mac::ObjCCastStrict<PasswordDetailsCollectionViewController>(
+              settings_navigation_controller.topViewController);
+  [password_details_collection_view_controller
+      setReauthenticationModule:mock_reauthentication_module];
+  return mock_reauthentication_module;
+}
+
+}  // namespace
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
index e344cd60..6d9ce55d 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
@@ -84,7 +84,7 @@
       grey_allOf(buttonMatcher, grey_interactable(), nil);
   [[[EarlGrey selectElementWithMatcher:interactableButtonMatcher]
          usingSearchAction:ScrollDown()
-      onElementWithMatcher:grey_accessibilityID(kSettingsCollectionViewId)]
+      onElementWithMatcher:chrome_test_util::SettingsCollectionView()]
       performAction:grey_tap()];
 }
 
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.h b/ios/chrome/test/earl_grey/chrome_matchers.h
index 19775677..586a511 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers.h
@@ -156,6 +156,26 @@
 // Returns matcher for the error confirmation view for payment request.
 id<GREYMatcher> PaymentRequestErrorView();
 
+// Returns matcher for the voice search button on the main Settings screen.
+id<GREYMatcher> VoiceSearchButton();
+
+// Returns matcher for the settings main menu view.
+id<GREYMatcher> SettingsCollectionView();
+
+// Returns matcher for the clear browsing history cell on the clear browsing
+// data panel.
+id<GREYMatcher> ClearBrowsingHistoryButton();
+
+// Returns matcher for the clear cookies cell on the clear browsing data panel.
+id<GREYMatcher> ClearCookiesButton();
+
+// Returns matcher for the clear cache cell on the clear browsing data panel.
+id<GREYMatcher> ClearCacheButton();
+
+// Returns matcher for the clear saved passwords cell on the clear browsing data
+// panel.
+id<GREYMatcher> ClearSavedPasswordsButton();
+
 }  // namespace chrome_test_util
 
 #endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_MATCHERS_H_
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm
index 29f360c..c9013c9cb 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -273,4 +273,29 @@
   return grey_accessibilityID(kPaymentRequestErrorCollectionViewID);
 }
 
+id<GREYMatcher> VoiceSearchButton() {
+  return grey_allOf(grey_accessibilityID(kSettingsVoiceSearchCellId),
+                    grey_accessibilityTrait(UIAccessibilityTraitButton), nil);
+}
+
+id<GREYMatcher> SettingsCollectionView() {
+  return grey_accessibilityID(kSettingsCollectionViewId);
+}
+
+id<GREYMatcher> ClearBrowsingHistoryButton() {
+  return grey_accessibilityID(kClearBrowsingHistoryCellId);
+}
+
+id<GREYMatcher> ClearCookiesButton() {
+  return grey_accessibilityID(kClearCookiesCellId);
+}
+
+id<GREYMatcher> ClearCacheButton() {
+  return grey_accessibilityID(kClearCacheCellId);
+}
+
+id<GREYMatcher> ClearSavedPasswordsButton() {
+  return grey_accessibilityID(kClearSavedPasswordsCellId);
+}
+
 }  // namespace chrome_test_util
diff --git a/ios/clean/chrome/app/steps/root_coordinator_initializer.mm b/ios/clean/chrome/app/steps/root_coordinator_initializer.mm
index b2f03a5..8f3e446 100644
--- a/ios/clean/chrome/app/steps/root_coordinator_initializer.mm
+++ b/ios/clean/chrome/app/steps/root_coordinator_initializer.mm
@@ -7,7 +7,9 @@
 #import "ios/chrome/app/startup/provider_registration.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
 #import "ios/chrome/browser/ui/browser_list/browser_list.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_factory.h"
 #import "ios/chrome/browser/ui/browser_list/browser_list_session_service.h"
 #import "ios/chrome/browser/ui/browser_list/browser_list_session_service_factory.h"
 #import "ios/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
@@ -38,7 +40,7 @@
 - (void)runFeature:(NSString*)feature withContext:(id<StepContext>)context {
   _rootCoordinator = [[RootCoordinator alloc] init];
   [_rootCoordinator
-      setBrowser:BrowserList::FromBrowserState(context.browserState)
+      setBrowser:BrowserListFactory::GetForBrowserState(context.browserState)
                      ->CreateNewBrowser()];
 
   BrowserListSessionService* service =
diff --git a/ios/clean/chrome/browser/ui/overlays/overlay_service_factory.mm b/ios/clean/chrome/browser/ui/overlays/overlay_service_factory.mm
index 9c7dac09..f2ac2869 100644
--- a/ios/clean/chrome/browser/ui/overlays/overlay_service_factory.mm
+++ b/ios/clean/chrome/browser/ui/overlays/overlay_service_factory.mm
@@ -12,7 +12,7 @@
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/ui/browser_list/browser_list.h"
+#import "ios/chrome/browser/ui/browser_list/browser_list_factory.h"
 #import "ios/clean/chrome/browser/ui/overlays/overlay_service_impl.h"
 #import "ios/web/public/certificate_policy_cache.h"
 #import "ios/web/public/web_state/session_certificate_policy_cache.h"
@@ -36,7 +36,9 @@
 OverlayServiceFactory::OverlayServiceFactory()
     : BrowserStateKeyedServiceFactory(
           "OverlayService",
-          BrowserStateDependencyManager::GetInstance()) {}
+          BrowserStateDependencyManager::GetInstance()) {
+  DependsOn(BrowserListFactory::GetInstance());
+}
 
 OverlayServiceFactory::~OverlayServiceFactory() {}
 
@@ -45,7 +47,7 @@
   ios::ChromeBrowserState* browser_state =
       ios::ChromeBrowserState::FromBrowserState(context);
   return base::MakeUnique<OverlayServiceImpl>(
-      BrowserList::FromBrowserState(browser_state));
+      BrowserListFactory::GetForBrowserState(browser_state));
 }
 
 web::BrowserState* OverlayServiceFactory::GetBrowserStateToUse(
diff --git a/ios/clean/chrome/browser/ui/overlays/overlay_service_impl.mm b/ios/clean/chrome/browser/ui/overlays/overlay_service_impl.mm
index 0c25e787..cf05226 100644
--- a/ios/clean/chrome/browser/ui/overlays/overlay_service_impl.mm
+++ b/ios/clean/chrome/browser/ui/overlays/overlay_service_impl.mm
@@ -20,7 +20,7 @@
 OverlayServiceImpl::OverlayServiceImpl(BrowserList* browser_list)
     : browser_list_(browser_list) {
   DCHECK(browser_list_);
-  for (int index = 0; index < browser_list_->count(); ++index) {
+  for (int index = 0; index < browser_list_->GetCount(); ++index) {
     StartServiceForBrowser(browser_list_->GetBrowserAtIndex(index));
   }
   browser_list_->AddObserver(this);
@@ -45,7 +45,7 @@
 #pragma mark - KeyedService
 
 void OverlayServiceImpl::Shutdown() {
-  for (int index = 0; index < browser_list_->count(); ++index) {
+  for (int index = 0; index < browser_list_->GetCount(); ++index) {
     StopServiceForBrowser(browser_list_->GetBrowserAtIndex(index));
   }
   browser_list_->RemoveObserver(this);
@@ -59,11 +59,12 @@
     web::WebState* web_state) {
   DCHECK(scheduler);
   Browser* scheduler_browser = nullptr;
-  for (int index = 0; !scheduler_browser && index < browser_list_->count();
-       ++index) {
+  for (int index = 0; index < browser_list_->GetCount(); ++index) {
     Browser* browser = browser_list_->GetBrowserAtIndex(index);
-    if (OverlayScheduler::FromBrowser(browser) == scheduler)
+    if (OverlayScheduler::FromBrowser(browser) == scheduler) {
       scheduler_browser = browser;
+      break;
+    }
   }
   DCHECK(scheduler_browser);
   for (auto& observer : observers_) {
@@ -98,7 +99,7 @@
 }
 
 void OverlayServiceImpl::CancelOverlays() {
-  for (int index = 0; index < browser_list_->count(); ++index) {
+  for (int index = 0; index < browser_list_->GetCount(); ++index) {
     OverlayScheduler::FromBrowser(browser_list_->GetBrowserAtIndex(index))
         ->CancelOverlays();
   }
diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn
index 4262139..5753bd5 100644
--- a/ipc/BUILD.gn
+++ b/ipc/BUILD.gn
@@ -250,7 +250,12 @@
 
   test("ipc_perftests") {
     sources = [
+      "ipc_cpu_perftest.cc",
       "ipc_mojo_perftest.cc",
+      "ipc_perftest_messages.cc",
+      "ipc_perftest_messages.h",
+      "ipc_perftest_util.cc",
+      "ipc_perftest_util.h",
       "run_all_perftests.cc",
     ]
 
diff --git a/ipc/DEPS b/ipc/DEPS
index f8d30bb..662e341 100644
--- a/ipc/DEPS
+++ b/ipc/DEPS
@@ -14,7 +14,8 @@
     "+mojo/edk/embedder",
     "+mojo/edk/test",
   ],
-  "ipc_perftest_support\.cc": [
+  "ipc_.*perftest.*\.cc": [
+    "+mojo/edk/embedder",
     "+mojo/edk/test",
   ],
   "run_all_(unit|perf)tests\.cc": [
diff --git a/ipc/OWNERS b/ipc/OWNERS
index 0617e7bf..22545c9 100644
--- a/ipc/OWNERS
+++ b/ipc/OWNERS
@@ -8,6 +8,8 @@
 # new sandbox escapes.
 per-file ipc_message_start.h=set noparent
 per-file ipc_message_start.h=file://ipc/SECURITY_OWNERS
+per-file *_messages.cc=set noparent
+per-file *_messages.cc=file://ipc/SECURITY_OWNERS
 per-file *_messages*.h=set noparent
 per-file *_messages*.h=file://ipc/SECURITY_OWNERS
 per-file *.mojom=set noparent
diff --git a/ipc/ipc_channel_proxy.cc b/ipc/ipc_channel_proxy.cc
index f48e44c..edf37ba 100644
--- a/ipc/ipc_channel_proxy.cc
+++ b/ipc/ipc_channel_proxy.cc
@@ -14,7 +14,6 @@
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
@@ -188,10 +187,6 @@
 
 // Called on the IPC::Channel thread
 void ChannelProxy::Context::OnChannelClosed() {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "477117 ChannelProxy::Context::OnChannelClosed"));
   // It's okay for IPC::ChannelProxy::Close to be called more than once, which
   // would result in this branch being taken.
   if (!channel_)
@@ -226,10 +221,6 @@
 
 // Called on the IPC::Channel thread
 void ChannelProxy::Context::OnSendMessage(std::unique_ptr<Message> message) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "477117 ChannelProxy::Context::OnSendMessage"));
   if (!channel_) {
     OnChannelClosed();
     return;
diff --git a/ipc/ipc_cpu_perftest.cc b/ipc/ipc_cpu_perftest.cc
new file mode 100644
index 0000000..976ca1f
--- /dev/null
+++ b/ipc/ipc_cpu_perftest.cc
@@ -0,0 +1,416 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "base/message_loop/message_loop.h"
+#include "base/process/process_metrics.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/perf_log.h"
+#include "base/timer/timer.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_perftest_messages.h"
+#include "ipc/ipc_perftest_util.h"
+#include "ipc/ipc_sync_channel.h"
+#include "ipc/ipc_test.mojom.h"
+#include "ipc/ipc_test_base.h"
+#include "mojo/edk/test/mojo_test_base.h"
+#include "mojo/edk/test/multiprocess_test_helper.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+
+namespace IPC {
+namespace {
+
+struct TestParams {
+  TestParams() {}
+  TestParams(size_t in_message_size,
+             size_t in_frames_per_second,
+             size_t in_messages_per_frame,
+             size_t in_duration_in_seconds)
+      : message_size(in_message_size),
+        frames_per_second(in_frames_per_second),
+        messages_per_frame(in_messages_per_frame),
+        duration_in_seconds(in_duration_in_seconds) {}
+
+  size_t message_size;
+  size_t frames_per_second;
+  size_t messages_per_frame;
+  size_t duration_in_seconds;
+};
+
+std::vector<TestParams> GetDefaultTestParams() {
+  std::vector<TestParams> list;
+  list.push_back({144, 20, 10, 10});
+  list.push_back({144, 60, 10, 10});
+  return list;
+}
+
+std::string GetLogTitle(const std::string& label, const TestParams& params) {
+  return base::StringPrintf(
+      "%s_MsgSize_%zu_FrmPerSec_%zu_MsgPerFrm_%zu", label.c_str(),
+      params.message_size, params.frames_per_second, params.messages_per_frame);
+}
+
+base::TimeDelta GetFrameTime(size_t frames_per_second) {
+  return base::TimeDelta::FromSecondsD(1.0 / frames_per_second);
+}
+
+class PerfCpuLogger {
+ public:
+  explicit PerfCpuLogger(base::StringPiece test_name)
+      : test_name_(test_name),
+        process_metrics_(base::ProcessMetrics::CreateCurrentProcessMetrics()) {
+    process_metrics_->GetPlatformIndependentCPUUsage();
+  }
+
+  ~PerfCpuLogger() {
+    double result = process_metrics_->GetPlatformIndependentCPUUsage();
+    base::LogPerfResult(test_name_.c_str(), result, "%");
+  }
+
+ private:
+  std::string test_name_;
+  std::unique_ptr<base::ProcessMetrics> process_metrics_;
+
+  DISALLOW_COPY_AND_ASSIGN(PerfCpuLogger);
+};
+
+MULTIPROCESS_TEST_MAIN(MojoPerfTestClientTestChildMain) {
+  MojoPerfTestClient client;
+  int rv = mojo::edk::test::MultiprocessTestHelper::RunClientMain(
+      base::Bind(&MojoPerfTestClient::Run, base::Unretained(&client)),
+      true /* pass_pipe_ownership_to_main */);
+
+  base::RunLoop run_loop;
+  run_loop.RunUntilIdle();
+
+  return rv;
+}
+
+class ChannelSteadyPingPongListener : public Listener {
+ public:
+  ChannelSteadyPingPongListener() = default;
+
+  ~ChannelSteadyPingPongListener() override = default;
+
+  void Init(Sender* sender) {
+    DCHECK(!sender_);
+    sender_ = sender;
+  }
+
+  void SetTestParams(const TestParams& params,
+                     const std::string& label,
+                     bool sync,
+                     const base::Closure& quit_closure) {
+    params_ = params;
+    label_ = label;
+    sync_ = sync;
+    quit_closure_ = quit_closure;
+    payload_ = std::string(params.message_size, 'a');
+  }
+
+  bool OnMessageReceived(const Message& message) override {
+    CHECK(sender_);
+
+    bool handled = true;
+    IPC_BEGIN_MESSAGE_MAP(ChannelSteadyPingPongListener, message)
+      IPC_MESSAGE_HANDLER(TestMsg_Hello, OnHello)
+      IPC_MESSAGE_HANDLER(TestMsg_Ping, OnPing)
+      IPC_MESSAGE_UNHANDLED(handled = false)
+    IPC_END_MESSAGE_MAP()
+    return handled;
+  }
+
+  void OnHello() {
+    cpu_logger_ = std::make_unique<PerfCpuLogger>(GetLogTitle(label_, params_));
+
+    frame_count_down_ = params_.frames_per_second * params_.duration_in_seconds;
+
+    timer_.Start(FROM_HERE, GetFrameTime(params_.frames_per_second), this,
+                 &ChannelSteadyPingPongListener::StartPingPong);
+  }
+
+  void StartPingPong() {
+    if (sync_) {
+      base::TimeTicks before = base::TimeTicks::Now();
+      for (count_down_ = params_.messages_per_frame; count_down_ > 0;
+           --count_down_) {
+        std::string response;
+        sender_->Send(new TestMsg_SyncPing(payload_, &response));
+        DCHECK_EQ(response, payload_);
+      }
+
+      if (base::TimeTicks::Now() - before >
+          GetFrameTime(params_.frames_per_second)) {
+        LOG(ERROR) << "Frame " << frame_count_down_
+                   << " wasn't able to complete on time!";
+      }
+
+      CHECK_GT(frame_count_down_, 0);
+      frame_count_down_--;
+      if (frame_count_down_ == 0)
+        StopPingPong();
+    } else {
+      if (count_down_ != 0) {
+        LOG(ERROR) << "Frame " << frame_count_down_
+                   << " wasn't able to complete on time!";
+      } else {
+        SendPong();
+      }
+      count_down_ = params_.messages_per_frame;
+    }
+  }
+
+  void StopPingPong() {
+    cpu_logger_.reset();
+    timer_.AbandonAndStop();
+    quit_closure_.Run();
+  }
+
+  void OnPing(const std::string& payload) {
+    // Include message deserialization in latency.
+    DCHECK_EQ(payload_.size(), payload.size());
+
+    CHECK_GT(count_down_, 0);
+    count_down_--;
+    if (count_down_ > 0) {
+      SendPong();
+    } else {
+      CHECK_GT(frame_count_down_, 0);
+      frame_count_down_--;
+      if (frame_count_down_ == 0)
+        StopPingPong();
+    }
+  }
+
+  void SendPong() { sender_->Send(new TestMsg_Ping(payload_)); }
+
+ private:
+  Sender* sender_ = nullptr;
+  TestParams params_;
+  std::string payload_;
+  std::string label_;
+  bool sync_ = false;
+
+  int count_down_ = 0;
+  int frame_count_down_ = 0;
+
+  base::RepeatingTimer timer_;
+  std::unique_ptr<PerfCpuLogger> cpu_logger_;
+
+  base::Closure quit_closure_;
+};
+
+class ChannelSteadyPingPongTest : public IPCChannelMojoTestBase {
+ public:
+  ChannelSteadyPingPongTest() = default;
+  ~ChannelSteadyPingPongTest() override = default;
+
+  void RunPingPongServer(const std::string& label, bool sync) {
+    Init("MojoPerfTestClient");
+
+    // Set up IPC channel and start client.
+    ChannelSteadyPingPongListener listener;
+
+    std::unique_ptr<ChannelProxy> channel_proxy;
+    std::unique_ptr<base::WaitableEvent> shutdown_event;
+
+    if (sync) {
+      shutdown_event = std::make_unique<base::WaitableEvent>(
+          base::WaitableEvent::ResetPolicy::MANUAL,
+          base::WaitableEvent::InitialState::NOT_SIGNALED);
+      channel_proxy = IPC::SyncChannel::Create(
+          TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
+          GetIOThreadTaskRunner(), false, shutdown_event.get());
+    } else {
+      channel_proxy = IPC::ChannelProxy::Create(
+          TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
+          GetIOThreadTaskRunner());
+    }
+    listener.Init(channel_proxy.get());
+
+    LockThreadAffinity thread_locker(kSharedCore);
+    std::vector<TestParams> params_list = GetDefaultTestParams();
+    for (const auto& params : params_list) {
+      base::RunLoop run_loop;
+
+      listener.SetTestParams(params, label, sync,
+                             run_loop.QuitWhenIdleClosure());
+
+      // This initial message will kick-start the ping-pong of messages.
+      channel_proxy->Send(new TestMsg_Hello);
+
+      run_loop.Run();
+    }
+
+    // Send quit message.
+    channel_proxy->Send(new TestMsg_Quit);
+
+    EXPECT_TRUE(WaitForClientShutdown());
+    channel_proxy.reset();
+  }
+};
+
+TEST_F(ChannelSteadyPingPongTest, AsyncPingPong) {
+  RunPingPongServer("IPC_CPU_Async", false);
+}
+
+TEST_F(ChannelSteadyPingPongTest, SyncPingPong) {
+  RunPingPongServer("IPC_CPU_Sync", true);
+}
+
+class MojoSteadyPingPongTest : public mojo::edk::test::MojoTestBase {
+ public:
+  MojoSteadyPingPongTest() = default;
+
+ protected:
+  void RunPingPongServer(MojoHandle mp, const std::string& label, bool sync) {
+    label_ = label;
+    sync_ = sync;
+
+    mojo::MessagePipeHandle mp_handle(mp);
+    mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
+    ping_receiver_.Bind(IPC::mojom::ReflectorPtrInfo(std::move(scoped_mp), 0u));
+
+    LockThreadAffinity thread_locker(kSharedCore);
+    std::vector<TestParams> params_list = GetDefaultTestParams();
+    for (const auto& params : params_list) {
+      params_ = params;
+      payload_ = std::string(params.message_size, 'a');
+
+      ping_receiver_->Ping("hello", base::Bind(&MojoSteadyPingPongTest::OnHello,
+                                               base::Unretained(this)));
+      base::RunLoop run_loop;
+      quit_closure_ = run_loop.QuitWhenIdleClosure();
+      run_loop.Run();
+    }
+
+    ping_receiver_->Quit();
+
+    ignore_result(ping_receiver_.PassInterface().PassHandle().release());
+  }
+
+  void OnHello(const std::string& value) {
+    cpu_logger_ = std::make_unique<PerfCpuLogger>(GetLogTitle(label_, params_));
+
+    frame_count_down_ = params_.frames_per_second * params_.duration_in_seconds;
+
+    timer_.Start(FROM_HERE, GetFrameTime(params_.frames_per_second), this,
+                 &MojoSteadyPingPongTest::StartPingPong);
+  }
+
+  void StartPingPong() {
+    if (sync_) {
+      base::TimeTicks before = base::TimeTicks::Now();
+      for (count_down_ = params_.messages_per_frame; count_down_ > 0;
+           --count_down_) {
+        std::string response;
+        ping_receiver_->SyncPing(payload_, &response);
+        DCHECK_EQ(response, payload_);
+      }
+
+      if (base::TimeTicks::Now() - before >
+          GetFrameTime(params_.frames_per_second)) {
+        LOG(ERROR) << "Frame " << frame_count_down_
+                   << " wasn't able to complete on time!";
+      }
+
+      CHECK_GT(frame_count_down_, 0);
+      frame_count_down_--;
+      if (frame_count_down_ == 0)
+        StopPingPong();
+    } else {
+      if (count_down_ != 0) {
+        LOG(ERROR) << "Frame " << frame_count_down_
+                   << " wasn't able to complete on time!";
+      } else {
+        SendPing();
+      }
+      count_down_ = params_.messages_per_frame;
+    }
+  }
+
+  void StopPingPong() {
+    cpu_logger_.reset();
+    timer_.AbandonAndStop();
+    quit_closure_.Run();
+  }
+
+  void OnPong(const std::string& value) {
+    // Include message deserialization in latency.
+    DCHECK_EQ(payload_.size(), value.size());
+
+    CHECK_GT(count_down_, 0);
+    count_down_--;
+    if (count_down_ > 0) {
+      SendPing();
+    } else {
+      CHECK_GT(frame_count_down_, 0);
+      frame_count_down_--;
+      if (frame_count_down_ == 0)
+        StopPingPong();
+    }
+  }
+
+  void SendPing() {
+    ping_receiver_->Ping(payload_, base::Bind(&MojoSteadyPingPongTest::OnPong,
+                                              base::Unretained(this)));
+  }
+
+  static int RunPingPongClient(MojoHandle mp) {
+    mojo::MessagePipeHandle mp_handle(mp);
+    mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
+
+    LockThreadAffinity thread_locker(kSharedCore);
+    base::RunLoop run_loop;
+    ReflectorImpl impl(std::move(scoped_mp), run_loop.QuitWhenIdleClosure());
+    run_loop.Run();
+    return 0;
+  }
+
+ private:
+  TestParams params_;
+  std::string payload_;
+  std::string label_;
+  bool sync_ = false;
+
+  IPC::mojom::ReflectorPtr ping_receiver_;
+
+  int count_down_ = 0;
+  int frame_count_down_ = 0;
+
+  base::RepeatingTimer timer_;
+  std::unique_ptr<PerfCpuLogger> cpu_logger_;
+
+  base::Closure quit_closure_;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoSteadyPingPongTest);
+};
+
+DEFINE_TEST_CLIENT_WITH_PIPE(PingPongClient, MojoSteadyPingPongTest, h) {
+  base::MessageLoop main_message_loop;
+  return RunPingPongClient(h);
+}
+
+// Similar to ChannelSteadyPingPongTest above, but uses a Mojo interface
+// instead of raw IPC::Messages.
+TEST_F(MojoSteadyPingPongTest, AsyncPingPong) {
+  RunTestClient("PingPongClient", [&](MojoHandle h) {
+    base::MessageLoop main_message_loop;
+    RunPingPongServer(h, "Mojo_CPU_Async", false);
+  });
+}
+
+TEST_F(MojoSteadyPingPongTest, SyncPingPong) {
+  RunTestClient("PingPongClient", [&](MojoHandle h) {
+    base::MessageLoop main_message_loop;
+    RunPingPongServer(h, "Mojo_CPU_Sync", true);
+  });
+}
+
+}  // namespace
+}  // namespace IPC
diff --git a/ipc/ipc_mojo_perftest.cc b/ipc/ipc_mojo_perftest.cc
index 45f5203..b7cea2a9 100644
--- a/ipc/ipc_mojo_perftest.cc
+++ b/ipc/ipc_mojo_perftest.cc
@@ -15,6 +15,8 @@
 #include "base/threading/thread.h"
 #include "build/build_config.h"
 #include "ipc/ipc_channel_mojo.h"
+#include "ipc/ipc_perftest_messages.h"
+#include "ipc/ipc_perftest_util.h"
 #include "ipc/ipc_sync_channel.h"
 #include "ipc/ipc_test.mojom.h"
 #include "ipc/ipc_test_base.h"
@@ -27,25 +29,9 @@
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 
-#define IPC_MESSAGE_IMPL
-#include "ipc/ipc_message_macros.h"
-
-#define IPC_MESSAGE_START TestMsgStart
-
-IPC_MESSAGE_CONTROL0(TestMsg_Hello)
-IPC_MESSAGE_CONTROL0(TestMsg_Quit)
-IPC_MESSAGE_CONTROL1(TestMsg_Ping, std::string)
-IPC_SYNC_MESSAGE_CONTROL1_1(TestMsg_SyncPing, std::string, std::string)
-
 namespace IPC {
 namespace {
 
-scoped_refptr<base::SingleThreadTaskRunner> GetIOThreadTaskRunner() {
-  scoped_refptr<base::TaskRunner> runner = mojo::edk::GetIOTaskRunner();
-  return scoped_refptr<base::SingleThreadTaskRunner>(
-      static_cast<base::SingleThreadTaskRunner*>(runner.get()));
-}
-
 class PerformanceChannelListener : public Listener {
  public:
   explicit PerformanceChannelListener(const std::string& label)
@@ -136,102 +122,6 @@
   std::unique_ptr<base::PerfTimeLogger> perf_logger_;
 };
 
-// This channel listener just replies to all messages with the exact same
-// message. It assumes each message has one string parameter. When the string
-// "quit" is sent, it will exit.
-class ChannelReflectorListener : public Listener {
- public:
-  ChannelReflectorListener() : channel_(NULL) {
-    VLOG(1) << "Client listener up";
-  }
-
-  ~ChannelReflectorListener() override { VLOG(1) << "Client listener down"; }
-
-  void Init(Sender* channel) {
-    DCHECK(!channel_);
-    channel_ = channel;
-  }
-
-  bool OnMessageReceived(const Message& message) override {
-    CHECK(channel_);
-    bool handled = true;
-    IPC_BEGIN_MESSAGE_MAP(ChannelReflectorListener, message)
-      IPC_MESSAGE_HANDLER(TestMsg_Hello, OnHello)
-      IPC_MESSAGE_HANDLER(TestMsg_Ping, OnPing)
-      IPC_MESSAGE_HANDLER(TestMsg_SyncPing, OnSyncPing)
-      IPC_MESSAGE_HANDLER(TestMsg_Quit, OnQuit)
-      IPC_MESSAGE_UNHANDLED(handled = false)
-    IPC_END_MESSAGE_MAP()
-    return handled;
-  }
-
-  void OnHello() { channel_->Send(new TestMsg_Hello); }
-
-  void OnPing(const std::string& payload) {
-    channel_->Send(new TestMsg_Ping(payload));
-  }
-
-  void OnSyncPing(const std::string& payload, std::string* response) {
-    *response = payload;
-  }
-
-  void OnQuit() { base::RunLoop::QuitCurrentWhenIdleDeprecated(); }
-
-  void Send(IPC::Message* message) { channel_->Send(message); }
-
- private:
-  Sender* channel_;
-};
-
-// This class locks the current thread to a particular CPU core. This is
-// important because otherwise the different threads and processes of these
-// tests end up on different CPU cores which means that all of the cores are
-// lightly loaded so the OS (Windows and Linux) fails to ramp up the CPU
-// frequency, leading to unpredictable and often poor performance.
-class LockThreadAffinity {
- public:
-  explicit LockThreadAffinity(int cpu_number) : affinity_set_ok_(false) {
-#if defined(OS_WIN)
-    const DWORD_PTR thread_mask = static_cast<DWORD_PTR>(1) << cpu_number;
-    old_affinity_ = SetThreadAffinityMask(GetCurrentThread(), thread_mask);
-    affinity_set_ok_ = old_affinity_ != 0;
-#elif defined(OS_LINUX)
-    cpu_set_t cpuset;
-    CPU_ZERO(&cpuset);
-    CPU_SET(cpu_number, &cpuset);
-    auto get_result = sched_getaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
-    DCHECK_EQ(0, get_result);
-    auto set_result = sched_setaffinity(0, sizeof(cpuset), &cpuset);
-    // Check for get_result failure, even though it should always succeed.
-    affinity_set_ok_ = (set_result == 0) && (get_result == 0);
-#endif
-    if (!affinity_set_ok_)
-      LOG(WARNING) << "Failed to set thread affinity to CPU " << cpu_number;
-  }
-
-  ~LockThreadAffinity() {
-    if (!affinity_set_ok_)
-      return;
-#if defined(OS_WIN)
-    auto set_result = SetThreadAffinityMask(GetCurrentThread(), old_affinity_);
-    DCHECK_NE(0u, set_result);
-#elif defined(OS_LINUX)
-    auto set_result = sched_setaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
-    DCHECK_EQ(0, set_result);
-#endif
-  }
-
- private:
-  bool affinity_set_ok_;
-#if defined(OS_WIN)
-  DWORD_PTR old_affinity_;
-#elif defined(OS_LINUX)
-  cpu_set_t old_cpuset_;
-#endif
-
-  DISALLOW_COPY_AND_ASSIGN(LockThreadAffinity);
-};
-
 class PingPongTestParams {
  public:
   PingPongTestParams(size_t size, int count)
@@ -287,10 +177,6 @@
   return list;
 }
 
-// Avoid core 0 due to conflicts with Intel's Power Gadget.
-// Setting thread affinity will fail harmlessly on single/dual core machines.
-const int kSharedCore = 2;
-
 class MojoChannelPerfTest : public IPCChannelMojoTestBase {
  public:
   MojoChannelPerfTest() = default;
@@ -374,34 +260,6 @@
   run_loop.RunUntilIdle();
 }
 
-class MojoPerfTestClient {
- public:
-  MojoPerfTestClient() : listener_(new ChannelReflectorListener()) {
-    mojo::edk::test::MultiprocessTestHelper::ChildSetup();
-  }
-
-  ~MojoPerfTestClient() = default;
-
-  int Run(MojoHandle handle) {
-    handle_ = mojo::MakeScopedHandle(mojo::MessagePipeHandle(handle));
-    LockThreadAffinity thread_locker(kSharedCore);
-
-    std::unique_ptr<ChannelProxy> channel =
-        IPC::ChannelProxy::Create(handle_.release(), Channel::MODE_CLIENT,
-                                  listener_.get(), GetIOThreadTaskRunner());
-    listener_->Init(channel.get());
-
-    base::RunLoop().Run();
-    return 0;
-  }
-
- private:
-  base::MessageLoop main_message_loop_;
-  std::unique_ptr<ChannelReflectorListener> listener_;
-  std::unique_ptr<Channel> channel_;
-  mojo::ScopedMessagePipeHandle handle_;
-};
-
 MULTIPROCESS_TEST_MAIN(MojoPerfTestClientTestChildMain) {
   MojoPerfTestClient client;
   int rv = mojo::edk::test::MultiprocessTestHelper::RunClientMain(
@@ -414,29 +272,6 @@
   return rv;
 }
 
-class ReflectorImpl : public IPC::mojom::Reflector {
- public:
-  explicit ReflectorImpl(mojo::ScopedMessagePipeHandle handle)
-      : binding_(this, IPC::mojom::ReflectorRequest(std::move(handle))) {}
-  ~ReflectorImpl() override {
-    ignore_result(binding_.Unbind().PassMessagePipe().release());
-  }
-
- private:
-  // IPC::mojom::Reflector:
-  void Ping(const std::string& value, PingCallback callback) override {
-    std::move(callback).Run(value);
-  }
-
-  void SyncPing(const std::string& value, PingCallback callback) override {
-    std::move(callback).Run(value);
-  }
-
-  void Quit() override { base::RunLoop::QuitCurrentWhenIdleDeprecated(); }
-
-  mojo::Binding<IPC::mojom::Reflector> binding_;
-};
-
 class MojoInterfacePerfTest : public mojo::edk::test::MojoTestBase {
  public:
   MojoInterfacePerfTest() : message_count_(0), count_down_(0) {}
@@ -509,8 +344,9 @@
         base::MessageLoop::current());
 
     LockThreadAffinity thread_locker(kSharedCore);
-    ReflectorImpl impl(std::move(scoped_mp));
-    base::RunLoop().Run();
+    base::RunLoop run_loop;
+    ReflectorImpl impl(std::move(scoped_mp), run_loop.QuitWhenIdleClosure());
+    run_loop.Run();
     return 0;
   }
 
@@ -798,7 +634,7 @@
   mojo::MessagePipeHandle mp_handle(client_handle);
   mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
   LockThreadAffinity thread_locker(kSharedCore);
-  ReflectorImpl impl(std::move(scoped_mp));
+  ReflectorImpl impl(std::move(scoped_mp), base::Closure());
 
   RunPingPongServer(server_handle, "SingleProcess");
 }
diff --git a/ipc/ipc_perftest_messages.cc b/ipc/ipc_perftest_messages.cc
new file mode 100644
index 0000000..4a84dae
--- /dev/null
+++ b/ipc/ipc_perftest_messages.cc
@@ -0,0 +1,7 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Get basic type definitions.
+#define IPC_MESSAGE_IMPL
+#include "ipc/ipc_perftest_messages.h"
diff --git a/ipc/ipc_perftest_messages.h b/ipc/ipc_perftest_messages.h
new file mode 100644
index 0000000..1dc0890
--- /dev/null
+++ b/ipc/ipc_perftest_messages.h
@@ -0,0 +1,16 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Multiply-included message header, no traditional include guard.
+
+#include <string>
+
+#include "ipc/ipc_message_macros.h"
+
+#define IPC_MESSAGE_START TestMsgStart
+
+IPC_MESSAGE_CONTROL0(TestMsg_Hello)
+IPC_MESSAGE_CONTROL0(TestMsg_Quit)
+IPC_MESSAGE_CONTROL1(TestMsg_Ping, std::string)
+IPC_SYNC_MESSAGE_CONTROL1_1(TestMsg_SyncPing, std::string, std::string)
diff --git a/ipc/ipc_perftest_util.cc b/ipc/ipc_perftest_util.cc
new file mode 100644
index 0000000..57f054d
--- /dev/null
+++ b/ipc/ipc_perftest_util.cc
@@ -0,0 +1,145 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipc/ipc_perftest_util.h"
+
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_perftest_messages.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/test/multiprocess_test_helper.h"
+
+namespace IPC {
+
+scoped_refptr<base::SingleThreadTaskRunner> GetIOThreadTaskRunner() {
+  scoped_refptr<base::TaskRunner> runner = mojo::edk::GetIOTaskRunner();
+  return scoped_refptr<base::SingleThreadTaskRunner>(
+      static_cast<base::SingleThreadTaskRunner*>(runner.get()));
+}
+
+ChannelReflectorListener::ChannelReflectorListener() : channel_(NULL) {
+  VLOG(1) << "Client listener up";
+}
+
+ChannelReflectorListener::~ChannelReflectorListener() {
+  VLOG(1) << "Client listener down";
+}
+
+void ChannelReflectorListener::Init(Sender* channel,
+                                    const base::Closure& quit_closure) {
+  DCHECK(!channel_);
+  channel_ = channel;
+  quit_closure_ = quit_closure;
+}
+
+bool ChannelReflectorListener::OnMessageReceived(const Message& message) {
+  CHECK(channel_);
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(ChannelReflectorListener, message)
+    IPC_MESSAGE_HANDLER(TestMsg_Hello, OnHello)
+    IPC_MESSAGE_HANDLER(TestMsg_Ping, OnPing)
+    IPC_MESSAGE_HANDLER(TestMsg_SyncPing, OnSyncPing)
+    IPC_MESSAGE_HANDLER(TestMsg_Quit, OnQuit)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  return handled;
+}
+
+void ChannelReflectorListener::OnHello() {
+  channel_->Send(new TestMsg_Hello);
+}
+
+void ChannelReflectorListener::OnPing(const std::string& payload) {
+  channel_->Send(new TestMsg_Ping(payload));
+}
+
+void ChannelReflectorListener::OnSyncPing(const std::string& payload,
+                                          std::string* response) {
+  *response = payload;
+}
+
+void ChannelReflectorListener::OnQuit() {
+  quit_closure_.Run();
+}
+
+void ChannelReflectorListener::Send(IPC::Message* message) {
+  channel_->Send(message);
+}
+
+LockThreadAffinity::LockThreadAffinity(int cpu_number)
+    : affinity_set_ok_(false) {
+#if defined(OS_WIN)
+  const DWORD_PTR thread_mask = static_cast<DWORD_PTR>(1) << cpu_number;
+  old_affinity_ = SetThreadAffinityMask(GetCurrentThread(), thread_mask);
+  affinity_set_ok_ = old_affinity_ != 0;
+#elif defined(OS_LINUX)
+  cpu_set_t cpuset;
+  CPU_ZERO(&cpuset);
+  CPU_SET(cpu_number, &cpuset);
+  auto get_result = sched_getaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
+  DCHECK_EQ(0, get_result);
+  auto set_result = sched_setaffinity(0, sizeof(cpuset), &cpuset);
+  // Check for get_result failure, even though it should always succeed.
+  affinity_set_ok_ = (set_result == 0) && (get_result == 0);
+#endif
+  if (!affinity_set_ok_)
+    LOG(WARNING) << "Failed to set thread affinity to CPU " << cpu_number;
+}
+
+LockThreadAffinity::~LockThreadAffinity() {
+  if (!affinity_set_ok_)
+    return;
+#if defined(OS_WIN)
+  auto set_result = SetThreadAffinityMask(GetCurrentThread(), old_affinity_);
+  DCHECK_NE(0u, set_result);
+#elif defined(OS_LINUX)
+  auto set_result = sched_setaffinity(0, sizeof(old_cpuset_), &old_cpuset_);
+  DCHECK_EQ(0, set_result);
+#endif
+}
+
+MojoPerfTestClient::MojoPerfTestClient()
+    : listener_(new ChannelReflectorListener()) {
+  mojo::edk::test::MultiprocessTestHelper::ChildSetup();
+}
+
+MojoPerfTestClient::~MojoPerfTestClient() = default;
+
+int MojoPerfTestClient::Run(MojoHandle handle) {
+  handle_ = mojo::MakeScopedHandle(mojo::MessagePipeHandle(handle));
+  LockThreadAffinity thread_locker(kSharedCore);
+
+  base::RunLoop run_loop;
+  std::unique_ptr<ChannelProxy> channel =
+      IPC::ChannelProxy::Create(handle_.release(), Channel::MODE_CLIENT,
+                                listener_.get(), GetIOThreadTaskRunner());
+  listener_->Init(channel.get(), run_loop.QuitWhenIdleClosure());
+  run_loop.Run();
+  return 0;
+}
+
+ReflectorImpl::ReflectorImpl(mojo::ScopedMessagePipeHandle handle,
+                             const base::Closure& quit_closure)
+    : quit_closure_(quit_closure),
+      binding_(this, IPC::mojom::ReflectorRequest(std::move(handle))) {}
+
+ReflectorImpl::~ReflectorImpl() {
+  ignore_result(binding_.Unbind().PassMessagePipe().release());
+}
+
+void ReflectorImpl::Ping(const std::string& value, PingCallback callback) {
+  std::move(callback).Run(value);
+}
+
+void ReflectorImpl::SyncPing(const std::string& value, PingCallback callback) {
+  std::move(callback).Run(value);
+}
+
+void ReflectorImpl::Quit() {
+  if (quit_closure_)
+    quit_closure_.Run();
+}
+
+}  // namespace IPC
diff --git a/ipc/ipc_perftest_util.h b/ipc/ipc_perftest_util.h
new file mode 100644
index 0000000..3c9ceeb
--- /dev/null
+++ b/ipc/ipc_perftest_util.h
@@ -0,0 +1,119 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPC_IPC_PERFTEST_UTIL_H_
+#define IPC_IPC_PERFTEST_UTIL_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/process/process_metrics.h"
+#include "base/single_thread_task_runner.h"
+#include "build/build_config.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_listener.h"
+#include "ipc/ipc_message.h"
+#include "ipc/ipc_sender.h"
+#include "ipc/ipc_test.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace IPC {
+
+scoped_refptr<base::SingleThreadTaskRunner> GetIOThreadTaskRunner();
+
+// This channel listener just replies to all messages with the exact same
+// message. It assumes each message has one string parameter. When the string
+// "quit" is sent, it will exit.
+class ChannelReflectorListener : public Listener {
+ public:
+  ChannelReflectorListener();
+
+  ~ChannelReflectorListener() override;
+
+  void Init(Sender* channel, const base::Closure& quit_closure);
+
+  bool OnMessageReceived(const Message& message) override;
+
+  void OnHello();
+
+  void OnPing(const std::string& payload);
+
+  void OnSyncPing(const std::string& payload, std::string* response);
+
+  void OnQuit();
+
+  void Send(IPC::Message* message);
+
+ private:
+  Sender* channel_;
+  base::Closure quit_closure_;
+};
+
+// This class locks the current thread to a particular CPU core. This is
+// important because otherwise the different threads and processes of these
+// tests end up on different CPU cores which means that all of the cores are
+// lightly loaded so the OS (Windows and Linux) fails to ramp up the CPU
+// frequency, leading to unpredictable and often poor performance.
+class LockThreadAffinity {
+ public:
+  explicit LockThreadAffinity(int cpu_number);
+
+  ~LockThreadAffinity();
+
+ private:
+  bool affinity_set_ok_;
+#if defined(OS_WIN)
+  DWORD_PTR old_affinity_;
+#elif defined(OS_LINUX)
+  cpu_set_t old_cpuset_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(LockThreadAffinity);
+};
+
+// Avoid core 0 due to conflicts with Intel's Power Gadget.
+// Setting thread affinity will fail harmlessly on single/dual core machines.
+const int kSharedCore = 2;
+
+class MojoPerfTestClient {
+ public:
+  MojoPerfTestClient();
+
+  ~MojoPerfTestClient();
+
+  int Run(MojoHandle handle);
+
+ private:
+  base::MessageLoop main_message_loop_;
+  std::unique_ptr<ChannelReflectorListener> listener_;
+  std::unique_ptr<Channel> channel_;
+  mojo::ScopedMessagePipeHandle handle_;
+};
+
+class ReflectorImpl : public IPC::mojom::Reflector {
+ public:
+  explicit ReflectorImpl(mojo::ScopedMessagePipeHandle handle,
+                         const base::Closure& quit_closure);
+
+  ~ReflectorImpl() override;
+
+ private:
+  // IPC::mojom::Reflector:
+  void Ping(const std::string& value, PingCallback callback) override;
+
+  void SyncPing(const std::string& value, PingCallback callback) override;
+
+  void Quit() override;
+
+  base::Closure quit_closure_;
+  mojo::Binding<IPC::mojom::Reflector> binding_;
+};
+
+}  // namespace IPC
+
+#endif  // IPC_IPC_PERFTEST_UTIL_H_
diff --git a/media/BUILD.gn b/media/BUILD.gn
index db7efe9..7558e08e 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -129,62 +129,6 @@
   }
 }
 
-# TODO(xhwang): Move these into source_sets in respective subfolders.
-source_set("unit_tests") {
-  testonly = true
-  sources = [
-    "muxers/webm_muxer_unittest.cc",
-  ]
-
-  data = [
-    "test/data/",
-  ]
-
-  configs += [
-    ":media_config",
-
-    # TODO(crbug.com/167187): Fix size_t to int truncations.
-    "//build/config/compiler:no_size_t_to_int_warning",
-  ]
-
-  deps = [
-    ":test_support",
-    "//base/test:test_support",
-    "//crypto",
-    "//gpu:test_support",
-    "//gpu/command_buffer/common",
-    "//media/cdm:cdm_paths",
-    "//skia",  # Direct dependency required to inherit config.
-    "//testing/gmock",
-    "//testing/gtest",
-    "//third_party/libwebm",
-    "//third_party/libyuv",
-    "//third_party/widevine/cdm:headers",
-    "//ui/gfx:test_support",
-    "//url",
-  ]
-
-  data_deps = []
-
-  if (proprietary_codecs) {
-    if (enable_hls_sample_aes) {
-      deps += [ "//third_party/boringssl" ]
-    }
-  }
-
-  if (is_mac || is_ios) {
-    libs = [
-      "AppKit.framework",
-      "Foundation.framework",
-    ]
-  }
-
-  if (media_use_ffmpeg) {
-    # Direct dependency required to inherit config.
-    deps += [ "//third_party/ffmpeg" ]
-  }
-}
-
 # Note: This can't be a static_library since it does not have any sources.
 source_set("test_support") {
   testonly = true
@@ -202,7 +146,6 @@
 # TODO(xhwang): Move mojo/capture/remoting tests here where applicable.
 test("media_unittests") {
   deps = [
-    ":unit_tests",
     "//media/audio:unit_tests",
     "//media/base:unit_tests",
     "//media/cdm:unit_tests",
@@ -211,12 +154,19 @@
     "//media/formats:unit_tests",
     "//media/gpu:unit_tests",
     "//media/mojo:unit_tests",
+    "//media/muxers:unit_tests",
     "//media/renderers:unit_tests",
     "//media/test:pipeline_integration_tests",
     "//media/test:run_all_unittests",
     "//media/video:unit_tests",
   ]
 
+  data = [
+    "test/data/",
+  ]
+
+  data_deps = []
+
   if (media_use_ffmpeg) {
     deps += [ "//media/ffmpeg:unit_tests" ]
   }
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn
index 762f5ac8..aabcb784 100644
--- a/media/base/BUILD.gn
+++ b/media/base/BUILD.gn
@@ -410,6 +410,7 @@
     "//base/test:test_support",
     "//testing/gmock",
     "//ui/gfx:test_support",
+    "//url",
   ]
 }
 
diff --git a/media/base/mock_filters.cc b/media/base/mock_filters.cc
index 1eb8659..49a29a1a 100644
--- a/media/base/mock_filters.cc
+++ b/media/base/mock_filters.cc
@@ -183,11 +183,15 @@
   MarkPromiseSettled();
 }
 
-MockCdm::MockCdm(const SessionMessageCB& session_message_cb,
+MockCdm::MockCdm(const std::string& key_system,
+                 const url::Origin& security_origin,
+                 const SessionMessageCB& session_message_cb,
                  const SessionClosedCB& session_closed_cb,
                  const SessionKeysChangeCB& session_keys_change_cb,
                  const SessionExpirationUpdateCB& session_expiration_update_cb)
-    : session_message_cb_(session_message_cb),
+    : key_system_(key_system),
+      security_origin_(security_origin),
+      session_message_cb_(session_message_cb),
       session_closed_cb_(session_closed_cb),
       session_keys_change_cb_(session_keys_change_cb),
       session_expiration_update_cb_(session_expiration_update_cb) {}
@@ -258,7 +262,7 @@
 
 void MockCdmFactory::Create(
     const std::string& key_system,
-    const url::Origin& /* security_origin */,
+    const url::Origin& security_origin,
     const CdmConfig& /* cdm_config */,
     const SessionMessageCB& session_message_cb,
     const SessionClosedCB& session_closed_cb,
@@ -278,8 +282,8 @@
   // Create and return a new MockCdm. Keep a pointer to the created CDM so
   // that tests can access it. Calls to GetCdmContext() can be ignored.
   scoped_refptr<MockCdm> cdm = new StrictMock<MockCdm>(
-      session_message_cb, session_closed_cb, session_keys_change_cb,
-      session_expiration_update_cb);
+      key_system, security_origin, session_message_cb, session_closed_cb,
+      session_keys_change_cb, session_expiration_update_cb);
   created_cdm_ = cdm.get();
   EXPECT_CALL(*created_cdm_.get(), GetCdmContext());
   cdm_created_cb.Run(std::move(cdm), "");
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index c11da37e..7e7c0d8d 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -39,6 +39,7 @@
 #include "media/base/video_frame.h"
 #include "media/base/video_renderer.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "url/origin.h"
 
 namespace media {
 
@@ -459,7 +460,9 @@
 
 class MockCdm : public ContentDecryptionModule {
  public:
-  MockCdm(const SessionMessageCB& session_message_cb,
+  MockCdm(const std::string& key_system,
+          const url::Origin& security_origin,
+          const SessionMessageCB& session_message_cb,
           const SessionClosedCB& session_closed_cb,
           const SessionKeysChangeCB& session_keys_change_cb,
           const SessionExpirationUpdateCB& session_expiration_update_cb);
@@ -524,10 +527,16 @@
   void CallSessionExpirationUpdateCB(const std::string& session_id,
                                      base::Time new_expiry_time);
 
+  const std::string& GetKeySystem() const { return key_system_; }
+  const url::Origin& GetSecurityOrigin() const { return security_origin_; }
+
  protected:
   ~MockCdm() override;
 
  private:
+  std::string key_system_;
+  url::Origin security_origin_;
+
   // Callbacks.
   SessionMessageCB session_message_cb_;
   SessionClosedCB session_closed_cb_;
diff --git a/media/blink/multibuffer.cc b/media/blink/multibuffer.cc
index c17d80c8..73650353 100644
--- a/media/blink/multibuffer.cc
+++ b/media/blink/multibuffer.cc
@@ -122,14 +122,13 @@
   SchedulePrune();
 }
 
-void MultiBuffer::GlobalLRU::Prune(int64_t max_to_free) {
+void MultiBuffer::GlobalLRU::TryFree(int64_t max_to_free) {
   // We group the blocks by multibuffer so that we can free as many blocks as
   // possible in one call. This reduces the number of callbacks to clients
   // when their available ranges change.
   std::map<MultiBuffer*, std::vector<MultiBufferBlockId>> to_free;
   int64_t freed = 0;
-  while (data_size_ - freed > max_size_ && !lru_.Empty() &&
-         freed < max_to_free) {
+  while (!lru_.Empty() && freed < max_to_free) {
     GlobalBlockId block_id = lru_.Pop();
     to_free[block_id.first].push_back(block_id.second);
     freed++;
@@ -139,6 +138,23 @@
   }
 }
 
+void MultiBuffer::GlobalLRU::TryFreeAll() {
+  // Since TryFree also allocates memory, avoid freeing everything
+  // in one large chunk to avoid running out of memory before we
+  // start freeing memory. Freeing 100 at a time should be a reasonable
+  // compromise between efficiency and not building large data structures.
+  while (true) {
+    int64_t data_size_before = data_size_;
+    TryFree(100);
+    if (data_size_ >= data_size_before)
+      break;
+  }
+}
+
+void MultiBuffer::GlobalLRU::Prune(int64_t max_to_free) {
+  TryFree(std::min(max_to_free, data_size_ - max_size_));
+}
+
 int64_t MultiBuffer::GlobalLRU::Size() const {
   return lru_.Size();
 }
diff --git a/media/blink/multibuffer.h b/media/blink/multibuffer.h
index 23280c8..ab423b1c 100644
--- a/media/blink/multibuffer.h
+++ b/media/blink/multibuffer.h
@@ -139,9 +139,16 @@
     explicit GlobalLRU(
         const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
 
-    // Free elements from cache if needed and possible.
+    // Free elements from cache if possible.
     // Don't free more than |max_to_free| blocks.
-    // Virtual for testing purposes.
+    void TryFree(int64_t max_to_free);
+
+    // Free as much memory from the cache as possible.
+    // Only used during critical memory pressure.
+    void TryFreeAll();
+
+    // Like TryFree, but only frees blocks if the data
+    // number of the blocks in the cache is too large.
     void Prune(int64_t max_to_free);
 
     // Returns true if there are prunable blocks.
diff --git a/media/blink/multibuffer_data_source.cc b/media/blink/multibuffer_data_source.cc
index db5c9b6..630ef8d 100644
--- a/media/blink/multibuffer_data_source.cc
+++ b/media/blink/multibuffer_data_source.cc
@@ -47,6 +47,12 @@
 // Keep this many seconds of data for going back by default.
 const int64_t kTargetSecondsBufferedBehind = 2;
 
+// Extra buffer accumulation speed, in terms of download buffer.
+const int kSlowPreloadPercentage = 10;
+
+// Update buffer sizes every 32 progress updates.
+const int kUpdateBufferSizeFrequency = 32;
+
 }  // namespace
 
 namespace media {
@@ -393,6 +399,7 @@
         static_cast<int>(std::min<int64_t>(available, read_op_->size()));
     bytes_read =
         reader_->TryReadAt(read_op_->position(), read_op_->data(), bytes_read);
+    url_data_->AddBytesRead(bytes_read);
 
     int64_t new_pos = read_op_->position() + bytes_read;
     if (reader_->AvailableAt(new_pos) <= reader_->Available())
@@ -524,6 +531,11 @@
     host_->AddBufferedByteRange(begin, end);
   }
 
+  if (buffer_size_update_counter_ > 0) {
+    buffer_size_update_counter_--;
+  } else {
+    UpdateBufferSizes();
+  }
   UpdateLoadingState_Locked(false);
 }
 
@@ -568,6 +580,8 @@
   if (!reader_)
     return;
 
+  buffer_size_update_counter_ = kUpdateBufferSizeFrequency;
+
   // Use a default bit rate if unknown and clamp to prevent overflow.
   int64_t bitrate = clamp<int64_t>(bitrate_, 0, kMaxBitrate);
   if (bitrate == 0)
@@ -585,6 +599,15 @@
   // Preload 10 seconds of data, clamped to some min/max value.
   int64_t preload = clamp(kTargetSecondsBufferedAhead * bytes_per_second,
                           kMinBufferPreload, kMaxBufferPreload);
+
+  // Increase buffering slowly at a rate of 10% of data downloaded so
+  // far, maxing out at the preload size.
+  int64_t extra_buffer = std::min(
+      preload, url_data_->BytesReadFromCache() * kSlowPreloadPercentage / 100);
+
+  // Add extra buffer to preload.
+  preload += extra_buffer;
+
   // We preload this much, then we stop unil we read |preload| before resuming.
   int64_t preload_high = preload + kPreloadHighExtra;
 
@@ -604,8 +627,9 @@
   // the data in pinned region is not present in the cache.
   int64_t buffer_size =
       std::min((kTargetSecondsBufferedAhead + kTargetSecondsBufferedBehind) *
-                   bytes_per_second,
-               preload_high + pin_backward);
+                       bytes_per_second +
+                   extra_buffer * 3,
+               preload_high + pin_backward + extra_buffer);
 
   if (url_data_->FullyCached() ||
       (url_data_->length() != kPositionNotSpecified &&
diff --git a/media/blink/multibuffer_data_source.h b/media/blink/multibuffer_data_source.h
index 3775ae4..726bbaf 100644
--- a/media/blink/multibuffer_data_source.h
+++ b/media/blink/multibuffer_data_source.h
@@ -221,6 +221,8 @@
 
   MediaLog* media_log_;
 
+  int buffer_size_update_counter_;
+
   // Host object to report buffered byte range changes to.
   BufferedDataSourceHost* host_;
 
diff --git a/media/blink/multibuffer_data_source_unittest.cc b/media/blink/multibuffer_data_source_unittest.cc
index 74a9785..0ca95b9 100644
--- a/media/blink/multibuffer_data_source_unittest.cc
+++ b/media/blink/multibuffer_data_source_unittest.cc
@@ -1542,7 +1542,7 @@
   EXPECT_EQ(3 << 20, preload_high());
   EXPECT_EQ(25 << 20, max_buffer_forward());
   EXPECT_EQ(kFileSize * 2, max_buffer_backward());
-  EXPECT_EQ(5013504 /* file size rounted up to blocks size */, buffer_size());
+  EXPECT_EQ(5013504 /* file size rounded up to blocks size */, buffer_size());
 
   data_source_->SetBitrate(80 << 20);  // 80 mbit / s
   base::RunLoop().RunUntilIdle();
@@ -1551,7 +1551,33 @@
   EXPECT_EQ(51 << 20, preload_high());
   EXPECT_EQ(51 << 20, max_buffer_forward());
   EXPECT_EQ(20 << 20, max_buffer_backward());
-  EXPECT_EQ(5013504 /* file size rounted up to blocks size */, buffer_size());
+  EXPECT_EQ(5013504 /* file size rounded up to blocks size */, buffer_size());
+}
+
+TEST_F(MultibufferDataSourceTest, CheckBufferSizeAfterReadingALot) {
+  InitializeWith206Response();
+
+  EXPECT_CALL(*this, ReadCallback(kDataSize));
+  ReadAt(0);
+
+  const int to_read = 40;
+
+  for (int i = 1; i < to_read; i++) {
+    EXPECT_CALL(*this, ReadCallback(kDataSize));
+    EXPECT_CALL(host_, AddBufferedByteRange(0, kDataSize * (i + 1)));
+    ReadAt(i * kDataSize);
+    ReceiveData(kDataSize);
+  }
+
+  data_source_->SetBitrate(1 << 20);  // 1 mbit / s
+  base::RunLoop().RunUntilIdle();
+  int64_t extra_buffer = to_read / 10 * kDataSize;
+  EXPECT_EQ(1 << 20, data_source_bitrate());
+  EXPECT_EQ((2 << 20) + extra_buffer, preload_low());
+  EXPECT_EQ((3 << 20) + extra_buffer, preload_high());
+  EXPECT_EQ(25 << 20, max_buffer_forward());
+  EXPECT_EQ(kFileSize * 2, max_buffer_backward());
+  EXPECT_EQ(5013504 /* file size rounded up to blocks size */, buffer_size());
 }
 
 // Provoke an edge case where the loading state may not end up transitioning
diff --git a/media/blink/multibuffer_unittest.cc b/media/blink/multibuffer_unittest.cc
index ae607798..4788d31 100644
--- a/media/blink/multibuffer_unittest.cc
+++ b/media/blink/multibuffer_unittest.cc
@@ -404,6 +404,36 @@
   EXPECT_EQ(0, lru_->Size());
 }
 
+TEST_F(MultiBufferTest, LRUTest2) {
+  int64_t max_size = 17;
+  int64_t current_size = 0;
+  lru_->IncrementMaxSize(max_size);
+
+  multibuffer_.SetMaxWriters(1);
+  size_t pos = 0;
+  size_t end = 10000;
+  multibuffer_.SetFileSize(10000);
+  MultiBufferReader reader(&multibuffer_, pos, end,
+                           base::Callback<void(int64_t, int64_t)>());
+  reader.SetPreload(10000, 10000);
+  // Note, no pinning, all data should end up in LRU.
+  EXPECT_EQ(current_size, lru_->Size());
+  current_size += max_size;
+  while (AdvanceAll()) {
+  }
+  EXPECT_EQ(current_size, lru_->Size());
+  // Pruning shouldn't do anything here, because LRU is small enough already.
+  lru_->Prune(3);
+  EXPECT_EQ(current_size, lru_->Size());
+  // However TryFree should still work
+  lru_->TryFree(3);
+  current_size -= 3;
+  EXPECT_EQ(current_size, lru_->Size());
+  lru_->TryFreeAll();
+  EXPECT_EQ(0, lru_->Size());
+  lru_->IncrementMaxSize(-max_size);
+}
+
 TEST_F(MultiBufferTest, LRUTestExpirationTest) {
   int64_t max_size = 17;
   int64_t current_size = 0;
diff --git a/media/blink/url_index.cc b/media/blink/url_index.cc
index 6070158d..7bd83839 100644
--- a/media/blink/url_index.cc
+++ b/media/blink/url_index.cc
@@ -205,7 +205,9 @@
 UrlIndex::UrlIndex(ResourceFetchContext* fetch_context, int block_shift)
     : fetch_context_(fetch_context),
       lru_(new MultiBuffer::GlobalLRU(base::ThreadTaskRunnerHandle::Get())),
-      block_shift_(block_shift) {}
+      block_shift_(block_shift),
+      memory_pressure_listener_(
+          base::Bind(&UrlIndex::OnMemoryPressure, base::Unretained(this))) {}
 
 UrlIndex::~UrlIndex() {
 #if DCHECK_IS_ON()
@@ -246,6 +248,20 @@
   return new UrlData(url, cors_mode, this);
 }
 
+void UrlIndex::OnMemoryPressure(
+    base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
+  switch (memory_pressure_level) {
+    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
+      break;
+    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
+      lru_->TryFree(128);  // try to free 128 32kb blocks if possible
+      break;
+    case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
+      lru_->TryFreeAll();  // try to free as many blocks as possible
+      break;
+  }
+}
+
 namespace {
 bool IsStrongEtag(const std::string& etag) {
   return etag.size() > 2 && etag[0] == '"';
diff --git a/media/blink/url_index.h b/media/blink/url_index.h
index 8b1f55d..73ab5ea 100644
--- a/media/blink/url_index.h
+++ b/media/blink/url_index.h
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/memory/memory_pressure_listener.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
@@ -255,6 +256,9 @@
   virtual scoped_refptr<UrlData> NewUrlData(const GURL& url,
                                             UrlData::CORSMode cors_mode);
 
+  void OnMemoryPressure(
+      base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
+
   ResourceFetchContext* fetch_context_;
   using UrlDataMap = std::map<UrlData::KeyType, scoped_refptr<UrlData>>;
   UrlDataMap unindexed_data_;
@@ -264,6 +268,8 @@
   // log2 of block size in multibuffer cache. Defaults to kBlockSizeShift.
   // Currently only changed for testing purposes.
   const int block_shift_;
+
+  base::MemoryPressureListener memory_pressure_listener_;
 };
 
 }  // namespace media
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index a93ac1d..bd97701 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -818,15 +818,6 @@
   pipeline_controller_.OnSelectedVideoTrackChanged(selected_video_track_id);
 }
 
-bool WebMediaPlayerImpl::GetLastUploadedFrameInfo(unsigned* width,
-                                                  unsigned* height,
-                                                  double* timestamp) {
-  *width = last_uploaded_frame_size_.width();
-  *height = last_uploaded_frame_size_.height();
-  *timestamp = last_uploaded_frame_timestamp_.InSecondsF();
-  return true;
-}
-
 blink::WebSize WebMediaPlayerImpl::NaturalSize() const {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
 
@@ -1019,7 +1010,9 @@
 
 void WebMediaPlayerImpl::Paint(blink::WebCanvas* canvas,
                                const blink::WebRect& rect,
-                               cc::PaintFlags& flags) {
+                               cc::PaintFlags& flags,
+                               int already_uploaded_id,
+                               VideoFrameUploadMetadata* out_metadata) {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   TRACE_EVENT0("media", "WebMediaPlayerImpl:paint");
 
@@ -1039,6 +1032,15 @@
     if (!context_3d.gr_context)
       return;  // The context has been lost since and can't setup a GrContext.
   }
+  if (out_metadata) {
+    // WebGL last-uploaded-frame-metadata API enabled. https://crbug.com/639174
+    ComputeFrameUploadMetadata(video_frame.get(), already_uploaded_id,
+                               out_metadata);
+    if (out_metadata->skipped) {
+      // Skip uploading this frame.
+      return;
+    }
+  }
   skcanvas_video_renderer_.Paint(video_frame, canvas, gfx::RectF(gfx_rect),
                                  flags, pipeline_metadata_.video_rotation,
                                  context_3d);
@@ -1097,7 +1099,9 @@
     unsigned type,
     int level,
     bool premultiply_alpha,
-    bool flip_y) {
+    bool flip_y,
+    int already_uploaded_id,
+    VideoFrameUploadMetadata* out_metadata) {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   TRACE_EVENT0("media", "WebMediaPlayerImpl:copyVideoTextureToPlatformTexture");
 
@@ -1109,6 +1113,16 @@
   if (!video_frame.get() || !video_frame->HasTextures()) {
     return false;
   }
+  if (out_metadata) {
+    // WebGL last-uploaded-frame-metadata API is enabled.
+    // https://crbug.com/639174
+    ComputeFrameUploadMetadata(video_frame.get(), already_uploaded_id,
+                               out_metadata);
+    if (out_metadata->skipped) {
+      // Skip uploading this frame.
+      return true;
+    }
+  }
 
   Context3D context_3d;
   if (!context_3d_cb_.is_null())
@@ -1118,6 +1132,19 @@
       format, type, level, premultiply_alpha, flip_y);
 }
 
+void WebMediaPlayerImpl::ComputeFrameUploadMetadata(
+    VideoFrame* frame,
+    int already_uploaded_id,
+    VideoFrameUploadMetadata* out_metadata) {
+  DCHECK(out_metadata);
+  out_metadata->frame_id = frame->unique_id();
+  out_metadata->visible_rect = frame->visible_rect();
+  out_metadata->timestamp = frame->timestamp();
+  bool skip_possible = already_uploaded_id != -1;
+  bool same_frame_id = frame->unique_id() == already_uploaded_id;
+  out_metadata->skipped = skip_possible && same_frame_id;
+}
+
 void WebMediaPlayerImpl::SetContentDecryptionModule(
     blink::WebContentDecryptionModule* cdm,
     blink::WebContentDecryptionModuleResult result) {
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index f45243f..c42562f 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -134,7 +134,9 @@
   // WebGL texImage2D, ImageBitmap, printing and capturing capabilities.
   void Paint(blink::WebCanvas* canvas,
              const blink::WebRect& rect,
-             cc::PaintFlags& flags) override;
+             cc::PaintFlags& flags,
+             int already_uploaded_id,
+             VideoFrameUploadMetadata* out_metadata) override;
 
   // True if the loaded media has a playable video/audio track.
   bool HasVideo() const override;
@@ -146,10 +148,6 @@
   void SelectedVideoTrackChanged(
       blink::WebMediaPlayer::TrackId* selectedTrackId) override;
 
-  bool GetLastUploadedFrameInfo(unsigned* width,
-                                unsigned* height,
-                                double* timestamp) override;
-
   // Dimensions of the video.
   blink::WebSize NaturalSize() const override;
 
@@ -181,15 +179,23 @@
   size_t AudioDecodedByteCount() const override;
   size_t VideoDecodedByteCount() const override;
 
-  bool CopyVideoTextureToPlatformTexture(gpu::gles2::GLES2Interface* gl,
-                                         unsigned int target,
-                                         unsigned int texture,
-                                         unsigned internal_format,
-                                         unsigned format,
-                                         unsigned type,
-                                         int level,
-                                         bool premultiply_alpha,
-                                         bool flip_y) override;
+  bool CopyVideoTextureToPlatformTexture(
+      gpu::gles2::GLES2Interface* gl,
+      unsigned int target,
+      unsigned int texture,
+      unsigned internal_format,
+      unsigned format,
+      unsigned type,
+      int level,
+      bool premultiply_alpha,
+      bool flip_y,
+      int already_uploaded_id,
+      VideoFrameUploadMetadata* out_metadata) override;
+
+  static void ComputeFrameUploadMetadata(
+      VideoFrame* frame,
+      int already_uploaded_id,
+      VideoFrameUploadMetadata* out_metadata);
 
   blink::WebAudioSourceProvider* GetAudioSourceProvider() override;
 
diff --git a/media/capture/BUILD.gn b/media/capture/BUILD.gn
index 22758f8..53c8322 100644
--- a/media/capture/BUILD.gn
+++ b/media/capture/BUILD.gn
@@ -248,6 +248,7 @@
     "video/mac/video_capture_device_factory_mac_unittest.mm",
     "video/video_capture_device_client_unittest.cc",
     "video/video_capture_device_unittest.cc",
+    "video_capture_types_unittest.cc",
   ]
 
   deps = [
diff --git a/media/capture/content/animated_content_sampler.cc b/media/capture/content/animated_content_sampler.cc
index 7f09cd1..d53fad8 100644
--- a/media/capture/content/animated_content_sampler.cc
+++ b/media/capture/content/animated_content_sampler.cc
@@ -51,6 +51,11 @@
 AnimatedContentSampler::~AnimatedContentSampler() {
 }
 
+void AnimatedContentSampler::SetMinCapturePeriod(base::TimeDelta period) {
+  DCHECK_GT(period, base::TimeDelta());
+  min_capture_period_ = period;
+}
+
 void AnimatedContentSampler::SetTargetSamplingPeriod(base::TimeDelta period) {
   target_sampling_period_ = period;
 }
diff --git a/media/capture/content/animated_content_sampler.h b/media/capture/content/animated_content_sampler.h
index 0539de7..04e4ccd9 100644
--- a/media/capture/content/animated_content_sampler.h
+++ b/media/capture/content/animated_content_sampler.h
@@ -27,6 +27,9 @@
   explicit AnimatedContentSampler(base::TimeDelta min_capture_period);
   ~AnimatedContentSampler();
 
+  // Sets a new minimum capture period.
+  void SetMinCapturePeriod(base::TimeDelta period);
+
   // Get/Set the target sampling period.  This is used to determine whether to
   // subsample the frames of animated content.
   base::TimeDelta target_sampling_period() const {
@@ -111,7 +114,7 @@
       base::TimeDelta min_capture_period);
 
   // The client expects frame timestamps to be at least this far apart.
-  const base::TimeDelta min_capture_period_;
+  base::TimeDelta min_capture_period_;
 
   // A recent history of observations in chronological order, maintained by
   // AddObservation().
diff --git a/media/capture/content/capture_resolution_chooser.cc b/media/capture/content/capture_resolution_chooser.cc
index 3c7f3e5..2841635 100644
--- a/media/capture/content/capture_resolution_chooser.cc
+++ b/media/capture/content/capture_resolution_chooser.cc
@@ -8,7 +8,6 @@
 #include <limits>
 
 #include "base/strings/string_util.h"
-#include "media/base/limits.h"
 #include "media/base/video_util.h"
 
 namespace media {
@@ -27,34 +26,6 @@
 // If they are too-far apart, quality is being sacrificed.
 const int kMinAreaDecreasePercent = 15;
 
-// Compute the minimum frame size from the given |max_frame_size| and
-// |resolution_change_policy|.
-gfx::Size ComputeMinimumCaptureSize(
-    const gfx::Size& max_frame_size,
-    ResolutionChangePolicy resolution_change_policy) {
-  switch (resolution_change_policy) {
-    case RESOLUTION_POLICY_FIXED_RESOLUTION:
-      return max_frame_size;
-    case RESOLUTION_POLICY_FIXED_ASPECT_RATIO: {
-      // TODO(miu): This is a place-holder until "min constraints" are plumbed-
-      // in from the MediaStream framework.  http://crbug.com/473336
-      const int kMinLines = 180;
-      if (max_frame_size.height() <= kMinLines)
-        return max_frame_size;
-      const gfx::Size result(
-          kMinLines * max_frame_size.width() / max_frame_size.height(),
-          kMinLines);
-      if (result.width() <= 0 || result.width() > limits::kMaxDimension)
-        return max_frame_size;
-      return result;
-    }
-    case RESOLUTION_POLICY_ANY_WITHIN_LIMIT:
-      return gfx::Size(1, 1);
-  }
-  NOTREACHED();
-  return gfx::Size(1, 1);
-}
-
 // Returns |size|, unless it exceeds |max_size| or is under |min_size|.  When
 // the bounds are exceeded, computes and returns an alternate size of similar
 // aspect ratio that is within the bounds.
@@ -82,50 +53,41 @@
 
 }  // namespace
 
-CaptureResolutionChooser::CaptureResolutionChooser(
-    const gfx::Size& max_frame_size,
-    ResolutionChangePolicy resolution_change_policy)
-    : max_frame_size_(max_frame_size),
-      min_frame_size_(
-          ComputeMinimumCaptureSize(max_frame_size, resolution_change_policy)),
-      resolution_change_policy_(resolution_change_policy),
-      target_area_(std::numeric_limits<decltype(target_area_)>::max()) {
-  DCHECK_LT(0, max_frame_size_.width());
-  DCHECK_LT(0, max_frame_size_.height());
-  DCHECK_LE(min_frame_size_.width(), max_frame_size_.width());
-  DCHECK_LE(min_frame_size_.height(), max_frame_size_.height());
-  DCHECK_LE(resolution_change_policy_, RESOLUTION_POLICY_LAST);
+// static
+constexpr gfx::Size CaptureResolutionChooser::kDefaultCaptureSize;
 
-  UpdateSnappedFrameSizes(max_frame_size_);
-  RecomputeCaptureSize();
-}
+CaptureResolutionChooser::CaptureResolutionChooser()
+    : min_frame_size_(kDefaultCaptureSize),
+      max_frame_size_(kDefaultCaptureSize),
+      apply_aspect_ratio_adjustment_(false),
+      target_area_(std::numeric_limits<decltype(target_area_)>::max()),
+      capture_size_(kDefaultCaptureSize),
+      snapped_sizes_({kDefaultCaptureSize}) {}
 
 CaptureResolutionChooser::~CaptureResolutionChooser() {
 }
 
+void CaptureResolutionChooser::SetConstraints(const gfx::Size& min_frame_size,
+                                              const gfx::Size& max_frame_size,
+                                              bool use_fixed_aspect_ratio) {
+  DCHECK_LT(0, min_frame_size.width());
+  DCHECK_LT(0, min_frame_size.height());
+  DCHECK_LE(min_frame_size.width(), max_frame_size.width());
+  DCHECK_LE(min_frame_size.height(), max_frame_size.height());
+
+  min_frame_size_ = min_frame_size;
+  max_frame_size_ = max_frame_size;
+  apply_aspect_ratio_adjustment_ =
+      (min_frame_size != max_frame_size && use_fixed_aspect_ratio);
+
+  UpdateSnappedFrameSizes();
+  RecomputeCaptureSize();
+}
+
 void CaptureResolutionChooser::SetSourceSize(const gfx::Size& source_size) {
-  if (source_size.IsEmpty())
-    return;
-
-  switch (resolution_change_policy_) {
-    case RESOLUTION_POLICY_FIXED_RESOLUTION:
-      // Source size changes do not affect the frame resolution.  Frame
-      // resolution is always fixed to |max_frame_size_|.
-      break;
-
-    case RESOLUTION_POLICY_FIXED_ASPECT_RATIO:
-      UpdateSnappedFrameSizes(ComputeBoundedCaptureSize(
-          PadToMatchAspectRatio(source_size, max_frame_size_), min_frame_size_,
-          max_frame_size_));
-      RecomputeCaptureSize();
-      break;
-
-    case RESOLUTION_POLICY_ANY_WITHIN_LIMIT:
-      UpdateSnappedFrameSizes(ComputeBoundedCaptureSize(
-          source_size, min_frame_size_, max_frame_size_));
-      RecomputeCaptureSize();
-      break;
-  }
+  source_size_ = source_size;
+  UpdateSnappedFrameSizes();
+  RecomputeCaptureSize();
 }
 
 void CaptureResolutionChooser::SetTargetFrameArea(int area) {
@@ -199,10 +161,19 @@
       << "% of ideal size)";
 }
 
-void CaptureResolutionChooser::UpdateSnappedFrameSizes(
-    const gfx::Size& constrained_size) {
-  // The |constrained_size| is always in the set of possible capture sizes and
-  // is the largest one.
+void CaptureResolutionChooser::UpdateSnappedFrameSizes() {
+  // Compute the largest snapped frame size, and the one that will determine the
+  // aspect ratio of all the snapped frame sizes. If the |source_size_| has not
+  // yet been set, use the |capture_size_| as a substitute.
+  gfx::Size constrained_size =
+      source_size_.IsEmpty() ? capture_size_ : source_size_;
+  constrained_size = ComputeBoundedCaptureSize(
+      apply_aspect_ratio_adjustment_
+          ? PadToMatchAspectRatio(constrained_size, max_frame_size_)
+          : constrained_size,
+      min_frame_size_, max_frame_size_);
+
+  // Start with the constrained size as the largest in the set.
   snapped_sizes_.clear();
   snapped_sizes_.push_back(constrained_size);
 
diff --git a/media/capture/content/capture_resolution_chooser.h b/media/capture/content/capture_resolution_chooser.h
index f6a1a899..a3104536 100644
--- a/media/capture/content/capture_resolution_chooser.h
+++ b/media/capture/content/capture_resolution_chooser.h
@@ -8,22 +8,17 @@
 #include <vector>
 
 #include "media/capture/capture_export.h"
-#include "media/capture/video_capture_types.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace media {
 
 // Encapsulates the logic that determines the capture frame resolution based on:
-//   1. The configured maximum frame resolution and resolution change policy.
-//   2. Changes to resolution of the source content.
-//   3. Changes to the (externally-computed) target data volume, provided in
+//   1. The configured minimum/maximum frame resolution.
+//   2. Whether the capture frame resolution must be of a fixed aspect ratio.
+//   3. Changes to resolution of the source content.
+//   4. Changes to the (externally-computed) target data volume, provided in
 //      terms of the number of pixels in the frame.
 //
-// CaptureResolutionChooser always computes capture sizes less than the maximum
-// frame size, and adheres to the configured resolution change policy.  Within
-// those hard limits, the capture size is computed to be as close to the
-// targeted frame area as possible.
-//
 // In variable-resolution use cases, the capture sizes are "snapped" to a small
 // (i.e., usually less than a dozen) set of possibilities.  This is to prevent
 // the end-to-end system from having to deal with rapidly-changing video frame
@@ -33,15 +28,25 @@
 // aspect ratio.
 class CAPTURE_EXPORT CaptureResolutionChooser {
  public:
-  // media::ResolutionChangePolicy determines whether the variable frame
-  // resolutions being computed must adhere to a fixed aspect ratio or not, or
-  // that there must only be a single fixed resolution.
-  CaptureResolutionChooser(const gfx::Size& max_frame_size,
-                           ResolutionChangePolicy resolution_change_policy);
+  // Default constructor. Capture size is fixed at kDefaultCaptureSize until
+  // SetConstraints() is called.
+  CaptureResolutionChooser();
+
   ~CaptureResolutionChooser();
 
   // Returns the current capture frame resolution to use.
-  gfx::Size capture_size() const { return capture_size_; }
+  const gfx::Size& capture_size() const { return capture_size_; }
+
+  // Specifies a new range of acceptable capture resolutions and whether a fixed
+  // aspect ratio is required. When |min_frame_size| is equal to
+  // |max_frame_size|, capture resolution will be held constant. If a fixed
+  // aspect ratio is required, the aspect ratio of |max_frame_size| is used.
+  void SetConstraints(const gfx::Size& min_frame_size,
+                      const gfx::Size& max_frame_size,
+                      bool use_fixed_aspect_ratio);
+
+  // Returns the currently-set source size.
+  const gfx::Size& source_size() const { return source_size_; }
 
   // Updates the capture size based on a change in the resolution of the source
   // content.
@@ -59,19 +64,26 @@
   gfx::Size FindLargerFrameSize(int area, int num_steps_up) const;
   gfx::Size FindSmallerFrameSize(int area, int num_steps_down) const;
 
+  // The default capture size, if SetConstraints() is never called.
+  static constexpr gfx::Size kDefaultCaptureSize = gfx::Size(640, 360);
+
  private:
   // Called after any update that requires |capture_size_| be re-computed.
   void RecomputeCaptureSize();
 
   // Recomputes the |snapped_sizes_| cache.
-  void UpdateSnappedFrameSizes(const gfx::Size& constrained_size);
+  void UpdateSnappedFrameSizes();
 
   // Hard constraints.
-  const gfx::Size max_frame_size_;
-  const gfx::Size min_frame_size_;  // Computed from the ctor arguments.
+  gfx::Size min_frame_size_;
+  gfx::Size max_frame_size_;
 
-  // Specifies the set of heuristics to use.
-  const ResolutionChangePolicy resolution_change_policy_;
+  // If true, adjust the |source_size_| to match the aspect ratio of
+  // |max_frame_size_| before computing the snapped frame sizes.
+  bool apply_aspect_ratio_adjustment_;
+
+  // Current source size.
+  gfx::Size source_size_;
 
   // |capture_size_| will be computed such that its area is as close to this
   // value as possible.
diff --git a/media/capture/content/capture_resolution_chooser_unittest.cc b/media/capture/content/capture_resolution_chooser_unittest.cc
index a0346b1020..587a91fc 100644
--- a/media/capture/content/capture_resolution_chooser_unittest.cc
+++ b/media/capture/content/capture_resolution_chooser_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/location.h"
 #include "base/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/size.h"
 
 using tracked_objects::Location;
 
@@ -17,10 +18,14 @@
 namespace {
 
 // 16:9 maximum and minimum frame sizes.
-const int kMaxFrameWidth = 3840;
-const int kMaxFrameHeight = 2160;
-const int kMinFrameWidth = 320;
-const int kMinFrameHeight = 180;
+constexpr int kMaxFrameWidth = 3840;
+constexpr int kMaxFrameHeight = 2160;
+constexpr int kMinFrameWidth = 320;
+constexpr int kMinFrameHeight = 180;
+
+// Smallest non-empty size for I420 video.
+constexpr int kSmallestNonEmptyWidth = 2;
+constexpr int kSmallestNonEmptyHeight = 2;
 
 // Checks whether |size| is strictly between (inclusive) |min_size| and
 // |max_size| and has the same aspect ratio as |max_size|.
@@ -186,13 +191,90 @@
   EXPECT_EQ(smallest_size, chooser->capture_size());
 }
 
+// Determines that there is only one snapped frame size by implication: This
+// function searches for the nearest/larger/smaller frame sizes around
+// |the_one_size| and checks that there is only ever one result.
+void ExpectOnlyOneSnappedFrameSize(const CaptureResolutionChooser& chooser,
+                                   const gfx::Size& the_one_size) {
+  for (int i = 1; i < 4; ++i) {
+    EXPECT_EQ(the_one_size,
+              chooser.FindNearestFrameSize(the_one_size.GetArea() * i));
+    EXPECT_EQ(the_one_size,
+              chooser.FindSmallerFrameSize(the_one_size.GetArea(), i));
+    EXPECT_EQ(the_one_size,
+              chooser.FindLargerFrameSize(the_one_size.GetArea(), i));
+  }
+}
+
 }  // namespace
 
+// While clients should always strive to set their own constraints before
+// querying the CaptureResolutionChooser, do test that the reasonable "default"
+// behavior is exhibited beforehand.
+TEST(CaptureResolutionChooserTest, DefaultCaptureSizeIfNeverSetConstraints) {
+  CaptureResolutionChooser chooser;
+  EXPECT_EQ(CaptureResolutionChooser::kDefaultCaptureSize,
+            chooser.capture_size());
+
+  chooser.SetSourceSize(gfx::Size(kMinFrameWidth, kMinFrameHeight));
+  EXPECT_EQ(CaptureResolutionChooser::kDefaultCaptureSize,
+            chooser.capture_size());
+
+  chooser.SetSourceSize(gfx::Size());
+  EXPECT_EQ(CaptureResolutionChooser::kDefaultCaptureSize,
+            chooser.capture_size());
+
+  chooser.SetSourceSize(gfx::Size(kMaxFrameWidth, kMaxFrameHeight));
+  EXPECT_EQ(CaptureResolutionChooser::kDefaultCaptureSize,
+            chooser.capture_size());
+
+  ExpectOnlyOneSnappedFrameSize(chooser,
+                                CaptureResolutionChooser::kDefaultCaptureSize);
+}
+
+// While clients should always strive to set the source size before querying the
+// CaptureResolutionChooser, do test that the reasonable "default" behavior is
+// exhibited beforehand.
+TEST(CaptureResolutionChooserTest, ReasonableCaptureSizeWhenMissingSourceSize) {
+  CaptureResolutionChooser chooser;
+  // CaptureResolutionChooser starts out with capture size set to
+  // kDefaultCaptureSize.
+  EXPECT_EQ(CaptureResolutionChooser::kDefaultCaptureSize,
+            chooser.capture_size());
+
+  // Setting constraints around kDefaultCaptureSize means the capture size
+  // should remain as kDefaultCaptureSize.
+  chooser.SetConstraints(gfx::Size(kMinFrameWidth, kMinFrameHeight),
+                         gfx::Size(kMaxFrameWidth, kMaxFrameHeight), false);
+  EXPECT_EQ(CaptureResolutionChooser::kDefaultCaptureSize,
+            chooser.capture_size());
+
+  // Setting constraints to a fixed size less than kDefaultCaptureSize will
+  // change the capture size, to meet the required constraints range.
+  chooser.SetConstraints(gfx::Size(kMinFrameWidth, kMinFrameHeight),
+                         gfx::Size(kMinFrameWidth, kMinFrameHeight), false);
+  EXPECT_EQ(gfx::Size(kMinFrameWidth, kMinFrameHeight), chooser.capture_size());
+
+  // Setting the constraints back to the wide range should not change the
+  // capture size, since the new range includes the former capture size.
+  chooser.SetConstraints(gfx::Size(kMinFrameWidth, kMinFrameHeight),
+                         gfx::Size(kMaxFrameWidth, kMaxFrameHeight), false);
+  EXPECT_EQ(gfx::Size(kMinFrameWidth, kMinFrameHeight), chooser.capture_size());
+
+  // Finally, updating the source size to be exactly in the middle of the
+  // constraints range should result in the capture size being updated to that
+  // same size.
+  const gfx::Size middle_size((kMinFrameWidth + kMaxFrameWidth) / 2,
+                              (kMinFrameHeight + kMaxFrameHeight) / 2);
+  chooser.SetSourceSize(middle_size);
+  EXPECT_EQ(middle_size, chooser.capture_size());
+}
+
 TEST(CaptureResolutionChooserTest,
      FixedResolutionPolicy_CaptureSizeAlwaysFixed) {
   const gfx::Size the_one_frame_size(kMaxFrameWidth, kMaxFrameHeight);
-  CaptureResolutionChooser chooser(the_one_frame_size,
-                                   RESOLUTION_POLICY_FIXED_RESOLUTION);
+  CaptureResolutionChooser chooser;
+  chooser.SetConstraints(the_one_frame_size, the_one_frame_size, false);
   EXPECT_EQ(the_one_frame_size, chooser.capture_size());
 
   chooser.SetSourceSize(the_one_frame_size);
@@ -207,16 +289,7 @@
   chooser.SetSourceSize(gfx::Size(kMinFrameWidth, kMinFrameHeight));
   EXPECT_EQ(the_one_frame_size, chooser.capture_size());
 
-  // Ensure that there is only one snapped frame size.
-  chooser.SetSourceSize(the_one_frame_size);
-  for (int i = 1; i < 4; ++i) {
-    EXPECT_EQ(the_one_frame_size,
-              chooser.FindNearestFrameSize(the_one_frame_size.GetArea() * i));
-    EXPECT_EQ(the_one_frame_size,
-              chooser.FindSmallerFrameSize(the_one_frame_size.GetArea(), i));
-    EXPECT_EQ(the_one_frame_size,
-              chooser.FindLargerFrameSize(the_one_frame_size.GetArea(), i));
-  }
+  ExpectOnlyOneSnappedFrameSize(chooser, the_one_frame_size);
 
   // Ensure that changing the target frame area does not change the computed
   // frame size.
@@ -228,12 +301,12 @@
 
 TEST(CaptureResolutionChooserTest,
      FixedAspectRatioPolicy_CaptureSizeHasSameAspectRatio) {
-  CaptureResolutionChooser chooser(gfx::Size(kMaxFrameWidth, kMaxFrameHeight),
-                                   RESOLUTION_POLICY_FIXED_ASPECT_RATIO);
-
-  // Starting condition.
   const gfx::Size min_size(kMinFrameWidth, kMinFrameHeight);
   const gfx::Size max_size(kMaxFrameWidth, kMaxFrameHeight);
+  CaptureResolutionChooser chooser;
+  chooser.SetConstraints(min_size, max_size, true);
+
+  // Starting condition.
   ExpectIsWithinBoundsAndSameAspectRatio(FROM_HERE, min_size, max_size,
                                          chooser.capture_size());
 
@@ -299,12 +372,14 @@
 
 TEST(CaptureResolutionChooserTest,
      AnyWithinLimitPolicy_CaptureSizeIsAnythingWithinLimits) {
+  const gfx::Size min_size(kSmallestNonEmptyWidth, kSmallestNonEmptyHeight);
   const gfx::Size max_size(kMaxFrameWidth, kMaxFrameHeight);
-  CaptureResolutionChooser chooser(max_size,
-                                   RESOLUTION_POLICY_ANY_WITHIN_LIMIT);
+  CaptureResolutionChooser chooser;
+  chooser.SetConstraints(min_size, max_size, false);
 
   // Starting condition.
-  EXPECT_EQ(max_size, chooser.capture_size());
+  EXPECT_EQ(CaptureResolutionChooser::kDefaultCaptureSize,
+            chooser.capture_size());
 
   // Max size in --> max size out.
   chooser.SetSourceSize(max_size);
diff --git a/media/capture/content/thread_safe_capture_oracle.cc b/media/capture/content/thread_safe_capture_oracle.cc
index 665b0d2..72905a2 100644
--- a/media/capture/content/thread_safe_capture_oracle.cc
+++ b/media/capture/content/thread_safe_capture_oracle.cc
@@ -47,13 +47,17 @@
     const VideoCaptureParams& params,
     bool enable_auto_throttling)
     : client_(std::move(client)),
-      oracle_(base::TimeDelta::FromMicroseconds(static_cast<int64_t>(
-                  1000000.0 / params.requested_format.frame_rate +
-                  0.5 /* to round to nearest int */)),
-              params.requested_format.frame_size,
-              params.resolution_change_policy,
-              enable_auto_throttling),
-      params_(params) {}
+      oracle_(enable_auto_throttling),
+      params_(params) {
+  DCHECK_GE(params.requested_format.frame_rate, 1e-6f);
+  oracle_.SetMinCapturePeriod(base::TimeDelta::FromMicroseconds(
+      static_cast<int64_t>(1000000.0 / params.requested_format.frame_rate +
+                           0.5 /* to round to nearest int */)));
+  const auto constraints = params.SuggestConstraints();
+  oracle_.SetCaptureSizeConstraints(constraints.min_frame_size,
+                                    constraints.max_frame_size,
+                                    constraints.fixed_aspect_ratio);
+}
 
 ThreadSafeCaptureOracle::~ThreadSafeCaptureOracle() {
 }
diff --git a/media/capture/content/video_capture_oracle.cc b/media/capture/content/video_capture_oracle.cc
index e679390..1672384 100644
--- a/media/capture/content/video_capture_oracle.cc
+++ b/media/capture/content/video_capture_oracle.cc
@@ -95,19 +95,17 @@
 
 }  // anonymous namespace
 
-VideoCaptureOracle::VideoCaptureOracle(
-    base::TimeDelta min_capture_period,
-    const gfx::Size& max_frame_size,
-    media::ResolutionChangePolicy resolution_change_policy,
-    bool enable_auto_throttling)
+// static
+constexpr base::TimeDelta VideoCaptureOracle::kDefaultMinCapturePeriod;
+
+VideoCaptureOracle::VideoCaptureOracle(bool enable_auto_throttling)
     : auto_throttling_enabled_(enable_auto_throttling),
       next_frame_number_(0),
       source_is_dirty_(true),
       last_successfully_delivered_frame_number_(-1),
       num_frames_pending_(0),
-      smoothing_sampler_(min_capture_period),
-      content_sampler_(min_capture_period),
-      resolution_chooser_(max_frame_size, resolution_change_policy),
+      smoothing_sampler_(kDefaultMinCapturePeriod),
+      content_sampler_(kDefaultMinCapturePeriod),
       buffer_pool_utilization_(base::TimeDelta::FromMicroseconds(
           kBufferUtilizationEvaluationMicros)),
       estimated_capable_area_(base::TimeDelta::FromMicroseconds(
@@ -119,6 +117,20 @@
 VideoCaptureOracle::~VideoCaptureOracle() {
 }
 
+void VideoCaptureOracle::SetMinCapturePeriod(base::TimeDelta period) {
+  DCHECK_GT(period, base::TimeDelta());
+  smoothing_sampler_.SetMinCapturePeriod(period);
+  content_sampler_.SetMinCapturePeriod(period);
+}
+
+void VideoCaptureOracle::SetCaptureSizeConstraints(
+    const gfx::Size& min_size,
+    const gfx::Size& max_size,
+    bool use_fixed_aspect_ratio) {
+  resolution_chooser_.SetConstraints(min_size, max_size,
+                                     use_fixed_aspect_ratio);
+}
+
 void VideoCaptureOracle::SetSourceSize(const gfx::Size& source_size) {
   resolution_chooser_.SetSourceSize(source_size);
   // If the |resolution_chooser_| computed a new capture size, that will become
@@ -202,9 +214,8 @@
     }
     const base::TimeDelta upper_bound =
         base::TimeDelta::FromMilliseconds(kUpperBoundDurationEstimateMicros);
-    duration_of_next_frame_ =
-        std::max(std::min(duration_of_next_frame_, upper_bound),
-                 smoothing_sampler_.min_capture_period());
+    duration_of_next_frame_ = std::max(
+        std::min(duration_of_next_frame_, upper_bound), min_capture_period());
   }
 
   // Update |capture_size_| and reset all feedback signal accumulators if
@@ -224,10 +235,6 @@
   return true;
 }
 
-int VideoCaptureOracle::next_frame_number() const {
-  return next_frame_number_;
-}
-
 void VideoCaptureOracle::RecordCapture(double pool_utilization) {
   DCHECK(std::isfinite(pool_utilization) && pool_utilization >= 0.0);
 
diff --git a/media/capture/content/video_capture_oracle.h b/media/capture/content/video_capture_oracle.h
index 3f62c72..9abca2c 100644
--- a/media/capture/content/video_capture_oracle.h
+++ b/media/capture/content/video_capture_oracle.h
@@ -30,16 +30,33 @@
     kNumEvents,
   };
 
-  VideoCaptureOracle(base::TimeDelta min_capture_period,
-                     const gfx::Size& max_frame_size,
-                     media::ResolutionChangePolicy resolution_change_policy,
-                     bool enable_auto_throttling);
+  // Constructs a VideoCaptureOracle with a default min capture period and
+  // capture size constraints. Clients should call SetMinCapturePeriod() and
+  // SetCaptureSizeConstraints() to provide more-accurate hard limits. If
+  // |enable_auto_throttling| is true, enable realtime analysis of system
+  // performance and auto-adjust the capture resolution and sampling decisions
+  // to provide the best user experience.
+  explicit VideoCaptureOracle(bool enable_auto_throttling);
 
   virtual ~VideoCaptureOracle();
 
-  // Sets the source content size.  This may not have an immediate effect on the
-  // proposed capture size, as the oracle will prevent too-frequent changes from
-  // occurring.
+  // Get/Update the minimum capture period.
+  base::TimeDelta min_capture_period() const {
+    return smoothing_sampler_.min_capture_period();
+  }
+  void SetMinCapturePeriod(base::TimeDelta period);
+
+  // Sets the range of acceptable capture sizes and whether a fixed aspect ratio
+  // is required. If a fixed aspect ratio is required, the aspect ratio of
+  // |max_size| is used.
+  void SetCaptureSizeConstraints(const gfx::Size& min_size,
+                                 const gfx::Size& max_size,
+                                 bool use_fixed_aspect_ratio);
+
+  // Get/Update the source content size.  Changes may not have an immediate
+  // effect on the proposed capture size, as the oracle will prevent too-
+  // frequent changes from occurring.
+  gfx::Size source_size() const { return resolution_chooser_.source_size(); }
   void SetSourceSize(const gfx::Size& source_size);
 
   // Record a event of type |event|, and decide whether the caller should do a
@@ -51,7 +68,7 @@
                                     base::TimeTicks event_time);
 
   // Returns the |frame_number| to be used with CompleteCapture().
-  int next_frame_number() const;
+  int next_frame_number() const { return next_frame_number_; }
 
   // Record and update internal state based on whether the frame capture will be
   // started.  |pool_utilization| is a value in the range 0.0 to 1.0 to indicate
@@ -78,10 +95,6 @@
   // returned true.
   void RecordConsumerFeedback(int frame_number, double resource_utilization);
 
-  base::TimeDelta min_capture_period() const {
-    return smoothing_sampler_.min_capture_period();
-  }
-
   // Returns the oracle's estimate of the duration of the next frame.  This
   // should be called just after ObserveEventAndDecideCapture(), and will only
   // be non-zero if the call returned true.
@@ -103,6 +116,12 @@
   // |event|.
   static const char* EventAsString(Event event);
 
+  // Default minimum capture period. This is a rather low framerate for safety.
+  // Clients are expected to set a better minimum capture period after
+  // VideoCaptureOracle is constructed.
+  static constexpr base::TimeDelta kDefaultMinCapturePeriod =
+      base::TimeDelta::FromMicroseconds(1000000 / 5);  // 5 FPS
+
  private:
   // Retrieve/Assign a frame timestamp by capture |frame_number|.  Only valid
   // when IsFrameInRecentHistory(frame_number) returns true.
@@ -138,7 +157,7 @@
   // size in response to end-to-end utilization.
   const bool auto_throttling_enabled_;
 
-  // Incremented every time a paint or update event occurs.
+  // Incremented every time RecordCapture() is called.
   int next_frame_number_;
 
   // Stores the last |event_time| from the last observation/decision.  Used to
diff --git a/media/capture/content/video_capture_oracle_unittest.cc b/media/capture/content/video_capture_oracle_unittest.cc
index d75daae..873ed05 100644
--- a/media/capture/content/video_capture_oracle_unittest.cc
+++ b/media/capture/content/video_capture_oracle_unittest.cc
@@ -31,6 +31,10 @@
   return gfx::Size(640, 360);
 }
 
+gfx::Size GetSmallestNonEmptySize() {
+  return gfx::Size(2, 2);
+}
+
 }  // namespace
 
 // Tests that VideoCaptureOracle filters out events whose timestamps are
@@ -39,8 +43,9 @@
   const gfx::Rect damage_rect(Get720pSize());
   const base::TimeDelta event_increment = Get30HzPeriod() * 2;
 
-  VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
-                            media::RESOLUTION_POLICY_FIXED_RESOLUTION, false);
+  VideoCaptureOracle oracle(false);
+  oracle.SetMinCapturePeriod(Get30HzPeriod());
+  oracle.SetCaptureSizeConstraints(Get720pSize(), Get720pSize(), false);
 
   base::TimeTicks t = InitialTestTimeTicks();
   for (int i = 0; i < 10; ++i) {
@@ -71,8 +76,9 @@
   const gfx::Rect damage_rect(Get720pSize());
   const base::TimeDelta event_increment = Get30HzPeriod() * 2;
 
-  VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
-                            media::RESOLUTION_POLICY_FIXED_RESOLUTION, false);
+  VideoCaptureOracle oracle(false);
+  oracle.SetMinCapturePeriod(Get30HzPeriod());
+  oracle.SetCaptureSizeConstraints(Get720pSize(), Get720pSize(), false);
 
   // Most basic scenario: Frames delivered one at a time, with no additional
   // captures in-between deliveries.
@@ -148,8 +154,9 @@
   const gfx::Rect animation_damage_rect(Get720pSize());
   const base::TimeDelta event_increment = Get30HzPeriod() * 2;
 
-  VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
-                            media::RESOLUTION_POLICY_FIXED_RESOLUTION, false);
+  VideoCaptureOracle oracle(false);
+  oracle.SetMinCapturePeriod(Get30HzPeriod());
+  oracle.SetCaptureSizeConstraints(Get720pSize(), Get720pSize(), false);
 
   // Run sequences of animation events and non-animation events through the
   // oracle.  As the oracle transitions between each sampler, make sure the
@@ -210,8 +217,9 @@
   const base::TimeDelta refresh_interval =
       base::TimeDelta::FromMilliseconds(125);  // 8 FPS
 
-  VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
-                            media::RESOLUTION_POLICY_FIXED_RESOLUTION, false);
+  VideoCaptureOracle oracle(false);
+  oracle.SetMinCapturePeriod(Get30HzPeriod());
+  oracle.SetCaptureSizeConstraints(Get720pSize(), Get720pSize(), false);
 
   // Have the oracle observe some compositor events.  Simulate that each capture
   // completes successfully.
@@ -303,8 +311,11 @@
 // to allow both the source content and the rest of the end-to-end system to
 // stabilize.
 TEST(VideoCaptureOracleTest, DoesNotRapidlyChangeCaptureSize) {
-  VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
-                            media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT, true);
+  VideoCaptureOracle oracle(true);
+  oracle.SetMinCapturePeriod(Get30HzPeriod());
+  oracle.SetCaptureSizeConstraints(GetSmallestNonEmptySize(), Get720pSize(),
+                                   false);
+  oracle.SetSourceSize(Get1080pSize());
 
   // Run 30 seconds of frame captures without any source size changes.
   base::TimeTicks t = InitialTestTimeTicks();
@@ -357,8 +368,9 @@
   const base::TimeDelta event_increment = Get30HzPeriod() * 2;
   const base::TimeDelta short_event_increment = Get30HzPeriod() / 4;
 
-  VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
-                            media::RESOLUTION_POLICY_FIXED_RESOLUTION, false);
+  VideoCaptureOracle oracle(false);
+  oracle.SetMinCapturePeriod(Get30HzPeriod());
+  oracle.SetCaptureSizeConstraints(Get720pSize(), Get720pSize(), false);
 
   base::TimeTicks t = InitialTestTimeTicks();
   int last_frame_number;
@@ -402,8 +414,11 @@
                << "(is_content_animating=" << is_content_animating
                << ", with_consumer_feedback=" << with_consumer_feedback << ")");
 
-  VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
-                            media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT, true);
+  VideoCaptureOracle oracle(true);
+  oracle.SetMinCapturePeriod(Get30HzPeriod());
+  oracle.SetCaptureSizeConstraints(GetSmallestNonEmptySize(), Get720pSize(),
+                                   false);
+  oracle.SetSourceSize(Get1080pSize());
 
   // Run 10 seconds of frame captures with 90% utilization expect no capture
   // size changes.
@@ -522,8 +537,10 @@
 // Otherwise, capture size increases should only be made cautiously, after a
 // long "proving period of under-utilization" has elapsed.
 TEST(VideoCaptureOracleTest, IncreasesFrequentlyOnlyAfterSourceSizeChange) {
-  VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
-                            media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT, true);
+  VideoCaptureOracle oracle(true);
+  oracle.SetMinCapturePeriod(Get30HzPeriod());
+  oracle.SetCaptureSizeConstraints(GetSmallestNonEmptySize(), Get720pSize(),
+                                   false);
 
   // Start out the source size at 360p, so there is room to grow to the 720p
   // maximum.
@@ -637,8 +654,10 @@
 // Tests that VideoCaptureOracle does not change the capture size if
 // auto-throttling is enabled when using a fixed resolution policy.
 TEST(VideoCaptureOracleTest, DoesNotAutoThrottleWhenResolutionIsFixed) {
-  VideoCaptureOracle oracle(Get30HzPeriod(), Get720pSize(),
-                            media::RESOLUTION_POLICY_FIXED_RESOLUTION, true);
+  VideoCaptureOracle oracle(true);
+  oracle.SetMinCapturePeriod(Get30HzPeriod());
+  oracle.SetCaptureSizeConstraints(Get720pSize(), Get720pSize(), false);
+  oracle.SetSourceSize(Get1080pSize());
 
   // Run 10 seconds of frame captures with 90% utilization expect no capture
   // size changes.
diff --git a/media/capture/video/mac/video_capture_device_factory_mac.mm b/media/capture/video/mac/video_capture_device_factory_mac.mm
index 3ed8c39..d3ff0a4 100644
--- a/media/capture/video/mac/video_capture_device_factory_mac.mm
+++ b/media/capture/video/mac/video_capture_device_factory_mac.mm
@@ -12,7 +12,6 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_util.h"
 #include "base/task_runner_util.h"
@@ -70,11 +69,6 @@
 
 void VideoCaptureDeviceFactoryMac::GetDeviceDescriptors(
     VideoCaptureDeviceDescriptors* device_descriptors) {
-  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/458397 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "458397 VideoCaptureDeviceFactoryMac::GetDeviceDescriptors"));
   DCHECK(thread_checker_.CalledOnValidThread());
   // Loop through all available devices and add to |device_descriptors|.
   NSDictionary* capture_devices;
diff --git a/media/capture/video/shared_memory_handle_provider.cc b/media/capture/video/shared_memory_handle_provider.cc
index db5836b..bb10c07 100644
--- a/media/capture/video/shared_memory_handle_provider.cc
+++ b/media/capture/video/shared_memory_handle_provider.cc
@@ -4,23 +4,36 @@
 
 #include "media/capture/video/shared_memory_handle_provider.h"
 
-#include "base/logging.h"
-
 namespace media {
 
-SharedMemoryHandleProvider::SharedMemoryHandleProvider() : map_ref_count_(0) {}
+SharedMemoryHandleProvider::SharedMemoryHandleProvider() {
+#if DCHECK_IS_ON()
+  map_ref_count_ = 0;
+#endif
+}
 
 SharedMemoryHandleProvider::~SharedMemoryHandleProvider() {
+  base::AutoLock lock(mapping_lock_);
+
   // If the tracker is being destroyed, there must be no outstanding
   // Handles. If this DCHECK() triggers, it means that either there is a logic
   // flaw in VideoCaptureBufferPoolImpl, or a client did not delete all of its
   // owned VideoCaptureBufferHandles before calling Pool::ReliquishXYZ().
-  base::AutoLock lock(mapping_lock_);
+#if DCHECK_IS_ON()
   DCHECK_EQ(map_ref_count_, 0);
+#endif
+
+  if (shared_memory_ && shared_memory_->memory()) {
+    DVLOG(3) << __func__ << ": Unmapping memory for in-process access @"
+             << shared_memory_->memory() << '.';
+    CHECK(shared_memory_->Unmap());
+  }
 }
 
 bool SharedMemoryHandleProvider::InitForSize(size_t size) {
+#if DCHECK_IS_ON()
   DCHECK_EQ(map_ref_count_, 0);
+#endif
   DCHECK(!shared_memory_);
   shared_memory_.emplace();
   if (shared_memory_->CreateAnonymous(size)) {
@@ -33,7 +46,9 @@
 
 bool SharedMemoryHandleProvider::InitFromMojoHandle(
     mojo::ScopedSharedBufferHandle buffer_handle) {
+#if DCHECK_IS_ON()
   DCHECK_EQ(map_ref_count_, 0);
+#endif
   DCHECK(!shared_memory_);
 
   base::SharedMemoryHandle memory_handle;
@@ -67,9 +82,11 @@
 SharedMemoryHandleProvider::GetHandleForInProcessAccess() {
   {
     base::AutoLock lock(mapping_lock_);
+#if DCHECK_IS_ON()
     DCHECK_GE(map_ref_count_, 0);
     ++map_ref_count_;
-    if (map_ref_count_ == 1) {
+#endif
+    if (!shared_memory_->memory()) {
       CHECK(shared_memory_->Map(mapped_size_));
       DVLOG(3) << __func__ << ": Mapped memory for in-process access @"
                << shared_memory_->memory() << '.';
@@ -79,22 +96,21 @@
   return std::make_unique<Handle>(this);
 }
 
+#if DCHECK_IS_ON()
 void SharedMemoryHandleProvider::OnHandleDestroyed() {
   base::AutoLock lock(mapping_lock_);
   DCHECK_GT(map_ref_count_, 0);
   --map_ref_count_;
-  if (map_ref_count_ == 0) {
-    DVLOG(3) << __func__ << ": Unmapping memory for in-process access @"
-             << shared_memory_->memory() << '.';
-    CHECK(shared_memory_->Unmap());
-  }
 }
+#endif
 
 SharedMemoryHandleProvider::Handle::Handle(SharedMemoryHandleProvider* owner)
     : owner_(owner) {}
 
 SharedMemoryHandleProvider::Handle::~Handle() {
+#if DCHECK_IS_ON()
   owner_->OnHandleDestroyed();
+#endif
 }
 
 size_t SharedMemoryHandleProvider::Handle::mapped_size() const {
diff --git a/media/capture/video/shared_memory_handle_provider.h b/media/capture/video/shared_memory_handle_provider.h
index 7acec38e..d844a98 100644
--- a/media/capture/video/shared_memory_handle_provider.h
+++ b/media/capture/video/shared_memory_handle_provider.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/logging.h"
 #include "base/memory/shared_memory.h"
 #include "base/optional.h"
 #include "media/capture/capture_export.h"
@@ -42,9 +43,10 @@
       override;
 
  private:
-  // Accessor to mapped memory. While one or more of these exists, the shared
-  // memory is mapped. When the last of these is destroyed, the shared memory is
-  // unmapped.
+  // Accessor to mapped memory. When the first of these is created, the shared
+  // memory is mapped. The unmapping, however, does not occur until the
+  // SharedMemoryHandleProvider is destroyed. Therefore, the provider must
+  // outlive all of its Handles.
   class Handle : public VideoCaptureBufferHandle {
    public:
     explicit Handle(SharedMemoryHandleProvider* owner);
@@ -58,9 +60,10 @@
     SharedMemoryHandleProvider* const owner_;
   };
 
-  // Called by Handle to decrement |map_ref_count_| and, if zero, unmap the
-  // memory from the process. This is thread-safe.
+#if DCHECK_IS_ON()
+  // Called by Handle to decrement |map_ref_count_|. This is thread-safe.
   void OnHandleDestroyed();
+#endif
 
   // These are set by one of the InitXYZ() methods.
   base::Optional<base::SharedMemory> shared_memory_;
@@ -73,8 +76,12 @@
   // code that runs on a diffrent thread.
   base::Lock mapping_lock_;
 
-  // The number of Handle instances that are referencing the mapped memory.
+#if DCHECK_IS_ON()
+  // The number of Handle instances that are referencing the mapped memory. This
+  // is only used while DCHECKs are turned on, as a sanity-check that the object
+  // graph/lifetimes have not changed in a bad way.
   int map_ref_count_;
+#endif
 
   DISALLOW_COPY_AND_ASSIGN(SharedMemoryHandleProvider);
 };
diff --git a/media/capture/video/win/video_capture_device_factory_win.cc b/media/capture/video/win/video_capture_device_factory_win.cc
index f60c0459..0dcb425 100644
--- a/media/capture/video/win/video_capture_device_factory_win.cc
+++ b/media/capture/video/win/video_capture_device_factory_win.cc
@@ -61,6 +61,11 @@
               "kBlacklistedCameraNames should be same size as "
               "BlacklistedCameraNames enum");
 
+static bool IsDeviceBlacklistedForQueryingDetailedFrameRates(
+    const std::string& display_name) {
+  return display_name.find("WebcamMax") != std::string::npos;
+}
+
 static bool LoadMediaFoundationDlls() {
   static const wchar_t* const kMfDLLs[] = {
       L"%WINDIR%\\system32\\mf.dll",
@@ -245,9 +250,12 @@
                                                 VideoCaptureFormats* formats) {
   DVLOG(1) << "GetDeviceSupportedFormatsDirectShow for "
            << descriptor.display_name;
+  bool query_detailed_frame_rates =
+      !IsDeviceBlacklistedForQueryingDetailedFrameRates(
+          descriptor.display_name);
   CapabilityList capability_list;
-  VideoCaptureDeviceWin::GetDeviceCapabilityList(descriptor.device_id,
-                                                 &capability_list);
+  VideoCaptureDeviceWin::GetDeviceCapabilityList(
+      descriptor.device_id, query_detailed_frame_rates, &capability_list);
   for (const auto& entry : capability_list) {
     formats->emplace_back(entry.supported_format);
     DVLOG(1) << descriptor.display_name << " "
diff --git a/media/capture/video/win/video_capture_device_win.cc b/media/capture/video/win/video_capture_device_win.cc
index 1c010fe..06ee3ba 100644
--- a/media/capture/video/win/video_capture_device_win.cc
+++ b/media/capture/video/win/video_capture_device_win.cc
@@ -104,6 +104,7 @@
 // static
 void VideoCaptureDeviceWin::GetDeviceCapabilityList(
     const std::string& device_id,
+    bool query_detailed_frame_rates,
     CapabilityList* out_capability_list) {
   base::win::ScopedComPtr<IBaseFilter> capture_filter;
   HRESULT hr = VideoCaptureDeviceWin::GetDeviceFilter(
@@ -122,13 +123,15 @@
     return;
   }
 
-  GetPinCapabilityList(capture_filter, output_capture_pin, out_capability_list);
+  GetPinCapabilityList(capture_filter, output_capture_pin,
+                       query_detailed_frame_rates, out_capability_list);
 }
 
 // static
 void VideoCaptureDeviceWin::GetPinCapabilityList(
     base::win::ScopedComPtr<IBaseFilter> capture_filter,
     base::win::ScopedComPtr<IPin> output_capture_pin,
+    bool query_detailed_frame_rates,
     CapabilityList* out_capability_list) {
   ScopedComPtr<IAMStreamConfig> stream_config;
   HRESULT hr = output_capture_pin.CopyTo(stream_config.GetAddressOf());
@@ -175,10 +178,10 @@
           reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
       format.frame_size.SetSize(h->bmiHeader.biWidth, h->bmiHeader.biHeight);
 
-      // Try to get a better |time_per_frame| from IAMVideoControl. If not, use
-      // the value from VIDEOINFOHEADER.
       std::vector<float> frame_rates;
-      if (video_control.Get()) {
+      if (query_detailed_frame_rates && video_control.Get()) {
+        // Try to get a better |time_per_frame| from IAMVideoControl. If not,
+        // use the value from VIDEOINFOHEADER.
         ScopedCoMem<LONGLONG> time_per_frame_list;
         LONG list_size = 0;
         const SIZE size = {format.frame_size.width(),
@@ -186,12 +189,12 @@
         hr = video_control->GetFrameRateList(output_capture_pin.Get(), i, size,
                                              &list_size, &time_per_frame_list);
         // Sometimes |list_size| will be > 0, but time_per_frame_list will be
-        // NULL. Some drivers may return an HRESULT of S_FALSE which SUCCEEDED()
-        // translates into success, so explicitly check S_OK.
-        // See http://crbug.com/306237.
+        // NULL. Some drivers may return an HRESULT of S_FALSE which
+        // SUCCEEDED() translates into success, so explicitly check S_OK. See
+        // http://crbug.com/306237.
         if (hr == S_OK && list_size > 0 && time_per_frame_list) {
           for (int k = 0; k < list_size; k++) {
-            LONGLONG time_per_frame = *(time_per_frame_list + k);
+            LONGLONG time_per_frame = *(time_per_frame_list.get() + k);
             if (time_per_frame <= 0)
               continue;
             frame_rates.push_back(kSecondsToReferenceTime /
@@ -860,7 +863,8 @@
 
 bool VideoCaptureDeviceWin::CreateCapabilityMap() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  GetPinCapabilityList(capture_filter_, output_capture_pin_, &capabilities_);
+  GetPinCapabilityList(capture_filter_, output_capture_pin_,
+                       true /* query_detailed_frame_rates */, &capabilities_);
   return !capabilities_.empty();
 }
 
diff --git a/media/capture/video/win/video_capture_device_win.h b/media/capture/video/win/video_capture_device_win.h
index facee7e3..27a23b1 100644
--- a/media/capture/video/win/video_capture_device_win.h
+++ b/media/capture/video/win/video_capture_device_win.h
@@ -57,10 +57,12 @@
   };
 
   static void GetDeviceCapabilityList(const std::string& device_id,
+                                      bool query_detailed_frame_rates,
                                       CapabilityList* out_capability_list);
   static void GetPinCapabilityList(
       base::win::ScopedComPtr<IBaseFilter> capture_filter,
       base::win::ScopedComPtr<IPin> output_capture_pin,
+      bool query_detailed_frame_rates,
       CapabilityList* out_capability_list);
   static HRESULT GetDeviceFilter(const std::string& device_id,
                                  IBaseFilter** filter);
diff --git a/media/capture/video_capture_types.cc b/media/capture/video_capture_types.cc
index 81e5053..bae7afe 100644
--- a/media/capture/video_capture_types.cc
+++ b/media/capture/video_capture_types.cc
@@ -107,4 +107,56 @@
          power_line_frequency <= PowerLineFrequency::FREQUENCY_MAX;
 }
 
+VideoCaptureParams::SuggestedConstraints
+VideoCaptureParams::SuggestConstraints() const {
+  // The requested frame size is always the maximum frame size. Ensure that it
+  // rounds to even numbers (to match I420 chroma sample sizes).
+  gfx::Size max_frame_size = requested_format.frame_size;
+  if (max_frame_size.width() % 2 != 0)
+    max_frame_size.set_width(max_frame_size.width() - 1);
+  if (max_frame_size.height() % 2 != 0)
+    max_frame_size.set_height(max_frame_size.height() - 1);
+
+  // Compute the minimum frame size as a function of the maximum frame size and
+  // policy.
+  gfx::Size min_frame_size;
+  switch (resolution_change_policy) {
+    case RESOLUTION_POLICY_FIXED_RESOLUTION:
+      min_frame_size = max_frame_size;
+      break;
+
+    case RESOLUTION_POLICY_FIXED_ASPECT_RATIO: {
+      // TODO(miu): This is a place-holder until "min constraints" are plumbed-
+      // in from the MediaStream framework.  http://crbug.com/473336
+      constexpr int kMinLines = 180;
+      if (max_frame_size.height() <= kMinLines) {
+        min_frame_size = max_frame_size;
+      } else {
+        const double ideal_width = static_cast<double>(kMinLines) *
+                                   max_frame_size.width() /
+                                   max_frame_size.height();
+        // Round |ideal_width| to the nearest even whole number.
+        const int even_width = static_cast<int>(ideal_width / 2.0 + 0.5) * 2;
+        min_frame_size = gfx::Size(even_width, kMinLines);
+        if (min_frame_size.width() <= 0 ||
+            min_frame_size.width() > max_frame_size.width()) {
+          min_frame_size = max_frame_size;
+        }
+      }
+      break;
+    }
+
+    case RESOLUTION_POLICY_ANY_WITHIN_LIMIT:
+      if (!max_frame_size.IsEmpty())
+        min_frame_size = gfx::Size(2, 2);
+      break;
+  }
+  DCHECK(min_frame_size.width() % 2 == 0);
+  DCHECK(min_frame_size.height() % 2 == 0);
+
+  return SuggestedConstraints{
+      min_frame_size, max_frame_size,
+      resolution_change_policy == RESOLUTION_POLICY_FIXED_ASPECT_RATIO};
+}
+
 }  // namespace media
diff --git a/media/capture/video_capture_types.h b/media/capture/video_capture_types.h
index 7d8c340c..0c2a14fc 100644
--- a/media/capture/video_capture_types.h
+++ b/media/capture/video_capture_types.h
@@ -122,12 +122,24 @@
 // format of frames in which the client would like to have captured frames
 // returned.
 struct CAPTURE_EXPORT VideoCaptureParams {
+  // Result struct for SuggestContraints() method.
+  struct SuggestedConstraints {
+    gfx::Size min_frame_size;
+    gfx::Size max_frame_size;
+    bool fixed_aspect_ratio;
+  };
+
   VideoCaptureParams();
 
   // Returns true if requested_format.IsValid() and all other values are within
   // their expected ranges.
   bool IsValid() const;
 
+  // Computes and returns suggested capture constraints based on the requested
+  // format and resolution change policy: minimum resolution, maximum
+  // resolution, and whether a fixed aspect ratio is required.
+  SuggestedConstraints SuggestConstraints() const;
+
   bool operator==(const VideoCaptureParams& other) const {
     return requested_format == other.requested_format &&
            resolution_change_policy == other.resolution_change_policy &&
diff --git a/media/capture/video_capture_types_unittest.cc b/media/capture/video_capture_types_unittest.cc
new file mode 100644
index 0000000..894dbc8
--- /dev/null
+++ b/media/capture/video_capture_types_unittest.cc
@@ -0,0 +1,76 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/capture/video_capture_types.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace media {
+
+namespace {
+
+using SuggestedConstraints = VideoCaptureParams::SuggestedConstraints;
+
+void ExpectEqualConstraints(const SuggestedConstraints& a,
+                            const SuggestedConstraints& b) {
+  EXPECT_EQ(a.min_frame_size, b.min_frame_size);
+  EXPECT_EQ(a.max_frame_size, b.max_frame_size);
+  EXPECT_EQ(a.fixed_aspect_ratio, b.fixed_aspect_ratio);
+}
+
+}  // namespace
+
+TEST(VideoCaptureTypesTest, SuggestsConstraints) {
+  VideoCaptureParams params;
+
+  // For empty/invalid VideoCaptureParams, results will be empty sizes.
+  ExpectEqualConstraints(
+      SuggestedConstraints({gfx::Size(), gfx::Size(), false}),
+      params.SuggestConstraints());
+
+  // Set an impractically small frame size, and confirm the same suggestion as
+  // if the params were empty.
+  params.requested_format.frame_size = gfx::Size(1, 1);
+  ExpectEqualConstraints(
+      SuggestedConstraints({gfx::Size(), gfx::Size(), false}),
+      params.SuggestConstraints());
+
+  // Test: Fixed 1080p resolution.
+  params.requested_format.frame_size = gfx::Size(1920, 1080);
+  params.resolution_change_policy = RESOLUTION_POLICY_FIXED_RESOLUTION;
+  ExpectEqualConstraints(SuggestedConstraints({gfx::Size(1920, 1080),
+                                               gfx::Size(1920, 1080), false}),
+                         params.SuggestConstraints());
+
+  // Test: Max 1080p resolution, fixed aspect ratio.
+  params.requested_format.frame_size = gfx::Size(1920, 1080);
+  params.resolution_change_policy = RESOLUTION_POLICY_FIXED_ASPECT_RATIO;
+  ExpectEqualConstraints(
+      SuggestedConstraints({gfx::Size(320, 180), gfx::Size(1920, 1080), true}),
+      params.SuggestConstraints());
+
+  // Test: Max 1080p resolution, any aspect ratio.
+  params.requested_format.frame_size = gfx::Size(1920, 1080);
+  params.resolution_change_policy = RESOLUTION_POLICY_ANY_WITHIN_LIMIT;
+  ExpectEqualConstraints(
+      SuggestedConstraints({gfx::Size(2, 2), gfx::Size(1920, 1080), false}),
+      params.SuggestConstraints());
+
+  // Test: Odd-valued resolution, fixed aspect ratio.
+  params.requested_format.frame_size = gfx::Size(999, 777);
+  params.resolution_change_policy = RESOLUTION_POLICY_FIXED_ASPECT_RATIO;
+  ExpectEqualConstraints(
+      SuggestedConstraints({gfx::Size(232, 180), gfx::Size(998, 776), true}),
+      params.SuggestConstraints());
+
+  // Test: Max resolution less the hard-coded 180-line minimum, fixed aspect.
+  params.requested_format.frame_size = gfx::Size(160, 90);
+  params.resolution_change_policy = RESOLUTION_POLICY_FIXED_ASPECT_RATIO;
+  ExpectEqualConstraints(
+      SuggestedConstraints({gfx::Size(160, 90), gfx::Size(160, 90), true}),
+      params.SuggestConstraints());
+}
+
+}  // namespace media
diff --git a/media/device_monitors/device_monitor_mac.mm b/media/device_monitors/device_monitor_mac.mm
index 9a02e90..e8b5c2f 100644
--- a/media/device_monitors/device_monitor_mac.mm
+++ b/media/device_monitors/device_monitor_mac.mm
@@ -12,7 +12,6 @@
 #include "base/mac/bind_objc_block.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/task_runner_util.h"
 #include "base/threading/thread_checker.h"
 
@@ -450,15 +449,9 @@
 
 void DeviceMonitorMac::StartMonitoring() {
   DCHECK(thread_checker_.CalledOnValidThread());
-
-    // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/458404
-    // is fixed.
-    tracked_objects::ScopedTracker tracking_profile(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "458404 DeviceMonitorMac::StartMonitoring::AVFoundation"));
-    DVLOG(1) << "Monitoring via AVFoundation";
-    device_monitor_impl_.reset(
-        new AVFoundationMonitorImpl(this, device_task_runner_));
+  DVLOG(1) << "Monitoring via AVFoundation";
+  device_monitor_impl_.reset(
+      new AVFoundationMonitorImpl(this, device_task_runner_));
 }
 
 void DeviceMonitorMac::NotifyDeviceChanged(
diff --git a/media/filters/source_buffer_range.cc b/media/filters/source_buffer_range.cc
index 3484024..0e2aa116 100644
--- a/media/filters/source_buffer_range.cc
+++ b/media/filters/source_buffer_range.cc
@@ -88,18 +88,13 @@
   if (last_appended_buffer->is_duration_estimated()) {
     base::TimeDelta timestamp_delta =
         new_buffers.front()->timestamp() - last_appended_buffer->timestamp();
-    DCHECK(timestamp_delta > base::TimeDelta());
+    DCHECK_GE(timestamp_delta, base::TimeDelta());
     if (last_appended_buffer->duration() != timestamp_delta) {
       DVLOG(1) << "Replacing estimated duration ("
                << last_appended_buffer->duration()
                << ") from previous range-end with derived duration ("
                << timestamp_delta << ").";
       last_appended_buffer->set_duration(timestamp_delta);
-      // To update the range end time here, there is no need to inspect an
-      // entire GOP or even reset the currently tracked |highest_frame_| because
-      // estimated durations should only occur in WebM, which doesn't contain
-      // out-of-order presentation/decode sequences.
-      CHECK_EQ(last_appended_buffer.get(), highest_frame_.get());
     }
   }
 }
diff --git a/media/filters/source_buffer_range.h b/media/filters/source_buffer_range.h
index 0929a17..5d16ec1 100644
--- a/media/filters/source_buffer_range.h
+++ b/media/filters/source_buffer_range.h
@@ -260,7 +260,8 @@
   // Called during AppendBuffersToEnd to adjust estimated duration at the
   // end of the last append to match the delta in timestamps between
   // the last append and the upcoming append. This is a workaround for
-  // WebM media where a duration is not always specified.
+  // WebM media where a duration is not always specified. Caller should take
+  // care of updating |highest_frame_|.
   void AdjustEstimatedDurationForNewAppend(const BufferQueue& new_buffers);
 
   // Seeks the range to the next keyframe after |timestamp|. If
diff --git a/media/filters/source_buffer_stream.cc b/media/filters/source_buffer_stream.cc
index 4889111..be160bd 100644
--- a/media/filters/source_buffer_stream.cc
+++ b/media/filters/source_buffer_stream.cc
@@ -415,12 +415,8 @@
   // |last_appended_buffer_timestamp_| (if known), or if unknown and we are
   // still at the beginning of a new coded frame group, then will be into the
   // range (if any) to which |coded_frame_group_start_time_| belongs.
-  if (last_appended_buffer_timestamp_ != kNoDecodeTimestamp()) {
-    // TODO(wolenetz): Determine if this +1us is still necessary. See
-    // https://crbug.com/589295.
-    return last_appended_buffer_timestamp_ +
-           base::TimeDelta::FromInternalValue(1);
-  }
+  if (last_appended_buffer_timestamp_ != kNoDecodeTimestamp())
+    return last_appended_buffer_timestamp_;
 
   if (new_coded_frame_group_)
     return coded_frame_group_start_time_;
diff --git a/media/filters/source_buffer_stream_unittest.cc b/media/filters/source_buffer_stream_unittest.cc
index 7bbc0dd..7b4d5e6 100644
--- a/media/filters/source_buffer_stream_unittest.cc
+++ b/media/filters/source_buffer_stream_unittest.cc
@@ -34,6 +34,12 @@
 using ::testing::InSequence;
 using ::testing::StrictMock;
 
+namespace {
+
+enum class TimeGranularity { kMicrosecond, kMillisecond };
+
+}  // namespace
+
 namespace media {
 
 typedef StreamParser::BufferQueue BufferQueue;
@@ -232,7 +238,9 @@
     EXPECT_EQ(expected, ss.str());
   }
 
-  void CheckExpectedRangesByTimestamp(const std::string& expected) {
+  void CheckExpectedRangesByTimestamp(
+      const std::string& expected,
+      TimeGranularity granularity = TimeGranularity::kMillisecond) {
     Ranges<base::TimeDelta> r = stream_->GetBufferedTime();
 
     std::stringstream ss;
@@ -243,8 +251,12 @@
       // internally tracked range end time, and that the "highest presentation
       // timestamp" for the stream matches the last range's highest pts.  See
       // https://crbug.com/718641.
-      int64_t start = r.start(i).InMilliseconds();
-      int64_t end = r.end(i).InMilliseconds();
+      int64_t start = r.start(i).InMicroseconds();
+      int64_t end = r.end(i).InMicroseconds();
+      if (granularity == TimeGranularity::kMillisecond) {
+        start /= base::Time::kMicrosecondsPerMillisecond;
+        end /= base::Time::kMicrosecondsPerMillisecond;
+      }
       ss << "[" << start << "," << end << ") ";
     }
     ss << "}";
@@ -332,7 +344,9 @@
     EXPECT_EQ(ending_position + 1, current_position);
   }
 
-  void CheckExpectedBuffers(const std::string& expected) {
+  void CheckExpectedBuffers(
+      const std::string& expected,
+      TimeGranularity granularity = TimeGranularity::kMillisecond) {
     std::vector<std::string> timestamps = base::SplitString(
         expected, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
     std::stringstream ss;
@@ -367,11 +381,17 @@
       if (status != SourceBufferStream::kSuccess)
         break;
 
-      ss << buffer->timestamp().InMilliseconds();
+      if (granularity == TimeGranularity::kMillisecond)
+        ss << buffer->timestamp().InMilliseconds();
+      else
+        ss << buffer->timestamp().InMicroseconds();
 
       if (buffer->GetDecodeTimestamp() !=
           DecodeTimestamp::FromPresentationTime(buffer->timestamp())) {
-        ss << "|" << buffer->GetDecodeTimestamp().InMilliseconds();
+        if (granularity == TimeGranularity::kMillisecond)
+          ss << "|" << buffer->GetDecodeTimestamp().InMilliseconds();
+        else
+          ss << "|" << buffer->GetDecodeTimestamp().InMicroseconds();
       }
 
       // Check duration if expected timestamp contains it.
@@ -516,23 +536,27 @@
   // coded strings of timestamps separated by spaces.
   //
   // Supported syntax (options must be in this order):
-  // pp[|dd][Dzz][E][P][K]
+  // pp[u][|dd[u]][Dzz][E][P][K]
   //
   // pp:
   // Generates a StreamParserBuffer with decode and presentation timestamp xx.
   // E.g., "0 1 2 3".
+  // pp is interpreted as milliseconds, unless suffixed with "u", in which case
+  // pp is interpreted as microseconds.
   //
   // pp|dd:
   // Generates a StreamParserBuffer with presentation timestamp pp and decode
-  // timestamp dd. E.g., "0|0 3|1 1|2 2|3".
+  // timestamp dd. E.g., "0|0 3|1 1|2 2|3". dd is interpreted as milliseconds,
+  // unless suffixed with "u", in which case dd is interpreted as microseconds.
   //
-  // Dzz
+  // Dzz[u]
   // Explicitly describe the duration of the buffer. zz specifies the duration
-  // in milliseconds. If the duration isn't specified with this syntax, the
-  // duration is derived using the timestamp delta between this buffer and the
-  // next buffer. If not specified, the final buffer will simply copy the
-  // duration of the previous buffer. If the queue only contains 1 buffer then
-  // the duration must be explicitly specified with this format.
+  // in milliseconds (or in microseconds if suffixed with "u"). If the duration
+  // isn't specified with this syntax, the duration is derived using the
+  // timestamp delta between this buffer and the next buffer. If not specified,
+  // the final buffer will simply copy the duration of the previous buffer. If
+  // the queue only contains 1 buffer then the duration must be explicitly
+  // specified with this format.
   // E.g. "0D10 10D20"
   //
   // E:
@@ -577,41 +601,63 @@
         timestamps[i] = timestamps[i].substr(0, timestamps[i].length() - 1);
       }
 
-      int duration_in_ms = -1;
+      int duration_in_us = -1;
       size_t duration_pos = timestamps[i].find('D');
       if (duration_pos != std::string::npos) {
+        bool is_duration_us = false;  // Default to millisecond interpretation.
+        if (base::EndsWith(timestamps[i], "u", base::CompareCase::SENSITIVE)) {
+          is_duration_us = true;
+          timestamps[i] = timestamps[i].substr(0, timestamps[i].length() - 1);
+        }
         CHECK(base::StringToInt(timestamps[i].substr(duration_pos + 1),
-                                &duration_in_ms));
+                                &duration_in_us));
+        if (!is_duration_us)
+          duration_in_us *= base::Time::kMicrosecondsPerMillisecond;
         timestamps[i] = timestamps[i].substr(0, duration_pos);
       }
 
-      std::vector<std::string> buffer_timestamps = base::SplitString(
+      std::vector<std::string> buffer_timestamp_strings = base::SplitString(
           timestamps[i], "|", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
 
-      if (buffer_timestamps.size() == 1)
-        buffer_timestamps.push_back(buffer_timestamps[0]);
+      if (buffer_timestamp_strings.size() == 1)
+        buffer_timestamp_strings.push_back(buffer_timestamp_strings[0]);
 
-      CHECK_EQ(2u, buffer_timestamps.size());
+      CHECK_EQ(2u, buffer_timestamp_strings.size());
 
-      int pts_in_ms = 0;
-      int dts_in_ms = 0;
-      CHECK(base::StringToInt(buffer_timestamps[0], &pts_in_ms));
-      CHECK(base::StringToInt(buffer_timestamps[1], &dts_in_ms));
+      std::vector<base::TimeDelta> buffer_timestamps;
+
+      // Parse PTS, then DTS into TimeDeltas.
+      for (size_t j = 0; j < 2; ++j) {
+        int us = 0;
+        bool is_us = false;  // Default to millisecond interpretation.
+
+        if (base::EndsWith(buffer_timestamp_strings[j], "u",
+                           base::CompareCase::SENSITIVE)) {
+          is_us = true;
+          buffer_timestamp_strings[j] = buffer_timestamp_strings[j].substr(
+              0, buffer_timestamp_strings[j].length() - 1);
+        }
+        CHECK(base::StringToInt(buffer_timestamp_strings[j], &us));
+        if (!is_us)
+          us *= base::Time::kMicrosecondsPerMillisecond;
+
+        buffer_timestamps.push_back(base::TimeDelta::FromMicroseconds(us));
+      }
 
       // Create buffer. Buffer type and track ID are meaningless to these tests.
       scoped_refptr<StreamParserBuffer> buffer =
           StreamParserBuffer::CopyFrom(&kDataA, kDataSize, is_keyframe,
                                        DemuxerStream::AUDIO, 0);
-      buffer->set_timestamp(base::TimeDelta::FromMilliseconds(pts_in_ms));
+      buffer->set_timestamp(buffer_timestamps[0]);
       buffer->set_is_duration_estimated(is_duration_estimated);
 
-      if (dts_in_ms != pts_in_ms) {
+      if (buffer_timestamps[1] != buffer_timestamps[0]) {
         buffer->SetDecodeTimestamp(
-            DecodeTimestamp::FromMilliseconds(dts_in_ms));
+            DecodeTimestamp::FromPresentationTime(buffer_timestamps[1]));
       }
 
-      if (duration_in_ms >= 0)
-        buffer->set_duration(base::TimeDelta::FromMilliseconds(duration_in_ms));
+      if (duration_in_us >= 0)
+        buffer->set_duration(base::TimeDelta::FromMicroseconds(duration_in_us));
 
       // Simulate preroll buffers by just generating another buffer and sticking
       // it as the preroll.
@@ -4030,6 +4076,50 @@
   CheckExpectedRangesByTimestamp("{ [120,180) }");
 }
 
+TEST_F(SourceBufferStreamTest,
+       OverlappingAppendRangeMembership_OneMicrosecond_Video) {
+  NewCodedFrameGroupAppend("10D20K");
+  CheckExpectedRangesByTimestamp("{ [10000,30000) }",
+                                 TimeGranularity::kMicrosecond);
+
+  // Append a buffer 1 microsecond earlier, with estimated duration.
+  NewCodedFrameGroupAppend("9999uD20EK");
+  CheckExpectedRangesByTimestamp("{ [9999,30000) }",
+                                 TimeGranularity::kMicrosecond);
+
+  // Append that same buffer again, but without any discontinuity signalled / no
+  // new coded frame group.
+  AppendBuffers("9999uD20EK");
+  CheckExpectedRangesByTimestamp("{ [9999,30000) }",
+                                 TimeGranularity::kMicrosecond);
+
+  Seek(0);
+  CheckExpectedBuffers("9999K 9999K 10000K", TimeGranularity::kMicrosecond);
+  CheckNoNextBuffer();
+}
+
+TEST_F(SourceBufferStreamTest,
+       OverlappingAppendRangeMembership_TwoMicroseconds_Video) {
+  NewCodedFrameGroupAppend("10D20K");
+  CheckExpectedRangesByTimestamp("{ [10000,30000) }",
+                                 TimeGranularity::kMicrosecond);
+
+  // Append an exactly abutting buffer 2us earlier.
+  NewCodedFrameGroupAppend("9998uD20EK");
+  CheckExpectedRangesByTimestamp("{ [9998,30000) }",
+                                 TimeGranularity::kMicrosecond);
+
+  // Append that same buffer again, but without any discontinuity signalled / no
+  // new coded frame group.
+  AppendBuffers("9998uD20EK");
+  CheckExpectedRangesByTimestamp("{ [9998,30000) }",
+                                 TimeGranularity::kMicrosecond);
+
+  Seek(0);
+  CheckExpectedBuffers("9998K 9998K 10000K", TimeGranularity::kMicrosecond);
+  CheckNoNextBuffer();
+}
+
 TEST_F(SourceBufferStreamTest, Text_Append_SingleRange) {
   SetTextStream();
   NewCodedFrameGroupAppend("0K 500K 1000K");
@@ -4987,6 +5077,30 @@
   }
 }
 
+TEST_F(SourceBufferStreamTest, SameTimestampEstimatedDurations_Video) {
+  // Start a coded frame group with a frame having a non-estimated duration.
+  NewCodedFrameGroupAppend("10D10K");
+
+  // In the same coded frame group, append a same-timestamp frame with estimated
+  // duration smaller than the first frame. (This can happen at least if there
+  // was an intervening init segment resetting the estimation logic.) This
+  // second frame need not be a keyframe. We use a non-keyframe here to
+  // differentiate the buffers in CheckExpectedBuffers(), below.
+  AppendBuffers("10D9E");
+
+  // The next append, which triggered https://crbug.com/761567, didn't need to
+  // be with same timestamp as the earlier ones; it just needs to be in the same
+  // buffered range.  Also, it doesn't need to be a keyframe, have an estimated
+  // duration, nor be in the same coded frame group to trigger that issue.
+  NewCodedFrameGroupAppend("11D10K");
+
+  Seek(0);
+  CheckExpectedRangesByTimestamp("{ [10,21) }");
+  CheckExpectedRangeEndTimes("{ <11,21> }");
+  CheckExpectedBuffers("10K 10 11K");
+  CheckNoNextBuffer();
+}
+
 TEST_F(SourceBufferStreamTest, RangeIsNextInPTS_Simple) {
   // Append a simple GOP where DTS==PTS, perform basic PTS continuity checks.
   NewCodedFrameGroupAppend("10D10K");
diff --git a/media/mojo/clients/mojo_cdm.cc b/media/mojo/clients/mojo_cdm.cc
index 4d932f2b..72579a4d 100644
--- a/media/mojo/clients/mojo_cdm.cc
+++ b/media/mojo/clients/mojo_cdm.cc
@@ -116,10 +116,8 @@
 
   pending_init_promise_ = std::move(promise);
 
-  // TODO(jrummell): Pass |security_origin| as a url.mojom.Origin.
-  // http://crbug.com/639438.
   remote_cdm_->Initialize(
-      key_system, security_origin.Serialize(), cdm_config,
+      key_system, security_origin, cdm_config,
       base::Bind(&MojoCdm::OnCdmInitialized, base::Unretained(this)));
 }
 
diff --git a/media/mojo/clients/mojo_cdm_unittest.cc b/media/mojo/clients/mojo_cdm_unittest.cc
index ed448378..5e7c976 100644
--- a/media/mojo/clients/mojo_cdm_unittest.cc
+++ b/media/mojo/clients/mojo_cdm_unittest.cc
@@ -135,6 +135,9 @@
     EXPECT_EQ(SUCCESS, expected_result);
     mojo_cdm_ = cdm;
     remote_cdm_ = cdm_factory_.GetCreatedCdm();
+    EXPECT_EQ(kClearKeyKeySystem, remote_cdm_->GetKeySystem());
+    EXPECT_EQ(kTestSecurityOrigin,
+              remote_cdm_->GetSecurityOrigin().Serialize());
   }
 
   void ForceConnectionError() {
diff --git a/media/mojo/clients/mojo_renderer_unittest.cc b/media/mojo/clients/mojo_renderer_unittest.cc
index f55ef4a..c685f71 100644
--- a/media/mojo/clients/mojo_renderer_unittest.cc
+++ b/media/mojo/clients/mojo_renderer_unittest.cc
@@ -31,6 +31,8 @@
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "url/origin.h"
 
 using ::testing::_;
 using ::testing::DoAll;
@@ -180,7 +182,8 @@
                                          &cdm_factory_),
         mojo::MakeRequest(&remote_cdm_));
     remote_cdm_->Initialize(
-        kClearKeyKeySystem, "https://www.test.com", CdmConfig(),
+        kClearKeyKeySystem, url::Origin(GURL("https://www.test.com")),
+        CdmConfig(),
         base::Bind(&MojoRendererTest::OnCdmCreated, base::Unretained(this)));
     base::RunLoop().RunUntilIdle();
   }
diff --git a/media/mojo/interfaces/content_decryption_module.mojom b/media/mojo/interfaces/content_decryption_module.mojom
index 3606062b..f72ea7e26 100644
--- a/media/mojo/interfaces/content_decryption_module.mojom
+++ b/media/mojo/interfaces/content_decryption_module.mojom
@@ -5,6 +5,7 @@
 module media.mojom;
 
 import "media/mojo/interfaces/decryptor.mojom";
+import "url/mojo/origin.mojom";
 import "url/mojo/url.mojom";
 
 // See media::EmeInitDataType.
@@ -69,7 +70,9 @@
   // will be zero. Upon success, |cdm_id| will be non-zero and will later be
   // used to locate the CDM at the remote side. |decryptor| is the remote
   // Decryptor.
-  Initialize(string key_system, string security_origin, CdmConfig cdm_config)
+  Initialize(string key_system,
+             url.mojom.Origin security_origin,
+             CdmConfig cdm_config)
       => (CdmPromiseResult result, int32 cdm_id, Decryptor? decryptor);
 
   // Provides a server certificate to be used to encrypt messages to the
diff --git a/media/mojo/services/media_service_unittest.cc b/media/mojo/services/media_service_unittest.cc
index 57a3725..790424b7 100644
--- a/media/mojo/services/media_service_unittest.cc
+++ b/media/mojo/services/media_service_unittest.cc
@@ -29,6 +29,8 @@
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "services/service_manager/public/cpp/service_test.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "url/gurl.h"
+#include "url/origin.h"
 
 using testing::Exactly;
 using testing::Invoke;
@@ -110,7 +112,8 @@
     EXPECT_CALL(*this, OnCdmInitializedInternal(expected_result, cdm_id))
         .Times(Exactly(1))
         .WillOnce(InvokeWithoutArgs(run_loop_.get(), &base::RunLoop::Quit));
-    cdm_->Initialize(key_system, kSecurityOrigin, CdmConfig(),
+    cdm_->Initialize(key_system, url::Origin(GURL(kSecurityOrigin)),
+                     CdmConfig(),
                      base::Bind(&MediaServiceTest::OnCdmInitialized,
                                 base::Unretained(this)));
   }
diff --git a/media/mojo/services/mojo_cdm_service.cc b/media/mojo/services/mojo_cdm_service.cc
index 3f525242..c2bb359 100644
--- a/media/mojo/services/mojo_cdm_service.cc
+++ b/media/mojo/services/mojo_cdm_service.cc
@@ -19,7 +19,6 @@
 #include "media/cdm/cdm_manager.h"
 #include "media/mojo/common/media_type_converters.h"
 #include "media/mojo/services/mojo_cdm_service_context.h"
-#include "url/gurl.h"
 #include "url/origin.h"
 
 namespace media {
@@ -60,7 +59,7 @@
 }
 
 void MojoCdmService::Initialize(const std::string& key_system,
-                                const std::string& security_origin,
+                                const url::Origin& security_origin,
                                 const CdmConfig& cdm_config,
                                 InitializeCallback callback) {
   DVLOG(1) << __func__ << ": " << key_system;
@@ -68,7 +67,7 @@
 
   auto weak_this = weak_factory_.GetWeakPtr();
   cdm_factory_->Create(
-      key_system, url::Origin(GURL(security_origin)), cdm_config,
+      key_system, security_origin, cdm_config,
       base::Bind(&MojoCdmService::OnSessionMessage, weak_this),
       base::Bind(&MojoCdmService::OnSessionClosed, weak_this),
       base::Bind(&MojoCdmService::OnSessionKeysChange, weak_this),
diff --git a/media/mojo/services/mojo_cdm_service.h b/media/mojo/services/mojo_cdm_service.h
index 9ae5a94..d0801bb 100644
--- a/media/mojo/services/mojo_cdm_service.h
+++ b/media/mojo/services/mojo_cdm_service.h
@@ -50,7 +50,7 @@
   // mojom::ContentDecryptionModule implementation.
   void SetClient(mojom::ContentDecryptionModuleClientPtr client) final;
   void Initialize(const std::string& key_system,
-                  const std::string& security_origin,
+                  const url::Origin& security_origin,
                   const CdmConfig& cdm_config,
                   InitializeCallback callback) final;
   void SetServerCertificate(const std::vector<uint8_t>& certificate_data,
diff --git a/media/muxers/BUILD.gn b/media/muxers/BUILD.gn
index 51b5a95..71e73ff 100644
--- a/media/muxers/BUILD.gn
+++ b/media/muxers/BUILD.gn
@@ -23,3 +23,22 @@
 
   configs += [ "//media:subcomponent_config" ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "webm_muxer_unittest.cc",
+  ]
+
+  deps = [
+    "//base/test:test_support",
+    "//media:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+
+  configs += [
+    # TODO(crbug.com/167187): Fix size_t to int truncations.
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
+}
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc
index bf289bf..c40601c 100644
--- a/mojo/edk/system/core.cc
+++ b/mojo/edk/system/core.cc
@@ -799,6 +799,10 @@
   create_options.capacity_num_bytes = options && options->capacity_num_bytes
                                           ? options->capacity_num_bytes
                                           : 64 * 1024;
+  if (!create_options.element_num_bytes || !create_options.capacity_num_bytes ||
+      create_options.capacity_num_bytes < create_options.element_num_bytes) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
 
   scoped_refptr<PlatformSharedBuffer> ring_buffer =
       GetNodeController()->CreateSharedBuffer(
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.cc b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
index ad32846..9a3762c 100644
--- a/mojo/edk/system/data_pipe_consumer_dispatcher.cc
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
@@ -372,6 +372,10 @@
   }
 
   const SerializedState* state = static_cast<const SerializedState*>(data);
+  if (!state->options.capacity_num_bytes || !state->options.element_num_bytes ||
+      state->options.capacity_num_bytes < state->options.element_num_bytes) {
+    return nullptr;
+  }
 
   NodeController* node_controller = internal::g_core->GetNodeController();
   ports::PortRef port;
diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.cc b/mojo/edk/system/data_pipe_producer_dispatcher.cc
index 0edfc586..97d25b2 100644
--- a/mojo/edk/system/data_pipe_producer_dispatcher.cc
+++ b/mojo/edk/system/data_pipe_producer_dispatcher.cc
@@ -335,6 +335,10 @@
   }
 
   const SerializedState* state = static_cast<const SerializedState*>(data);
+  if (!state->options.capacity_num_bytes || !state->options.element_num_bytes ||
+      state->options.capacity_num_bytes < state->options.element_num_bytes) {
+    return nullptr;
+  }
 
   NodeController* node_controller = internal::g_core->GetNodeController();
   ports::PortRef port;
diff --git a/mojo/public/c/system/data_pipe.h b/mojo/public/c/system/data_pipe.h
index aaeafcf2..62adbea1 100644
--- a/mojo/public/c/system/data_pipe.h
+++ b/mojo/public/c/system/data_pipe.h
@@ -129,8 +129,9 @@
 //
 // Returns:
 //   |MOJO_RESULT_OK| on success.
-//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
-//       |*options| is invalid).
+//   |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid, e.g.,
+//       |*options| is invalid, specified capacity or element size is zero, or
+//       the specified element size exceeds the specified capacity.
 //   |MOJO_RESULT_RESOURCE_EXHAUSTED| if a process/system/quota/etc. limit has
 //       been reached (e.g., if the requested capacity was too large, or if the
 //       maximum number of handles was exceeded).
diff --git a/net/base/file_stream_context.cc b/net/base/file_stream_context.cc
index b1bc8633..f7b0e15 100644
--- a/net/base/file_stream_context.cc
+++ b/net/base/file_stream_context.cc
@@ -9,7 +9,6 @@
 #include "base/debug/alias.h"
 #include "base/files/file_path.h"
 #include "base/location.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/task_runner.h"
 #include "base/task_runner_util.h"
 #include "base/threading/thread_restrictions.h"
@@ -235,10 +234,6 @@
 void FileStream::Context::OnAsyncCompleted(
     const Int64CompletionCallback& callback,
     const IOResult& result) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "477117 FileStream::Context::OnAsyncCompleted"));
   // Reset this before Run() as Run() may issue a new async operation. Also it
   // should be reset before Close() because it shouldn't run if any async
   // operation is in progress.
diff --git a/net/base/file_stream_context_posix.cc b/net/base/file_stream_context_posix.cc
index 67c772a0..80fdf80 100644
--- a/net/base/file_stream_context_posix.cc
+++ b/net/base/file_stream_context_posix.cc
@@ -14,7 +14,6 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/posix/eintr_wrapper.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/task_runner.h"
 #include "base/task_runner_util.h"
 #include "net/base/io_buffer.h"
@@ -94,10 +93,6 @@
 FileStream::Context::IOResult FileStream::Context::ReadFileImpl(
     scoped_refptr<IOBuffer> buf,
     int buf_len) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "477117 FileStream::Context::ReadFileImpl"));
   int res = file_.ReadAtCurrentPosNoBestEffort(buf->data(), buf_len);
   if (res == -1)
     return IOResult::FromOSError(errno);
diff --git a/net/base/network_delegate.cc b/net/base/network_delegate.cc
index 2b47025..b17827e 100644
--- a/net/base/network_delegate.cc
+++ b/net/base/network_delegate.cc
@@ -5,7 +5,6 @@
 #include "net/base/network_delegate.h"
 
 #include "base/logging.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/trace_event/trace_event.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
@@ -29,11 +28,6 @@
 
   // ClusterFuzz depends on the following VLOG. See: crbug.com/715656
   VLOG(1) << "NetworkDelegate::NotifyBeforeURLRequest: " << request->url();
-
-  // TODO(cbentzel): Remove ScopedTracker below once crbug.com/475753 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "475753 NetworkDelegate::OnBeforeURLRequest"));
   return OnBeforeURLRequest(request, callback, new_url);
 }
 
@@ -121,10 +115,6 @@
   TRACE_EVENT0(kNetTracingCategory, "NetworkDelegate::NotifyCompleted");
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(request);
-  // TODO(cbentzel): Remove ScopedTracker below once crbug.com/475753 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("475753 NetworkDelegate::OnCompleted"));
-
   OnCompleted(request, started, net_error);
 }
 
diff --git a/net/cert/x509_certificate.cc b/net/cert/x509_certificate.cc
index e60a697..746e21b7 100644
--- a/net/cert/x509_certificate.cc
+++ b/net/cert/x509_certificate.cc
@@ -19,7 +19,6 @@
 #include "base/memory/singleton.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/pickle.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
@@ -242,12 +241,6 @@
 scoped_refptr<X509Certificate> X509Certificate::CreateFromDERCertChain(
     const std::vector<base::StringPiece>& der_certs) {
   TRACE_EVENT0("io", "X509Certificate::CreateFromDERCertChain");
-
-  // TODO(cbentzel): Remove ScopedTracker below once crbug.com/424386 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "424386 X509Certificate::CreateFromDERCertChain"));
-
   if (der_certs.empty())
     return NULL;
 
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index bd9455ff..f0fee4be 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -56,7 +56,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
@@ -1051,12 +1050,6 @@
     std::vector<std::unique_ptr<CanonicalCookie>> cookies) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  // TODO(erikwright): Remove ScopedTracker below once crbug.com/457528 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "457528 CookieMonster::StoreLoadedCookies"));
-
   // Even if a key is expired, insert it so it can be garbage collected,
   // removed, and sync'd.
   CookieItVector cookies_with_control_chars;
diff --git a/net/disk_cache/blockfile/in_flight_io.cc b/net/disk_cache/blockfile/in_flight_io.cc
index 03dda643..952f389f 100644
--- a/net/disk_cache/blockfile/in_flight_io.cc
+++ b/net/disk_cache/blockfile/in_flight_io.cc
@@ -6,7 +6,6 @@
 
 #include "base/bind.h"
 #include "base/location.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/task_runner.h"
 #include "base/threading/thread_restrictions.h"
@@ -22,9 +21,6 @@
 
 // Runs on the primary thread.
 void BackgroundIO::OnIOSignalled() {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("477117 BackgroundIO::OnIOSignalled"));
   if (controller_)
     controller_->InvokeCallback(this, false);
 }
diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc
index 1bc1f29..55906dd 100644
--- a/net/dns/dns_transaction.cc
+++ b/net/dns/dns_transaction.cc
@@ -18,7 +18,6 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/rand_util.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
@@ -400,11 +399,6 @@
   }
 
   int DoConnectComplete(int rv) {
-    // TODO(rvargas): Remove ScopedTracker below once crbug.com/462784 is fixed.
-    tracked_objects::ScopedTracker tracking_profile(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "462784 DnsTCPAttempt::DoConnectComplete"));
-
     DCHECK_NE(ERR_IO_PENDING, rv);
     if (rv < 0)
       return rv;
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index 2584d8f1..cc0fffe 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -37,7 +37,6 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -2400,10 +2399,6 @@
 
 bool HostResolverImpl::IsGloballyReachable(const IPAddress& dest,
                                            const NetLogWithSource& net_log) {
-  // TODO(eroman): Remove ScopedTracker below once crbug.com/455942 is fixed.
-  tracked_objects::ScopedTracker tracking_profile_1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("455942 IsGloballyReachable"));
-
   std::unique_ptr<DatagramClientSocket> socket(
       ClientSocketFactory::GetDefaultFactory()->CreateDatagramClientSocket(
           DatagramSocket::DEFAULT_BIND, RandIntCallback(), net_log.net_log(),
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 2290f32..a111d104 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -16,7 +16,6 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -853,11 +852,6 @@
 }
 
 int HttpNetworkTransaction::DoCreateStream() {
-  // TODO(mmenke): Remove ScopedTracker below once crbug.com/424359 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "424359 HttpNetworkTransaction::DoCreateStream"));
-
   response_.network_accessed = true;
 
   next_state_ = STATE_CREATE_STREAM_COMPLETE;
@@ -1187,11 +1181,6 @@
 }
 
 int HttpNetworkTransaction::DoSendRequest() {
-  // TODO(mmenke): Remove ScopedTracker below once crbug.com/424359 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "424359 HttpNetworkTransaction::DoSendRequest"));
-
   send_start_time_ = base::TimeTicks::Now();
   next_state_ = STATE_SEND_REQUEST_COMPLETE;
 
diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc
index 80c600d9..0998318 100644
--- a/net/http/http_stream_factory_impl_job.cc
+++ b/net/http/http_stream_factory_impl_job.cc
@@ -15,7 +15,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
@@ -843,10 +842,6 @@
 }
 
 int HttpStreamFactoryImpl::Job::DoInitConnectionImpl() {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/462812 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "462812 HttpStreamFactoryImpl::Job::DoInitConnection"));
   DCHECK(!connection_->is_initialized());
 
   if (using_quic_ && !proxy_info_.is_quic() && !proxy_info_.is_direct()) {
@@ -1156,10 +1151,6 @@
 }
 
 int HttpStreamFactoryImpl::Job::DoCreateStream() {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/462811 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "462811 HttpStreamFactoryImpl::Job::DoCreateStream"));
   DCHECK(connection_->socket() || existing_spdy_session_.get() || using_quic_);
   DCHECK(!using_quic_);
 
diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc
index 7da5adaf..1d5dec49 100644
--- a/net/http/http_stream_parser.cc
+++ b/net/http/http_stream_parser.cc
@@ -10,7 +10,6 @@
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_util.h"
 #include "base/values.h"
 #include "net/base/io_buffer.h"
@@ -448,11 +447,6 @@
 }
 
 int HttpStreamParser::DoSendHeaders() {
-  // TODO(mmenke): Remove ScopedTracker below once crbug.com/424359 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "424359 HttpStreamParser::DoSendHeaders"));
-
   int bytes_remaining = request_headers_->BytesRemaining();
   DCHECK_GT(bytes_remaining, 0);
 
diff --git a/net/proxy/dhcp_proxy_script_fetcher_win.cc b/net/proxy/dhcp_proxy_script_fetcher_win.cc
index 8685084d..8c89124a 100644
--- a/net/proxy/dhcp_proxy_script_fetcher_win.cc
+++ b/net/proxy/dhcp_proxy_script_fetcher_win.cc
@@ -10,7 +10,6 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/memory/free_deleter.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "net/base/net_errors.h"
 #include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
@@ -72,11 +71,6 @@
 
 int DhcpProxyScriptFetcherWin::Fetch(base::string16* utf16_text,
                                      const CompletionCallback& callback) {
-  // TODO(joi): Remove ScopedTracker below once crbug.com/476182 is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "476182 DhcpProxyScriptFetcherWin::Fetch 1"));
-
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (state_ != STATE_START && state_ != STATE_DONE) {
     NOTREACHED();
@@ -91,10 +85,6 @@
   destination_string_ = utf16_text;
 
   last_query_ = ImplCreateAdapterQuery();
-  // TODO(joi): Remove ScopedTracker below once crbug.com/476182 is fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "476182 DhcpProxyScriptFetcherWin::Fetch 2"));
   GetTaskRunner()->PostTaskAndReply(
       FROM_HERE,
       base::Bind(
@@ -151,12 +141,6 @@
 
 void DhcpProxyScriptFetcherWin::OnGetCandidateAdapterNamesDone(
     scoped_refptr<AdapterQuery> query) {
-  // TODO(joi): Remove ScopedTracker below once crbug.com/476182 is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "476182 "
-          "DhcpProxyScriptFetcherWin::OnGetCandidateAdapterNamesDone 1"));
-
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // This can happen if this object is reused for multiple queries,
@@ -175,12 +159,6 @@
 
   state_ = STATE_NO_RESULTS;
 
-  // TODO(joi): Remove ScopedTracker below once crbug.com/476182 is fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "476182 "
-          "DhcpProxyScriptFetcherWin::OnGetCandidateAdapterNamesDone 2"));
-
   const std::set<std::string>& adapter_names = query->adapter_names();
 
   if (adapter_names.empty()) {
@@ -188,12 +166,6 @@
     return;
   }
 
-  // TODO(joi): Remove ScopedTracker below once crbug.com/476182 is fixed.
-  tracked_objects::ScopedTracker tracking_profile3(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "476182 "
-          "DhcpProxyScriptFetcherWin::OnGetCandidateAdapterNamesDone 3"));
-
   for (std::set<std::string>::const_iterator it = adapter_names.begin();
        it != adapter_names.end();
        ++it) {
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index c4e05f3..b7ec2a64 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -18,7 +18,6 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
@@ -1016,11 +1015,6 @@
     rv = SSL_do_handshake(ssl_.get());
   } else {
     if (g_first_run_completed.Get().Get()) {
-      // TODO(cbentzel): Remove ScopedTracker below once crbug.com/424386 is
-      // fixed.
-      tracked_objects::ScopedTracker tracking_profile(
-          FROM_HERE_WITH_EXPLICIT_FUNCTION("424386 SSL_do_handshake()"));
-
       rv = SSL_do_handshake(ssl_.get());
     } else {
       g_first_run_completed.Get().Set(true);
diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc
index f7c6f5b1..3df1e71 100644
--- a/net/socket/ssl_client_socket_pool.cc
+++ b/net/socket/ssl_client_socket_pool.cc
@@ -13,7 +13,6 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
 #include "net/base/host_port_pair.h"
@@ -304,10 +303,6 @@
 
 int SSLConnectJob::DoSSLConnect() {
   TRACE_EVENT0(kNetTracingCategory, "SSLConnectJob::DoSSLConnect");
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/462815 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("462815 SSLConnectJob::DoSSLConnect"));
-
   next_state_ = STATE_SSL_CONNECT_COMPLETE;
 
   // Reset the timeout to just the time allowed for the SSL handshake.
@@ -345,11 +340,6 @@
   // Version interference probes should not result in success.
   DCHECK(!version_interference_probe_ || result != OK);
 
-  // TODO(rvargas): Remove ScopedTracker below once crbug.com/462784 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "462784 SSLConnectJob::DoSSLConnectComplete"));
-
   connect_timing_.ssl_end = base::TimeTicks::Now();
 
   if (result != OK && !server_address_.address().empty()) {
diff --git a/net/socket/tcp_client_socket.cc b/net/socket/tcp_client_socket.cc
index df07d11..d6dce18 100644
--- a/net/socket/tcp_client_socket.cc
+++ b/net/socket/tcp_client_socket.cc
@@ -9,7 +9,6 @@
 #include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/time/time.h"
 #include "net/base/io_buffer.h"
 #include "net/base/ip_endpoint.h"
@@ -374,11 +373,6 @@
                                            int result) {
   if (result > 0)
     use_history_.set_was_used_to_convey_data();
-
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/462780 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "462780 TCPClientSocket::DidCompleteReadWrite"));
   callback.Run(result);
 }
 
diff --git a/net/socket/tcp_socket_win.cc b/net/socket/tcp_socket_win.cc
index fc85d800..289636e 100644
--- a/net/socket/tcp_socket_win.cc
+++ b/net/socket/tcp_socket_win.cc
@@ -14,7 +14,6 @@
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "net/base/address_list.h"
 #include "net/base/io_buffer.h"
 #include "net/base/ip_endpoint.h"
@@ -913,17 +912,9 @@
   int result;
 
   WSANETWORKEVENTS events;
-  int rv;
-  int os_error = 0;
-  {
-    // TODO(pkasting): Remove ScopedTracker below once crbug.com/462784 is
-    // fixed.
-    tracked_objects::ScopedTracker tracking_profile1(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "462784 TCPSocketWin::DidCompleteConnect -> WSAEnumNetworkEvents"));
-    rv = WSAEnumNetworkEvents(socket_, core_->read_overlapped_.hEvent, &events);
-    os_error = WSAGetLastError();
-  }
+  int rv =
+      WSAEnumNetworkEvents(socket_, core_->read_overlapped_.hEvent, &events);
+  int os_error = WSAGetLastError();
   if (rv == SOCKET_ERROR) {
     NOTREACHED();
     result = MapSystemError(os_error);
@@ -939,10 +930,6 @@
   DoConnectComplete(result);
   waiting_connect_ = false;
 
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/462784 is fixed.
-  tracked_objects::ScopedTracker tracking_profile4(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "462784 TCPSocketWin::DidCompleteConnect -> read_callback_"));
   DCHECK_NE(result, ERR_IO_PENDING);
   base::ResetAndReturn(&read_callback_).Run(result);
 }
@@ -997,11 +984,6 @@
   if (rv == SOCKET_ERROR) {
     rv = MapSystemError(os_error);
   } else if (network_events.lNetworkEvents) {
-    // TODO(pkasting): Remove ScopedTracker below once crbug.com/462778 is
-    // fixed.
-    tracked_objects::ScopedTracker tracking_profile2(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "462778 TCPSocketWin::DidSignalRead -> DoRead"));
     DCHECK_EQ(network_events.lNetworkEvents & ~(FD_READ | FD_CLOSE), 0);
     // If network_events.lNetworkEvents is FD_CLOSE and
     // network_events.iErrorCode[FD_CLOSE_BIT] is 0, it is a graceful
diff --git a/net/spdy/chromium/spdy_session.cc b/net/spdy/chromium/spdy_session.cc
index 87ec620..bb1c022 100644
--- a/net/spdy/chromium/spdy_session.cc
+++ b/net/spdy/chromium/spdy_session.cc
@@ -17,7 +17,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
@@ -2013,10 +2012,6 @@
       }
     }
 
-    // TODO(pkasting): Remove ScopedTracker below once crbug.com/457517 is
-    // fixed.
-    tracked_objects::ScopedTracker tracking_profile1(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION("457517 SpdySession::DoWrite1"));
     in_flight_write_ = producer->ProduceBuffer();
     if (!in_flight_write_) {
       NOTREACHED();
@@ -2033,9 +2028,6 @@
   // Explicitly store in a scoped_refptr<IOBuffer> to avoid problems
   // with Socket implementations that don't store their IOBuffer
   // argument in a scoped_refptr<IOBuffer> (see crbug.com/232345).
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/457517 is fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("457517 SpdySession::DoWrite2"));
   scoped_refptr<IOBuffer> write_io_buffer =
       in_flight_write_->GetIOBufferForRemainingData();
   return connection_->socket()->Write(
diff --git a/net/url_request/url_fetcher_core.cc b/net/url_request/url_fetcher_core.cc
index c6db196..0fe63d49 100644
--- a/net/url_request/url_fetcher_core.cc
+++ b/net/url_request/url_fetcher_core.cc
@@ -10,7 +10,6 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
@@ -933,12 +932,6 @@
 
 void URLFetcherCore::InformDelegateDownloadProgress() {
   DCHECK(network_task_runner_->BelongsToCurrentThread());
-
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455952 is fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455952 delegate_task_runner_->PostTask()"));
-
   delegate_task_runner_->PostTask(
       FROM_HERE,
       base::Bind(
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index a66d192b..f86be7d 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -13,7 +13,6 @@
 #include "base/lazy_instance.h"
 #include "base/memory/singleton.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/rand_util.h"
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -515,10 +514,6 @@
   if (!status_.is_success())
     return;
 
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("456327 URLRequest::Start"));
-
   // Some values can be NULL, but the job factory must not be.
   DCHECK(context_->job_factory());
 
@@ -534,10 +529,6 @@
   load_timing_info_.request_start = base::TimeTicks::Now();
 
   if (network_delegate_) {
-    // TODO(mmenke): Remove ScopedTracker below once crbug.com/456327 is fixed.
-    tracked_objects::ScopedTracker tracking_profile25(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION("456327 URLRequest::Start 2.5"));
-
     OnCallToDelegate();
     int error = network_delegate_->NotifyBeforeURLRequest(
         this, before_request_callback_, &delegate_redirect_url_);
@@ -548,10 +539,6 @@
     return;
   }
 
-  // TODO(mmenke): Remove ScopedTracker below once crbug.com/456327 is fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("456327 URLRequest::Start 2"));
-
   StartJob(URLRequestJobManager::GetInstance()->CreateJob(
       this, network_delegate_));
 }
@@ -629,10 +616,6 @@
 }
 
 void URLRequest::StartJob(URLRequestJob* job) {
-  // TODO(mmenke): Remove ScopedTracker below once crbug.com/456327 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("456327 URLRequest::StartJob"));
-
   DCHECK(!is_pending_);
   DCHECK(!job_.get());
 
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 6b115af..a61f5905 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -17,7 +17,6 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/rand_util.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_util.h"
@@ -297,10 +296,6 @@
 }
 
 void URLRequestHttpJob::Start() {
-  // TODO(mmenke): Remove ScopedTracker below once crbug.com/456327 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("456327 URLRequestHttpJob::Start"));
-
   DCHECK(!transaction_.get());
 
   // URLRequest::SetReferrer ensures that we do not send username and password
@@ -472,11 +467,6 @@
 }
 
 void URLRequestHttpJob::StartTransaction() {
-  // TODO(mmenke): Remove ScopedTracker below once crbug.com/456327 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456327 URLRequestHttpJob::StartTransaction"));
-
   if (network_delegate()) {
     OnCallToDelegate();
     // The NetworkDelegate must watch for OnRequestDestroyed and not modify
@@ -506,11 +496,6 @@
 }
 
 void URLRequestHttpJob::MaybeStartTransactionInternal(int result) {
-  // TODO(mmenke): Remove ScopedTracker below once crbug.com/456327 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456327 URLRequestHttpJob::MaybeStartTransactionInternal"));
-
   OnCallToDelegateComplete();
   if (result == OK) {
     StartTransactionInternal();
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index 9112c44..95ccf3b2 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -13,7 +13,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/power_monitor/power_monitor.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
@@ -569,11 +568,6 @@
   DCHECK(request_->status().is_io_pending());
   DCHECK_NE(ERR_IO_PENDING, result);
 
-  // TODO(cbentzel): Remove ScopedTracker below once crbug.com/475755 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "475755 URLRequestJob::RawReadCompleted"));
-
   // The headers should be complete before reads complete
   DCHECK(has_handled_response_);
 
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 937bae4..e96c601 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -10387,8 +10387,9 @@
 }
 
 static bool SystemSupportsOCSP() {
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
   // TODO(jnd): http://crbug.com/117478 - EV verification is not yet supported.
+  // TODO(crbug.com/762380): Enable on Fuchsia once it's implemented.
   return false;
 #else
   return true;
@@ -11002,7 +11003,8 @@
                         testing::ValuesIn(kOCSPVerifyData));
 
 static bool SystemSupportsAIA() {
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
+  // TODO(crbug.com/762380): Enable on Fuchsia once it's implemented.
   return false;
 #else
   return true;
@@ -11312,7 +11314,8 @@
 }
 
 TEST_F(HTTPSCRLSetTest, CRLSetRevoked) {
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
+  // TODO(crbug.com/762380): Enable on Fuchsia once it's implemented.
   LOG(WARNING) << "Skipping test because system doesn't support CRLSets";
   return;
 #endif
diff --git a/remoting/host/chromeos/aura_desktop_capturer.cc b/remoting/host/chromeos/aura_desktop_capturer.cc
index 8942354..297f9f9f 100644
--- a/remoting/host/chromeos/aura_desktop_capturer.cc
+++ b/remoting/host/chromeos/aura_desktop_capturer.cc
@@ -41,8 +41,10 @@
 
 void AuraDesktopCapturer::CaptureFrame() {
   std::unique_ptr<viz::CopyOutputRequest> request =
-      viz::CopyOutputRequest::CreateBitmapRequest(base::BindOnce(
-          &AuraDesktopCapturer::OnFrameCaptured, weak_factory_.GetWeakPtr()));
+      std::make_unique<viz::CopyOutputRequest>(
+          viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+          base::BindOnce(&AuraDesktopCapturer::OnFrameCaptured,
+                         weak_factory_.GetWeakPtr()));
 
   gfx::Rect window_rect(desktop_window_->bounds().size());
 
@@ -58,12 +60,8 @@
     return;
   }
 
-  DCHECK(result->HasBitmap());
-
-  std::unique_ptr<SkBitmap> bitmap = result->TakeBitmap();
-
-  std::unique_ptr<webrtc::DesktopFrame> frame(
-      SkiaBitmapDesktopFrame::Create(std::move(bitmap)));
+  std::unique_ptr<webrtc::DesktopFrame> frame(SkiaBitmapDesktopFrame::Create(
+      std::make_unique<SkBitmap>(result->AsSkBitmap())));
 
   // |VideoFramePump| will not encode the frame if |updated_region| is empty.
   const webrtc::DesktopRect& rect = webrtc::DesktopRect::MakeWH(
diff --git a/remoting/host/chromeos/aura_desktop_capturer_unittest.cc b/remoting/host/chromeos/aura_desktop_capturer_unittest.cc
index 76f0a7d..1cc973eb 100644
--- a/remoting/host/chromeos/aura_desktop_capturer_unittest.cc
+++ b/remoting/host/chromeos/aura_desktop_capturer_unittest.cc
@@ -60,13 +60,13 @@
 
  protected:
   void SimulateFrameCapture() {
-    std::unique_ptr<SkBitmap> bitmap(new SkBitmap());
+    SkBitmap bitmap;
     const SkImageInfo& info =
         SkImageInfo::Make(3, 4, kBGRA_8888_SkColorType, kPremul_SkAlphaType);
-    bitmap->installPixels(info, const_cast<unsigned char*>(frame_data), 12);
+    bitmap.installPixels(info, const_cast<unsigned char*>(frame_data), 12);
 
-    capturer_->OnFrameCaptured(
-        viz::CopyOutputResult::CreateBitmapResult(std::move(bitmap)));
+    capturer_->OnFrameCaptured(std::make_unique<viz::CopyOutputSkBitmapResult>(
+        gfx::Rect(0, 0, bitmap.width(), bitmap.height()), bitmap));
   }
 
   std::unique_ptr<AuraDesktopCapturer> capturer_;
diff --git a/remoting/host/file_proxy_wrapper_linux.cc b/remoting/host/file_proxy_wrapper_linux.cc
index 5793f32..bc91dfa 100644
--- a/remoting/host/file_proxy_wrapper_linux.cc
+++ b/remoting/host/file_proxy_wrapper_linux.cc
@@ -160,6 +160,12 @@
     LOG(ERROR) << "Creating the temp file failed with error: " << error;
     CancelWithError(FileErrorToResponseError(error));
   } else {
+    // Now that the temp file has been created successfully, we could lock it
+    // using base::File::Lock(), but this would not prevent the file from being
+    // deleted. When the file is deleted, WriteChunk() will continue to write to
+    // the file as if the file was still there, and an error will occur when
+    // calling base::Move() to move the temp file. Chrome exhibits the same
+    // behavior with its downloads.
     temp_file_created_ = true;
     // Chunks to write may have been queued while we were creating the file,
     // start writing them now if there were any.
diff --git a/remoting/host/setup/daemon_controller_delegate_linux.cc b/remoting/host/setup/daemon_controller_delegate_linux.cc
index addc529..59251efd 100644
--- a/remoting/host/setup/daemon_controller_delegate_linux.cc
+++ b/remoting/host/setup/daemon_controller_delegate_linux.cc
@@ -120,6 +120,7 @@
   }
   base::CommandLine command_line(script_path);
   command_line.AppendArg("--get-status");
+  command_line.AppendArg("--config=" + GetConfigPath().value());
 
   std::string status;
   int exit_code = 0;
diff --git a/services/device/public/interfaces/BUILD.gn b/services/device/public/interfaces/BUILD.gn
index e2493ff..4a722c6 100644
--- a/services/device/public/interfaces/BUILD.gn
+++ b/services/device/public/interfaces/BUILD.gn
@@ -23,9 +23,6 @@
   public_deps = [
     ":constants",
   ]
-
-  # TODO(crbug.com/699569): Convert to use the new JS bindings.
-  js_bindings_mode = "both"
 }
 
 mojom("generic_sensor") {
@@ -43,7 +40,4 @@
   sources = [
     "constants.mojom",
   ]
-
-  # TODO(crbug.com/699569): Convert to use the new JS bindings.
-  js_bindings_mode = "both"
 }
diff --git a/services/ui/ws/event_dispatcher.cc b/services/ui/ws/event_dispatcher.cc
index 3b3cac7..5577e74 100644
--- a/services/ui/ws/event_dispatcher.cc
+++ b/services/ui/ws/event_dispatcher.cc
@@ -57,8 +57,7 @@
       event_targeter_(base::MakeUnique<EventTargeter>(this)),
       mouse_button_down_(false),
       mouse_cursor_source_window_(nullptr),
-      mouse_cursor_in_non_client_area_(false),
-      next_mouse_button_flags_(0) {}
+      mouse_cursor_in_non_client_area_(false) {}
 
 EventDispatcher::~EventDispatcher() {
   SetMouseCursorSourceWindow(nullptr);
@@ -86,22 +85,6 @@
   mouse_button_down_ = false;
 }
 
-std::unique_ptr<ui::Event> EventDispatcher::GenerateMouseMoveFor(
-    const gfx::Point& display_location) const {
-  // Create a synthetic mouse event and dispatch it directly to ourselves so we
-  // update internal caches and possibly send exit events in case the window
-  // the cursor is over changes.
-  // TODO: This uses state here that may be out of sync at the time the event is
-  // actually processed. Fix.
-  std::unique_ptr<PointerEvent> event = base::MakeUnique<PointerEvent>(
-      ui::ET_POINTER_MOVED, display_location, display_location,
-      next_mouse_button_flags_, 0 /* changed_button_flags */,
-      ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE,
-                         ui::MouseEvent::kMousePointerId),
-      base::TimeTicks::Now());
-  return event;
-}
-
 ui::CursorData EventDispatcher::GetCurrentMouseCursor() const {
   if (drag_controller_)
     return drag_controller_->current_cursor();
@@ -503,12 +486,6 @@
       mouse_button_down_ = true;
     else if (is_pointer_going_up)
       mouse_button_down_ = false;
-
-    if (event.type() == ui::ET_POINTER_UP) {
-      next_mouse_button_flags_ = event.flags() & ~event.changed_button_flags();
-    } else {
-      next_mouse_button_flags_ = event.flags();
-    }
   }
 
   if (drag_controller_) {
diff --git a/services/ui/ws/event_dispatcher.h b/services/ui/ws/event_dispatcher.h
index a85b60e00..2e203fb 100644
--- a/services/ui/ws/event_dispatcher.h
+++ b/services/ui/ws/event_dispatcher.h
@@ -74,11 +74,6 @@
   // any events to the delegate.
   void Reset();
 
-  // Generates a mouse move event corresponding to the mouse moving to a
-  // particular location.
-  std::unique_ptr<ui::Event> GenerateMouseMoveFor(
-      const gfx::Point& display_location) const;
-
   const gfx::Point& mouse_pointer_last_location() const {
     return mouse_pointer_last_location_;
   }
@@ -360,12 +355,6 @@
   ServerWindow* mouse_cursor_source_window_;
   bool mouse_cursor_in_non_client_area_;
 
-  // We calculate out the button flags for any synthetic mouse events we need
-  // to create during SetMousePointerDisplayLocation(). We don't try to set
-  // this on a per-PointerTarget because modified PointerTargets aren't always
-  // committed back into |pointer_targets_|.
-  int next_mouse_button_flags_;
-
   // The location of the mouse pointer in display coordinates. This can be
   // outside the bounds of |mouse_cursor_source_window_|, which can capture the
   // cursor.
diff --git a/services/ui/ws/event_dispatcher_unittest.cc b/services/ui/ws/event_dispatcher_unittest.cc
index 17b1613..aa0fa1ec 100644
--- a/services/ui/ws/event_dispatcher_unittest.cc
+++ b/services/ui/ws/event_dispatcher_unittest.cc
@@ -285,9 +285,6 @@
                      const ui::Event& event,
                      int64_t display_id,
                      EventDispatcher::AcceleratorMatchPhase match_phase);
-  void SetMousePointerDisplayLocation(EventDispatcher* dispatcher,
-                                      const gfx::Point& display_location,
-                                      int64_t display_id);
   void RunMouseEventTests(EventDispatcher* dispatcher,
                           TestEventDispatcherDelegate* dispatcher_delegate,
                           MouseEventTest* tests,
@@ -338,16 +335,6 @@
   RunTasks();
 }
 
-void EventDispatcherTest::SetMousePointerDisplayLocation(
-    EventDispatcher* dispatcher,
-    const gfx::Point& display_location,
-    int64_t display_id) {
-  std::unique_ptr<ui::Event> event =
-      dispatcher->GenerateMouseMoveFor(display_location);
-  DispatchEvent(dispatcher, *event, display_id,
-                EventDispatcher::AcceleratorMatchPhase::ANY);
-}
-
 void EventDispatcherTest::RunMouseEventTests(
     EventDispatcher* dispatcher,
     TestEventDispatcherDelegate* dispatcher_delegate,
@@ -1099,90 +1086,6 @@
   EXPECT_EQ(child2.get(), details->window);
 }
 
-TEST_P(EventDispatcherTest, SetMousePointerDisplayLocationWithFlags) {
-  std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
-
-  root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
-  child->SetBounds(gfx::Rect(10, 10, 20, 20));
-
-  TestEventDispatcherDelegate* event_dispatcher_delegate =
-      test_event_dispatcher_delegate();
-  EventDispatcher* dispatcher = event_dispatcher();
-
-  // Move to the child window with a mouse event which holds down the left
-  // mouse button.
-  const ui::PointerEvent move1(
-      ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(11, 11), gfx::Point(11, 11),
-                     base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, 0));
-  DispatchEvent(event_dispatcher(), move1, 0,
-                EventDispatcher::AcceleratorMatchPhase::ANY);
-  std::unique_ptr<DispatchedEventDetails> details =
-      event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
-  EXPECT_EQ(child.get(), details->window);
-
-  // Manually set the mouse pointer.
-  SetMousePointerDisplayLocation(dispatcher, gfx::Point(1, 1), 0);
-
-  // The first dispatched event is a leave to the child. It should also
-  // maintain the button state from the first event above.
-  details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
-  EXPECT_EQ(child.get(), details->window);
-  EXPECT_EQ(ui::ET_POINTER_EXITED, details->event->type());
-  EXPECT_TRUE((details->event->flags() & EF_LEFT_MOUSE_BUTTON) != 0);
-
-  // The second dispatched event is an enter to the root window.
-  details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
-  EXPECT_EQ(root_window(), details->window);
-  EXPECT_EQ(ui::ET_POINTER_MOVED, details->event->type());
-  EXPECT_TRUE((details->event->flags() & EF_LEFT_MOUSE_BUTTON) != 0);
-}
-
-TEST_P(EventDispatcherTest, SetMousePointerDisplayLocationWithoutFlags) {
-  std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
-
-  root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
-  child->SetBounds(gfx::Rect(10, 10, 20, 20));
-
-  TestEventDispatcherDelegate* event_dispatcher_delegate =
-      test_event_dispatcher_delegate();
-  EventDispatcher* dispatcher = event_dispatcher();
-
-  // Move to the child window with a mouse event which holds down the left
-  // mouse button.
-  const ui::PointerEvent move1(ui::MouseEvent(
-      ui::ET_MOUSE_PRESSED, gfx::Point(11, 11), gfx::Point(11, 11),
-      base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, 0));
-  DispatchEvent(event_dispatcher(), move1, 0,
-                EventDispatcher::AcceleratorMatchPhase::ANY);
-  std::unique_ptr<DispatchedEventDetails> details =
-      event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
-  EXPECT_EQ(child.get(), details->window);
-
-  // Release the mouse button.
-  const ui::PointerEvent release1(ui::MouseEvent(
-      ui::ET_MOUSE_RELEASED, gfx::Point(11, 11), gfx::Point(11, 11),
-      base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
-  DispatchEvent(event_dispatcher(), release1, 0,
-                EventDispatcher::AcceleratorMatchPhase::ANY);
-  details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
-  EXPECT_EQ(child.get(), details->window);
-
-  // Manually set the mouse pointer.
-  SetMousePointerDisplayLocation(dispatcher, gfx::Point(1, 1), 0);
-
-  // The first dispatched event is a leave to the child.
-  details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
-  EXPECT_EQ(child.get(), details->window);
-  EXPECT_EQ(ui::ET_POINTER_EXITED, details->event->type());
-  EXPECT_EQ(details->event->flags(), 0);
-
-  // The second dispatched event is an enter to the root window.
-  details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
-  EXPECT_EQ(root_window(), details->window);
-  EXPECT_EQ(ui::ET_POINTER_MOVED, details->event->type());
-  EXPECT_EQ(details->event->flags(), 0);
-}
-
 TEST_P(EventDispatcherTest, DestroyWindowWhileGettingEvents) {
   std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
 
@@ -2261,7 +2164,12 @@
   root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
   c1->SetBounds(gfx::Rect(10, 10, 20, 20));
 
-  SetMousePointerDisplayLocation(event_dispatcher(), gfx::Point(15, 15), 0);
+  const ui::PointerEvent move_event(
+      ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(15, 15), gfx::Point(15, 15),
+                     base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, 0));
+  DispatchEvent(event_dispatcher(), move_event, 0,
+                EventDispatcher::AcceleratorMatchPhase::ANY);
+
   EXPECT_EQ(c1.get(), event_dispatcher()->mouse_cursor_source_window());
   c1.reset();
   EXPECT_EQ(nullptr, event_dispatcher()->mouse_cursor_source_window());
diff --git a/services/ui/ws/ids.h b/services/ui/ws/ids.h
index f8910f88f..1b44be8 100644
--- a/services/ui/ws/ids.h
+++ b/services/ui/ws/ids.h
@@ -23,6 +23,9 @@
 // id.
 const ClientSpecificId kInvalidClientId = 0;
 
+// A client id used to indicate WindowServer.
+const ClientSpecificId kWindowServerClientId = 1;
+
 // Every window has a unique id associated with it (WindowId). The id is a
 // combination of the id assigned to the client (the high order bits) and
 // a unique id for the window. Each client (WindowTree) refers to the window
@@ -91,7 +94,7 @@
 
 // Returns a root window id with a given index offset.
 inline WindowId RootWindowId(uint16_t index) {
-  return WindowId(kInvalidClientId, 2 + index);
+  return WindowId(kWindowServerClientId, 2 + index);
 }
 
 struct ClientWindowIdHash {
diff --git a/services/ui/ws/test_utils.cc b/services/ui/ws/test_utils.cc
index ee559545..f3f0941 100644
--- a/services/ui/ws/test_utils.cc
+++ b/services/ui/ws/test_utils.cc
@@ -576,7 +576,7 @@
 ServerWindow* WindowEventTargetingHelper::CreatePrimaryTree(
     const gfx::Rect& root_window_bounds,
     const gfx::Rect& window_bounds) {
-  WindowTree* wm_tree = window_server()->GetTreeWithId(1);
+  WindowTree* wm_tree = window_server()->GetTreeWithId(kWindowManagerClientId);
   const ClientWindowId embed_window_id(wm_tree->id(),
                                        next_primary_tree_window_id_++);
   EXPECT_TRUE(wm_tree->NewWindow(embed_window_id, ServerWindow::Properties()));
diff --git a/services/ui/ws/test_utils.h b/services/ui/ws/test_utils.h
index 32035bb..0ed610f 100644
--- a/services/ui/ws/test_utils.h
+++ b/services/ui/ws/test_utils.h
@@ -43,6 +43,10 @@
 namespace ws {
 namespace test {
 
+const ClientSpecificId kWindowManagerClientId = kWindowServerClientId + 1;
+const std::string kWindowManagerClientIdString =
+    std::to_string(kWindowManagerClientId);
+
 // Collection of utilities useful in creating mus tests.
 
 // Test ScreenManager instance that allows adding/modifying/removing displays.
diff --git a/services/ui/ws/window_manager_client_unittest.cc b/services/ui/ws/window_manager_client_unittest.cc
index 3d60c07..aadd8893 100644
--- a/services/ui/ws/window_manager_client_unittest.cc
+++ b/services/ui/ws/window_manager_client_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "services/ui/common/util.h"
+#include "services/ui/ws/test_utils.h"
 #include "services/ui/ws/window_server_test_base.h"
 #include "ui/aura/client/transient_window_client.h"
 #include "ui/aura/env.h"
@@ -507,10 +508,12 @@
   aura::Window* window12 = NewVisibleWindow(embed_root, embedded);
   ASSERT_TRUE(WaitForTreeSizeToMatch(window1, 3u));
 
-  // |embedded|'s WindowTree has an id_ of 2, so window11's client_id part
-  // should be 2 in the WindowTree for window_manager(). Similar for window12.
-  Id window11_in_wm = 2 << 16 | LoWord(server_id(window11));
-  Id window12_in_wm = 2 << 16 | LoWord(server_id(window12));
+  // |embedded|'s WindowTree has an id_ of embedded_client_id, so window11's
+  // client_id part should be embedded_client_id in the WindowTree for
+  // window_manager(). Similar for window12.
+  ClientSpecificId embedded_client_id = test::kWindowManagerClientId + 1;
+  Id window11_in_wm = embedded_client_id << 16 | LoWord(server_id(window11));
+  Id window12_in_wm = embedded_client_id << 16 | LoWord(server_id(window12));
 
   {
     window11->parent()->StackChildAtTop(window11);
diff --git a/services/ui/ws/window_manager_state.cc b/services/ui/ws/window_manager_state.cc
index ef52cd66..633bfa9 100644
--- a/services/ui/ws/window_manager_state.cc
+++ b/services/ui/ws/window_manager_state.cc
@@ -199,11 +199,8 @@
     return;
   }
 
+  // MoveCursorTo() implicitly generates a mouse event.
   display->platform_display()->MoveCursorTo(display_pixels);
-
-  std::unique_ptr<ui::Event> event =
-      event_dispatcher_.GenerateMouseMoveFor(display_pixels);
-  ProcessEvent(*event, display_id);
 }
 
 void WindowManagerState::SetKeyEventsThatDontHideCursor(
@@ -310,9 +307,16 @@
                                   int64_t display_id) {
   SetAllRootWindowsVisible(true);
   event_dispatcher_.Reset();
-  std::unique_ptr<ui::Event> event =
-      event_dispatcher_.GenerateMouseMoveFor(mouse_location_on_display);
-  ProcessEvent(*event, display_id);
+
+  // Fake a mouse event to update cursor and ensure mouse location in client
+  // is up to date.
+  const PointerEvent move_event(
+      ET_POINTER_MOVED, mouse_location_on_display, mouse_location_on_display,
+      EF_NONE, EF_NONE,
+      PointerDetails(EventPointerType::POINTER_TYPE_MOUSE,
+                     MouseEvent::kMousePointerId),
+      base::TimeTicks::Now());
+  ProcessEvent(move_event, display_id);
 }
 
 void WindowManagerState::Deactivate() {
diff --git a/services/ui/ws/window_manager_state_unittest.cc b/services/ui/ws/window_manager_state_unittest.cc
index 9ee583d..0a9b4cf 100644
--- a/services/ui/ws/window_manager_state_unittest.cc
+++ b/services/ui/ws/window_manager_state_unittest.cc
@@ -57,7 +57,8 @@
   // This is the tree associated with the WindowManagerState. That is, this is
   // the WindowTree of the WindowManager.
   WindowTree* tree() {
-    return window_event_targeting_helper_.window_server()->GetTreeWithId(1);
+    return window_event_targeting_helper_.window_server()->GetTreeWithId(
+        kWindowManagerClientId);
   }
   // This is *not* the tree associated with the WindowManagerState, use tree()
   // if you need the window manager tree.
@@ -212,8 +213,9 @@
   WindowTree* target_tree = window_tree();
   TestChangeTracker* tracker = window_tree_client()->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
-  EXPECT_EQ("InputEvent window=1,1 event_action=7",
-            ChangesToDescription1(*tracker->changes())[0]);
+  EXPECT_EQ(
+      "InputEvent window=" + kWindowManagerClientIdString + ",1 event_action=7",
+      ChangesToDescription1(*tracker->changes())[0]);
 
   WindowTreeTestApi(target_tree).AckOldestEvent();
   EXPECT_FALSE(window_manager()->on_accelerator_called());
@@ -231,8 +233,9 @@
   DispatchInputEventToWindow(target, display->GetId(), key, accelerator.get());
   TestChangeTracker* tracker = window_tree_client()->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
-  EXPECT_EQ("InputEvent window=1,1 event_action=7",
-            ChangesToDescription1(*tracker->changes())[0]);
+  EXPECT_EQ(
+      "InputEvent window=" + kWindowManagerClientIdString + ",1 event_action=7",
+      ChangesToDescription1(*tracker->changes())[0]);
 
   WindowTreeTestApi(window_tree()).AckOldestEvent();
   EXPECT_TRUE(window_manager()->on_accelerator_called());
@@ -382,8 +385,9 @@
   DispatchInputEventToWindow(target, display->GetId(), key, accelerator.get());
   TestChangeTracker* tracker = window_tree_client()->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
-  EXPECT_EQ("InputEvent window=1,1 event_action=7",
-            ChangesToDescription1(*tracker->changes())[0]);
+  EXPECT_EQ(
+      "InputEvent window=" + kWindowManagerClientIdString + ",1 event_action=7",
+      ChangesToDescription1(*tracker->changes())[0]);
 
   EXPECT_TRUE(WindowManagerStateTestApi(window_manager_state())
                   .AckInFlightEvent(mojom::EventResult::HANDLED));
@@ -402,8 +406,9 @@
   DispatchInputEventToWindow(target, display->GetId(), key, accelerator.get());
   TestChangeTracker* tracker = window_tree_client()->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
-  EXPECT_EQ("InputEvent window=1,1 event_action=7",
-            ChangesToDescription1(*tracker->changes())[0]);
+  EXPECT_EQ(
+      "InputEvent window=" + kWindowManagerClientIdString + ",1 event_action=7",
+      ChangesToDescription1(*tracker->changes())[0]);
 
   accelerator.reset();
   EXPECT_TRUE(WindowManagerStateTestApi(window_manager_state())
@@ -423,8 +428,9 @@
   DispatchInputEventToWindow(target, display->GetId(), key, accelerator.get());
   TestChangeTracker* tracker = window_tree_client()->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
-  EXPECT_EQ("InputEvent window=1,1 event_action=7",
-            ChangesToDescription1(*tracker->changes())[0]);
+  EXPECT_EQ(
+      "InputEvent window=" + kWindowManagerClientIdString + ",1 event_action=7",
+      ChangesToDescription1(*tracker->changes())[0]);
 
   tracker->changes()->clear();
   ui::KeyEvent key2(ui::ET_KEY_PRESSED, ui::VKEY_Y, ui::EF_CONTROL_DOWN);
@@ -440,8 +446,9 @@
 
   WindowTreeTestApi(window_tree()).AckOldestEvent();
   ASSERT_EQ(1u, tracker->changes()->size());
-  EXPECT_EQ("InputEvent window=1,1 event_action=7",
-            ChangesToDescription1(*tracker->changes())[0]);
+  EXPECT_EQ(
+      "InputEvent window=" + kWindowManagerClientIdString + ",1 event_action=7",
+      ChangesToDescription1(*tracker->changes())[0]);
   EXPECT_TRUE(window_manager()->on_accelerator_called());
   EXPECT_EQ(accelerator->id(), window_manager()->on_accelerator_id());
 }
@@ -457,8 +464,9 @@
   DispatchInputEventToWindow(target, display->GetId(), key, accelerator.get());
   TestChangeTracker* tracker = window_tree_client()->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
-  EXPECT_EQ("InputEvent window=1,1 event_action=7",
-            ChangesToDescription1(*tracker->changes())[0]);
+  EXPECT_EQ(
+      "InputEvent window=" + kWindowManagerClientIdString + ",1 event_action=7",
+      ChangesToDescription1(*tracker->changes())[0]);
 
   window_manager_state()->OnWillDestroyTree(tree());
   EXPECT_FALSE(window_manager()->on_accelerator_called());
@@ -506,8 +514,9 @@
                        EF_LEFT_MOUSE_BUTTON);
   DispatchInputEventToWindow(target, display->GetId(), press, nullptr);
   ASSERT_EQ(1u, tracker->changes()->size());
-  EXPECT_EQ("InputEvent window=1,1 event_action=1",
-            ChangesToDescription1(*tracker->changes())[0]);
+  EXPECT_EQ(
+      "InputEvent window=" + kWindowManagerClientIdString + ",1 event_action=1",
+      ChangesToDescription1(*tracker->changes())[0]);
   tracker->changes()->clear();
   // The above is not setting TreeAwaitingInputAck.
 
@@ -533,8 +542,9 @@
                              accelerator.get());
   TestChangeTracker* tracker = window_tree_client()->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
-  EXPECT_EQ("InputEvent window=1,1 event_action=7",
-            ChangesToDescription1(*tracker->changes())[0]);
+  EXPECT_EQ(
+      "InputEvent window=" + kWindowManagerClientIdString + ",1 event_action=7",
+      ChangesToDescription1(*tracker->changes())[0]);
 
   OnEventAckTimeout(window()->id().client_id);
   EXPECT_TRUE(window_manager()->on_accelerator_called());
@@ -655,8 +665,9 @@
                              accelerator.get());
   TestChangeTracker* tracker = window_tree_client()->tracker();
   ASSERT_EQ(1u, tracker->changes()->size());
-  EXPECT_EQ("InputEvent window=1,1 event_action=7",
-            ChangesToDescription1(*tracker->changes())[0]);
+  EXPECT_EQ(
+      "InputEvent window=" + kWindowManagerClientIdString + ",1 event_action=7",
+      ChangesToDescription1(*tracker->changes())[0]);
   tracker->changes()->clear();
   WindowTreeTestApi(window_tree()).AckLastEvent(mojom::EventResult::HANDLED);
   EXPECT_FALSE(window_manager()->on_accelerator_called());
@@ -668,8 +679,9 @@
   DispatchInputEventToWindow(target, display->GetId(), non_accelerator_key,
                              nullptr);
   ASSERT_EQ(1u, tracker->changes()->size());
-  EXPECT_EQ("InputEvent window=1,1 event_action=7",
-            ChangesToDescription1(*tracker->changes())[0]);
+  EXPECT_EQ(
+      "InputEvent window=" + kWindowManagerClientIdString + ",1 event_action=7",
+      ChangesToDescription1(*tracker->changes())[0]);
   WindowTreeTestApi(window_tree()).AckLastEvent(mojom::EventResult::UNHANDLED);
   EXPECT_FALSE(window_manager()->on_accelerator_called());
 }
diff --git a/services/ui/ws/window_server.cc b/services/ui/ws/window_server.cc
index 4e5ec19..fccabe9 100644
--- a/services/ui/ws/window_server.cc
+++ b/services/ui/ws/window_server.cc
@@ -61,7 +61,7 @@
 
 WindowServer::WindowServer(WindowServerDelegate* delegate)
     : delegate_(delegate),
-      next_client_id_(1),
+      next_client_id_(kWindowServerClientId + 1),
       display_manager_(new DisplayManager(this, &user_id_tracker_)),
       current_operation_(nullptr),
       in_destructor_(false),
@@ -228,8 +228,8 @@
 }
 
 ServerWindow* WindowServer::GetWindow(const WindowId& id) {
-  // kInvalidClientId is used for Display and WindowManager nodes.
-  if (id.client_id == kInvalidClientId) {
+  // kWindowServerClientId is used for Display and WindowManager nodes.
+  if (id.client_id == kWindowServerClientId) {
     for (Display* display : display_manager_->displays()) {
       ServerWindow* window = display->GetRootWithId(id);
       if (window)
diff --git a/services/ui/ws/window_tree_client_unittest.cc b/services/ui/ws/window_tree_client_unittest.cc
index 334b98d1..72d50d6 100644
--- a/services/ui/ws/window_tree_client_unittest.cc
+++ b/services/ui/ws/window_tree_client_unittest.cc
@@ -605,6 +605,7 @@
 
   int client_id_1() const { return client_id_1_; }
   int client_id_2() const { return client_id_2_; }
+  int client_id_3() const { return client_id_3_; }
 
   void EstablishSecondClientWithRoot(Id root_id) {
     ASSERT_TRUE(wt_client2_.get() == nullptr);
@@ -727,8 +728,9 @@
 
  private:
   std::unique_ptr<WindowTreeClientFactory> client_factory_;
-  int client_id_1_ = 1;
-  int client_id_2_ = 2;
+  int client_id_1_ = kWindowServerClientId + 1;
+  int client_id_2_ = client_id_1_ + 1;
+  int client_id_3_ = client_id_2_ + 1;
   Id root_window_id_;
   service_manager::BinderRegistry registry_;
 
@@ -858,10 +860,13 @@
     // NOTE: we expect a match of WindowParentToString(window_2_2, window_1_1),
     // but the ids are in the id space of client2, which is not the same as
     // the id space of wt1().
-    EXPECT_EQ("window=2,1 parent=0,1", windows[1].ToString());
+    EXPECT_EQ("window=" + std::to_string(client_id_2()) + ",1 parent=0,1",
+              windows[1].ToString());
     // Same thing here, we really want to test for
     // WindowParentToString(window_3_3, window_2_2).
-    EXPECT_EQ("window=3,1 parent=2,1", windows[2].ToString());
+    EXPECT_EQ("window=" + std::to_string(client_id_3()) +
+                  ",1 parent=" + std::to_string(client_id_2()) + ",1",
+              windows[2].ToString());
   }
 }
 
@@ -890,7 +895,7 @@
 
   // Client 2 shouldn't be able to remove window 3.
   ASSERT_FALSE(wt_client2()->RemoveWindowFromParent(
-      BuildWindowId(3, LoWord(window_3_1))));
+      BuildWindowId(client_id_3(), LoWord(window_3_1))));
 }
 
 // Verifies client gets a valid id.
@@ -975,7 +980,7 @@
 TEST_F(WindowTreeClientTest, WindowHierarchyChangedWindows) {
   // Create the embed point now so that the ids line up.
   Id window_1_1 = wt_client1()->NewWindow(1);
-  // 1,2->1,11.
+  // client_id_1(),2->client_id_1(),11.
   Id window_1_2 = wt_client1()->NewWindow(2);
   ASSERT_TRUE(window_1_2);
   ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_2, true));
@@ -993,15 +998,16 @@
   // window_1_1 has a client_id part of client_id_1 in wt2.
   Id window11_in_wt2 = BuildWindowId(client_id_1(), LoWord(window_1_1));
 
-  // 1,1->1,2->1,11
+  // client_id_1(),1->client_id_1(),2->client_id_1(),11
   {
-    // Client 2 should not get anything (1,2 is from another client).
+    // Client 2 should not get anything (client_id_1(),2 is from another
+    // client).
     ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_2));
     ASSERT_TRUE(wt_client2()->WaitForAllMessages());
     EXPECT_TRUE(changes2()->empty());
   }
 
-  // 0,1->1,1->1,2->1,11.
+  // 0,1->client_id_1(),1->client_id_1(),2->client_id_1(),11.
   {
     // Client 2 is now connected to the root, so it should have gotten a drawn
     // notification.
@@ -1012,7 +1018,7 @@
               SingleChangeToDescription(*changes2()));
   }
 
-  // 1,1->1,2->1,11.
+  // client_id_1(),1->client_id_1(),2->client_id_1(),11.
   {
     // Client 2 is no longer connected to the root, should get drawn state
     // changed.
@@ -1024,7 +1030,7 @@
               SingleChangeToDescription(*changes2()));
   }
 
-  // 1,1->1,2->1,11->1,111.
+  // client_id_1(),1->client_id_1(),2->client_id_1(),11->client_id_1(),111.
   Id window_1_111 = wt_client1()->NewWindow(111);
   ASSERT_TRUE(window_1_111);
   ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_111, true));
@@ -1035,7 +1041,7 @@
     EXPECT_TRUE(changes2()->empty());
   }
 
-  // 0,1->1,1->1,2->1,11->1,111
+  // 0,1->client_id_1(),1->client_id_1(),2->client_id_1(),11->client_id_1(),111
   {
     changes2()->clear();
     ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
@@ -1074,10 +1080,11 @@
     ASSERT_TRUE(wt_client2()->RemoveWindowFromParent(window_2_11));
 
     wt_client1_->WaitForChangeCount(1);
-    // 2,1 should be IdToString(window_2_11), but window_2_11 is in the id
-    // space of client2, not client1.
-    EXPECT_EQ("HierarchyChanged window=2,1 old_parent=" +
-                  IdToString(window11_in_wt1) + " new_parent=null",
+    // client_id_2(),1 should be IdToString(window_2_11), but window_2_11 is in
+    // the id space of client2, not client1.
+    EXPECT_EQ("HierarchyChanged window=" + std::to_string(client_id_2()) +
+                  ",1 old_parent=" + IdToString(window11_in_wt1) +
+                  " new_parent=null",
               SingleChangeToDescription(*changes1()));
   }
 
@@ -1089,11 +1096,12 @@
     EXPECT_EQ("HierarchyChanged window=" + IdToString(window22_in_wt1) +
                   " old_parent=null new_parent=" + IdToString(window11_in_wt1),
               SingleChangeToDescription(*changes1()));
-    // "window=2,3 parent=2,2]" should be,
+    // "window=client_id_2(),3 parent=client_id_2(),2]" should be,
     // WindowParentToString(window_2_21, window_2_2), but isn't because of
     // differing id spaces.
     EXPECT_EQ("[" + WindowParentToString(window22_in_wt1, window11_in_wt1) +
-                  "],[window=2,3 parent=2,2]",
+                  "],[window=" + std::to_string(client_id_2()) +
+                  ",3 parent=" + std::to_string(client_id_2()) + ",2]",
               ChangeWindowDescription(*changes1()));
   }
 }
@@ -1104,9 +1112,9 @@
   Id window_2_1 = wt_client2()->NewWindow(1);
   Id window_2_2 = wt_client2()->NewWindow(2);
   Id window_2_3 = wt_client2()->NewWindow(3);
-  Id window_1_4 = wt_client1()->NewWindow(4);  // Peer to 1,1
-  Id window_1_5 = wt_client1()->NewWindow(5);  // Peer to 1,1
-  Id window_2_6 = wt_client2()->NewWindow(6);  // Child of 1,2.
+  Id window_1_4 = wt_client1()->NewWindow(4);  // Peer to client_id_1(),1
+  Id window_1_5 = wt_client1()->NewWindow(5);  // Peer to client_id_1(),1
+  Id window_2_6 = wt_client2()->NewWindow(6);  // Child of client_id_1(),2.
   Id window_2_7 = wt_client2()->NewWindow(7);  // Unparented.
   Id window_2_8 = wt_client2()->NewWindow(8);  // Unparented.
   ASSERT_TRUE(window_2_1);
@@ -1351,9 +1359,9 @@
               windows[4].ToString());
   }
 
-  // Verifies GetWindowTree() on the window 1,1 from wt2(). wt2() sees 1,1 as
-  // 1,1
-  // is wt2()'s root and wt2() sees all the windows it created.
+  // Verifies GetWindowTree() on the window client_id_1(),1 from wt2(). wt2()
+  // sees client_id_1(),1 as client_id_1(),1 is wt2()'s root and wt2() sees all
+  // the windows it created.
   {
     std::vector<TestWindow> windows;
     GetWindowTree(wt2(), window_1_1, &windows);
@@ -1516,7 +1524,7 @@
               ChangesToDescription1(*changes2())[1]);
   }
 
-  // Client 2 has no root. Verify it can't see window 1,1 anymore.
+  // Client 2 has no root. Verify it can't see window client_id_1(),1 anymore.
   {
     std::vector<TestWindow> windows;
     GetWindowTree(wt2(), window_1_1, &windows);
@@ -1542,7 +1550,7 @@
 
   // wt1 created window_1_1 but not window_3_1.
   Id window11_in_wt1 = LoWord(window_1_1);
-  Id window31_in_wt1 = BuildWindowId(3, LoWord(window_3_1));
+  Id window31_in_wt1 = BuildWindowId(client_id_3(), LoWord(window_3_1));
 
   // Client 1 should have been told about the add (it owns the window).
   {
@@ -1552,7 +1560,7 @@
               SingleChangeToDescription(*changes1()));
   }
 
-  // Embed 1,1 again.
+  // Embed client_id_1(),1 again.
   {
     changes3()->clear();
 
@@ -1571,15 +1579,15 @@
               ChangesToDescription1(*changes3())[1]);
   }
 
-  // wt3() has no root. Verify it can't see window 1,1 anymore.
+  // wt3() has no root. Verify it can't see window client_id_1(),1 anymore.
   {
     std::vector<TestWindow> windows;
     GetWindowTree(wt3(), window_1_1, &windows);
     EXPECT_TRUE(windows.empty());
   }
 
-  // Verify 3,1 is no longer parented to 1,1. We have to do this from 1,1 as
-  // wt3() can no longer see 1,1.
+  // Verify client_id_3(),1 is no longer parented to client_id_1(),1. We have to
+  // do this from client_id_1(),1 as wt3() can no longer see client_id_1(),1.
   {
     std::vector<TestWindow> windows;
     GetWindowTree(wt1(), window_1_1, &windows);
@@ -1588,7 +1596,7 @@
               windows[0].ToString());
   }
 
-  // Verify wt3() can still see the window it created 3,1.
+  // Verify wt3() can still see the window it created client_id_3(),1.
   {
     std::vector<TestWindow> windows;
     GetWindowTree(wt3(), window_3_1, &windows);
@@ -1689,7 +1697,8 @@
 
 // Assertions for SetWindowVisibility sending notifications.
 TEST_F(WindowTreeClientTest, SetWindowVisibilityNotifications) {
-  // Create 1,1 and 1,2. 1,2 is made a child of 1,1 and 1,1 a child of the root.
+  // Create client_id_1(),1 and client_id_1(),2. client_id_1(),2 is made a child
+  // of client_id_1(),1 and client_id_1(),1 a child of the root.
   Id window_1_1 = wt_client1()->NewWindow(1);
   ASSERT_TRUE(window_1_1);
   ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true));
@@ -1702,10 +1711,10 @@
   ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
   ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_2));
 
-  // Establish the second client at 1,2.
+  // Establish the second client at client_id_1(),2.
   ASSERT_NO_FATAL_FAILURE(EstablishSecondClientWithRoot(window_1_2));
 
-  // Add 2,3 as a child of 1,2.
+  // Add client_id_2(),3 as a child of client_id_1(),2.
   Id window_2_1 = wt_client2()->NewWindow(1);
   ASSERT_TRUE(window_2_1);
   ASSERT_TRUE(wt_client2()->SetWindowVisibility(window_2_1, true));
@@ -1715,7 +1724,7 @@
   ASSERT_TRUE(wt_client1()->WaitForAllMessages());
 
   changes2()->clear();
-  // Hide 1,2 from client 1. Client 2 should see this.
+  // Hide client_id_1(),2 from client 1. Client 2 should see this.
   ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_2, false));
   {
     wt_client2_->WaitForChangeCount(1);
@@ -1725,7 +1734,7 @@
   }
 
   changes1()->clear();
-  // Show 1,2 from client 2, client 1 should be notified.
+  // Show client_id_1(),2 from client 2, client 1 should be notified.
   ASSERT_TRUE(wt_client2()->SetWindowVisibility(window12_in_wt2, true));
   {
     wt_client1_->WaitForChangeCount(1);
@@ -1735,7 +1744,7 @@
   }
 
   changes2()->clear();
-  // Hide 1,1, client 2 should be told the draw state changed.
+  // Hide client_id_1(),1, client 2 should be told the draw state changed.
   ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, false));
   {
     wt_client2_->WaitForChangeCount(1);
@@ -1745,7 +1754,7 @@
   }
 
   changes2()->clear();
-  // Show 1,1 from client 1. Client 2 should see this.
+  // Show client_id_1(),1 from client 1. Client 2 should see this.
   ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true));
   {
     wt_client2_->WaitForChangeCount(1);
@@ -1754,7 +1763,7 @@
               SingleChangeToDescription(*changes2()));
   }
 
-  // Change visibility of 2,3, client 1 should see this.
+  // Change visibility of client_id_2(),3, client 1 should see this.
   changes1()->clear();
   ASSERT_TRUE(wt_client2()->SetWindowVisibility(window_2_1, false));
   {
@@ -1766,7 +1775,8 @@
   }
 
   changes2()->clear();
-  // Remove 1,1 from the root, client 2 should see drawn state changed.
+  // Remove client_id_1(),1 from the root, client 2 should see drawn state
+  // changed.
   ASSERT_TRUE(wt_client1()->RemoveWindowFromParent(window_1_1));
   {
     wt_client2_->WaitForChangeCount(1);
@@ -1776,7 +1786,8 @@
   }
 
   changes2()->clear();
-  // Add 1,1 back to the root, client 2 should see drawn state changed.
+  // Add client_id_1(),1 back to the root, client 2 should see drawn state
+  // changed.
   ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
   {
     wt_client2_->WaitForChangeCount(1);
@@ -1788,7 +1799,8 @@
 
 // Assertions for SetWindowVisibility sending notifications.
 TEST_F(WindowTreeClientTest, SetWindowVisibilityNotifications2) {
-  // Create 1,1 and 1,2. 1,2 is made a child of 1,1 and 1,1 a child of the root.
+  // Create client_id_1(),1 and client_id_1(),2. client_id_1(),2 is made a child
+  // of client_id_1(),1 and client_id_1(),1 a child of the root.
   Id window_1_1 = wt_client1()->NewWindow(1);
   ASSERT_TRUE(window_1_1);
   ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true));
@@ -1797,14 +1809,14 @@
   ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
   ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_2));
 
-  // Establish the second client at 1,2.
+  // Establish the second client at client_id_1(),2.
   ASSERT_NO_FATAL_FAILURE(EstablishSecondClientWithRoot(window_1_2));
   // window_1_2 has a client_id part of client_id_1 in wt2.
   Id window12_in_wt2 = BuildWindowId(client_id_1(), LoWord(window_1_2));
   EXPECT_EQ("OnEmbed drawn=true", SingleChangeToDescription2(*changes2()));
   changes2()->clear();
 
-  // Show 1,2 from client 1. Client 2 should see this.
+  // Show client_id_1(),2 from client 1. Client 2 should see this.
   ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_2, true));
   {
     wt_client2_->WaitForChangeCount(1);
@@ -1816,7 +1828,8 @@
 
 // Assertions for SetWindowVisibility sending notifications.
 TEST_F(WindowTreeClientTest, SetWindowVisibilityNotifications3) {
-  // Create 1,1 and 1,2. 1,2 is made a child of 1,1 and 1,1 a child of the root.
+  // Create client_id_1(),1 and client_id_1(),2. client_id_1(),2 is made a child
+  // of client_id_1(),1 and client_id_1(),1 a child of the root.
   Id window_1_1 = wt_client1()->NewWindow(1);
   ASSERT_TRUE(window_1_1);
   Id window_1_2 = wt_client1()->NewWindow(2);
@@ -1824,7 +1837,7 @@
   ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_1));
   ASSERT_TRUE(wt_client1()->AddWindow(window_1_1, window_1_2));
 
-  // Establish the second client at 1,2.
+  // Establish the second client at client_id_1(),2.
   // TODO(fsamuel): Currently the FrameSinkId maps directly to the server's
   // window ID. This is likely bad from a security perspective and should be
   // fixed.
@@ -1834,7 +1847,8 @@
   EXPECT_EQ("OnEmbed drawn=false", SingleChangeToDescription2(*changes2()));
   changes2()->clear();
 
-  // Show 1,1, drawn should be true for 1,2 (as that is all the child sees).
+  // Show client_id_1(),1, drawn should be true for client_id_1(),2 (as that is
+  // all the child sees).
   ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_1, true));
   {
     wt_client2_->WaitForChangeCount(1);
@@ -1844,7 +1858,7 @@
   }
   changes2()->clear();
 
-  // Show 1,2, visible should be true.
+  // Show client_id_1(),2, visible should be true.
   ASSERT_TRUE(wt_client1()->SetWindowVisibility(window_1_2, true));
   {
     wt_client2_->WaitForChangeCount(1);
@@ -2151,16 +2165,16 @@
   ASSERT_TRUE(window_1_100);
   ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_100));
 
-  // Establish the second client at 1,100.
+  // Establish the second client at client_id_1(),100.
   ASSERT_NO_FATAL_FAILURE(EstablishSecondClientWithRoot(window_1_100));
 
-  // 1,100 is the id in the wt_client1's id space. The new client should see
-  // 2,1 (the server id).
+  // client_id_1(),100 is the id in the wt_client1's id space. The new client
+  // should see client_id_2(),1 (the server id).
   const Id window_1_100_in_ws2 = BuildWindowId(client_id_1(), 1);
   EXPECT_EQ(window_1_100_in_ws2, wt_client2()->root_window_id());
 
-  // The first window created in the second client gets a server id of 2,1
-  // regardless of the id the client uses.
+  // The first window created in the second client gets a server id of
+  // client_id_2(),1 regardless of the id the client uses.
   const Id window_2_101 = wt_client2()->NewWindow(101);
   ASSERT_TRUE(wt_client2()->AddWindow(window_1_100_in_ws2, window_2_101));
   const Id window_2_101_in_ws1 = BuildWindowId(client_id_2(), 1);
@@ -2247,12 +2261,12 @@
   ASSERT_TRUE(window_1_100);
   ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_100));
 
-  // Establish the second client at 1,100.
+  // Establish the second client at client_id_1(),100.
   ASSERT_NO_FATAL_FAILURE(EstablishSecondClientWithRoot(window_1_100));
   changes2()->clear();
 
-  // 1,100 is the id in the wt_client1's id space. The new client should see
-  // 2,1 (the server id).
+  // client_id_1(),100 is the id in the wt_client1's id space. The new client
+  // should see client_id_2(),1 (the server id).
   const Id window_1_100_in_ws2 = BuildWindowId(client_id_1(), 1);
   EXPECT_EQ(window_1_100_in_ws2, wt_client2()->root_window_id());
 
@@ -2284,8 +2298,8 @@
             changes1()->back().surface_id.frame_sink_id().client_id());
   changes1()->clear();
 
-  // The first window created in the second client gets a server id of 2,1
-  // regardless of the id the client uses.
+  // The first window created in the second client gets a server id of
+  // client_id_2(),1 regardless of the id the client uses.
   const Id window_2_101 = wt_client2()->NewWindow(101);
   ASSERT_TRUE(wt_client2()->AddWindow(window_1_100_in_ws2, window_2_101));
   const Id window_2_101_in_ws2 = BuildWindowId(client_id_2(), 1);
@@ -2328,7 +2342,7 @@
   ASSERT_TRUE(window_1_100);
   ASSERT_TRUE(wt_client1()->AddWindow(root_window_id(), window_1_100));
 
-  // Establish the second client at 1,100.
+  // Establish the second client at client_id_1(),100.
   ASSERT_NO_FATAL_FAILURE(EstablishSecondClientWithRoot(window_1_100));
   const Id window_2_1 = wt_client2()->NewWindow(1000);
   const Id window_2_2 = wt_client2()->NewWindow(2000);
@@ -2362,8 +2376,8 @@
   // Establish the second client at |window1|.
   ASSERT_NO_FATAL_FAILURE(EstablishSecondClientWithRoot(window1));
 
-  // The first window created in the second client gets a server id of 2,1
-  // regardless of the id the client uses.
+  // The first window created in the second client gets a server id of
+  // client_id_2(),1 regardless of the id the client uses.
   const Id window1_in_client2 = BuildWindowId(client_id_1(), 1);
   const Id window2 = wt_client2()->NewWindow(11);
   ASSERT_TRUE(wt_client2()->AddWindow(window1_in_client2, window2));
diff --git a/services/ui/ws/window_tree_unittest.cc b/services/ui/ws/window_tree_unittest.cc
index f9388c0..609e2f8 100644
--- a/services/ui/ws/window_tree_unittest.cc
+++ b/services/ui/ws/window_tree_unittest.cc
@@ -46,6 +46,8 @@
 namespace {
 
 const UserId kTestUserId1 = "2";
+const std::string kNextWindowClientIdString =
+    std::to_string(kWindowManagerClientId + 1);
 
 std::string WindowIdToString(const WindowId& id) {
   return base::StringPrintf("%d,%d", id.client_id, id.window_id);
@@ -161,7 +163,8 @@
     return window_event_targeting_helper_.window_server();
   }
   WindowTree* wm_tree() {
-    return window_event_targeting_helper_.window_server()->GetTreeWithId(1);
+    return window_event_targeting_helper_.window_server()->GetTreeWithId(
+        kWindowManagerClientId);
   }
   WindowTree* last_tree() {
     return window_event_targeting_helper_.last_binding()
@@ -453,9 +456,8 @@
   ui::KeyEvent key_pressed(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE);
   DispatchEventAndAckImmediately(key_pressed);
   EXPECT_EQ(0u, other_binding->client()->tracker()->changes()->size());
-  // clients that created this window is receiving the event, so client_id part
-  // would be reset to 0 before sending back to clients.
-  EXPECT_EQ("InputEvent window=0,3 event_action=7",
+  EXPECT_EQ("InputEvent window=" + std::to_string(kWindowServerClientId) +
+                ",3 event_action=7",
             SingleChangeToDescription(*wm_client()->tracker()->changes()));
 
   WindowTreeTestApi(wm_tree()).StartPointerWatcher(false);
@@ -467,9 +469,8 @@
 TEST_F(WindowTreeTest, KeyEventSentToWindowManagerWhenNothingFocused) {
   ui::KeyEvent key_pressed(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE);
   DispatchEventAndAckImmediately(key_pressed);
-  // clients that created this window is receiving the event, so client_id part
-  // would be reset to 0 before sending back to clients.
-  EXPECT_EQ("InputEvent window=0,3 event_action=7",
+  EXPECT_EQ("InputEvent window=" + std::to_string(kWindowServerClientId) +
+                ",3 event_action=7",
             SingleChangeToDescription(*wm_client()->tracker()->changes()));
 }
 
@@ -642,9 +643,8 @@
   wm_client()->tracker()->changes()->clear();
   DispatchEventWithoutAck(CreateMouseMoveEvent(21, 22));
   ASSERT_EQ(1u, wm_client()->tracker()->changes()->size());
-  // clients that created this window is receiving the event, so client_id part
-  // would be reset to 0 before sending back to clients.
-  EXPECT_EQ("InputEvent window=0,3 event_action=17",
+  EXPECT_EQ("InputEvent window=" + std::to_string(kWindowServerClientId) +
+                ",3 event_action=17",
             ChangesToDescription1(*wm_client()->tracker()->changes())[0]);
   wm_client()->tracker()->changes()->clear();
 
@@ -655,7 +655,8 @@
   // Ack the first event. That should trigger the dispatch of the second event.
   AckPreviousEvent();
   ASSERT_EQ(1u, wm_client()->tracker()->changes()->size());
-  EXPECT_EQ("InputEvent window=0,3 event_action=17",
+  EXPECT_EQ("InputEvent window=" + std::to_string(kWindowServerClientId) +
+                ",3 event_action=17",
             ChangesToDescription1(*wm_client()->tracker()->changes())[0]);
 }
 
@@ -1478,7 +1479,8 @@
   embed_client->tracker()->changes()->clear();
   EXPECT_TRUE(embed_tree->SetCapture(embed_child_window_id));
   ASSERT_TRUE(!wm_client()->tracker()->changes()->empty());
-  EXPECT_EQ("OnCaptureChanged new_window=2,1 old_window=null",
+  EXPECT_EQ("OnCaptureChanged new_window=" + kNextWindowClientIdString +
+                ",1 old_window=null",
             ChangesToDescription1(*wm_client()->tracker()->changes())[0]);
   EXPECT_TRUE(embed_client->tracker()->changes()->empty());
 
@@ -1486,7 +1488,8 @@
   wm_client()->tracker()->changes()->clear();
   EXPECT_TRUE(embed_tree->SetCapture(FirstRootId(embed_tree)));
   ASSERT_TRUE(!wm_client()->tracker()->changes()->empty());
-  EXPECT_EQ("OnCaptureChanged new_window=1,1 old_window=2,1",
+  EXPECT_EQ("OnCaptureChanged new_window=" + kWindowManagerClientIdString +
+                ",1 old_window=" + kNextWindowClientIdString + ",1",
             ChangesToDescription1(*wm_client()->tracker()->changes())[0]);
   EXPECT_TRUE(embed_client->tracker()->changes()->empty());
   wm_client()->tracker()->changes()->clear();
@@ -1496,7 +1499,8 @@
       ClientWindowIdForWindow(owning_tree, FirstRoot(embed_tree))));
   EXPECT_TRUE(wm_client()->tracker()->changes()->empty());
   ASSERT_TRUE(!embed_client->tracker()->changes()->empty());
-  EXPECT_EQ("OnCaptureChanged new_window=null old_window=1,1",
+  EXPECT_EQ("OnCaptureChanged new_window=null old_window=" +
+                kWindowManagerClientIdString + ",1",
             ChangesToDescription1(*embed_client->tracker()->changes())[0]);
 }
 
@@ -1568,8 +1572,6 @@
   // Have the window manager move the cursor within the embed window.
   static_cast<mojom::WindowManagerClient*>(wm_tree())
       ->WmMoveCursorToDisplayLocation(gfx::Point(21, 21), -1);
-
-  EXPECT_EQ(ui::CursorType::kIBeam, cursor_type());
 }
 
 TEST_F(WindowTreeTest, TestWindowManagerConfineCursor) {
diff --git a/services/viz/public/cpp/compositing/copy_output_request_struct_traits.cc b/services/viz/public/cpp/compositing/copy_output_request_struct_traits.cc
index a416893..db17619 100644
--- a/services/viz/public/cpp/compositing/copy_output_request_struct_traits.cc
+++ b/services/viz/public/cpp/compositing/copy_output_request_struct_traits.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "cc/ipc/copy_output_result_struct_traits.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 
 namespace {
@@ -17,15 +18,18 @@
 class CopyOutputResultSenderImpl : public viz::mojom::CopyOutputResultSender {
  public:
   CopyOutputResultSenderImpl(
+      viz::CopyOutputRequest::ResultFormat result_format,
       viz::CopyOutputRequest::CopyOutputRequestCallback result_callback)
-      : result_callback_(std::move(result_callback)) {
+      : result_format_(result_format),
+        result_callback_(std::move(result_callback)) {
     DCHECK(result_callback_);
   }
 
   ~CopyOutputResultSenderImpl() override {
     if (result_callback_) {
       std::move(result_callback_)
-          .Run(viz::CopyOutputResult::CreateEmptyResult());
+          .Run(std::make_unique<viz::CopyOutputResult>(result_format_,
+                                                       gfx::Rect()));
     }
   }
 
@@ -37,6 +41,7 @@
   }
 
  private:
+  const viz::CopyOutputRequest::ResultFormat result_format_;
   viz::CopyOutputRequest::CopyOutputRequestCallback result_callback_;
 };
 
@@ -56,7 +61,7 @@
     result_sender(const std::unique_ptr<viz::CopyOutputRequest>& request) {
   viz::mojom::CopyOutputResultSenderPtr result_sender;
   auto impl = std::make_unique<CopyOutputResultSenderImpl>(
-      std::move(request->result_callback_));
+      request->result_format(), std::move(request->result_callback_));
   MakeStrongBinding(std::move(impl), MakeRequest(&result_sender));
   return result_sender;
 }
@@ -66,9 +71,14 @@
                   std::unique_ptr<viz::CopyOutputRequest>>::
     Read(viz::mojom::CopyOutputRequestDataView data,
          std::unique_ptr<viz::CopyOutputRequest>* out_p) {
-  auto request = viz::CopyOutputRequest::CreateEmptyRequest();
+  viz::CopyOutputRequest::ResultFormat result_format;
+  if (!data.ReadResultFormat(&result_format))
+    return false;
 
-  request->force_bitmap_result_ = data.force_bitmap_result();
+  auto result_sender =
+      data.TakeResultSender<viz::mojom::CopyOutputResultSenderPtr>();
+  auto request = std::make_unique<viz::CopyOutputRequest>(
+      result_format, base::BindOnce(SendResult, base::Passed(&result_sender)));
 
   if (!data.ReadSource(&request->source_))
     return false;
@@ -79,11 +89,6 @@
   if (!data.ReadTextureMailbox(&request->texture_mailbox_))
     return false;
 
-  auto result_sender =
-      data.TakeResultSender<viz::mojom::CopyOutputResultSenderPtr>();
-  request->result_callback_ =
-      base::BindOnce(SendResult, base::Passed(&result_sender));
-
   *out_p = std::move(request);
 
   return true;
diff --git a/services/viz/public/cpp/compositing/copy_output_request_struct_traits.h b/services/viz/public/cpp/compositing/copy_output_request_struct_traits.h
index b6dc5f49..ca33fa43 100644
--- a/services/viz/public/cpp/compositing/copy_output_request_struct_traits.h
+++ b/services/viz/public/cpp/compositing/copy_output_request_struct_traits.h
@@ -16,16 +16,16 @@
 template <>
 struct StructTraits<viz::mojom::CopyOutputRequestDataView,
                     std::unique_ptr<viz::CopyOutputRequest>> {
+  static viz::CopyOutputRequest::ResultFormat result_format(
+      const std::unique_ptr<viz::CopyOutputRequest>& request) {
+    return request->result_format();
+  }
+
   static const base::Optional<base::UnguessableToken>& source(
       const std::unique_ptr<viz::CopyOutputRequest>& request) {
     return request->source_;
   }
 
-  static bool force_bitmap_result(
-      const std::unique_ptr<viz::CopyOutputRequest>& request) {
-    return request->force_bitmap_result_;
-  }
-
   static const base::Optional<gfx::Rect>& area(
       const std::unique_ptr<viz::CopyOutputRequest>& request) {
     return request->area_;
diff --git a/services/viz/public/cpp/compositing/struct_traits_unittest.cc b/services/viz/public/cpp/compositing/struct_traits_unittest.cc
index 737d588..750f2a36 100644
--- a/services/viz/public/cpp/compositing/struct_traits_unittest.cc
+++ b/services/viz/public/cpp/compositing/struct_traits_unittest.cc
@@ -89,26 +89,6 @@
 
 }  // namespace
 
-void CopyOutputRequestCallbackRunsOnceCallback(
-    int* n_called,
-    std::unique_ptr<CopyOutputResult> result) {
-  ++*n_called;
-}
-
-void CopyOutputRequestCallback(base::Closure const& quit_closure,
-                               gfx::Size const& expected_size,
-                               std::unique_ptr<CopyOutputResult> result) {
-  EXPECT_EQ(expected_size, result->size());
-  quit_closure.Run();
-}
-
-void CopyOutputRequestMessagePipeBrokenCallback(
-    base::Closure const& quit_closure,
-    std::unique_ptr<CopyOutputResult> result) {
-  EXPECT_TRUE(result->IsEmpty());
-  quit_closure.Run();
-}
-
 TEST_F(StructTraitsTest, BeginFrameArgs) {
   const base::TimeTicks frame_time = base::TimeTicks::Now();
   const base::TimeTicks deadline = base::TimeTicks::Now();
@@ -249,82 +229,137 @@
 
 TEST_F(StructTraitsTest, CopyOutputRequest_BitmapRequest) {
   base::test::ScopedTaskEnvironment scoped_task_environment;
+
+  const auto result_format = CopyOutputRequest::ResultFormat::RGBA_BITMAP;
   const gfx::Rect area(5, 7, 44, 55);
   const auto source =
       base::UnguessableToken::Deserialize(0xdeadbeef, 0xdeadf00d);
-  gfx::Size size(9, 8);
-  auto bitmap = base::MakeUnique<SkBitmap>();
-  bitmap->allocN32Pixels(size.width(), size.height());
+
   base::RunLoop run_loop;
-  std::unique_ptr<CopyOutputRequest> input =
-      CopyOutputRequest::CreateBitmapRequest(base::BindOnce(
-          CopyOutputRequestCallback, run_loop.QuitClosure(), size));
+  std::unique_ptr<CopyOutputRequest> input(new CopyOutputRequest(
+      result_format,
+      base::BindOnce(
+          [](const base::Closure& quit_closure, const gfx::Rect& expected_rect,
+             std::unique_ptr<CopyOutputResult> result) {
+            EXPECT_EQ(expected_rect, result->rect());
+            quit_closure.Run();
+          },
+          run_loop.QuitClosure(), area)));
   input->set_area(area);
   input->set_source(source);
   std::unique_ptr<CopyOutputRequest> output;
   SerializeAndDeserialize<mojom::CopyOutputRequest>(input, &output);
-  EXPECT_TRUE(output->force_bitmap_result());
-  EXPECT_FALSE(output->has_texture_mailbox());
+
+  EXPECT_EQ(output->result_format(), result_format);
+  EXPECT_TRUE(output->has_source());
+  EXPECT_EQ(source, output->source());
   EXPECT_TRUE(output->has_area());
   EXPECT_EQ(area, output->area());
-  EXPECT_EQ(source, output->source());
-  output->SendBitmapResult(std::move(bitmap));
-  // If CopyOutputRequestCallback is called, this ends. Otherwise, the test
+
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(area.width(), area.height());
+  output->SendResult(std::make_unique<CopyOutputSkBitmapResult>(area, bitmap));
+  // If the CopyOutputRequest callback is called, this ends. Otherwise, the test
   // will time out and fail.
   run_loop.Run();
 }
 
 TEST_F(StructTraitsTest, CopyOutputRequest_MessagePipeBroken) {
   base::test::ScopedTaskEnvironment scoped_task_environment;
+
   base::RunLoop run_loop;
-  auto request = CopyOutputRequest::CreateRequest(base::BindOnce(
-      CopyOutputRequestMessagePipeBrokenCallback, run_loop.QuitClosure()));
+  auto request = std::make_unique<CopyOutputRequest>(
+      CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+      base::BindOnce(
+          [](const base::Closure& quit_closure,
+             std::unique_ptr<CopyOutputResult> result) {
+            EXPECT_TRUE(result->IsEmpty());
+            quit_closure.Run();
+          },
+          run_loop.QuitClosure()));
   auto result_sender = mojo::StructTraits<
       mojom::CopyOutputRequestDataView,
       std::unique_ptr<CopyOutputRequest>>::result_sender(request);
   result_sender.reset();
-  // The callback must be called with an empty CopyOutputResult. If it's
-  // never called, this will never end and the test times out.
+  // The callback must be called with an empty CopyOutputResult. If it's never
+  // called, this will never end and the test times out.
   run_loop.Run();
 }
 
 TEST_F(StructTraitsTest, CopyOutputRequest_TextureRequest) {
   base::test::ScopedTaskEnvironment scoped_task_environment;
+
+  const auto result_format = CopyOutputRequest::ResultFormat::RGBA_TEXTURE;
   const int8_t mailbox_name[GL_MAILBOX_SIZE_CHROMIUM] = {
       0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 9, 7, 5, 3, 1, 3};
   const uint32_t target = 3;
   gpu::Mailbox mailbox;
   mailbox.SetName(mailbox_name);
   TextureMailbox texture_mailbox(mailbox, gpu::SyncToken(), target);
-  base::RunLoop run_loop;
-  std::unique_ptr<CopyOutputRequest> input =
-      CopyOutputRequest::CreateRequest(base::BindOnce(
-          CopyOutputRequestCallback, run_loop.QuitClosure(), gfx::Size()));
+  const gfx::Rect result_rect(10, 10);
+
+  base::RunLoop run_loop_for_result;
+  std::unique_ptr<CopyOutputRequest> input(new CopyOutputRequest(
+      result_format,
+      base::BindOnce(
+          [](const base::Closure& quit_closure, const gfx::Rect& expected_rect,
+             std::unique_ptr<CopyOutputResult> result) {
+            EXPECT_EQ(expected_rect, result->rect());
+            quit_closure.Run();
+          },
+          run_loop_for_result.QuitClosure(), result_rect)));
   input->SetTextureMailbox(texture_mailbox);
   std::unique_ptr<CopyOutputRequest> output;
   SerializeAndDeserialize<mojom::CopyOutputRequest>(input, &output);
 
-  EXPECT_TRUE(output->has_texture_mailbox());
+  EXPECT_EQ(output->result_format(), result_format);
+  EXPECT_FALSE(output->has_source());
   EXPECT_FALSE(output->has_area());
+  EXPECT_TRUE(output->has_texture_mailbox());
   EXPECT_EQ(mailbox, output->texture_mailbox().mailbox());
   EXPECT_EQ(target, output->texture_mailbox().target());
-  EXPECT_FALSE(output->has_source());
-  output->SendEmptyResult();
-  // If CopyOutputRequestCallback is called, this ends. Otherwise, the test
-  // will time out and fail.
-  run_loop.Run();
+
+  base::RunLoop run_loop_for_release;
+  output->SendResult(std::make_unique<CopyOutputTextureResult>(
+      result_rect, texture_mailbox,
+      SingleReleaseCallback::Create(base::Bind(
+          [](const base::Closure& quit_closure,
+             const gpu::SyncToken& expected_sync_token,
+             const gpu::SyncToken& sync_token, bool is_lost) {
+            EXPECT_EQ(expected_sync_token, sync_token);
+            EXPECT_FALSE(is_lost);
+            quit_closure.Run();
+          },
+          run_loop_for_release.QuitClosure(), gpu::SyncToken()))));
+
+  // Wait for the result to be delivered to the other side: The
+  // CopyOutputRequest callback will be called, at which point
+  // |run_loop_for_result| ends. Otherwise, the test will time out and fail.
+  run_loop_for_result.Run();
+
+  // Now, wait for the the texture release callback on this side to be run:
+  // The CopyOutputResult callback will be called, at which point
+  // |run_loop_for_release| ends. Otherwise, the test will time out and fail.
+  run_loop_for_release.Run();
 }
 
 TEST_F(StructTraitsTest, CopyOutputRequest_CallbackRunsOnce) {
   base::test::ScopedTaskEnvironment scoped_task_environment;
+
   int n_called = 0;
-  auto request = CopyOutputRequest::CreateRequest(
-      base::BindOnce(CopyOutputRequestCallbackRunsOnceCallback, &n_called));
+  auto request = std::make_unique<CopyOutputRequest>(
+      CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+      base::BindOnce(
+          [](int* n_called, std::unique_ptr<CopyOutputResult> result) {
+            ++*n_called;
+          },
+          base::Unretained(&n_called)));
   auto result_sender = mojo::StructTraits<
       mojom::CopyOutputRequestDataView,
       std::unique_ptr<CopyOutputRequest>>::result_sender(request);
   for (int i = 0; i < 10; i++)
-    result_sender->SendResult(CopyOutputResult::CreateEmptyResult());
+    result_sender->SendResult(std::make_unique<CopyOutputResult>(
+        request->result_format(), gfx::Rect()));
   EXPECT_EQ(0, n_called);
   result_sender.FlushForTesting();
   EXPECT_EQ(1, n_called);
diff --git a/services/viz/public/interfaces/compositing/copy_output_request.mojom b/services/viz/public/interfaces/compositing/copy_output_request.mojom
index 317a534..9a862db 100644
--- a/services/viz/public/interfaces/compositing/copy_output_request.mojom
+++ b/services/viz/public/interfaces/compositing/copy_output_request.mojom
@@ -9,12 +9,17 @@
 import "mojo/common/unguessable_token.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 
-// See cc/output/copy_output_request.h.
+// See components/viz/common/quads/copy_output_request.h.
 struct CopyOutputRequest {
+  cc.mojom.CopyOutputResultFormat result_format;
+
   mojo.common.mojom.UnguessableToken? source;
-  bool force_bitmap_result;
   gfx.mojom.Rect? area;
+
+  // DEPRECATED: To be removed once tab capture is moved into VIZ.
+  // http://crbug.com/754872
   cc.mojom.TextureMailbox? texture_mailbox;
+
   CopyOutputResultSender result_sender;
 };
 
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index d97c4fc5..5b2bb86 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -13903,13 +13903,13 @@
             {
               "gpu": "8086:0a2e",
               "hidpi": "0",
-              "os": "Mac-10.12.5",
+              "os": "Mac-10.12.6",
               "pool": "Chrome"
             },
             {
               "gpu": "8086:0d26",
               "hidpi": "1",
-              "os": "Mac-10.12.5",
+              "os": "Mac-10.12.6",
               "pool": "Chrome"
             }
           ],
diff --git a/testing/buildbot/chromium.webkit.json b/testing/buildbot/chromium.webkit.json
index 0448e80..665c1c2 100644
--- a/testing/buildbot/chromium.webkit.json
+++ b/testing/buildbot/chromium.webkit.json
@@ -478,7 +478,7 @@
             {
               "gpu": "8086:0a2e",
               "hidpi": "0",
-              "os": "Mac-10.12.5"
+              "os": "Mac-10.12.6"
             }
           ],
           "shards": 2
diff --git a/testing/buildbot/filters/fuchsia.net_unittests.filter b/testing/buildbot/filters/fuchsia.net_unittests.filter
index 584c006..9791ef2 100644
--- a/testing/buildbot/filters/fuchsia.net_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.net_unittests.filter
@@ -1,5 +1,4 @@
 # TODO(fuchsia): Fix these tests and remove the filter. https://crbug.com/731302 .
--*HTTPS*
 -*QuicEndToEndTest.Large*
 -*QuicEndToEndTest.TokenBinding*
 -*QuicEndToEndTest.Uber*
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 0c3e5c17..24c4ab1a 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -544,6 +544,34 @@
             ]
         }
     ],
+    "ChromeHome": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Default"
+                },
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ChromeHome"
+                    ],
+                    "disable_features": [
+                        "ChromeHomeExpandButton"
+                    ]
+                },
+                {
+                    "name": "Enabled_Button",
+                    "enable_features": [
+                        "ChromeHome",
+                        "ChromeHomeExpandButton"
+                    ]
+                }
+            ]
+        }
+    ],
     "ClearOldOnDemandFavicons": [
         {
             "platforms": [
@@ -1453,6 +1481,23 @@
             ]
         }
     ],
+    "MarkNonSecureAs": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "ios",
+                "linux",
+                "mac",
+                "win"
+            ],
+            "experiments": [
+                {
+                    "name": "non-secure-while-incognito-or-editing"
+                }
+            ]
+        }
+    ],
     "MediaFoundationH264Encoding": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index 07e18cc3..8b06dde 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -118,10 +118,8 @@
 Bug(none) compositing/backface-visibility/backface-visibility-webgl.html [ Failure ]
 Bug(none) compositing/canvas-with-object-fit-contain-in-composited-layer.html [ Failure ]
 Bug(none) compositing/change-preferCompositingToLCDText-setting.html [ Failure ]
-Bug(none) compositing/clip-child-by-non-stacking-ancestor.html [ Failure ]
 Bug(none) compositing/color-matching/image-color-matching.html [ Failure ]
 Bug(none) compositing/columns/composited-in-paginated.html [ Failure ]
-Bug(none) compositing/contents-opaque/background-clip.html [ Failure ]
 Bug(none) compositing/contents-opaque/background-color.html [ Failure ]
 Bug(none) compositing/contents-opaque/body-background-painted.html [ Failure ]
 Bug(none) compositing/contents-opaque/body-background-skipped.html [ Failure ]
@@ -129,7 +127,6 @@
 Bug(none) compositing/contents-opaque/hidden-with-visible-child.html [ Failure ]
 Bug(none) compositing/contents-opaque/hidden-with-visible-text.html [ Failure ]
 Bug(none) compositing/contents-opaque/layer-opacity.html [ Failure ]
-Bug(none) compositing/contents-opaque/layer-transform.html [ Failure ]
 Bug(none) compositing/contents-opaque/overflow-hidden-child-layers.html [ Failure ]
 Bug(none) compositing/culling/filter-occlusion-alpha-large.html [ Failure Crash ]
 Bug(none) compositing/culling/tile-occlusion-boundaries.html [ Failure ]
@@ -146,7 +143,6 @@
 Bug(none) compositing/flat-with-transformed-child.html [ Failure ]
 Bug(none) compositing/force-compositing-mode/overflow-iframe-enter-compositing.html [ Failure ]
 Bug(none) compositing/force-compositing-mode/overflow-iframe-layer.html [ Failure ]
-Bug(none) compositing/geometry/ancestor-overflow-change.html [ Failure ]
 Bug(none) compositing/geometry/bounds-clipped-composited-child.html [ Failure ]
 Bug(none) compositing/geometry/bounds-ignores-hidden-composited-descendant.html [ Failure ]
 Bug(none) compositing/geometry/bounds-ignores-hidden-dynamic-negzindex.html [ Failure ]
@@ -241,7 +237,6 @@
 Bug(none) compositing/iframes/connect-compositing-iframe3.html [ Failure ]
 Bug(none) compositing/iframes/iframe-resize.html [ Failure ]
 Bug(none) compositing/iframes/iframe-size-from-zero.html [ Failure ]
-Bug(none) compositing/iframes/invisible-nested-iframe-hide.html [ Crash Failure ]
 Bug(none) compositing/iframes/invisible-nested-iframe-show.html [ Crash Failure ]
 Bug(none) compositing/iframes/overlapped-iframe-iframe.html [ Failure ]
 Bug(none) compositing/iframes/overlapped-iframe.html [ Failure ]
@@ -260,7 +255,6 @@
 Bug(none) compositing/layer-creation/fixed-position-in-fixed-overflow.html [ Failure ]
 Bug(none) compositing/layer-creation/fixed-position-no-content.html [ Failure ]
 Bug(none) compositing/layer-creation/fixed-position-nonscrollable-body-mismatch-containers.html [ Failure ]
-Bug(none) compositing/layer-creation/fixed-position-nonscrollable-body-overlap.html [ Failure ]
 Bug(none) compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page.html [ Failure ]
 Bug(none) compositing/layer-creation/fixed-position-out-of-view-positioning.html [ Crash Failure ]
 Bug(none) compositing/layer-creation/fixed-position-out-of-view-scaled-scroll.html [ Failure ]
@@ -283,15 +277,12 @@
 Bug(none) compositing/layer-creation/overlap-transformed-layer.html [ Failure ]
 Bug(none) compositing/layer-creation/overlap-transformed-preserved-3d.html [ Failure ]
 Bug(none) compositing/layer-creation/overlap-transforms.html [ Failure ]
-Bug(none) compositing/layer-creation/remove-clipping-layer-with-no-children.html [ Failure ]
 Bug(none) compositing/layer-creation/rotate3d-overlap.html [ Failure ]
 Bug(none) compositing/layer-creation/scroll-partial-update.html [ Failure ]
 Bug(none) compositing/layer-creation/spanOverlapsCanvas.html [ Failure ]
 Bug(none) compositing/layer-creation/squashing-into-ancestor-clipping-layer-change.html [ Failure ]
 Bug(none) compositing/layer-creation/stacking-context-overlap-nested.html [ Failure ]
 Bug(none) compositing/layer-creation/stacking-context-overlap.html [ Failure ]
-Bug(none) compositing/layer-creation/translatez-added.html [ Failure ]
-Bug(none) compositing/layer-creation/translatez-overlap.html [ Failure ]
 Bug(none) compositing/layer-tree.html [ Failure ]
 Bug(none) compositing/layers-inside-overflow-scroll.html [ Failure ]
 Bug(none) compositing/layout-width-change.html [ Failure ]
@@ -305,8 +296,6 @@
 Bug(none) compositing/overflow/body-switch-composited-scrolling.html [ Failure ]
 Bug(none) compositing/overflow/clear-scroll-parent.html [ Failure ]
 Bug(none) compositing/overflow/clip-descendents.html [ Failure ]
-# puts the 'scroller' layer after 'overlap' and 'target'
-Bug(none) compositing/overflow/composited-scroll-overlap-test.html [ Failure ]
 Bug(none) compositing/overflow/composited-scrolling-paint-phases.html [ Failure ]
 Bug(none) compositing/overflow/content-loses-scrollbars.html [ Failure ]
 Bug(none) compositing/overflow/content-gains-scrollbars.html [ Failure ]
@@ -364,13 +353,10 @@
 Bug(none) compositing/reflections/reflection-in-composited.html [ Failure Crash ]
 Bug(none) compositing/reflections/simple-composited-reflections.html [ Failure Crash ]
 Bug(none) compositing/reflections/transform-inside-reflection.html [ Failure Crash ]
-Bug(none) compositing/rendering-contexts.html [ Failure ]
 Bug(none) compositing/rtl/rtl-absolute-overflow-scrolled.html [ Failure Crash ]
 Bug(none) compositing/rtl/rtl-absolute-overflow.html [ Failure Crash ]
-Bug(none) compositing/rtl/rtl-absolute.html [ Failure ]
 Bug(none) compositing/rtl/rtl-fixed-overflow-scrolled.html [ Failure Crash ]
 Bug(none) compositing/rtl/rtl-fixed-overflow.html [ Crash Failure ]
-Bug(none) compositing/rtl/rtl-fixed.html [ Failure ]
 Bug(none) compositing/rtl/rtl-iframe-absolute-overflow-scrolled.html [ Failure ]
 Bug(none) compositing/rtl/rtl-iframe-absolute-overflow.html [ Failure ]
 Bug(none) compositing/rtl/rtl-iframe-absolute.html [ Failure ]
@@ -379,7 +365,6 @@
 Bug(none) compositing/rtl/rtl-iframe-fixed.html [ Failure ]
 Bug(none) compositing/rtl/rtl-iframe-relative.html [ Failure ]
 Bug(none) compositing/rtl/rtl-overflow-invalidation.html [ Failure Crash ]
-Bug(none) compositing/rtl/rtl-relative.html [ Failure ]
 Bug(none) compositing/scrollbars/custom-composited-different-track-parts.html [ Failure ]
 Bug(none) compositing/scrollbars/nested-overlay-scrollbars.html [ Failure ]
 Bug(none) compositing/self-painting-layers.html [ Failure ]
@@ -390,10 +375,8 @@
 Bug(none) compositing/squashing/do-not-squash-scroll-child-with-composited-descendants.html [ Failure ]
 Bug(none) compositing/squashing/dont-squash-into-animated-layers.html [ Failure ]
 Bug(none) compositing/squashing/dont-squash-into-blend-mode.html [ Failure ]
-Bug(none) compositing/squashing/dont-squash-into-iframes.html [ Failure ]
 Bug(none) compositing/squashing/dont-squash-into-videos.html [ Failure ]
 Bug(none) compositing/squashing/dont-squash-with-scale-transform.html [ Failure ]
-Bug(none) compositing/squashing/iframes-are-never-squashed.html [ Failure ]
 Bug(none) compositing/squashing/invisible-layers-should-not-affect-geometry.html [ Failure ]
 Bug(none) compositing/squashing/move-squashing-layer.html [ Failure ]
 Bug(none) compositing/squashing/no-squashing-for-filters.html [ Failure Crash ]
@@ -429,8 +412,7 @@
 Bug(none) compositing/visibility/visibility-image-layers.html [ Failure ]
 Bug(none) compositing/visibility/visibility-simple-video-layer.html [ Failure ]
 Bug(none) compositing/webgl/webgl-reflection.html [ Failure Crash ]
-Bug(none) compositing/webgl/webgl-with-accelerated-background-color.html [ Failure ]
-Bug(none) compositing/will-change/composited-layers.html [ Failure ]
+Bug(none) compositing/webgl/webgl-with-accelerated-background-color.html [ Failure Pass ]
 Bug(none) compositing/will-change/will-change-preserve-backface-visibility.html [ Failure ]
 Bug(none) fast/backgrounds/background-inherit-color-bug.html [ Failure ]
 Bug(none) fast/backgrounds/background-leakage-transforms.html [ Failure ]
@@ -643,7 +625,6 @@
 Bug(none) fast/multicol/composited-layer-nested.html [ Failure Crash ]
 Bug(none) fast/multicol/composited-layer-single-fragment.html [ Failure ]
 Bug(none) fast/multicol/composited-layer-will-change.html [ Failure ]
-Bug(none) fast/multicol/composited-layer.html [ Failure ]
 Bug(none) fast/multicol/composited-opacity-2nd-and-3rd-column.html [ Failure ]
 Bug(none) fast/multicol/composited-relpos-overlapping-will-change.html [ Failure ]
 Bug(none) fast/multicol/composited-with-child-layer-in-next-column.html [ Failure ]
@@ -979,10 +960,8 @@
 Bug(none) paint/clipath/clip-path-with-background-and-box-behind.html [ Failure ]
 Bug(none) paint/frames/frameset-with-stacking-context-and-not-stacking-context-children.html [ Failure ]
 Bug(none) paint/frames/frameset-with-stacking-contexts.html [ Failure ]
-Bug(none) paint/overflow/fixed-background-scroll-in-frame.html [ Failure ]
 Bug(none) paint/pagination/pagination-change-clip-crash.html [ Failure ]
 Bug(none) paint/selection/text-selection-newline-mixed-ltr-rtl.html [ Failure ]
-Bug(none) paint/selection/text-selection-newline-rtl-double-linebreak.html [ Failure ]
 
 # Less invalidations or different invalidations without pixel failures.
 # Some might be good. Some might be under-invalidations for which under-invalidation
@@ -1132,15 +1111,6 @@
 Bug(none) paint/invalidation/svg/deep-nested-embedded-svg-size-changes-no-layout-triggers-1.html [ Failure ]
 Bug(none) paint/invalidation/svg/deep-nested-embedded-svg-size-changes-no-layout-triggers-2.html [ Failure ]
 
-# contentsOpaque
-Bug(none) paint/invalidation/compositing/containing-block-added.html [ Failure ]
-Bug(none) paint/invalidation/compositing/containing-block-added-individual.html [ Failure ]
-Bug(none) paint/invalidation/compositing/containing-block-removed.html [ Failure ]
-Bug(none) paint/invalidation/compositing/containing-block-removed-individual.html [ Failure ]
-Bug(none) paint/invalidation/compositing/should-not-repaint-composited-z-index.html [ Failure ]
-Bug(none) paint/invalidation/destroy-composited-scrollbar.html [ Failure ]
-Bug(none) paint/invalidation/filter-repaint-on-accelerated-layer.html [ Failure ]
-
 # Wrong invalidation/painting/rasterization for multicol. Different layer tree.
 crbug.com/648274 fast/text/letter-spacing-leading-and-trailing.html [ Failure ]
 crbug.com/648274 paint/invalidation/column-rules-fixed-height.html [ Failure ]
@@ -1177,7 +1147,6 @@
 Bug(none) paint/invalidation/compositing/composited-float-under-composited-inline.html [ Failure ]
 Bug(none) paint/invalidation/compositing/composited-float-under-composited-inline-individual.html [ Failure ]
 Bug(none) paint/invalidation/compositing/iframe-inside-squashed-layer.html [ Failure ]
-Bug(none) paint/invalidation/compositing/invalidations-on-composited-layers.html [ Failure ]
 Bug(none) paint/invalidation/compositing/overlap-test-with-filter.html [ Failure ]
 Bug(none) paint/invalidation/compositing/should-not-repaint-composited-descendants.html [ Failure ]
 Bug(none) paint/invalidation/filter-repaint-accelerated-child-with-filter-child.html [ Failure ]
@@ -1204,7 +1173,6 @@
 # Need to support partial invalidation for some changes.
 crbug.com/732612 paint/invalidation/canvas-composite-repaint-by-all-imagesource.html [ Failure ]
 crbug.com/732612 paint/invalidation/canvas-putImageData.html [ Failure ]
-crbug.com/732612 paint/invalidation/canvas-resize.html [ Failure ]
 crbug.com/732612 paint/invalidation/japanese-rl-selection-repaint.html [ Failure ]
 crbug.com/732612 paint/invalidation/repaint-across-writing-mode-boundary.html [ Failure ]
 
@@ -1377,7 +1345,6 @@
 Bug(none) transforms/transformed-caret.html [ Pass Failure ]
 Bug(none) transforms/transformed-focused-text-input.html [ Failure ]
 
-crbug.com/644358 compositing/force-compositing-mode/overflow-hidden-iframe-layer.html [ Failure ]
 crbug.com/644358 compositing/gestures/gesture-tapHighlight-2-iframe-scrolled-outer-late-composite.html [ Failure ]
 crbug.com/644358 compositing/iframes/connect-compositing-iframe-delayed.html [ Failure ]
 crbug.com/644358 compositing/iframes/connect-compositing-iframe.html [ Failure ]
@@ -1642,7 +1609,6 @@
 Bug(700530) compositing/overflow/paint-neg-z-order-descendants-into-scrolling-contents-layer.html [ Failure ]
 Bug(700530) fast/multicol/tall-content-in-inner-with-fixed-height.html [ Failure ]
 Bug(700530) compositing/geometry/abs-position-inside-opacity.html [ Failure ]
-Bug(700530) fast/css3-text/css3-text-decoration/text-underline-position/text-underline-position-cjk.html [ Failure ]
 
 # The following debug crashes / failures have not been triaged.
 crbug.com/702805 virtual/threaded/compositing/visibility/overlays-persist-on-navigation.html [ Crash Failure ]
@@ -1709,7 +1675,6 @@
 Bug(none) fast/forms/validation-bubble-appearance-iframe.html [ Failure ]
 Bug(none) fast/forms/validation-bubble-appearance-rtl-ui.html [ Failure ]
 Bug(none) fast/overflow/overflow-with-local-background-attachment.html [ Failure ]
-Bug(none) images/color-profile-background-clip-text.html [ Failure ]
 Bug(none) paint/filters/clip-under-filter.html [ Failure ]
 Bug(none) svg/text/text-selection-text-06-t.svg [ Failure ]
 Bug(none) svg/transforms/transformed-text-fill-pattern.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index f8721b6..51651f7 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -3119,11 +3119,6 @@
 # Sheriff failure 2017-08-29
 crbug.com/727252 [ Win7 ] external/wpt/media-source/mediasource-endofstream.html [ Pass Timeout ]
 
-# Ganesh dither changes
-crbug.com/753462 virtual/gpu/fast/canvas/canvas-text-alignment.html [ NeedsManualRebaseline ]
-crbug.com/753462 virtual/gpu/fast/canvas/fillrect_gradient.html [ NeedsManualRebaseline ]
-crbug.com/753462 virtual/gpu/fast/canvas/gradient-add-second-start-end-stop.html [ NeedsManualRebaseline ]
-
 crbug.com/746904 [ Win ] fast/text/ellipsis-in-relative-inline.html [ Failure Pass ]
 
 crbug.com/754819 external/wpt/longtask-timing/longtask-in-childiframe-crossorigin.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/battery-status/api-defined.html b/third_party/WebKit/LayoutTests/battery-status/api-defined.html
index 29343a2..299e21f 100644
--- a/third_party/WebKit/LayoutTests/battery-status/api-defined.html
+++ b/third_party/WebKit/LayoutTests/battery-status/api-defined.html
@@ -2,7 +2,8 @@
 <html>
 <body>
 <script src="../resources/js-test.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/device/public/interfaces/battery_monitor.mojom.js"></script>
 <script src="resources/mock-battery-monitor.js"></script>
 <script>
 description("Test basic API definitions.");
@@ -42,14 +43,11 @@
     setTimeout(finishJSTest, 0);
 }
 
-var promise;
-mockBatteryMonitorReady.then(() => {
-    promise = navigator.getBattery();
-    shouldBeDefined("promise");
-    shouldBeDefined("promise.then");
-    promise.then(batteryStatusSuccess, batteryStatusFailure);
-    setAndFireMockBatteryInfo(false, 10, 20, 0.5);
-});
+var promise = navigator.getBattery();
+shouldBeDefined("promise");
+shouldBeDefined("promise.then");
+promise.then(batteryStatusSuccess, batteryStatusFailure);
+setAndFireMockBatteryInfo(false, 10, 20, 0.5);
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/battery-status/multiple-promises-after-resolve.html b/third_party/WebKit/LayoutTests/battery-status/multiple-promises-after-resolve.html
index fdc0460..6c1dbe3b 100644
--- a/third_party/WebKit/LayoutTests/battery-status/multiple-promises-after-resolve.html
+++ b/third_party/WebKit/LayoutTests/battery-status/multiple-promises-after-resolve.html
@@ -2,7 +2,8 @@
 <html>
 <body>
 <script src="../resources/js-test.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/device/public/interfaces/battery_monitor.mojom.js"></script>
 <script src="resources/mock-battery-monitor.js"></script>
 <script>
 description("Test battery status API with multiple promises after resolve.");
@@ -31,11 +32,9 @@
     shouldBeTrue('promise1 === promise2');
 }
 
-mockBatteryMonitorReady.then(() => {
-    promise1 = navigator.getBattery();
-    promise1.then(batteryStatusSuccess, batteryStatusFailure);
-    setAndFireMockBatteryInfo(false, 10, 20, 0.5);
-});
+promise1 = navigator.getBattery();
+promise1.then(batteryStatusSuccess, batteryStatusFailure);
+setAndFireMockBatteryInfo(false, 10, 20, 0.5);
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/battery-status/multiple-promises.html b/third_party/WebKit/LayoutTests/battery-status/multiple-promises.html
index c177005..b0273655 100644
--- a/third_party/WebKit/LayoutTests/battery-status/multiple-promises.html
+++ b/third_party/WebKit/LayoutTests/battery-status/multiple-promises.html
@@ -2,7 +2,8 @@
 <html>
 <body>
 <script src="../resources/js-test.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/device/public/interfaces/battery_monitor.mojom.js"></script>
 <script src="resources/mock-battery-monitor.js"></script>
 <script>
 description("Test multiple promise resolution.");
@@ -26,28 +27,26 @@
 var promise1;
 var promise2;
 
-mockBatteryMonitorReady.then(() => {
-    promise1 = navigator.getBattery();
-    promise1.then(
-        function(battery) {
-            debug('first resolution');
-            testIfBatteryStatusIsUpToDate(battery);
-            promise1Count++;
-            finishIfReady();
-        }, batteryStatusFailure);
+promise1 = navigator.getBattery();
+promise1.then(
+    function(battery) {
+        debug('first resolution');
+        testIfBatteryStatusIsUpToDate(battery);
+        promise1Count++;
+        finishIfReady();
+    }, batteryStatusFailure);
 
-    promise2 = navigator.getBattery();
-    promise2.then(
-        function(battery) {
-            debug('second resolution');
-            testIfBatteryStatusIsUpToDate(battery);
-            promise2Count++;
-            finishIfReady();
-        }, batteryStatusFailure);
+promise2 = navigator.getBattery();
+promise2.then(
+    function(battery) {
+        debug('second resolution');
+        testIfBatteryStatusIsUpToDate(battery);
+        promise2Count++;
+        finishIfReady();
+    }, batteryStatusFailure);
 
-    shouldBeTrue('promise1 === promise2');
-    setAndFireMockBatteryInfo(false, 10, 20, 0.5);
-});
+shouldBeTrue('promise1 === promise2');
+setAndFireMockBatteryInfo(false, 10, 20, 0.5);
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/battery-status/multiple-windows-page-visibility.html b/third_party/WebKit/LayoutTests/battery-status/multiple-windows-page-visibility.html
index 9e25ea1..a311437d 100644
--- a/third_party/WebKit/LayoutTests/battery-status/multiple-windows-page-visibility.html
+++ b/third_party/WebKit/LayoutTests/battery-status/multiple-windows-page-visibility.html
@@ -2,7 +2,8 @@
 <html>
 <body>
 <script src="../resources/js-test.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/device/public/interfaces/battery_monitor.mojom.js"></script>
 <script src="resources/mock-battery-monitor.js"></script>
 <script>
 description("Test multiple windows with page visibility.");
@@ -61,10 +62,8 @@
 }
 
 debug("first window: page is visible");
-mockBatteryMonitorReady.then(() => {
-    navigator.getBattery().then(batteryStatusSuccess, batteryStatusFailure);
-    setAndFireMockBatteryInfo(false, 10, 20, 0.5);
-});
+navigator.getBattery().then(batteryStatusSuccess, batteryStatusFailure);
+setAndFireMockBatteryInfo(false, 10, 20, 0.5);
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/battery-status/multiple-windows.html b/third_party/WebKit/LayoutTests/battery-status/multiple-windows.html
index 8c94af28..10d73bb 100644
--- a/third_party/WebKit/LayoutTests/battery-status/multiple-windows.html
+++ b/third_party/WebKit/LayoutTests/battery-status/multiple-windows.html
@@ -2,7 +2,8 @@
 <html>
 <body>
 <script src="../resources/js-test.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/device/public/interfaces/battery_monitor.mojom.js"></script>
 <script src="resources/mock-battery-monitor.js"></script>
 <script>
 description("Test battery status API with multiple windows.");
@@ -30,10 +31,8 @@
         }, batteryStatusFailure);
 }
 
-mockBatteryMonitorReady.then(() => {
-    navigator.getBattery().then(batteryStatusSuccess, batteryStatusFailure);
-    setAndFireMockBatteryInfo(false, 10, 20, 0.5);
-});
+navigator.getBattery().then(batteryStatusSuccess, batteryStatusFailure);
+setAndFireMockBatteryInfo(false, 10, 20, 0.5);
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/battery-status/no-gc-with-eventlisteners.html b/third_party/WebKit/LayoutTests/battery-status/no-gc-with-eventlisteners.html
index 4bc9108..6f757ff4d 100644
--- a/third_party/WebKit/LayoutTests/battery-status/no-gc-with-eventlisteners.html
+++ b/third_party/WebKit/LayoutTests/battery-status/no-gc-with-eventlisteners.html
@@ -2,7 +2,8 @@
 <html>
 <body>
 <script src="../resources/js-test.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/device/public/interfaces/battery_monitor.mojom.js"></script>
 <script src="resources/mock-battery-monitor.js"></script>
 <script>
 description("Test no garbage collection of battery manager object when listeners are attached.");
@@ -20,18 +21,16 @@
     setAndFireMockBatteryInfo(false, 10, 20, 0.6);
 }
 
-mockBatteryMonitorReady.then(() => {
-    navigator.getBattery().then(function(battery) {
-        battery.addEventListener('levelchange', function() {
-            testIfBatteryStatusIsUpToDate(battery);
-            battery.removeEventListener('levelchange', arguments.callee);
-            setTimeout(finishJSTest, 0);
-        });
-        setTimeout(fireLevelChange, 0);
+navigator.getBattery().then(function(battery) {
+    battery.addEventListener('levelchange', function() {
+        testIfBatteryStatusIsUpToDate(battery);
+        battery.removeEventListener('levelchange', arguments.callee);
+        setTimeout(finishJSTest, 0);
     });
-
-    setAndFireMockBatteryInfo(false, 10, 20, 0.5);
+    setTimeout(fireLevelChange, 0);
 });
+
+setAndFireMockBatteryInfo(false, 10, 20, 0.5);
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/battery-status/page-visibility.html b/third_party/WebKit/LayoutTests/battery-status/page-visibility.html
index 440a02ec..46610977c 100644
--- a/third_party/WebKit/LayoutTests/battery-status/page-visibility.html
+++ b/third_party/WebKit/LayoutTests/battery-status/page-visibility.html
@@ -2,7 +2,8 @@
 <html>
 <body>
 <script src="../resources/js-test.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/device/public/interfaces/battery_monitor.mojom.js"></script>
 <script src="resources/mock-battery-monitor.js"></script>
 <script>
 description("Test with page visibility.");
@@ -50,10 +51,8 @@
 }
 
 debug("page is visible");
-mockBatteryMonitorReady.then(() => {
-    navigator.getBattery().then(batteryStatusSuccess, batteryStatusFailure);
-    setAndFireMockBatteryInfo(false, 10, 20, 0.5);
-});
+navigator.getBattery().then(batteryStatusSuccess, batteryStatusFailure);
+setAndFireMockBatteryInfo(false, 10, 20, 0.5);
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/battery-status/promise-with-eventlisteners.html b/third_party/WebKit/LayoutTests/battery-status/promise-with-eventlisteners.html
index 196cbf3..1fd2b4d 100644
--- a/third_party/WebKit/LayoutTests/battery-status/promise-with-eventlisteners.html
+++ b/third_party/WebKit/LayoutTests/battery-status/promise-with-eventlisteners.html
@@ -2,7 +2,8 @@
 <html>
 <body>
 <script src="../resources/js-test.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/device/public/interfaces/battery_monitor.mojom.js"></script>
 <script src="resources/mock-battery-monitor.js"></script>
 <script>
 description("Test promise resolution and event listeners callbacks.");
@@ -80,10 +81,8 @@
     }
 }
 
-mockBatteryMonitorReady.then(() => {
-    navigator.getBattery().then(batteryStatusSuccess, batteryStatusFailure);
-    setAndFireMockBatteryInfo(false, 10, 20, 0.5);
-});
+navigator.getBattery().then(batteryStatusSuccess, batteryStatusFailure);
+setAndFireMockBatteryInfo(false, 10, 20, 0.5);
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/battery-status/resources/mock-battery-monitor.js b/third_party/WebKit/LayoutTests/battery-status/resources/mock-battery-monitor.js
index 0d6d413..d54ad6f 100644
--- a/third_party/WebKit/LayoutTests/battery-status/resources/mock-battery-monitor.js
+++ b/third_party/WebKit/LayoutTests/battery-status/resources/mock-battery-monitor.js
@@ -1,63 +1,70 @@
 "use strict";
 
-let mockBatteryMonitor = loadMojoModules('mockBatteryMonitor', [
-                           'services/device/public/interfaces/battery_monitor.mojom',
-                           'services/device/public/interfaces/battery_status.mojom',
-                           'services/device/public/interfaces/constants.mojom',
-                           'mojo/public/js/bindings',
-                         ]).then(mojo => {
-  let [batteryMonitor, batteryStatus, deviceConstants, bindings] = mojo.modules;
-
-  class MockBatteryMonitor {
-    constructor(connector) {
-      connector.addInterfaceOverrideForTesting(
-          deviceConstants.kServiceName, batteryMonitor.BatteryMonitor.name,
-          handle => this.bindingSet_.addBinding(this, handle));
-
-      this.pendingRequests_ = [];
-      this.status_ = null;
-      this.bindingSet_ = new bindings.BindingSet(batteryMonitor.BatteryMonitor);
-    }
-
-    queryNextStatus() {
-      let result = new Promise(resolve => this.pendingRequests_.push(resolve));
-      this.runCallbacks_();
-      return result;
-    }
-
-    updateBatteryStatus(charging, chargingTime, dischargingTime, level) {
-      this.status_ = new batteryStatus.BatteryStatus();
-      this.status_.charging = charging;
-      this.status_.charging_time = chargingTime;
-      this.status_.discharging_time = dischargingTime;
-      this.status_.level = level;
-      this.runCallbacks_();
-    }
-
-    runCallbacks_() {
-      if (!this.status_ || !this.pendingRequests_.length)
-        return;
-
-      while (this.pendingRequests_.length) {
-        this.pendingRequests_.pop()({status: this.status_});
-      }
-      this.status_ = null;
-    }
+class BatteryMonitorImpl {
+  constructor(mock) {
+    this.mock_ = mock;
   }
-  return new MockBatteryMonitor(mojo.connector);
-});
+
+  queryNextStatus() {
+    return this.mock_.queryNextStatus();
+  }
+}
+
+class MockBatteryMonitor {
+  constructor() {
+    this.pendingRequests_ = [];
+    this.status_ = null;
+    this.bindingSet_ = new mojo.BindingSet(device.mojom.BatteryMonitor);
+
+    this.interceptor_ = new MojoInterfaceInterceptor(
+        device.mojom.BatteryMonitor.name, "process");
+    this.interceptor_.oninterfacerequest = e => this.bindRequest(e.handle);
+    this.interceptor_.start();
+  }
+
+  bindRequest(handle) {
+    let impl = new BatteryMonitorImpl(this);
+    this.bindingSet_.addBinding(impl, handle);
+  }
+
+  queryNextStatus() {
+    let result = new Promise(resolve => this.pendingRequests_.push(resolve));
+    this.runCallbacks_();
+    return result;
+  }
+
+  updateBatteryStatus(status) {
+    this.status_ = status;
+    this.runCallbacks_();
+  }
+
+  runCallbacks_() {
+    if (!this.status_ || !this.pendingRequests_.length)
+      return;
+
+    let result = {status: this.status_};
+    while (this.pendingRequests_.length) {
+      this.pendingRequests_.pop()(result);
+    }
+    this.status_ = null;
+  }
+}
+
+let mockBatteryMonitor = new MockBatteryMonitor();
 
 let batteryInfo;
 let lastSetMockBatteryInfo;
 
 function setAndFireMockBatteryInfo(charging, chargingTime, dischargingTime,
                                    level) {
-  lastSetMockBatteryInfo = { charging: charging,
-                      chargingTime: chargingTime,
-                      dischargingTime: dischargingTime,
-                      level: level };
-  mockBatteryMonitor.then(mock => mock.updateBatteryStatus(
-      charging, chargingTime, dischargingTime, level));
+  let status = new device.mojom.BatteryStatus();
+  status.charging = charging;
+  status.chargingTime = chargingTime;
+  status.dischargingTime = dischargingTime;
+  status.level = level;
+
+  lastSetMockBatteryInfo = status;
+  mockBatteryMonitor.updateBatteryStatus(status);
 }
 
 // compare obtained battery values with the mock values
@@ -76,5 +83,3 @@
   testFailed('failed to successfully resolve the promise');
   setTimeout(finishJSTest, 0);
 }
-
-var mockBatteryMonitorReady = mockBatteryMonitor.then();
diff --git a/third_party/WebKit/LayoutTests/battery-status/restricted-level-precision.html b/third_party/WebKit/LayoutTests/battery-status/restricted-level-precision.html
index 7b409425..d0a03e6 100644
--- a/third_party/WebKit/LayoutTests/battery-status/restricted-level-precision.html
+++ b/third_party/WebKit/LayoutTests/battery-status/restricted-level-precision.html
@@ -2,7 +2,8 @@
 <html>
 <body>
 <script src="../resources/js-test.js"></script>
-<script src="../resources/mojo-helpers.js"></script>
+<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
+<script src="file:///gen/services/device/public/interfaces/battery_monitor.mojom.js"></script>
 <script src="resources/mock-battery-monitor.js"></script>
 <script>
 description("Test to ensure level is reported with restricted precision.");
@@ -26,10 +27,8 @@
     setTimeout(finishJSTest, 0);
 }
 
-mockBatteryMonitorReady.then(() => {
-    navigator.getBattery().then(batteryStatusSuccess, batteryStatusFailure);
-    setAndFireMockBatteryInfo(false, 10, 20, levelFullPrecision);
-});
+navigator.getBattery().then(batteryStatusSuccess, batteryStatusFailure);
+setAndFireMockBatteryInfo(false, 10, 20, levelFullPrecision);
 </script>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/garbage-collection-ran-during-error.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/garbage-collection-ran-during-error.js
index b30d785..94596db 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/garbage-collection-ran-during-error.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/garbage-collection-ran-during-error.js
@@ -1,26 +1,21 @@
 'use strict';
-promise_test(() => {
-  let promise;
-  return setBluetoothFakeAdapter('MissingCharacteristicHeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryService('heart_rate'))
-    .then(service => {
-      promise = assert_promise_rejects_with_message(
+let test_desc = 'Garbage Collection ran during FUNCTION_NAME call that ' +
+   'fails. Should not crash';
+let service, func_promise;
+promise_test(() => getHealthThermometerService()
+  .then(({service}) => {
+    func_promise = assert_promise_rejects_with_message(
         service.CALLS([
-          getCharacteristic('measurement_interval')|
-          getCharacteristics()|
-          getCharacteristics('measurement_interval')[UUID]]),
+            getCharacteristic('measurement_interval')|
+            getCharacteristics()|
+            getCharacteristics('measurement_interval')[UUID]]),
         new DOMException(
-          'GATT Server is disconnected. Cannot retrieve characteristics. ' +
-          '(Re)connect first with `device.gatt.connect`.',
-          'NetworkError'));
-      // Disconnect called to clear attributeInstanceMap and allow the
-      // object to get garbage collected.
-      service.device.gatt.disconnect();
-    })
-    .then(runGarbageCollection)
-    .then(() => promise);
-}, 'Garbage Collection ran during FUNCTION_NAME call that fails. ' +
-   'Should not crash');
+            'GATT Server is disconnected. Cannot retrieve characteristics. ' +
+            '(Re)connect first with `device.gatt.connect`.',
+            'NetworkError'));
+    // Disconnect called to clear attributeInstanceMap and allow the object to
+    // get garbage collected.
+    service.device.gatt.disconnect();
+  })
+  .then(runGarbageCollection)
+  .then(() => func_promise), test_desc);
diff --git a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-garbage-collection-ran-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-garbage-collection-ran-during-error.html
index 26fd3421..ce16eb4 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-garbage-collection-ran-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-garbage-collection-ran-during-error.html
@@ -7,27 +7,22 @@
 <script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
-promise_test(() => {
-  let promise;
-  return setBluetoothFakeAdapter('MissingCharacteristicHeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryService('heart_rate'))
-    .then(service => {
-      promise = assert_promise_rejects_with_message(
+let test_desc = 'Garbage Collection ran during getCharacteristic call that ' +
+   'fails. Should not crash';
+let service, func_promise;
+promise_test(() => getHealthThermometerService()
+  .then(({service}) => {
+    func_promise = assert_promise_rejects_with_message(
         service.getCharacteristic('measurement_interval'),
         new DOMException(
-          'GATT Server is disconnected. Cannot retrieve characteristics. ' +
-          '(Re)connect first with `device.gatt.connect`.',
-          'NetworkError'));
-      // Disconnect called to clear attributeInstanceMap and allow the
-      // object to get garbage collected.
-      service.device.gatt.disconnect();
-    })
-    .then(runGarbageCollection)
-    .then(() => promise);
-}, 'Garbage Collection ran during getCharacteristic call that fails. ' +
-   'Should not crash');
+            'GATT Server is disconnected. Cannot retrieve characteristics. ' +
+            '(Re)connect first with `device.gatt.connect`.',
+            'NetworkError'));
+    // Disconnect called to clear attributeInstanceMap and allow the object to
+    // get garbage collected.
+    service.device.gatt.disconnect();
+  })
+  .then(runGarbageCollection)
+  .then(() => func_promise), test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-error-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-error-with-uuid.html
index d489e30..ec32de2a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-error-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-error-with-uuid.html
@@ -7,27 +7,22 @@
 <script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
-promise_test(() => {
-  let promise;
-  return setBluetoothFakeAdapter('MissingCharacteristicHeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryService('heart_rate'))
-    .then(service => {
-      promise = assert_promise_rejects_with_message(
+let test_desc = 'Garbage Collection ran during getCharacteristics call that ' +
+   'fails. Should not crash';
+let service, func_promise;
+promise_test(() => getHealthThermometerService()
+  .then(({service}) => {
+    func_promise = assert_promise_rejects_with_message(
         service.getCharacteristics('measurement_interval'),
         new DOMException(
-          'GATT Server is disconnected. Cannot retrieve characteristics. ' +
-          '(Re)connect first with `device.gatt.connect`.',
-          'NetworkError'));
-      // Disconnect called to clear attributeInstanceMap and allow the
-      // object to get garbage collected.
-      service.device.gatt.disconnect();
-    })
-    .then(runGarbageCollection)
-    .then(() => promise);
-}, 'Garbage Collection ran during getCharacteristics call that fails. ' +
-   'Should not crash');
+            'GATT Server is disconnected. Cannot retrieve characteristics. ' +
+            '(Re)connect first with `device.gatt.connect`.',
+            'NetworkError'));
+    // Disconnect called to clear attributeInstanceMap and allow the object to
+    // get garbage collected.
+    service.device.gatt.disconnect();
+  })
+  .then(runGarbageCollection)
+  .then(() => func_promise), test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-error.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-error.html
index c8503f0..5a1674a 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-error.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-error.html
@@ -7,27 +7,22 @@
 <script src="../../../resources/mojo-helpers.js"></script>
 <script>
 'use strict';
-promise_test(() => {
-  let promise;
-  return setBluetoothFakeAdapter('MissingCharacteristicHeartRateAdapter')
-    .then(() => requestDeviceWithKeyDown({
-      filters: [{services: ['heart_rate']}]}))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryService('heart_rate'))
-    .then(service => {
-      promise = assert_promise_rejects_with_message(
+let test_desc = 'Garbage Collection ran during getCharacteristics call that ' +
+   'fails. Should not crash';
+let service, func_promise;
+promise_test(() => getHealthThermometerService()
+  .then(({service}) => {
+    func_promise = assert_promise_rejects_with_message(
         service.getCharacteristics(),
         new DOMException(
-          'GATT Server is disconnected. Cannot retrieve characteristics. ' +
-          '(Re)connect first with `device.gatt.connect`.',
-          'NetworkError'));
-      // Disconnect called to clear attributeInstanceMap and allow the
-      // object to get garbage collected.
-      service.device.gatt.disconnect();
-    })
-    .then(runGarbageCollection)
-    .then(() => promise);
-}, 'Garbage Collection ran during getCharacteristics call that fails. ' +
-   'Should not crash');
+            'GATT Server is disconnected. Cannot retrieve characteristics. ' +
+            '(Re)connect first with `device.gatt.connect`.',
+            'NetworkError'));
+    // Disconnect called to clear attributeInstanceMap and allow the object to
+    // get garbage collected.
+    service.device.gatt.disconnect();
+  })
+  .then(runGarbageCollection)
+  .then(() => func_promise), test_desc);
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/compositing/clip-child-by-non-stacking-ancestor-expected.txt b/third_party/WebKit/LayoutTests/compositing/clip-child-by-non-stacking-ancestor-expected.txt
index a14e8f4..58f7a777 100644
--- a/third_party/WebKit/LayoutTests/compositing/clip-child-by-non-stacking-ancestor-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/clip-child-by-non-stacking-ancestor-expected.txt
@@ -8,10 +8,21 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='container'",
-      "position": [8, 8],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#C0C0C0"
+      "backgroundColor": "#C0C0C0",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/columns/composited-in-paginated-expected.txt b/third_party/WebKit/LayoutTests/compositing/columns/composited-in-paginated-expected.txt
index 1e94afca..61e8c0c 100644
--- a/third_party/WebKit/LayoutTests/compositing/columns/composited-in-paginated-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/columns/composited-in-paginated-expected.txt
@@ -8,10 +8,21 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='composited box'",
-      "position": [818, 145],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [818, 145, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/contents-opaque/background-clip-expected.txt b/third_party/WebKit/LayoutTests/compositing/contents-opaque/background-clip-expected.txt
index 33591cd..e798a78 100644
--- a/third_party/WebKit/LayoutTests/compositing/contents-opaque/background-clip-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/contents-opaque/background-clip-expected.txt
@@ -8,22 +8,51 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='box composited'",
-      "position": [13, 8],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV class='box composited padding-clip'",
-      "position": [13, 113],
       "bounds": [100, 100],
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow DIV class='box composited content-clip'",
-      "position": [13, 218],
       "bounds": [100, 100],
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 3
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [13, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [13, 113, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [13, 218, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/contents-opaque/background-color-expected.txt b/third_party/WebKit/LayoutTests/compositing/contents-opaque/background-color-expected.txt
index 9b4dc01..83fe8a0 100644
--- a/third_party/WebKit/LayoutTests/compositing/contents-opaque/background-color-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/contents-opaque/background-color-expected.txt
@@ -8,22 +8,51 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='box composited'",
-      "position": [8, 8],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV class='box translucent composited'",
-      "position": [8, 108],
       "bounds": [100, 100],
-      "backgroundColor": "#00FF0080"
+      "backgroundColor": "#00FF0080",
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow DIV class='box opaque composited'",
-      "position": [8, 208],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 3
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 108, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 208, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/contents-opaque/body-background-painted-expected.txt b/third_party/WebKit/LayoutTests/compositing/contents-opaque/body-background-painted-expected.txt
index 4574e6f..30f5af9 100644
--- a/third_party/WebKit/LayoutTests/compositing/contents-opaque/body-background-painted-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/contents-opaque/body-background-painted-expected.txt
@@ -13,15 +13,26 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='underbody'",
-      "position": [8, 8],
       "bounds": [200, 200],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow HTML (foreground) Layer",
       "bounds": [800, 600]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/contents-opaque/body-background-skipped-expected.txt b/third_party/WebKit/LayoutTests/compositing/contents-opaque/body-background-skipped-expected.txt
index d3b7bdbe..1f0dce3 100644
--- a/third_party/WebKit/LayoutTests/compositing/contents-opaque/body-background-skipped-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/contents-opaque/body-background-skipped-expected.txt
@@ -12,15 +12,26 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='underbody'",
-      "position": [8, 8],
       "bounds": [200, 200],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow HTML (foreground) Layer",
       "bounds": [800, 600]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/contents-opaque/filter-expected.txt b/third_party/WebKit/LayoutTests/compositing/contents-opaque/filter-expected.txt
index 5a6eab2b..5ff8988 100644
--- a/third_party/WebKit/LayoutTests/compositing/contents-opaque/filter-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/contents-opaque/filter-expected.txt
@@ -8,13 +8,33 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='composited container-box'",
-      "position": [-18, -18],
-      "bounds": [157, 157]
+      "bounds": [157, 157],
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV class='composited container-box'",
-      "position": [10, 120],
-      "bounds": [100, 100]
+      "bounds": [100, 100],
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [-18, -18, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 120, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/contents-opaque/hidden-with-visible-child-expected.txt b/third_party/WebKit/LayoutTests/compositing/contents-opaque/hidden-with-visible-child-expected.txt
index ab76b32..7248538 100644
--- a/third_party/WebKit/LayoutTests/compositing/contents-opaque/hidden-with-visible-child-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/contents-opaque/hidden-with-visible-child-expected.txt
@@ -8,9 +8,20 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='caption'",
-      "position": [8, 13],
       "bounds": [200, 100],
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 13, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/contents-opaque/hidden-with-visible-text-expected.txt b/third_party/WebKit/LayoutTests/compositing/contents-opaque/hidden-with-visible-text-expected.txt
index d47930c..f5de4b00 100644
--- a/third_party/WebKit/LayoutTests/compositing/contents-opaque/hidden-with-visible-text-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/contents-opaque/hidden-with-visible-text-expected.txt
@@ -9,9 +9,20 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='caption'",
-      "position": [8, 13],
       "bounds": [200, 100],
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 13, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/contents-opaque/layer-opacity-expected.txt b/third_party/WebKit/LayoutTests/compositing/contents-opaque/layer-opacity-expected.txt
index fe35ad16..f00b027e 100644
--- a/third_party/WebKit/LayoutTests/compositing/contents-opaque/layer-opacity-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/contents-opaque/layer-opacity-expected.txt
@@ -8,11 +8,22 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='box opaque-background translucent composited'",
-      "position": [8, 8],
       "bounds": [100, 100],
       "opacity": 0.5,
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/contents-opaque/overflow-hidden-child-layers-expected.txt b/third_party/WebKit/LayoutTests/compositing/contents-opaque/overflow-hidden-child-layers-expected.txt
index bbb85dff..5fdfeb2 100644
--- a/third_party/WebKit/LayoutTests/compositing/contents-opaque/overflow-hidden-child-layers-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/contents-opaque/overflow-hidden-child-layers-expected.txt
@@ -8,9 +8,20 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='box'",
-      "position": [39, 31],
       "bounds": [138, 138],
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [39, 31, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/filters/sw-layer-overlaps-hw-shadow-expected.txt b/third_party/WebKit/LayoutTests/compositing/filters/sw-layer-overlaps-hw-shadow-expected.txt
index cd8a6d05..92b63fb 100644
--- a/third_party/WebKit/LayoutTests/compositing/filters/sw-layer-overlaps-hw-shadow-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/filters/sw-layer-overlaps-hw-shadow-expected.txt
@@ -12,15 +12,27 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='composited'",
-      "position": [105, 105],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#000000"
+      "backgroundColor": "#000000",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='software')",
       "bounds": [100, 100]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [105, 105, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/filters/sw-nested-shadow-overlaps-hw-nested-shadow-expected.txt b/third_party/WebKit/LayoutTests/compositing/filters/sw-nested-shadow-overlaps-hw-nested-shadow-expected.txt
index ad968a87..a50d639 100644
--- a/third_party/WebKit/LayoutTests/compositing/filters/sw-nested-shadow-overlaps-hw-nested-shadow-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/filters/sw-nested-shadow-overlaps-hw-nested-shadow-expected.txt
@@ -8,15 +8,26 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='composited-parent'",
-      "position": [230, 230],
       "bounds": [200, 200],
-      "backgroundColor": "#000000"
+      "backgroundColor": "#000000",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='software-parent'",
       "bounds": [200, 200],
       "backgroundColor": "#008000"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [230, 230, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/filters/sw-shadow-overlaps-hw-layer-expected.txt b/third_party/WebKit/LayoutTests/compositing/filters/sw-shadow-overlaps-hw-layer-expected.txt
index 948861d..b2d6bf9e 100644
--- a/third_party/WebKit/LayoutTests/compositing/filters/sw-shadow-overlaps-hw-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/filters/sw-shadow-overlaps-hw-layer-expected.txt
@@ -8,10 +8,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='composited'",
-      "position": [105, 105],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#000000"
+      "backgroundColor": "#000000",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='software'",
@@ -19,6 +19,17 @@
       "contentsOpaque": true,
       "backgroundColor": "#008000"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [105, 105, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/filters/sw-shadow-overlaps-hw-shadow-expected.txt b/third_party/WebKit/LayoutTests/compositing/filters/sw-shadow-overlaps-hw-shadow-expected.txt
index 6277a2f9..2f2b2d50 100644
--- a/third_party/WebKit/LayoutTests/compositing/filters/sw-shadow-overlaps-hw-shadow-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/filters/sw-shadow-overlaps-hw-shadow-expected.txt
@@ -8,10 +8,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='composited'",
-      "position": [130, 130],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#000000"
+      "backgroundColor": "#000000",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='software'",
@@ -19,6 +19,17 @@
       "contentsOpaque": true,
       "backgroundColor": "#008000"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [130, 130, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/fixed-position-changed-to-absolute-expected.txt b/third_party/WebKit/LayoutTests/compositing/fixed-position-changed-to-absolute-expected.txt
index af7f75c0..d7c2932 100644
--- a/third_party/WebKit/LayoutTests/compositing/fixed-position-changed-to-absolute-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/fixed-position-changed-to-absolute-expected.txt
@@ -12,16 +12,28 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [8, 13],
       "bounds": [150, 150],
       "contentsOpaque": true,
-      "backgroundColor": "#D9CCA7"
+      "backgroundColor": "#D9CCA7",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='layer-A')",
       "position": [20, 20],
       "bounds": [226, 180]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 13, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-composited-descendant-expected.txt b/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-composited-descendant-expected.txt
new file mode 100644
index 0000000..6f01eba
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-composited-descendant-expected.txt
@@ -0,0 +1,60 @@
+
+
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='composited'",
+      "bounds": [590, 208],
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='composited'",
+      "bounds": [590, 208],
+      "transform": 2
+    },
+    {
+      "name": "LayoutBlockFlow (relative positioned) DIV class='composited inner box'",
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 3
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 260, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [490, 108, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-dynamic-expected.txt b/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-dynamic-expected.txt
index 2b9f6ea..9d12ba5 100644
--- a/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-dynamic-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-dynamic-expected.txt
@@ -1,4 +1,3 @@
-
 {
   "layers": [
     {
@@ -9,14 +8,34 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [10, 10],
-      "bounds": [540, 240]
+      "bounds": [540, 240],
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [10, 260],
       "bounds": [50, 50],
-      "contentsOpaque": true
+      "contentsOpaque": true,
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 260, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-dynamic-negzindex-expected.txt b/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-dynamic-negzindex-expected.txt
index 8f0105f..0013b0f 100644
--- a/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-dynamic-negzindex-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-dynamic-negzindex-expected.txt
@@ -18,8 +18,8 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [0, 250],
-      "bounds": [150, 150]
+      "bounds": [150, 150],
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow PRE id='layers')",
@@ -32,6 +32,18 @@
       "contentsOpaque": true,
       "backgroundColor": "#008000"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, 250, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-dynamic.html b/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-dynamic.html
index 31efb20..fb03a9a 100644
--- a/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-dynamic.html
+++ b/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-dynamic.html
@@ -28,10 +28,6 @@
   body.changed img.to-hidden {
       visibility: hidden;
   }
-  
-  #layers {
-      opacity: 0; /* hide from pixel result */
-  }
 </style>
 <script>
     if (window.testRunner) {
@@ -43,8 +39,8 @@
     {
         window.setTimeout(function() {
             document.body.classList.add('changed');
-            if (window.testRunner) {
-                document.getElementById('layers').innerText = window.internals.layerTreeAsText(document);
+            if (window.testRunner && window.internals) {
+                testRunner.setCustomTextOutput(internals.layerTreeAsText(document));
                 testRunner.notifyDone();
             }
         }, 0);
@@ -61,5 +57,4 @@
         <img style="position: absolute; left: 10px; top: 10px; z-index: 0;">
         <img style="position: absolute; left: 500px; top: 200px; z-index: 0;" class="to-hidden">
     </div>
-<pre id="layers">Layer tree goes here in DRT</pre>
 </body>
diff --git a/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-expected.txt b/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-expected.txt
index d526f52..4a32ea5 100644
--- a/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/geometry/bounds-ignores-hidden-expected.txt
@@ -9,9 +9,20 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [10, 10],
       "bounds": [50, 50],
-      "contentsOpaque": true
+      "contentsOpaque": true,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/geometry/composited-in-columns-expected.txt b/third_party/WebKit/LayoutTests/compositing/geometry/composited-in-columns-expected.txt
new file mode 100644
index 0000000..804d0006
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/geometry/composited-in-columns-expected.txt
@@ -0,0 +1,77 @@
+ {
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='composited block'",
+      "bounds": [210, 60],
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='composited box'",
+      "bounds": [50, 50],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 2
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='composited block'",
+      "bounds": [210, 60],
+      "backgroundColor": "#0000FF",
+      "transform": 3
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='composited box'",
+      "bounds": [50, 50],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 4
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 162, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [5, 5, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [272, 88, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [5, 5, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/compositing/geometry/flipped-writing-mode-expected.txt b/third_party/WebKit/LayoutTests/compositing/geometry/flipped-writing-mode-expected.txt
index 4cc608f2..3d094eb 100644
--- a/third_party/WebKit/LayoutTests/compositing/geometry/flipped-writing-mode-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/geometry/flipped-writing-mode-expected.txt
@@ -8,16 +8,37 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='composited flipped'",
-      "position": [18, 10],
       "bounds": [250, 200],
       "contentsOpaque": true,
-      "backgroundColor": "#C0C0C0"
+      "backgroundColor": "#C0C0C0",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV class='composited box'",
-      "position": [53, 20],
       "bounds": [195, 100],
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [35, 10, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/geometry/foreground-layer-expected.txt b/third_party/WebKit/LayoutTests/compositing/geometry/foreground-layer-expected.txt
index 20309a0..6d085a1 100644
--- a/third_party/WebKit/LayoutTests/compositing/geometry/foreground-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/geometry/foreground-layer-expected.txt
@@ -9,43 +9,45 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV class='main box'",
-      "position": [19, 89],
       "bounds": [318, 318],
-      "backgroundColor": "#FF0000"
+      "backgroundColor": "#FF0000",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='negative child'",
       "bounds": [50, 50],
       "drawsContent": false,
-      "transform": 2
+      "transform": 3
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV class='main box' (foreground) Layer",
-      "position": [19, 89],
-      "bounds": [318, 318]
+      "bounds": [318, 318],
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV class='main box'",
-      "position": [363, 19],
       "bounds": [318, 318],
-      "backgroundColor": "#FF0000"
+      "backgroundColor": "#FF0000",
+      "transform": 4
     },
     {
       "name": "Child Containment Layer",
-      "position": [422, 78],
+      "position": [59, 59],
       "bounds": [200, 200],
-      "drawsContent": false
-    },
-    {
-      "name": "LayoutBlockFlow (positioned) DIV class='negative child'",
-      "bounds": [50, 50],
       "drawsContent": false,
       "transform": 4
     },
     {
+      "name": "LayoutBlockFlow (positioned) DIV class='negative child'",
+      "bounds": [50, 50],
+      "drawsContent": false,
+      "transform": 6
+    },
+    {
       "name": "LayoutBlockFlow (relative positioned) DIV class='main box' (foreground) Layer",
-      "position": [422, 78],
-      "bounds": [200, 200]
+      "position": [59, 59],
+      "bounds": [200, 200],
+      "transform": 4
     }
   ],
   "transforms": [
@@ -55,7 +57,7 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
-        [78, 148, 0, 1]
+        [19, 89, 0, 1]
       ]
     },
     {
@@ -65,21 +67,41 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
-        [0, 0, 1, 1]
+        [59, 59, 0, 1]
       ]
     },
     {
       "id": 3,
+      "parent": 2,
       "transform": [
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
-        [422, 78, 0, 1]
+        [0, 0, 1, 1]
       ]
     },
     {
       "id": 4,
-      "parent": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [363, 19, 0, 1]
+      ]
+    },
+    {
+      "id": 5,
+      "parent": 4,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [59, 59, 0, 1]
+      ]
+    },
+    {
+      "id": 6,
+      "parent": 5,
       "transform": [
         [1, 0, 0, 0],
         [0, 1, 0, 0],
diff --git a/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-opacity-transition-expected.txt b/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-opacity-transition-expected.txt
index 702803d..67eb0450 100644
--- a/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-opacity-transition-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-opacity-transition-expected.txt
@@ -14,15 +14,27 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='compositing'",
-      "position": [8, 8],
       "bounds": [1, 1],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (relative positioned) DIV class='test')",
       "position": [-9992, -1],
       "bounds": [10100, 100]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/limit-layer-bounds-overflow-root-expected.txt b/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-overflow-root-expected.txt
similarity index 66%
rename from third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/limit-layer-bounds-overflow-root-expected.txt
rename to third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-overflow-root-expected.txt
index 6fae84d9..6acc8188 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/limit-layer-bounds-overflow-root-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-overflow-root-expected.txt
@@ -12,14 +12,26 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='compositing'",
-      "position": [21, 21],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='indicator')",
       "bounds": [211, 142]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [21, 21, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-positioned-expected.txt b/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-positioned-expected.txt
index 43d10d4..61a299d0 100644
--- a/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-positioned-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-positioned-expected.txt
@@ -13,15 +13,27 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='compositing'",
-      "position": [29, 29],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='indicator')",
       "position": [-9971, 8],
       "bounds": [10121, 142]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [29, 29, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-positioned-transition-expected.txt b/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-positioned-transition-expected.txt
index 26a4ae0..b6e885c 100644
--- a/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-positioned-transition-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-positioned-transition-expected.txt
@@ -13,15 +13,27 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='compositing'",
-      "position": [29, 29],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='indicator')",
       "position": [-771, 8],
       "bounds": [1200, 142]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [29, 29, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-transformed-expected.txt b/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-transformed-expected.txt
index e68766f..1073e43 100644
--- a/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-transformed-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/geometry/limit-layer-bounds-transformed-expected.txt
@@ -13,15 +13,27 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='compositing'",
-      "position": [129, 29],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='indicator')",
       "position": [-871, 8],
       "bounds": [1221, 142]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [129, 29, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/become-composited-nested-iframes-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/become-composited-nested-iframes-expected.txt
index e8071aa..9ac0b68b 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/become-composited-nested-iframes-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/become-composited-nested-iframes-expected.txt
@@ -76,10 +76,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [49, 141],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "LayoutIFrame IFRAME",
@@ -150,10 +150,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [49, 365],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow DIV id='box' class='composited'",
@@ -161,6 +161,26 @@
       "contentsOpaque": true,
       "backgroundColor": "#0000FF"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [49, 141, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [49, 365, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/become-overlapped-iframe-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/become-overlapped-iframe-expected.txt
index acbfd15..fcbfc29 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/become-overlapped-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/become-overlapped-iframe-expected.txt
@@ -43,10 +43,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [91, 83],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Frame Vertical Scrollbar Layer",
@@ -60,6 +60,17 @@
       "bounds": [150, 150],
       "backgroundColor": "#00000099"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [91, 83, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/composited-parent-iframe-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/composited-parent-iframe-expected.txt
index 36a7222..b3583bee 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/composited-parent-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/composited-parent-iframe-expected.txt
@@ -9,50 +9,77 @@
     },
     {
       "name": "LayoutIFrame IFRAME",
-      "position": [-11, -11],
-      "bounds": [368, 218]
+      "bounds": [368, 218],
+      "transform": 1
     },
     {
       "name": "Frame Overflow Controls Host Layer",
-      "position": [23, 23],
+      "position": [34, 34],
       "bounds": [300, 150],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Frame Clipping Layer",
-      "position": [23, 23],
+      "position": [34, 34],
       "bounds": [285, 150],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Frame Scrolling Layer",
-      "position": [23, 23],
-      "drawsContent": false
+      "position": [34, 34],
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Content Root Layer",
-      "position": [23, 23],
+      "position": [34, 34],
       "bounds": [285, 230],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutView #document",
-      "position": [23, 23],
+      "position": [34, 34],
       "bounds": [285, 230],
-      "backgroundColor": "#C0C0C0"
+      "backgroundColor": "#C0C0C0",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [41, 33],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 2
     },
     {
       "name": "Frame Vertical Scrollbar Layer",
-      "position": [308, 23],
+      "position": [319, 34],
       "bounds": [15, 150],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [-11, -11, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [52, 44, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe-delayed-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe-delayed-expected.txt
index f61877e..e90ade0 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe-delayed-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe-delayed-expected.txt
@@ -45,10 +45,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [61, 153],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Frame Vertical Scrollbar Layer",
@@ -58,10 +58,30 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='box' class='composited'",
-      "position": [8, 8],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [61, 153, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe-expected.txt
index 2686d7a8..b0f69a2 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe-expected.txt
@@ -9,50 +9,56 @@
     },
     {
       "name": "LayoutIFrame IFRAME id='parent-iframe'",
-      "position": [9, 9],
-      "bounds": [368, 218]
+      "bounds": [368, 218],
+      "transform": 1
     },
     {
       "name": "Frame Overflow Controls Host Layer",
-      "position": [43, 43],
+      "position": [34, 34],
       "bounds": [300, 150],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Frame Clipping Layer",
-      "position": [43, 43],
+      "position": [34, 34],
       "bounds": [285, 150],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Frame Scrolling Layer",
-      "position": [43, 43],
-      "drawsContent": false
+      "position": [34, 34],
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Content Root Layer",
-      "position": [43, 43],
+      "position": [34, 34],
       "bounds": [285, 230],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutView #document",
-      "position": [43, 43],
+      "position": [34, 34],
       "bounds": [285, 230],
-      "backgroundColor": "#C0C0C0"
+      "backgroundColor": "#C0C0C0",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV id='test' class='composited box'",
-      "position": [61, 53],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 2
     },
     {
       "name": "Frame Vertical Scrollbar Layer",
-      "position": [328, 43],
+      "position": [319, 34],
       "bounds": [15, 150],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='overlay'",
@@ -60,6 +66,27 @@
       "bounds": [50, 50],
       "backgroundColor": "#00000033"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [9, 9, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [52, 44, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe2-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe2-expected.txt
index ee29868..a09d7c1 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe2-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe2-expected.txt
@@ -9,50 +9,56 @@
     },
     {
       "name": "LayoutIFrame IFRAME id='iframe' class='composited'",
-      "position": [9, 9],
-      "bounds": [368, 218]
+      "bounds": [368, 218],
+      "transform": 1
     },
     {
       "name": "Frame Overflow Controls Host Layer",
-      "position": [43, 43],
+      "position": [34, 34],
       "bounds": [300, 150],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Frame Clipping Layer",
-      "position": [43, 43],
+      "position": [34, 34],
       "bounds": [285, 150],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Frame Scrolling Layer",
-      "position": [43, 43],
-      "drawsContent": false
+      "position": [34, 34],
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Content Root Layer",
-      "position": [43, 43],
+      "position": [34, 34],
       "bounds": [285, 230],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutView #document",
-      "position": [43, 43],
+      "position": [34, 34],
       "bounds": [285, 230],
-      "backgroundColor": "#C0C0C0"
+      "backgroundColor": "#C0C0C0",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [61, 53],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 2
     },
     {
       "name": "Frame Vertical Scrollbar Layer",
-      "position": [328, 43],
+      "position": [319, 34],
       "bounds": [15, 150],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='overlay'",
@@ -60,6 +66,27 @@
       "bounds": [50, 50],
       "backgroundColor": "#00000033"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [9, 9, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [52, 44, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe3-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe3-expected.txt
index aa42f3f..a6250c3 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe3-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/connect-compositing-iframe3-expected.txt
@@ -9,50 +9,77 @@
     },
     {
       "name": "LayoutIFrame IFRAME id='iframe' class='composited'",
-      "position": [9, 9],
-      "bounds": [368, 218]
+      "bounds": [368, 218],
+      "transform": 1
     },
     {
       "name": "Frame Overflow Controls Host Layer",
-      "position": [43, 43],
+      "position": [34, 34],
       "bounds": [300, 150],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Frame Clipping Layer",
-      "position": [43, 43],
+      "position": [34, 34],
       "bounds": [285, 150],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Frame Scrolling Layer",
-      "position": [43, 43],
-      "drawsContent": false
+      "position": [34, 34],
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Content Root Layer",
-      "position": [43, 43],
+      "position": [34, 34],
       "bounds": [285, 230],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutView #document",
-      "position": [43, 43],
+      "position": [34, 34],
       "bounds": [285, 230],
-      "backgroundColor": "#C0C0C0"
+      "backgroundColor": "#C0C0C0",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [61, 53],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 2
     },
     {
       "name": "Frame Vertical Scrollbar Layer",
-      "position": [328, 43],
+      "position": [319, 34],
       "bounds": [15, 150],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [9, 9, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [52, 44, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/enter-compositing-iframe-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/enter-compositing-iframe-expected.txt
index 2686d7a8..4e0aaae 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/enter-compositing-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/enter-compositing-iframe-expected.txt
@@ -43,10 +43,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='test' class='composited box'",
-      "position": [61, 53],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Frame Vertical Scrollbar Layer",
@@ -60,6 +60,17 @@
       "bounds": [50, 50],
       "backgroundColor": "#00000033"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [61, 53, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/iframe-resize-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/iframe-resize-expected.txt
index dfac2733..876c5f4 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/iframe-resize-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/iframe-resize-expected.txt
@@ -43,10 +43,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [61, 53],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Frame Vertical Scrollbar Layer",
@@ -60,6 +60,17 @@
       "bounds": [50, 50],
       "backgroundColor": "#00000033"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [61, 53, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/iframe-size-from-zero-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/iframe-size-from-zero-expected.txt
index e361adeb..cb4ddbc 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/iframe-size-from-zero-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/iframe-size-from-zero-expected.txt
@@ -43,10 +43,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [61, 53],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Frame Vertical Scrollbar Layer",
@@ -60,6 +60,17 @@
       "bounds": [50, 50],
       "backgroundColor": "#00000033"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [61, 53, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/invisible-nested-iframe-hide-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/invisible-nested-iframe-hide-expected.txt
index dc62e39..e1fe83f9 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/invisible-nested-iframe-hide-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/invisible-nested-iframe-hide-expected.txt
@@ -8,10 +8,21 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='box'",
-      "position": [18, 10],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/iframes/invisible-nested-iframe-show-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/invisible-nested-iframe-show-expected.txt
similarity index 90%
rename from third_party/WebKit/LayoutTests/platform/mac/compositing/iframes/invisible-nested-iframe-show-expected.txt
rename to third_party/WebKit/LayoutTests/compositing/iframes/invisible-nested-iframe-show-expected.txt
index 25dcd60..1853f125 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/iframes/invisible-nested-iframe-show-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/invisible-nested-iframe-show-expected.txt
@@ -76,10 +76,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [50, 42],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Frame Vertical Scrollbar Layer",
@@ -92,7 +92,7 @@
       "bounds": [210, 210],
       "contentsOpaque": true,
       "backgroundColor": "#0000FF",
-      "transform": 2
+      "transform": 3
     }
   ],
   "transforms": [
@@ -102,12 +102,21 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
-        [18, 202, 0, 1]
+        [50, 42, 0, 1]
       ]
     },
     {
       "id": 2,
-      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 202, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
       "transform": [
         [1, 0, 0, 0],
         [0, 1, 0, 0],
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/overlapped-iframe-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/overlapped-iframe-expected.txt
index d1bab85..68e1202 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/overlapped-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/overlapped-iframe-expected.txt
@@ -43,10 +43,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [61, 53],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Frame Vertical Scrollbar Layer",
@@ -60,6 +60,17 @@
       "bounds": [50, 50],
       "backgroundColor": "#00000033"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [61, 53, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/overlapped-iframe-iframe-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/overlapped-iframe-iframe-expected.txt
index 89d50a8..bee8ebc 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/overlapped-iframe-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/overlapped-iframe-iframe-expected.txt
@@ -41,16 +41,27 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [20, 12],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "LayoutIFrame (positioned) IFRAME id='overlap'",
       "position": [250, 0],
       "bounds": [304, 304]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 12, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/overlapped-nested-iframes-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/overlapped-nested-iframes-expected.txt
index dc27791..677914b 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/overlapped-nested-iframes-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/overlapped-nested-iframes-expected.txt
@@ -89,11 +89,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [49, 171],
       "bounds": [210, 210],
       "contentsOpaque": true,
       "backgroundColor": "#0000FF",
-      "transform": 1
+      "transform": 2
     },
     {
       "name": "LayoutIFrame IFRAME",
@@ -176,11 +175,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [49, 395],
       "bounds": [210, 210],
       "contentsOpaque": true,
       "backgroundColor": "#0000FF",
-      "transform": 1
+      "transform": 3
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='banner'",
@@ -199,6 +197,26 @@
         [0, 0, 1, 0],
         [0, -100, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [49, 171, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [49, 395, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/resizer-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/resizer-expected.txt
index ffa828a..aa0daa6 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/resizer-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/resizer-expected.txt
@@ -9,61 +9,89 @@
     },
     {
       "name": "LayoutIFrame IFRAME class='container'",
-      "position": [8, 8],
-      "bounds": [304, 154]
+      "bounds": [304, 154],
+      "transform": 1
     },
     {
       "name": "Frame Overflow Controls Host Layer",
-      "position": [10, 10],
+      "position": [2, 2],
       "bounds": [300, 150],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Frame Clipping Layer",
-      "position": [10, 10],
+      "position": [2, 2],
       "bounds": [285, 150],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Frame Scrolling Layer",
-      "position": [10, 10],
-      "drawsContent": false
+      "position": [2, 2],
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Content Root Layer",
-      "position": [10, 10],
+      "position": [2, 2],
       "bounds": [285, 230],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutView #document",
-      "position": [10, 10],
+      "position": [2, 2],
       "bounds": [285, 230],
-      "backgroundColor": "#C0C0C0"
+      "backgroundColor": "#C0C0C0",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [28, 20],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 2
     },
     {
       "name": "Frame Vertical Scrollbar Layer",
-      "position": [295, 10],
+      "position": [287, 2],
       "bounds": [15, 150],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Overflow Controls Host Layer",
-      "position": [8, 8],
       "bounds": [304, 154],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Scroll Corner Layer",
-      "position": [295, 145],
-      "bounds": [15, 15]
+      "position": [287, 137],
+      "bounds": [15, 15],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 12, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/scrolling-iframe-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/scrolling-iframe-expected.txt
index 72fa39d..7b71e285f 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/scrolling-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/scrolling-iframe-expected.txt
@@ -44,11 +44,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [151, 143],
       "bounds": [200, 200],
       "contentsOpaque": true,
       "backgroundColor": "#0000FF",
-      "transform": 1
+      "transform": 2
     },
     {
       "name": "Frame Horizontal Scrollbar Layer",
@@ -83,6 +82,16 @@
         [0, 0, 1, 0],
         [-80, -80, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [151, 143, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/images/clip-on-directly-composited-image-expected.txt b/third_party/WebKit/LayoutTests/compositing/images/clip-on-directly-composited-image-expected.txt
index e60a809..f49cc62 100644
--- a/third_party/WebKit/LayoutTests/compositing/images/clip-on-directly-composited-image-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/images/clip-on-directly-composited-image-expected.txt
@@ -9,8 +9,19 @@
     },
     {
       "name": "LayoutImage (positioned) IMG class='composited'",
-      "position": [210, 23],
-      "bounds": [140, 140]
+      "bounds": [140, 140],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [210, 23, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/images/direct-image-dynamic-border-draws-content-expected.txt b/third_party/WebKit/LayoutTests/compositing/images/direct-image-dynamic-border-draws-content-expected.txt
index 84e27ec..a7d1750 100644
--- a/third_party/WebKit/LayoutTests/compositing/images/direct-image-dynamic-border-draws-content-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/images/direct-image-dynamic-border-draws-content-expected.txt
@@ -11,9 +11,20 @@
     },
     {
       "name": "LayoutImage IMG class='composited'",
-      "position": [8, 8],
       "bounds": [256, 256],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -30,8 +41,19 @@
     },
     {
       "name": "LayoutImage IMG class='composited border'",
-      "position": [8, 8],
-      "bounds": [260, 260]
+      "bounds": [260, 260],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/animation-overlap-with-children-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/animation-overlap-with-children-expected.txt
index cd08a1a..ed73363be 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/animation-overlap-with-children-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/animation-overlap-with-children-expected.txt
@@ -20,10 +20,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='composited banner'",
-      "position": [14, 14],
       "bounds": [250, 50],
       "contentsOpaque": true,
-      "backgroundColor": "#C0C0C0"
+      "backgroundColor": "#C0C0C0",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='test1 box')",
@@ -37,6 +37,18 @@
       "contentsOpaque": true,
       "backgroundColor": "#0000FF"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 14, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/assumed-overlap-for-inline-transform-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/assumed-overlap-for-inline-transform-expected.txt
index 133cbd03..6fdbe32 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/assumed-overlap-for-inline-transform-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/assumed-overlap-for-inline-transform-expected.txt
@@ -12,16 +12,28 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV class='box'",
-      "position": [8, 8],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (relative positioned) DIV class='box')",
       "position": [8, 108],
       "bounds": [100, 100]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/fixed-position-and-transform-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/fixed-position-and-transform-expected.txt
index 4082b7be..96d0f8c5 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/fixed-position-and-transform-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/fixed-position-and-transform-expected.txt
@@ -9,11 +9,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='indicator'",
-      "position": [100, 1100],
       "bounds": [256, 256],
       "contentsOpaque": true,
       "backgroundColor": "#FF0000",
-      "transform": 1
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='overlap'",
@@ -33,6 +32,16 @@
         [0, 0, 1, 0],
         [0, -1000, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 1100, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt
index 4abb0ee..2ecdd98b 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/fixed-position-nonscrollable-body-overlap-expected.txt
@@ -10,10 +10,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='absolute composited red box'",
-      "position": [10, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#FF0000"
+      "backgroundColor": "#FF0000",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='fixed lime box'",
@@ -22,6 +22,17 @@
       "contentsOpaque": true,
       "backgroundColor": "#00FF00"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 100, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt
index c869873..557a057 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/fixed-position-nonscrollable-iframes-in-scrollable-page-expected.txt
@@ -9,21 +9,41 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box'",
-      "position": [50, 360],
       "bounds": [300, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#00FFFF"
+      "backgroundColor": "#00FFFF",
+      "transform": 1
     },
     {
       "name": "LayoutIFrame (positioned) IFRAME id='iframe2' class='composited'",
-      "position": [10, 200],
-      "bounds": [154, 154]
+      "bounds": [154, 154],
+      "transform": 2
     },
     {
       "name": "LayoutIFrame (positioned) IFRAME id='iframe3'",
       "position": [10, 380],
       "bounds": [154, 154]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [50, 360, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 200, 0, 1]
+      ]
+    }
   ]
 }
 Composited box underneath iframe. 
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/no-compositing-for-fixed-position-under-transform-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/no-compositing-for-fixed-position-under-transform-expected.txt
index 6b9b2c6..0b48806 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/no-compositing-for-fixed-position-under-transform-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/no-compositing-for-fixed-position-under-transform-expected.txt
@@ -9,8 +9,19 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='transform'",
-      "position": [8, 8],
-      "bounds": [100, 30]
+      "bounds": [100, 30],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/overflow-scroll-overlap-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/overflow-scroll-overlap-expected.txt
index 2fc74a4..fee7bdb 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/overflow-scroll-overlap-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/overflow-scroll-overlap-expected.txt
@@ -12,9 +12,9 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='composited'",
-      "position": [8, 8],
       "bounds": [30, 30],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='scroller' class='overflow')",
@@ -39,6 +39,18 @@
       "position": [43, 184],
       "bounds": [210, 100]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/overlap-clipping-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/overlap-clipping-expected.txt
index c795b8b7..2abf680 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/overlap-clipping-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/overlap-clipping-expected.txt
@@ -14,10 +14,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='child'",
-      "position": [50, 50],
       "bounds": [500, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
     },
     {
       "name": "Squashing Containment Layer",
@@ -25,16 +25,38 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='child'",
-      "position": [50, 200],
       "bounds": [500, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 2
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='overlap')",
       "position": [450, 200],
       "bounds": [100, 100]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [50, 50, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [50, 200, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/overlap-transformed-layer-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/overlap-transformed-layer-expected.txt
index 8db857c..2afbe8a7 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/overlap-transformed-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/overlap-transformed-layer-expected.txt
@@ -14,10 +14,9 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='composited'",
-      "position": [385, 0],
       "contentsOpaque": true,
       "drawsContent": false,
-      "transform": 2
+      "transform": 3
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='green'",
@@ -45,6 +44,16 @@
         [0, 0, 1, 0],
         [-10, 0, 0, 1]
       ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [385, 0, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/overlap-transformed-layer-with-transform-body-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/overlap-transformed-layer-with-transform-body-expected.txt
index e0f7139..110cf2a8 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/overlap-transformed-layer-with-transform-body-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/overlap-transformed-layer-with-transform-body-expected.txt
@@ -8,28 +8,27 @@
     },
     {
       "name": "LayoutBlockFlow BODY",
-      "position": [8, 8],
-      "bounds": [784, 584]
+      "bounds": [784, 584],
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV id='container'",
       "bounds": [256, 256],
       "contentsOpaque": true,
-      "transform": 2
+      "transform": 3
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='composited'",
-      "position": [385, 0],
       "contentsOpaque": true,
       "drawsContent": false,
-      "transform": 2
+      "transform": 4
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='green'",
-      "position": [8, 8],
       "bounds": [300, 300],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
     }
   ],
   "transforms": [
@@ -39,7 +38,7 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
-        [23, 8, 0, 1]
+        [8, 8, 0, 1]
       ]
     },
     {
@@ -49,8 +48,28 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
+        [15, 0, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
         [-10, 0, 0, 1]
       ]
+    },
+    {
+      "id": 4,
+      "parent": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [385, 0, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/remove-clipping-layer-with-no-children-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/remove-clipping-layer-with-no-children-expected.txt
index d10128e..9ecb187 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/remove-clipping-layer-with-no-children-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/remove-clipping-layer-with-no-children-expected.txt
@@ -8,10 +8,21 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='parent' class='fade'",
-      "position": [8, 8],
       "bounds": [400, 400],
       "contentsOpaque": true,
-      "backgroundColor": "#FFC0CB"
+      "backgroundColor": "#FFC0CB",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/rotate3d-overlap-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/rotate3d-overlap-expected.txt
index f804919..8f46d20 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/rotate3d-overlap-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/rotate3d-overlap-expected.txt
@@ -10,9 +10,9 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV class='box translateZ'",
-      "position": [23, 23],
       "bounds": [110, 110],
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Squashing Containment Layer",
@@ -22,7 +22,7 @@
       "name": "LayoutBlockFlow (relative positioned) DIV class='composited box rotate15'",
       "bounds": [110, 110],
       "backgroundColor": "#0000FF",
-      "transform": 2
+      "transform": 3
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (relative positioned) DIV class='box')",
@@ -37,7 +37,7 @@
       "name": "LayoutBlockFlow (relative positioned) DIV class='composited box rotate45'",
       "bounds": [110, 110],
       "backgroundColor": "#0000FF",
-      "transform": 4
+      "transform": 5
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (relative positioned) DIV class='box')",
@@ -52,13 +52,22 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
+        [23, 23, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
         [167, 23, 0, 1]
       ],
       "flattenInheritedTransform": false
     },
     {
-      "id": 2,
-      "parent": 1,
+      "id": 3,
+      "parent": 2,
       "transform": [
         [0.965925826289068, 0.258819045102521, 0, 0],
         [-0.258819045102521, 0.965925826289068, 0, 0],
@@ -69,7 +78,7 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 3,
+      "id": 4,
       "transform": [
         [1, 0, 0, 0],
         [0, 1, 0, 0],
@@ -79,8 +88,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 4,
-      "parent": 3,
+      "id": 5,
+      "parent": 4,
       "transform": [
         [0.707106781186548, 0.707106781186548, 0, 0],
         [-0.707106781186548, 0.707106781186548, 0, 0],
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/scroll-partial-update-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/scroll-partial-update-expected.txt
index 0d3793a8..8666325 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/scroll-partial-update-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/scroll-partial-update-expected.txt
@@ -13,15 +13,27 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='composited'",
-      "position": [8, 8],
       "bounds": [20, 20],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='overlay')",
       "position": [10, 8],
       "bounds": [400, 204]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/squashing-into-ancestor-clipping-layer-change-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/squashing-into-ancestor-clipping-layer-change-expected.txt
index 08cfe06..94a1b56 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/squashing-into-ancestor-clipping-layer-change-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/squashing-into-ancestor-clipping-layer-change-expected.txt
@@ -14,16 +14,28 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='composited'",
-      "position": [8, 58],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#FA8072"
+      "backgroundColor": "#FA8072",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (relative positioned) DIV id='squashed')",
       "position": [8, 58],
       "bounds": [100, 100]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 58, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/stacking-context-overlap-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/stacking-context-overlap-expected.txt
index b366fb3..10f6547ca 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/stacking-context-overlap-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/stacking-context-overlap-expected.txt
@@ -12,16 +12,28 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='trigger'",
-      "position": [8, 8],
       "bounds": [20, 20],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (relative positioned) DIV class='container')",
       "position": [8, 18],
       "bounds": [142, 142]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/stacking-context-overlap-nested-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/stacking-context-overlap-nested-expected.txt
index f79d5cd..5939975 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/stacking-context-overlap-nested-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/stacking-context-overlap-nested-expected.txt
@@ -8,15 +8,15 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box'",
-      "position": [10, 10],
       "bounds": [120, 120],
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box'",
-      "position": [60, 60],
       "bounds": [220, 120],
-      "backgroundColor": "#FF000099"
+      "backgroundColor": "#FF000099",
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='indicator'",
@@ -25,6 +25,27 @@
       "contentsOpaque": true,
       "backgroundColor": "#0000FF"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [50, 50, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/translatez-added-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/translatez-added-expected.txt
index b31c850..06578a0 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/translatez-added-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/translatez-added-expected.txt
@@ -8,10 +8,21 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='test' class='box composited'",
-      "position": [18, 10],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/layer-creation/translatez-overlap-expected.txt b/third_party/WebKit/LayoutTests/compositing/layer-creation/translatez-overlap-expected.txt
index 5eaa747..8a20c50 100644
--- a/third_party/WebKit/LayoutTests/compositing/layer-creation/translatez-overlap-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/layer-creation/translatez-overlap-expected.txt
@@ -8,10 +8,21 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV class='composited box'",
-      "position": [18, 10],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/masks/mask-layer-size-expected.txt b/third_party/WebKit/LayoutTests/compositing/masks/mask-layer-size-expected.txt
index 59adfc1..7f6c87d 100644
--- a/third_party/WebKit/LayoutTests/compositing/masks/mask-layer-size-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/masks/mask-layer-size-expected.txt
@@ -8,7 +8,6 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='masked'",
-      "position": [10, 10],
       "bounds": [400, 200],
       "contentsOpaque": true,
       "backgroundColor": "#000000",
@@ -17,6 +16,18 @@
           "name": "Mask Layer",
           "bounds": [400, 200]
         }
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
       ]
     }
   ]
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/composited-scrolling-paint-phases-expected.txt b/third_party/WebKit/LayoutTests/compositing/overflow/composited-scrolling-paint-phases-expected.txt
index b2b32313..11eb683 100644
--- a/third_party/WebKit/LayoutTests/compositing/overflow/composited-scrolling-paint-phases-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/composited-scrolling-paint-phases-expected.txt
@@ -24,7 +24,6 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV class='composited'",
-      "position": [29, 31],
       "bounds": [80, 10],
       "contentsOpaque": true,
       "backgroundColor": "#008000",
@@ -33,7 +32,8 @@
         "GraphicsLayerPaintForeground",
         "GraphicsLayerPaintMask",
         "GraphicsLayerPaintDecoration"
-      ]
+      ],
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow HTML (foreground) Layer",
@@ -122,6 +122,17 @@
         "GraphicsLayerPaintDecoration"
       ]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [29, 31, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/content-gains-scrollbars-expected.txt b/third_party/WebKit/LayoutTests/compositing/overflow/content-gains-scrollbars-expected.txt
index 6bee4c5..da50b6b 100644
--- a/third_party/WebKit/LayoutTests/compositing/overflow/content-gains-scrollbars-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/content-gains-scrollbars-expected.txt
@@ -8,147 +8,191 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='container'",
-      "position": [8, 13],
-      "bounds": [100, 100]
+      "bounds": [100, 100],
+      "transform": 1
     },
     {
       "name": "Scrolling Layer",
-      "position": [8, 13],
       "bounds": [85, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Scrolling Contents Layer",
-      "position": [8, 13],
       "bounds": [85, 200],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='vertical' class='content tall'",
-      "position": [8, 13],
       "bounds": [10, 200],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Overflow Controls Host Layer",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Vertical Scrollbar Layer",
-      "position": [93, 13],
+      "position": [85, 0],
       "bounds": [15, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='container'",
-      "position": [8, 13],
-      "bounds": [100, 100]
+      "bounds": [100, 100],
+      "transform": 2
     },
     {
       "name": "Scrolling Layer",
-      "position": [8, 13],
       "bounds": [100, 85],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "Scrolling Contents Layer",
-      "position": [8, 13],
       "bounds": [200, 85],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='horizontal' class='content wide'",
-      "position": [8, 13],
       "bounds": [200, 10],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "Overflow Controls Host Layer",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "Horizontal Scrollbar Layer",
-      "position": [8, 98],
+      "position": [0, 85],
       "bounds": [100, 15],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='container'",
-      "position": [8, 13],
-      "bounds": [100, 100]
+      "bounds": [100, 100],
+      "transform": 3
     },
     {
       "name": "Scrolling Layer",
-      "position": [8, 13],
       "bounds": [85, 85],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "Scrolling Contents Layer",
-      "position": [8, 13],
       "bounds": [200, 200],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='both' class='content wide tall'",
-      "position": [8, 13],
       "bounds": [200, 200],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "Overflow Controls Host Layer",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "Horizontal Scrollbar Layer",
-      "position": [8, 98],
+      "position": [0, 85],
       "bounds": [85, 15],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "Vertical Scrollbar Layer",
-      "position": [93, 13],
+      "position": [85, 0],
       "bounds": [15, 85],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "Scroll Corner Layer",
-      "position": [93, 98],
-      "bounds": [15, 15]
+      "position": [85, 85],
+      "bounds": [15, 15],
+      "transform": 3
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='corner' class='container resizeWidget'",
-      "position": [8, 13],
-      "bounds": [100, 100]
+      "bounds": [100, 100],
+      "transform": 4
     },
     {
       "name": "Child Containment Layer",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 4
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='content'",
-      "position": [8, 13],
       "bounds": [10, 10],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 4
     },
     {
       "name": "Overflow Controls Host Layer",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 4
     },
     {
       "name": "Scroll Corner Layer",
-      "position": [93, 98],
-      "bounds": [15, 15]
+      "position": [85, 85],
+      "bounds": [15, 15],
+      "transform": 4
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 13, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 13, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 13, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 13, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/content-loses-scrollbars-expected.txt b/third_party/WebKit/LayoutTests/compositing/overflow/content-loses-scrollbars-expected.txt
index e29b2fe6..a0c9111 100644
--- a/third_party/WebKit/LayoutTests/compositing/overflow/content-loses-scrollbars-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/content-loses-scrollbars-expected.txt
@@ -8,75 +8,113 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='container'",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Child Containment Layer",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='vertical' class='content'",
-      "position": [8, 13],
       "bounds": [10, 10],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='container'",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "Child Containment Layer",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='horizontal' class='content'",
-      "position": [8, 13],
       "bounds": [10, 10],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='container'",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "Child Containment Layer",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='both' class='content'",
-      "position": [8, 13],
       "bounds": [10, 10],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='corner' class='container'",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 4
     },
     {
       "name": "Child Containment Layer",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 4
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='content'",
-      "position": [8, 13],
       "bounds": [10, 10],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 4
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 13, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 13, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 13, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 13, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/overflow-scrollbar-layers-expected.txt b/third_party/WebKit/LayoutTests/compositing/overflow/overflow-scrollbar-layers-expected.txt
index 04475d2..4a2a3d1 100644
--- a/third_party/WebKit/LayoutTests/compositing/overflow/overflow-scrollbar-layers-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/overflow-scrollbar-layers-expected.txt
@@ -8,147 +8,191 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='container'",
-      "position": [8, 13],
-      "bounds": [100, 100]
+      "bounds": [100, 100],
+      "transform": 1
     },
     {
       "name": "Scrolling Layer",
-      "position": [8, 13],
       "bounds": [85, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Scrolling Contents Layer",
-      "position": [8, 13],
       "bounds": [85, 200],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='content tall'",
-      "position": [8, 13],
       "bounds": [10, 200],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Overflow Controls Host Layer",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Vertical Scrollbar Layer",
-      "position": [93, 13],
+      "position": [85, 0],
       "bounds": [15, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='container'",
-      "position": [8, 13],
-      "bounds": [100, 100]
+      "bounds": [100, 100],
+      "transform": 2
     },
     {
       "name": "Scrolling Layer",
-      "position": [8, 13],
       "bounds": [100, 85],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "Scrolling Contents Layer",
-      "position": [8, 13],
       "bounds": [200, 85],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='content wide'",
-      "position": [8, 13],
       "bounds": [200, 10],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "Overflow Controls Host Layer",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "Horizontal Scrollbar Layer",
-      "position": [8, 98],
+      "position": [0, 85],
       "bounds": [100, 15],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='container'",
-      "position": [8, 13],
-      "bounds": [100, 100]
+      "bounds": [100, 100],
+      "transform": 3
     },
     {
       "name": "Scrolling Layer",
-      "position": [8, 13],
       "bounds": [85, 85],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "Scrolling Contents Layer",
-      "position": [8, 13],
       "bounds": [200, 200],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='content wide tall'",
-      "position": [8, 13],
       "bounds": [200, 200],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "Overflow Controls Host Layer",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "Horizontal Scrollbar Layer",
-      "position": [8, 98],
+      "position": [0, 85],
       "bounds": [85, 15],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "Vertical Scrollbar Layer",
-      "position": [93, 13],
+      "position": [85, 0],
       "bounds": [15, 85],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "Scroll Corner Layer",
-      "position": [93, 98],
-      "bounds": [15, 15]
+      "position": [85, 85],
+      "bounds": [15, 15],
+      "transform": 3
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='container resizeWidget'",
-      "position": [8, 13],
-      "bounds": [100, 100]
+      "bounds": [100, 100],
+      "transform": 4
     },
     {
       "name": "Child Containment Layer",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 4
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='content'",
-      "position": [8, 13],
       "bounds": [10, 10],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 4
     },
     {
       "name": "Overflow Controls Host Layer",
-      "position": [8, 13],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 4
     },
     {
       "name": "Scroll Corner Layer",
-      "position": [93, 98],
-      "bounds": [15, 15]
+      "position": [85, 85],
+      "bounds": [15, 15],
+      "transform": 4
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 13, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 13, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 13, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 13, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/resize-painting-expected.txt b/third_party/WebKit/LayoutTests/compositing/overflow/resize-painting-expected.txt
index 943385e..51e18ea 100644
--- a/third_party/WebKit/LayoutTests/compositing/overflow/resize-painting-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/resize-painting-expected.txt
@@ -8,19 +8,31 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='resizable composited box'",
-      "position": [18, 10],
-      "bounds": [100, 100]
+      "bounds": [100, 100],
+      "transform": 1
     },
     {
       "name": "Overflow Controls Host Layer",
-      "position": [18, 10],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Scroll Corner Layer",
-      "position": [103, 95],
-      "bounds": [15, 15]
+      "position": [85, 85],
+      "bounds": [15, 15],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-expected.png b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-expected.png
index 78256d5..9ea844f 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-expected.png
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-expected.txt b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-expected.txt
index d943d4e..2199f022 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-expected.txt
@@ -8,10 +8,21 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='positioned layer'",
-      "position": [50, 50],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [50, 50, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-expected.png b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-expected.png
index 92bd01a..1e34e81 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-expected.png
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-expected.txt b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-expected.txt
index bdc35c3b..69d1bf69 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-expected.txt
@@ -9,11 +9,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='positioned layer'",
-      "position": [265, 50],
       "bounds": [100, 100],
       "contentsOpaque": true,
       "backgroundColor": "#008000",
-      "transform": 1
+      "transform": 2
     }
   ],
   "transforms": [
@@ -25,6 +24,16 @@
         [0, 0, 1, 0],
         [-215, 0, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [265, 50, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-scrolled-expected.png b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-scrolled-expected.png
new file mode 100644
index 0000000..d7951a5d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-scrolled-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-scrolled-expected.txt b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-scrolled-expected.txt
index f0483479..8f88f3d0 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-scrolled-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-scrolled-expected.txt
@@ -9,11 +9,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='layer' class='positioned'",
-      "position": [51, 50],
       "bounds": [100, 100],
       "contentsOpaque": true,
       "backgroundColor": "#008000",
-      "transform": 1
+      "transform": 2
     }
   ],
   "transforms": [
@@ -25,6 +24,16 @@
         [0, 0, 1, 0],
         [-1, 0, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [51, 50, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-scrolled.html b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-scrolled.html
index 561201d3..56177682 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-scrolled.html
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow-scrolled.html
@@ -29,12 +29,6 @@
         height: 1000px;
         background-color: white;
     }
-
-    #layertree {
-        position: absolute;
-        top: 10000px;
-        left: 0px;
-    }
 </style>
 <script>
     function doTest() {
@@ -50,9 +44,9 @@
 
         window.setTimeout(function() {
             window.scrollTo(offset, 0);
-            if (window.testRunner) {
+            if (window.testRunner && window.internals) {
                 if (top == self)
-                    document.getElementById('layertree').innerText = window.internals.layerTreeAsText(document);
+                    testRunner.setCustomTextOutput(internals.layerTreeAsText(document));
                 testRunner.notifyDone();
             }
         }, 0);
@@ -63,7 +57,5 @@
     <div id="root"></div>
     <div class="positioned" id="indicator"></div>
     <div class="positioned" id="layer"></div>
-
-    <pre id="layertree"></pre>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow.html b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow.html
index c406a05..e237ae1 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow.html
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute-overflow.html
@@ -29,18 +29,12 @@
         height: 1000px;
         background-color: white;
     }
-
-    #layertree {
-        position: absolute;
-        top: 10000px;
-        left: 0px;
-    }
 </style>
 <script>
     function doTest() {
-        if (window.testRunner) {
+        if (window.testRunner && window.internals) {
             if (top == self)
-                document.getElementById('layertree').innerText = window.internals.layerTreeAsText(document);
+                testRunner.setCustomTextOutput(internals.layerTreeAsText(document));
             testRunner.dumpAsTextWithPixelResults();
         }
     }
@@ -50,7 +44,5 @@
     <div id="root"></div>
     <div class="positioned indicator"></div>
     <div class="positioned layer"></div>
-
-    <pre id="layertree"></pre>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute.html b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute.html
index cbda9a1..ed638f3 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute.html
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-absolute.html
@@ -30,18 +30,12 @@
         /* root element should exactly cover red background */
         background-color: white;
     }
-
-    #layertree {
-        position: absolute;
-        top: 10000px;
-        left: 0px;
-    }
 </style>
 <script>
     function doTest() {
-        if (window.testRunner) {
+        if (window.testRunner && window.internals) {
             if (top == self)
-                document.getElementById('layertree').innerText = window.internals.layerTreeAsText(document);
+                testRunner.setCustomTextOutput(internals.layerTreeAsText(document));
             testRunner.dumpAsTextWithPixelResults();
         }
     }
@@ -51,7 +45,5 @@
     <div id="root"></div>
     <div class="positioned indicator"></div>
     <div class="positioned layer"></div>
-
-    <pre id="layertree"></pre>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed-expected.png b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed-expected.png
index 78256d5..9ea844f 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed-expected.png
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed-expected.txt b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed-expected.txt
index d943d4e..2199f022 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed-expected.txt
@@ -8,10 +8,21 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='positioned layer'",
-      "position": [50, 50],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [50, 50, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed-overflow-expected.txt b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed-overflow-expected.txt
index 4ce0b9b..7fcf57a 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed-overflow-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed-overflow-expected.txt
@@ -17,11 +17,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='positioned layer'",
-      "position": [265, 50],
       "bounds": [100, 100],
       "contentsOpaque": true,
       "backgroundColor": "#008000",
-      "transform": 1
+      "transform": 2
     }
   ],
   "transforms": [
@@ -33,6 +32,16 @@
         [0, 0, 1, 0],
         [-215, 0, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [265, 50, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed-overflow-scrolled-expected.txt b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed-overflow-scrolled-expected.txt
index 1215d9e8..c373403 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed-overflow-scrolled-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed-overflow-scrolled-expected.txt
@@ -17,11 +17,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='positioned layer'",
-      "position": [51, 50],
       "bounds": [100, 100],
       "contentsOpaque": true,
       "backgroundColor": "#008000",
-      "transform": 1
+      "transform": 2
     }
   ],
   "transforms": [
@@ -33,6 +32,16 @@
         [0, 0, 1, 0],
         [-1, 0, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [51, 50, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed.html b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed.html
index 40e55eb..2af0fce0 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed.html
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-fixed.html
@@ -29,18 +29,12 @@
         height: 100%;
         background-color: white;
     }
-
-    #layertree {
-        position: absolute;
-        top: 10000px;
-        left: 0px;
-    }
 </style>
 <script>
     function doTest() {
-        if (window.testRunner) {
+        if (window.testRunner && window.internals) {
             if (top == self)
-                document.getElementById('layertree').innerText = window.internals.layerTreeAsText(document);
+                testRunner.setCustomTextOutput(internals.layerTreeAsText(document));
             testRunner.dumpAsTextWithPixelResults();
         }
     }
@@ -50,7 +44,5 @@
     <div id="root"></div>
     <div class="positioned indicator"></div>
     <div class="positioned layer"></div>
-
-    <pre id="layertree"></pre>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-expected.txt b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-expected.txt
index 34746f2f..6694d14 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-expected.txt
@@ -36,10 +36,21 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='positioned layer'",
-      "position": [50, 50],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [50, 50, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-overflow-expected.txt b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-overflow-expected.txt
index dee79ff..aef4498 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-overflow-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-overflow-expected.txt
@@ -38,11 +38,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='positioned layer'",
-      "position": [665, 50],
       "bounds": [100, 100],
       "contentsOpaque": true,
       "backgroundColor": "#008000",
-      "transform": 1
+      "transform": 2
     },
     {
       "name": "Frame Horizontal Scrollbar Layer",
@@ -71,6 +70,16 @@
         [0, 0, 1, 0],
         [-615, 0, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [665, 50, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-overflow-scrolled-expected.txt b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-overflow-scrolled-expected.txt
index 5685ea57..766a410 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-overflow-scrolled-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-overflow-scrolled-expected.txt
@@ -38,11 +38,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='layer' class='positioned'",
-      "position": [51, 50],
       "bounds": [100, 100],
       "contentsOpaque": true,
       "backgroundColor": "#008000",
-      "transform": 1
+      "transform": 2
     },
     {
       "name": "Frame Horizontal Scrollbar Layer",
@@ -71,6 +70,16 @@
         [0, 0, 1, 0],
         [-615, 0, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [51, 50, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-overflow-scrolled.html b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-overflow-scrolled.html
index b2e632b..0e5aea56 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-overflow-scrolled.html
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-overflow-scrolled.html
@@ -13,17 +13,11 @@
         height: 400px;
         border: none;
     }
-
-    #layertree {
-        position: absolute;
-        top: 10000px;
-        left: 0px;
-    }
 </style>
 <script>
     function doTest() {
-        if (window.testRunner) {
-            document.getElementById('layertree').innerText = window.internals.layerTreeAsText(document);
+        if (window.testRunner && window.internals) {
+            testRunner.setCustomTextOutput(internals.layerTreeAsText(document));
             testRunner.dumpAsTextWithPixelResults();
         }
     }
@@ -31,7 +25,5 @@
 </script>
 <body>
     <iframe src="rtl-absolute-overflow-scrolled.html"></iframe>
-
-    <pre id="layertree"></pre>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-overflow.html b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-overflow.html
index c116745b..adcc2f8d 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-overflow.html
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute-overflow.html
@@ -13,17 +13,11 @@
         height: 400px;
         border: none;
     }
-
-    #layertree {
-        position: absolute;
-        top: 10000px;
-        left: 0px;
-    }
 </style>
 <script>
     function doTest() {
-        if (window.testRunner) {
-            document.getElementById('layertree').innerText = window.internals.layerTreeAsText(document);
+        if (window.testRunner && window.internals) {
+            testRunner.setCustomTextOutput(internals.layerTreeAsText(document));
             testRunner.dumpAsTextWithPixelResults();
         }
     }
@@ -31,7 +25,5 @@
 </script>
 <body>
     <iframe src="rtl-absolute-overflow.html"></iframe>
-
-    <pre id="layertree"></pre>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute.html b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute.html
index 90b5c50..6d2cfde 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute.html
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-absolute.html
@@ -13,17 +13,11 @@
         height: 400px;
         border: none;
     }
-
-    #layertree {
-        position: absolute;
-        top: 10000px;
-        left: 0px;
-    }
 </style>
 <script>
     function doTest() {
-        if (window.testRunner) {
-            document.getElementById('layertree').innerText = window.internals.layerTreeAsText(document);
+        if (window.testRunner && window.internals) {
+            testRunner.setCustomTextOutput(internals.layerTreeAsText(document));
             testRunner.dumpAsTextWithPixelResults();
         }
     }
@@ -31,7 +25,5 @@
 </script>
 <body>
     <iframe src="rtl-absolute.html"></iframe>
-
-    <pre id="layertree"></pre>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-expected.txt b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-expected.txt
index 34746f2f..6694d14 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-expected.txt
@@ -36,10 +36,21 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='positioned layer'",
-      "position": [50, 50],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [50, 50, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-overflow-expected.txt b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-overflow-expected.txt
index 8f27ec2..aa13695 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-overflow-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-overflow-expected.txt
@@ -46,11 +46,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='positioned layer'",
-      "position": [665, 50],
       "bounds": [100, 100],
       "contentsOpaque": true,
       "backgroundColor": "#008000",
-      "transform": 1
+      "transform": 2
     },
     {
       "name": "Frame Horizontal Scrollbar Layer",
@@ -79,6 +78,16 @@
         [0, 0, 1, 0],
         [-615, 0, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [665, 50, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-overflow-scrolled-expected.txt b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-overflow-scrolled-expected.txt
index 8f27ec2..aa13695 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-overflow-scrolled-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-overflow-scrolled-expected.txt
@@ -46,11 +46,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='positioned layer'",
-      "position": [665, 50],
       "bounds": [100, 100],
       "contentsOpaque": true,
       "backgroundColor": "#008000",
-      "transform": 1
+      "transform": 2
     },
     {
       "name": "Frame Horizontal Scrollbar Layer",
@@ -79,6 +78,16 @@
         [0, 0, 1, 0],
         [-615, 0, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [665, 50, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-overflow-scrolled.html b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-overflow-scrolled.html
index ec6d1c1..1219d57 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-overflow-scrolled.html
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-overflow-scrolled.html
@@ -13,17 +13,11 @@
         height: 400px;
         border: none;
     }
-
-    #layertree {
-        position: absolute;
-        top: 10000px;
-        left: 0px;
-    }
 </style>
 <script>
     function doTest() {
-        if (window.testRunner) {
-            document.getElementById('layertree').innerText = window.internals.layerTreeAsText(document);
+        if (window.testRunner && window.internals) {
+            testRunner.setCustomTextOutput(internals.layerTreeAsText(document));
             testRunner.dumpAsTextWithPixelResults();
         }
     }
@@ -31,7 +25,5 @@
 </script>
 <body>
     <iframe src="rtl-fixed-overflow-scrolled.html"></iframe>
-
-    <pre id="layertree"></pre>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-overflow.html b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-overflow.html
index 1d4305d..21bfcd6 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-overflow.html
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed-overflow.html
@@ -13,17 +13,11 @@
         height: 400px;
         border: none;
     }
-
-    #layertree {
-        position: absolute;
-        top: 10000px;
-        left: 0px;
-    }
 </style>
 <script>
     function doTest() {
-        if (window.testRunner) {
-            document.getElementById('layertree').innerText = window.internals.layerTreeAsText(document);
+        if (window.testRunner && window.internals) {
+            testRunner.setCustomTextOutput(internals.layerTreeAsText(document));
             testRunner.dumpAsTextWithPixelResults();
         }
     }
@@ -31,7 +25,5 @@
 </script>
 <body>
     <iframe src="rtl-fixed-overflow.html"></iframe>
-
-    <pre id="layertree"></pre>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed.html b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed.html
index 02e5366..2213e74 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed.html
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-fixed.html
@@ -13,17 +13,11 @@
         height: 400px;
         border: none;
     }
-
-    #layertree {
-        position: absolute;
-        top: 10000px;
-        left: 0px;
-    }
 </style>
 <script>
     function doTest() {
-        if (window.testRunner) {
-            document.getElementById('layertree').innerText = window.internals.layerTreeAsText(document);
+        if (window.testRunner && window.internals) {
+            testRunner.setCustomTextOutput(internals.layerTreeAsText(document));
             testRunner.dumpAsTextWithPixelResults();
         }
     }
@@ -31,7 +25,5 @@
 </script>
 <body>
     <iframe src="rtl-fixed.html"></iframe>
-
-    <pre id="layertree"></pre>
 </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-relative-expected.txt b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-relative-expected.txt
index de4ceea..5ef8849 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-relative-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-iframe-relative-expected.txt
@@ -35,10 +35,21 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [242, 58],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [242, 58, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-relative-expected.txt b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-relative-expected.txt
index 7bf2ef0..c43d7ed 100644
--- a/third_party/WebKit/LayoutTests/compositing/rtl/rtl-relative-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/rtl/rtl-relative-expected.txt
@@ -8,10 +8,21 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [642, 58],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [642, 58, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/add-remove-squashed-layers-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/add-remove-squashed-layers-expected.txt
index 0a230b1..42f0569 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/add-remove-squashed-layers-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/add-remove-squashed-layers-expected.txt
@@ -15,16 +15,28 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [60, 60],
       "bounds": [400, 400],
       "contentsOpaque": true,
-      "backgroundColor": "#808080"
+      "backgroundColor": "#808080",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='A' class='overlap1')",
       "position": [140, 140],
       "bounds": [180, 180]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [60, 60, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 CASE 2, overlap3 gets added:
@@ -42,10 +54,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [60, 60],
       "bounds": [400, 400],
       "contentsOpaque": true,
-      "backgroundColor": "#808080"
+      "backgroundColor": "#808080",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='A' class='overlap1')",
@@ -60,6 +72,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [60, 60, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='C' class='overlap3'",
@@ -86,10 +110,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [60, 60],
       "bounds": [400, 400],
       "contentsOpaque": true,
-      "backgroundColor": "#808080"
+      "backgroundColor": "#808080",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='A' class='overlap1')",
@@ -104,6 +128,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [60, 60, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='B' class='overlap2'",
@@ -126,10 +162,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [60, 60],
       "bounds": [400, 400],
       "contentsOpaque": true,
-      "backgroundColor": "#808080"
+      "backgroundColor": "#808080",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='C' class='overlap3')",
@@ -154,6 +190,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [60, 60, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='A' class='overlap1'",
@@ -184,10 +232,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [60, 60],
       "bounds": [400, 400],
       "contentsOpaque": true,
-      "backgroundColor": "#808080"
+      "backgroundColor": "#808080",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='B' class='overlap2')",
@@ -212,6 +260,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [60, 60, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='B' class='overlap2'",
@@ -250,10 +310,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [60, 60],
       "bounds": [400, 400],
       "contentsOpaque": true,
-      "backgroundColor": "#808080"
+      "backgroundColor": "#808080",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='A' class='overlap1')",
@@ -283,6 +343,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [60, 60, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='C' class='overlap3'",
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/clipping-ancestor-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/clipping-ancestor-expected.txt
index 80aaeb3..646cdc4 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/clipping-ancestor-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/clipping-ancestor-expected.txt
@@ -14,10 +14,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='inner'",
-      "position": [8, 8],
       "bounds": [200, 10],
       "contentsOpaque": true,
-      "backgroundColor": "#F5F5F5"
+      "backgroundColor": "#F5F5F5",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='hoverable'",
@@ -26,6 +26,18 @@
       "contentsOpaque": true,
       "backgroundColor": "#90EE90"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/composited-bounds-for-negative-z-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/composited-bounds-for-negative-z-expected.txt
index 3b134bbf..851dac0 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/composited-bounds-for-negative-z-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/composited-bounds-for-negative-z-expected.txt
@@ -13,10 +13,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV",
-      "position": [8, 100],
       "bounds": [10, 10],
       "contentsOpaque": true,
-      "backgroundColor": "#FF0000"
+      "backgroundColor": "#FF0000",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow HTML (foreground) Layer",
@@ -66,6 +66,17 @@
       "position": [108, 100],
       "bounds": [285, 1000]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 100, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/iframes-are-never-squashed-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/iframes-are-never-squashed-expected.txt
index 1fd9e527..faa90540 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/iframes-are-never-squashed-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/iframes-are-never-squashed-expected.txt
@@ -8,10 +8,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV",
-      "position": [8, 8],
       "bounds": [200, 200],
       "contentsOpaque": true,
-      "backgroundColor": "#D3D3D3"
+      "backgroundColor": "#D3D3D3",
+      "transform": 1
     },
     {
       "name": "LayoutIFrame (positioned) IFRAME",
@@ -19,6 +19,17 @@
       "contentsOpaque": true,
       "backgroundColor": "#ADD8E6"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/invisible-layers-should-not-affect-geometry-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/invisible-layers-should-not-affect-geometry-expected.txt
index e3281e4..10ed367 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/invisible-layers-should-not-affect-geometry-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/invisible-layers-should-not-affect-geometry-expected.txt
@@ -12,16 +12,28 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box'",
-      "position": [10, 10],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box')",
       "position": [10, 10],
       "bounds": [100, 100]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/move-squashing-layer-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/move-squashing-layer-expected.txt
index 9a77462..b9d18e90 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/move-squashing-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/move-squashing-layer-expected.txt
@@ -10,10 +10,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='background'",
-      "position": [8, 50],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFF00"
+      "backgroundColor": "#FFFF00",
+      "transform": 1
     },
     {
       "name": "Squashing Containment Layer",
@@ -21,16 +21,37 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='host'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 2
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (relative positioned) DIV id='squashed')",
       "position": [8, 8],
       "bounds": [100, 100]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 50, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/no-squashing-for-filters-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/no-squashing-for-filters-expected.txt
index 01cb100..ac0b57a 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/no-squashing-for-filters-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/no-squashing-for-filters-expected.txt
@@ -8,10 +8,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV",
-      "position": [8, 8],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#ADD8E6"
+      "backgroundColor": "#ADD8E6",
+      "transform": 1
     },
     {
       "name": "Squashing Containment Layer",
@@ -28,6 +28,17 @@
       "position": [50, 50],
       "bounds": [50, 50]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/squashing/no-squashing-into-another-clip-layer-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/no-squashing-into-another-clip-layer-expected.txt
similarity index 66%
rename from third_party/WebKit/LayoutTests/platform/mac/compositing/squashing/no-squashing-into-another-clip-layer-expected.txt
rename to third_party/WebKit/LayoutTests/compositing/squashing/no-squashing-into-another-clip-layer-expected.txt
index c8f208e..c130381 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/squashing/no-squashing-into-another-clip-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/no-squashing-into-another-clip-layer-expected.txt
@@ -8,23 +8,23 @@
     },
     {
       "name": "LayoutBlockFlow DIV",
-      "position": [8, 8],
       "bounds": [784, 10],
       "contentsOpaque": true,
-      "backgroundColor": "#ADD8E6"
+      "backgroundColor": "#ADD8E6",
+      "transform": 1
     },
     {
       "name": "Child Containment Layer",
-      "position": [8, 8],
       "bounds": [784, 10],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV id='inner'",
-      "position": [8, 8],
       "bounds": [784, 10],
       "contentsOpaque": true,
-      "backgroundColor": "#F5F5F5"
+      "backgroundColor": "#F5F5F5",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='hoverable'",
@@ -32,6 +32,17 @@
       "bounds": [211, 100],
       "backgroundColor": "#90EE90"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/no-squashing-into-fixed-position-that-clips-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/no-squashing-into-fixed-position-that-clips-expected.txt
index 7be9f4d..b7f7b6f 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/no-squashing-into-fixed-position-that-clips-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/no-squashing-into-fixed-position-that-clips-expected.txt
@@ -21,10 +21,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='compositedlayer'",
-      "position": [400, 40],
       "bounds": [24, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#D3D3D3"
+      "backgroundColor": "#D3D3D3",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='notsquashedelement'",
@@ -32,6 +32,17 @@
       "contentsOpaque": true,
       "backgroundColor": "#008000"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [400, 40, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/opacity-squashed-owner-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/opacity-squashed-owner-expected.txt
index 2a1d45d..261643e 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/opacity-squashed-owner-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/opacity-squashed-owner-expected.txt
@@ -12,17 +12,29 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='target' class='composited box opaque'",
-      "position": [8, 8],
       "bounds": [100, 100],
       "opacity": 0.5,
       "contentsOpaque": true,
-      "backgroundColor": "#ADD8E6"
+      "backgroundColor": "#ADD8E6",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='squashed')",
       "position": [16, 4],
       "bounds": [40, 90]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/repaint-child-of-squashed-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/repaint-child-of-squashed-expected.txt
index a1b871c0..4ad7be1 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/repaint-child-of-squashed-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/repaint-child-of-squashed-expected.txt
@@ -13,16 +13,28 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [50, 50],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box top')",
       "position": [130, 130],
       "bounds": [100, 100]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [50, 50, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 CASE 2, change color of "inner" to red
@@ -40,10 +52,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [50, 50],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box top')",
@@ -58,6 +70,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [50, 50, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='inner'",
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/squash-above-fixed-1-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/squash-above-fixed-1-expected.txt
index 1222eff..867cb55 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/squash-above-fixed-1-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/squash-above-fixed-1-expected.txt
@@ -13,10 +13,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited background'",
-      "position": [100, 150],
       "bounds": [300, 300],
       "contentsOpaque": true,
-      "backgroundColor": "#D3D3D3"
+      "backgroundColor": "#D3D3D3",
+      "transform": 1
     },
     {
       "name": "Squashing Containment Layer",
@@ -34,6 +34,17 @@
       "position": [0, 200],
       "bounds": [200, 300]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 150, 0, 1]
+      ]
+    }
   ]
 }
 CASE 2, scrolling y to 80, new layers will be squashed, so things repaint:
@@ -75,11 +86,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited background'",
-      "position": [100, 230],
       "bounds": [300, 300],
       "contentsOpaque": true,
       "backgroundColor": "#D3D3D3",
-      "transform": 1
+      "transform": 2
     },
     {
       "name": "Squashing Containment Layer",
@@ -139,6 +149,16 @@
         [0, 0, 1, 0],
         [0, -80, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 230, 0, 1]
+      ]
     }
   ],
   "objectPaintInvalidations": [
@@ -192,11 +212,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited background'",
-      "position": [100, 270],
       "bounds": [300, 300],
       "contentsOpaque": true,
       "backgroundColor": "#D3D3D3",
-      "transform": 1
+      "transform": 2
     },
     {
       "name": "Squashing Containment Layer",
@@ -227,6 +246,16 @@
         [0, 0, 1, 0],
         [0, -120, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 270, 0, 1]
+      ]
     }
   ]
 }
@@ -269,11 +298,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited background'",
-      "position": [100, 320],
       "bounds": [300, 300],
       "contentsOpaque": true,
       "backgroundColor": "#D3D3D3",
-      "transform": 1
+      "transform": 2
     },
     {
       "name": "Squashing Containment Layer",
@@ -333,6 +361,16 @@
         [0, 0, 1, 0],
         [0, -170, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 320, 0, 1]
+      ]
     }
   ],
   "objectPaintInvalidations": [
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/squash-above-fixed-3-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/squash-above-fixed-3-expected.txt
index d471bdc2..93f57dc 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/squash-above-fixed-3-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/squash-above-fixed-3-expected.txt
@@ -26,10 +26,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='innerFixed'",
-      "position": [100, 150],
       "bounds": [200, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='innerScrolling'",
@@ -38,6 +38,17 @@
       "contentsOpaque": true,
       "backgroundColor": "#00FF00"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 150, 0, 1]
+      ]
+    }
   ]
 }
 CASE 2, scrolling y by 10 pixels, both the "container" and "inner" should scroll properly.
@@ -52,11 +63,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='backgroundFixed'",
-      "position": [0, 10],
       "bounds": [400, 400],
       "contentsOpaque": true,
       "backgroundColor": "#808080",
-      "transform": 1
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='container'",
@@ -68,11 +78,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='innerFixed'",
-      "position": [100, 160],
       "bounds": [200, 100],
       "contentsOpaque": true,
       "backgroundColor": "#0000FF",
-      "transform": 1
+      "transform": 3
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='innerScrolling'",
@@ -92,6 +101,26 @@
         [0, 0, 1, 0],
         [0, -10, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 160, 0, 1]
+      ]
     }
   ]
 }
@@ -107,11 +136,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='backgroundFixed'",
-      "position": [0, 110],
       "bounds": [400, 400],
       "contentsOpaque": true,
       "backgroundColor": "#808080",
-      "transform": 1
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='container'",
@@ -129,11 +157,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='innerFixed'",
-      "position": [100, 260],
       "bounds": [200, 100],
       "contentsOpaque": true,
       "backgroundColor": "#0000FF",
-      "transform": 1
+      "transform": 3
     }
   ],
   "transforms": [
@@ -145,6 +172,26 @@
         [0, 0, 1, 0],
         [0, -110, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, 110, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 260, 0, 1]
+      ]
     }
   ],
   "objectPaintInvalidations": [
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/squash-compositing-hover-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/squash-compositing-hover-expected.txt
index 5267cdb..9fd9952 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/squash-compositing-hover-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/squash-compositing-hover-expected.txt
@@ -11,9 +11,9 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='composited'",
-      "position": [8, 8],
       "contentsOpaque": true,
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Containment Layer",
@@ -21,16 +21,37 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 2
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box middle')",
       "position": [180, 180],
       "bounds": [260, 260]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 Case 2: hovering over the "middle" element (causes that div to become its own composited layer)
@@ -44,16 +65,16 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='composited'",
-      "position": [8, 8],
       "contentsOpaque": true,
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 2
     },
     {
       "name": "Squashing Containment Layer",
@@ -61,16 +82,46 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='box middle'",
-      "position": [180, 180],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 3
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box middle2')",
       "position": [260, 260],
       "bounds": [180, 180]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [180, 180, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 Case 3: hovering over the "middle2" element (causes that div to become its own composited layer)
@@ -84,9 +135,9 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='composited'",
-      "position": [8, 8],
       "contentsOpaque": true,
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Containment Layer",
@@ -94,10 +145,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 2
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box middle')",
@@ -110,16 +161,47 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='box middle2'",
-      "position": [260, 260],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 3
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box top')",
       "position": [340, 340],
       "bounds": [100, 100]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [260, 260, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 Case 4: hovering over the "top" element (causes that div to become its own composited layer)
@@ -133,9 +215,9 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='composited'",
-      "position": [8, 8],
       "contentsOpaque": true,
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Containment Layer",
@@ -143,10 +225,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 2
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box middle')",
@@ -155,10 +237,40 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='box top'",
-      "position": [340, 340],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 3
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [340, 340, 0, 1]
+      ]
     }
   ]
 }
@@ -173,9 +285,9 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='composited'",
-      "position": [8, 8],
       "contentsOpaque": true,
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Containment Layer",
@@ -183,16 +295,37 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 2
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box middle')",
       "position": [180, 180],
       "bounds": [260, 260]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/squash-onto-distant-relative-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/squash-onto-distant-relative-expected.txt
index 56b1460b..968a5fc 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/squash-onto-distant-relative-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/squash-onto-distant-relative-expected.txt
@@ -12,16 +12,28 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='squashing'",
-      "position": [-33554430, 0],
       "bounds": [1, 1],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF"
+      "backgroundColor": "#FFFFFF",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='squashed')",
       "position": [600, 0],
       "bounds": [200, 200]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [-33554430, 0, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/squash-onto-nephew-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/squash-onto-nephew-expected.txt
index 3cbb803..a8c89b33 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/squash-onto-nephew-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/squash-onto-nephew-expected.txt
@@ -12,16 +12,28 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [125, 125],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box middle')",
       "position": [40, 40],
       "bounds": [180, 190]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [125, 125, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/squash-paint-invalidation-fixed-position-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/squash-paint-invalidation-fixed-position-expected.txt
index e587592..d2dde48 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/squash-paint-invalidation-fixed-position-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/squash-paint-invalidation-fixed-position-expected.txt
@@ -8,10 +8,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV",
-      "position": [8, 8],
       "bounds": [100, 5000],
       "contentsOpaque": true,
-      "backgroundColor": "#ADD8E6"
+      "backgroundColor": "#ADD8E6",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV",
@@ -35,6 +35,17 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='foo'",
@@ -52,10 +63,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV",
-      "position": [8, 8],
       "bounds": [100, 5000],
       "contentsOpaque": true,
-      "backgroundColor": "#ADD8E6"
+      "backgroundColor": "#ADD8E6",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV",
@@ -79,6 +90,17 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='foo'",
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/squash-simple-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/squash-simple-expected.txt
index 13e1daf7..4a04fd52 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/squash-simple-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/squash-simple-expected.txt
@@ -12,16 +12,28 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box middle')",
       "position": [20, 20],
       "bounds": [260, 260]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/squash-three-layers-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/squash-three-layers-expected.txt
index c7b2c4a..6a2e3c2 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/squash-three-layers-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/squash-three-layers-expected.txt
@@ -12,16 +12,28 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box middle')",
       "position": [40, 40],
       "bounds": [180, 190]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/squash-transform-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/squash-transform-expected.txt
index fdcae45..f5bd6d0 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/squash-transform-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/squash-transform-expected.txt
@@ -8,10 +8,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Squashing Containment Layer",
@@ -22,7 +22,7 @@
       "bounds": [100, 100],
       "contentsOpaque": true,
       "backgroundColor": "#00FF00",
-      "transform": 2
+      "transform": 3
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box top')",
@@ -37,13 +37,22 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
         [20, 20, 0, 1]
       ],
       "flattenInheritedTransform": false
     },
     {
-      "id": 2,
-      "parent": 1,
+      "id": 3,
+      "parent": 2,
       "transform": [
         [0.707106781186548, 0.707106781186548, 0, 0],
         [-0.707106781186548, 0.707106781186548, 0, 0],
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/squash-transform-repainting-child-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/squash-transform-repainting-child-expected.txt
index 7b66da86..ab197e05 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/squash-transform-repainting-child-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/squash-transform-repainting-child-expected.txt
@@ -9,10 +9,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Squashing Containment Layer",
@@ -23,7 +23,7 @@
       "bounds": [100, 100],
       "contentsOpaque": true,
       "backgroundColor": "#00FF00",
-      "transform": 2
+      "transform": 3
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box top')",
@@ -38,13 +38,22 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
         [20, 20, 0, 1]
       ],
       "flattenInheritedTransform": false
     },
     {
-      "id": 2,
-      "parent": 1,
+      "id": 3,
+      "parent": 2,
       "transform": [
         [0.707106781186548, 0.707106781186548, 0, 0],
         [-0.707106781186548, 0.707106781186548, 0, 0],
@@ -67,10 +76,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Squashing Containment Layer",
@@ -88,7 +97,7 @@
           "reason": "style change"
         }
       ],
-      "transform": 2
+      "transform": 3
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box top')",
@@ -103,13 +112,22 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
         [20, 20, 0, 1]
       ],
       "flattenInheritedTransform": false
     },
     {
-      "id": 2,
-      "parent": 1,
+      "id": 3,
+      "parent": 2,
       "transform": [
         [0.707106781186548, 0.707106781186548, 0, 0],
         [-0.707106781186548, 0.707106781186548, 0, 0],
@@ -138,10 +156,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Squashing Containment Layer",
@@ -164,7 +182,7 @@
           "reason": "style change"
         }
       ],
-      "transform": 2
+      "transform": 3
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box top')",
@@ -179,13 +197,22 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
         [20, 20, 0, 1]
       ],
       "flattenInheritedTransform": false
     },
     {
-      "id": 2,
-      "parent": 1,
+      "id": 3,
+      "parent": 2,
       "transform": [
         [0.707106781186548, 0.707106781186548, 0, 0],
         [-0.707106781186548, 0.707106781186548, 0, 0],
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/squash-transform-repainting-transformed-child-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/squash-transform-repainting-transformed-child-expected.txt
index 2aaad09..befb988 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/squash-transform-repainting-transformed-child-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/squash-transform-repainting-transformed-child-expected.txt
@@ -9,10 +9,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Squashing Containment Layer",
@@ -23,7 +23,7 @@
       "bounds": [100, 100],
       "contentsOpaque": true,
       "backgroundColor": "#00FF00",
-      "transform": 2
+      "transform": 3
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box top')",
@@ -38,13 +38,22 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
         [20, 20, 0, 1]
       ],
       "flattenInheritedTransform": false
     },
     {
-      "id": 2,
-      "parent": 1,
+      "id": 3,
+      "parent": 2,
       "transform": [
         [0.927183854566787, 0.374606593415912, 0, 0],
         [-0.374606593415912, 0.927183854566787, 0, 0],
@@ -67,10 +76,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Squashing Containment Layer",
@@ -88,7 +97,7 @@
           "reason": "style change"
         }
       ],
-      "transform": 2
+      "transform": 3
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box top')",
@@ -103,13 +112,22 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
         [20, 20, 0, 1]
       ],
       "flattenInheritedTransform": false
     },
     {
-      "id": 2,
-      "parent": 1,
+      "id": 3,
+      "parent": 2,
       "transform": [
         [0.927183854566787, 0.374606593415912, 0, 0],
         [-0.374606593415912, 0.927183854566787, 0, 0],
@@ -138,10 +156,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Squashing Containment Layer",
@@ -164,7 +182,7 @@
           "reason": "style change"
         }
       ],
-      "transform": 2
+      "transform": 3
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box top')",
@@ -179,13 +197,22 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
         [20, 20, 0, 1]
       ],
       "flattenInheritedTransform": false
     },
     {
-      "id": 2,
-      "parent": 1,
+      "id": 3,
+      "parent": 2,
       "transform": [
         [0.927183854566787, 0.374606593415912, 0, 0],
         [-0.374606593415912, 0.927183854566787, 0, 0],
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/squashed-layer-loses-graphicslayer-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/squashed-layer-loses-graphicslayer-expected.txt
index 2c79b9e5..1681e0bc 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/squashed-layer-loses-graphicslayer-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/squashed-layer-loses-graphicslayer-expected.txt
@@ -15,16 +15,28 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='forceComposited' class='composited underneath'",
-      "position": [60, 60],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#808080"
+      "backgroundColor": "#808080",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='A' class='overlap1')",
       "position": [140, 140],
       "bounds": [260, 260]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [60, 60, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 CASE 2, The original composited layer is no longer composited, which then also removes all squashing layers. The important point is that there should be an appropriate repaint to the root GraphicsLayer:
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/squashed-repaints-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/squashed-repaints-expected.txt
index e8e842b..11032386 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/squashed-repaints-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/squashed-repaints-expected.txt
@@ -15,16 +15,28 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [60, 60],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#808080"
+      "backgroundColor": "#808080",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='A' class='overlap1')",
       "position": [140, 140],
       "bounds": [260, 260]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [60, 60, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 CASE 2, overlap1 changes color:
@@ -42,10 +54,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [60, 60],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#808080"
+      "backgroundColor": "#808080",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='A' class='overlap1')",
@@ -60,6 +72,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [60, 60, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='A' class='overlap1'",
@@ -82,10 +106,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [60, 60],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#808080"
+      "backgroundColor": "#808080",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='A' class='overlap1')",
@@ -105,6 +129,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [60, 60, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='A' class='overlap1'",
@@ -131,10 +167,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [60, 60],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#808080"
+      "backgroundColor": "#808080",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='A' class='overlap1')",
@@ -154,6 +190,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [60, 60, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='B' class='overlap2'",
@@ -180,10 +228,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [60, 60],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#808080"
+      "backgroundColor": "#808080",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='A' class='overlap1')",
@@ -203,6 +251,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [60, 60, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='A' class='overlap1'",
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/squashing-sparsity-heuristic-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/squashing-sparsity-heuristic-expected.txt
index 556c6cd..dc94aaa 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/squashing-sparsity-heuristic-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/squashing-sparsity-heuristic-expected.txt
@@ -12,10 +12,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [60, 60],
       "bounds": [400, 400],
       "contentsOpaque": true,
-      "backgroundColor": "#808080"
+      "backgroundColor": "#808080",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='A' class='overlap1')",
@@ -38,6 +38,18 @@
       "position": [220, 300],
       "bounds": [25, 10]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [60, 60, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/compositing/tiled-layers-hidpi-expected.txt b/third_party/WebKit/LayoutTests/compositing/tiled-layers-hidpi-expected.txt
new file mode 100644
index 0000000..5c4633e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/tiled-layers-hidpi-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [1808, 585],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow DIV id='composited'",
+      "bounds": [1800, 10],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/compositing/visibility/visibility-image-layers-dynamic-expected.txt b/third_party/WebKit/LayoutTests/compositing/visibility/visibility-image-layers-dynamic-expected.txt
new file mode 100644
index 0000000..b436fe6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/visibility/visibility-image-layers-dynamic-expected.txt
@@ -0,0 +1,249 @@
+
+
+
+Initial
+
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [785, 626],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='composited container'",
+      "bounds": [749, 144],
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='composited hidden container'",
+      "bounds": [757, 152],
+      "drawsContent": false,
+      "contentsVisible": false,
+      "transform": 2
+    },
+    {
+      "name": "LayoutImage IMG class='visible composited box'",
+      "bounds": [100, 100],
+      "drawsContent": false,
+      "transform": 3
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 314, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [24, 24, 0, 1]
+      ]
+    }
+  ]
+}
+After step 1
+
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [785, 1531],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='composited container'",
+      "bounds": [749, 144],
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "LayoutImage IMG class='hidden composited box'",
+      "bounds": [100, 100],
+      "drawsContent": false,
+      "transform": 2
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='composited hidden container'",
+      "bounds": [757, 152],
+      "drawsContent": false,
+      "contentsVisible": false,
+      "transform": 3
+    },
+    {
+      "name": "LayoutImage IMG class='visible composited box'",
+      "bounds": [100, 100],
+      "drawsContent": false,
+      "transform": 4
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 20, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 314, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [24, 24, 0, 1]
+      ]
+    }
+  ]
+}
+After step 2
+
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [785, 2675],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='composited container'",
+      "bounds": [749, 144],
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "LayoutImage IMG class='hidden composited box'",
+      "bounds": [100, 100],
+      "drawsContent": false,
+      "transform": 2
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='composited hidden container'",
+      "bounds": [757, 152],
+      "transform": 3
+    },
+    {
+      "name": "LayoutImage IMG class='composited box'",
+      "bounds": [100, 100],
+      "drawsContent": false,
+      "transform": 4
+    },
+    {
+      "name": "LayoutBlockFlow DIV class='composited hidden container'",
+      "bounds": [757, 152],
+      "drawsContent": false,
+      "contentsVisible": false,
+      "transform": 5
+    },
+    {
+      "name": "LayoutImage IMG class='visible composited box'",
+      "bounds": [100, 100],
+      "drawsContent": false,
+      "transform": 6
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 20, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 160, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [24, 24, 0, 1]
+      ]
+    },
+    {
+      "id": 5,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 314, 0, 1]
+      ]
+    },
+    {
+      "id": 6,
+      "parent": 5,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [24, 24, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-2nd-stacking-context-composited-expected.txt b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-2nd-stacking-context-composited-expected.txt
index ae4cc71..6556a02 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-2nd-stacking-context-composited-expected.txt
+++ b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-2nd-stacking-context-composited-expected.txt
@@ -13,15 +13,26 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='accelerated stacking-context'",
-      "position": [8, 8],
       "bounds": [160, 90],
       "contentsOpaque": true,
-      "backgroundColor": "#FF0000"
+      "backgroundColor": "#FF0000",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow HTML (foreground) Layer",
       "bounds": [800, 600]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-composited-reason-children-expected.txt b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-composited-reason-children-expected.txt
index b39aec62..adb62359 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-composited-reason-children-expected.txt
+++ b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-composited-reason-children-expected.txt
@@ -34,9 +34,9 @@
     },
     {
       "name": "LayoutImage IMG class='accelerated'",
-      "position": [8, 8],
       "bounds": [160, 90],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='stacking-context' (foreground) Layer",
@@ -46,6 +46,17 @@
       "name": "LayoutBlockFlow HTML (foreground) Layer",
       "bounds": [800, 600]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolation-2-stacking-contexts-expected.txt b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolation-2-stacking-contexts-expected.txt
index 176d1921..cefa49c5 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolation-2-stacking-contexts-expected.txt
+++ b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolation-2-stacking-contexts-expected.txt
@@ -27,10 +27,10 @@
     },
     {
       "name": "LayoutImage IMG class='accelerated blended'",
-      "position": [8, 8],
       "bounds": [160, 90],
       "blendMode": "multiply",
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='stacking-context' (foreground) Layer",
@@ -38,33 +38,53 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='accelerated stacking-context'",
-      "position": [8, 8],
       "contentsOpaque": true,
-      "backgroundColor": "#FF0000"
+      "backgroundColor": "#FF0000",
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='stacking-context'",
-      "position": [8, 8],
       "bounds": [160, 90],
       "isolate": true,
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 2
     },
     {
       "name": "LayoutImage IMG class='accelerated blended'",
-      "position": [8, 8],
       "bounds": [160, 90],
       "blendMode": "multiply",
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='accelerated stacking-context' (foreground) Layer",
-      "position": [8, 8]
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow HTML (foreground) Layer",
       "bounds": [800, 600]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolation-layer-expected.txt b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolation-layer-expected.txt
index b5cf5d6d..f8aa4f8 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolation-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolation-layer-expected.txt
@@ -21,15 +21,26 @@
     },
     {
       "name": "LayoutImage IMG class='accelerated blended'",
-      "position": [8, 8],
       "bounds": [160, 90],
       "blendMode": "multiply",
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow HTML (foreground) Layer",
       "bounds": [800, 600]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolation-remove-expected.txt b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolation-remove-expected.txt
index b4dbe141..1b484a0 100644
--- a/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolation-remove-expected.txt
+++ b/third_party/WebKit/LayoutTests/css3/blending/mix-blend-mode-isolation-remove-expected.txt
@@ -8,26 +8,37 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='accelerated-stacking-context'",
-      "position": [8, 8],
       "bounds": [784, 90],
       "isolate": true,
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV id='intermediary' class='accelerated-no-stacking-context'",
-      "position": [8, 8],
       "bounds": [784, 90],
       "contentsOpaque": true,
       "backfaceVisibility": "hidden",
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
     },
     {
       "name": "LayoutImage IMG class='accelerated blended'",
-      "position": [8, 8],
       "bounds": [160, 90],
       "blendMode": "multiply",
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
index d9b9ec7..37f79f2 100644
--- a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
+++ b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
@@ -8,10 +8,21 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='composited-layer' class='final-drop-shadow'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
index b7df888..b72166c 100644
--- a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
+++ b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
@@ -8,10 +8,21 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='composited'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
index 69f1e37..07b31352 100644
--- a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
+++ b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -8,10 +8,21 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
-      "position": [250, 250],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
     }
   ]
 }
@@ -25,10 +36,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
-      "position": [250, 250],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
@@ -37,6 +48,17 @@
       "contentsOpaque": true,
       "backgroundColor": "#000000"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
index 69f1e37..07b31352 100644
--- a/third_party/WebKit/LayoutTests/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
+++ b/third_party/WebKit/LayoutTests/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -8,10 +8,21 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
-      "position": [250, 250],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
     }
   ]
 }
@@ -25,10 +36,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'",
-      "position": [250, 250],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'",
@@ -37,6 +48,17 @@
       "contentsOpaque": true,
       "backgroundColor": "#000000"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [250, 250, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/fast/borders/overflow-hidden-border-radius-force-backing-store-expected.txt b/third_party/WebKit/LayoutTests/fast/borders/overflow-hidden-border-radius-force-backing-store-expected.txt
index e89c93d..631d9a0 100644
--- a/third_party/WebKit/LayoutTests/fast/borders/overflow-hidden-border-radius-force-backing-store-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/borders/overflow-hidden-border-radius-force-backing-store-expected.txt
@@ -31,9 +31,21 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='content'",
-      "position": [50, 50],
       "bounds": [285, 1000],
-      "contentsOpaque": true
+      "contentsOpaque": true,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [50, 50, 0, 1]
+      ],
+      "flattenInheritedTransform": false
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-imageBitmap-from-imageBitmap-from-offscreen-canvas-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-imageBitmap-from-imageBitmap-from-offscreen-canvas-expected.txt
new file mode 100644
index 0000000..22c2f2f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-imageBitmap-from-imageBitmap-from-offscreen-canvas-expected.txt
@@ -0,0 +1,5 @@
+PASS successfullyParsed is true
+
+TEST COMPLETE
+ 
+Test PASSED
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-imageBitmap-from-imageBitmap-from-offscreen-canvas.html b/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-imageBitmap-from-imageBitmap-from-offscreen-canvas.html
new file mode 100644
index 0000000..eb778f39
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-imageBitmap-from-imageBitmap-from-offscreen-canvas.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="./resources/webgl-test-utils-full.js"></script>
+<script src="./resources/tex-image-and-sub-image-utils.js"></script>
+<script src="./resources/tex-image-and-sub-image-image-bitmap-utils.js"></script>
+<script src="../../../resources/js-test.js"></script>
+<script>
+
+window.jsTestIsAsync = true;
+
+var wtu = WebGLTestUtils;
+var tiu = TexImageUtils;
+var gl = null;
+var internalFormat = "RGBA";
+var pixelFormat = "RGBA";
+var pixelType = "UNSIGNED_BYTE";
+var redColor = [255, 0, 0];
+var greenColor = [0, 255, 0];
+var blackColor = [0, 0, 0];
+var darkRed = [26, 0, 0];
+var darkGreen = [0, 26, 0];
+
+function promiseRejected()
+{
+    document.getElementById("results").innerHTML = "Promise <span style='color:red'>REJECTED</span>";
+}
+
+function pass()
+{
+    document.getElementById("results").innerHTML = "Test <span style='color:green'>PASSED</span>";
+}
+
+function setCanvasToRedGreen(ctx) {
+    ctx.canvas.width = 2;
+    ctx.canvas.height = 2;
+    var width = ctx.canvas.width;
+    var halfWidth = Math.floor(width / 2);
+    var height = ctx.canvas.height;
+    var halfHeight = Math.floor(height / 2);
+    ctx.fillStyle = "rgba(255, 0, 0, 1)";
+    ctx.fillRect(0, 0, halfWidth, halfHeight);
+    ctx.fillStyle = "rgba(255, 0, 0, 0.1)";
+    ctx.fillRect(halfWidth, 0, halfWidth, halfHeight);
+    ctx.fillStyle = "rgba(0, 255, 0, 1)";
+    ctx.fillRect(0, halfHeight, halfWidth, halfHeight);
+    ctx.fillStyle = "rgba(0, 255, 0, 0.1)";
+    ctx.fillRect(halfWidth, halfHeight, halfWidth, halfHeight);
+}
+
+function generateTest()
+{
+    var bitmaps = [];
+
+    var canvas = document.getElementById("example");
+    gl = canvas.getContext("webgl");
+
+    gl.clearColor(0,0,0,1);
+    gl.clearDepth(1);
+
+    var testCanvas = document.createElement('canvas');
+    var offscreen = testCanvas.transferControlToOffscreen();
+    var ctx = offscreen.getContext('2d');
+    setCanvasToRedGreen(ctx);
+
+    var bitmap; // bitmap will be in unpremultiplied format
+    createImageBitmap(offscreen, {imageOrientation: "none", premultiplyAlpha: "none"}).then(function(imageBitmap) {
+        bitmap = imageBitmap;
+        var p1 = createImageBitmap(bitmap).then(function(imageBitmap) { bitmaps.defaultOption = imageBitmap });
+        var p2 = createImageBitmap(bitmap, {imageOrientation: "none", premultiplyAlpha: "premultiply"}).then(function(imageBitmap) { bitmaps.noFlipYPremul = imageBitmap });
+        var p3 = createImageBitmap(bitmap, {imageOrientation: "none", premultiplyAlpha: "default"}).then(function(imageBitmap) { bitmaps.noFlipYDefault = imageBitmap });
+        var p4 = createImageBitmap(bitmap, {imageOrientation: "none", premultiplyAlpha: "none"}).then(function(imageBitmap) { bitmaps.noFlipYUnpremul = imageBitmap });
+        var p5 = createImageBitmap(bitmap, {imageOrientation: "flipY", premultiplyAlpha: "premultiply"}).then(function(imageBitmap) { bitmaps.flipYPremul = imageBitmap });
+        var p6 = createImageBitmap(bitmap, {imageOrientation: "flipY", premultiplyAlpha: "default"}).then(function(imageBitmap) { bitmaps.flipYDefault = imageBitmap });
+        var p7 = createImageBitmap(bitmap, {imageOrientation: "flipY", premultiplyAlpha: "none"}).then(function(imageBitmap) { bitmaps.flipYUnpremul = imageBitmap });
+        var p8 = createImageBitmap(bitmap).then(function(imageBitmap) { bitmaps.colorSpaceDef = imageBitmap });
+        var p9 = createImageBitmap(bitmap, {colorSpaceConversion: "none"}).then(function(imageBitmap) { bitmaps.colorSpaceNone = imageBitmap });
+        var p10 = createImageBitmap(bitmap, {colorSpaceConversion: "default"}).then(function(imageBitmap) { bitmaps.colorSpaceDefault = imageBitmap });
+        Promise.all([p1, p2, p3, p4, p5, p6, p7, p8, p9, p10]).then(function() {
+            var alphaVal = 0.5;
+            var testPassed = runTest(bitmaps, alphaVal, false);
+            if (testPassed)
+                pass();
+            finishJSTest();
+        }, function() {
+            promiseRejected();
+            finishJSTest();
+        });
+    }, function() {
+        promiseRejected();
+        finishJSTest();
+    });
+}
+
+function init()
+{
+    if (window.testRunner) {
+        testRunner.overridePreference("WebKitWebGLEnabled", "1");
+        testRunner.dumpAsText();
+    }
+    generateTest();
+}
+</script>
+</head>
+<body onload="init()">
+<canvas id="texcanvas" width="2" height="2"></canvas>
+<canvas id="example" width="32" height="32"></canvas>
+<div id="results">Test <span style="color:red">FAILED</span></div>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-imageBitmap-from-offscreen-canvas-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-imageBitmap-from-offscreen-canvas-expected.txt
new file mode 100644
index 0000000..22c2f2f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-imageBitmap-from-offscreen-canvas-expected.txt
@@ -0,0 +1,5 @@
+PASS successfullyParsed is true
+
+TEST COMPLETE
+ 
+Test PASSED
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-imageBitmap-from-offscreen-canvas-resize.html b/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-imageBitmap-from-offscreen-canvas-resize.html
new file mode 100644
index 0000000..69446f9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-imageBitmap-from-offscreen-canvas-resize.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="./resources/webgl-test-utils-full.js"></script>
+<script src="./resources/tex-image-and-sub-image-utils.js"></script>
+<script src="./resources/tex-image-and-sub-image-image-bitmap-utils-resize.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<body>
+<script>
+var wtu = WebGLTestUtils;
+var tiu = TexImageUtils;
+var gl = null;
+var internalFormat = "RGBA";
+var pixelFormat = "RGBA";
+var pixelType = "UNSIGNED_BYTE";
+
+function setCanvasToRedGreen(ctx) {
+    var width = ctx.canvas.width;
+    var halfWidth = Math.floor(width / 2);
+    var height = ctx.canvas.height;
+    var halfHeight = Math.floor(height / 2);
+    ctx.fillStyle = "rgba(255, 0, 0, 1)";
+    ctx.fillRect(0, 0, halfWidth, halfHeight);
+    ctx.fillStyle = "rgba(255, 0, 0, 0.1)";
+    ctx.fillRect(halfWidth, 0, halfWidth, halfHeight);
+    ctx.fillStyle = "rgba(0, 255, 0, 1)";
+    ctx.fillRect(0, halfHeight, halfWidth, halfHeight);
+    ctx.fillStyle = "rgba(0, 255, 0, 0.1)";
+    ctx.fillRect(halfWidth, halfHeight, halfWidth, halfHeight);
+}
+
+promise_test(function() {
+    var bitmaps = [];
+
+    var canvas = document.createElement('canvas');
+    canvas.width = 4;
+    canvas.height = 4;
+    document.body.appendChild(canvas);
+    gl = canvas.getContext("webgl");
+
+    gl.clearColor(0,0,0,1);
+    gl.clearDepth(1);
+
+    var testCanvas = document.createElement('canvas');
+    testCanvas.width = 2;
+    testCanvas.height = 2;
+    var offscreen = testCanvas.transferControlToOffscreen();
+    var ctx = offscreen.getContext('2d');
+    setCanvasToRedGreen(ctx);
+
+    var options = {resizeWidth: 4, resizeHeight: 4, resizeQuality: "high"};
+    var p1 = createImageBitmap(offscreen, options).then(
+        function(imageBitmap) { bitmaps.defaultOption = imageBitmap });
+
+    options.imageOrientation = "none";
+    options.premultiplyAlpha = "premultiply";
+    var p2 = createImageBitmap(offscreen, options).then(
+        function(imageBitmap) { bitmaps.noFlipYPremul = imageBitmap });
+
+    options.premultiplyAlpha = "default";
+    var p3 = createImageBitmap(offscreen, options).then(
+        function(imageBitmap) { bitmaps.noFlipYDefault = imageBitmap });
+
+    options.premultiplyAlpha = "none";
+    var p4 = createImageBitmap(offscreen, options).then(
+        function(imageBitmap) { bitmaps.noFlipYUnpremul = imageBitmap });
+
+    options.imageOrientation = "flipY";
+    options.premultiplyAlpha = "premultiply";
+    var p5 = createImageBitmap(offscreen, options).then(
+        function(imageBitmap) { bitmaps.flipYPremul = imageBitmap });
+
+    options.premultiplyAlpha = "default";
+    var p6 = createImageBitmap(offscreen, options).then(
+        function(imageBitmap) { bitmaps.flipYDefault = imageBitmap });
+
+    options.premultiplyAlpha = "none";
+    var p7 = createImageBitmap(offscreen, options).then(
+        function(imageBitmap) { bitmaps.flipYUnpremul = imageBitmap });
+
+    options = {resizeWidth: 4, resizeHeight: 4, resizeQuality: "high"};
+    var p8 = createImageBitmap(offscreen, options).then(
+        function(imageBitmap) { bitmaps.colorSpaceDef = imageBitmap });
+
+    options.colorSpaceConversion = "none";
+    var p9 = createImageBitmap(offscreen, options).then(
+        function(imageBitmap) { bitmaps.colorSpaceNone = imageBitmap });
+
+    options.colorSpaceConversion = "default";
+    var p10 = createImageBitmap(offscreen, options).then(
+        function(imageBitmap) { bitmaps.colorSpaceDefault = imageBitmap });
+
+    return Promise.all([p1, p2, p3, p4, p5, p6, p7, p8, p9, p10]).then(function() {
+        var alphaVal = 0.5;
+        var testPassed = runTest(bitmaps, alphaVal, false);
+        if (!testPassed)
+            assert_true(false, 'Test failed');
+    }, function() {
+        assert_true(false, 'Promise rejected');
+    });
+}, 'createImageBitmap(HTMLCanvasElement) with resize and other options');
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-imageBitmap-from-offscreen-canvas.html b/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-imageBitmap-from-offscreen-canvas.html
new file mode 100644
index 0000000..f633636
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-imageBitmap-from-offscreen-canvas.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="./resources/webgl-test-utils-full.js"></script>
+<script src="./resources/tex-image-and-sub-image-utils.js"></script>
+<script src="./resources/tex-image-and-sub-image-image-bitmap-utils.js"></script>
+<script src="../../../resources/js-test.js"></script>
+<script>
+
+window.jsTestIsAsync = true;
+
+var wtu = WebGLTestUtils;
+var tiu = TexImageUtils;
+var gl = null;
+var internalFormat = "RGBA";
+var pixelFormat = "RGBA";
+var pixelType = "UNSIGNED_BYTE";
+var redColor = [255, 0, 0];
+var greenColor = [0, 255, 0];
+var blackColor = [0, 0, 0];
+var darkRed = [26, 0, 0];
+var darkGreen = [0, 26, 0];
+
+function promiseRejected()
+{
+    document.getElementById("results").innerHTML = "Promise <span style='color:red'>REJECTED</span>";
+}
+
+function pass()
+{
+    document.getElementById("results").innerHTML = "Test <span style='color:green'>PASSED</span>";
+}
+
+function setCanvasToRedGreen(ctx) {
+    var width = ctx.canvas.width;
+    var halfWidth = Math.floor(width / 2);
+    var height = ctx.canvas.height;
+    var halfHeight = Math.floor(height / 2);
+    ctx.fillStyle = "rgba(255, 0, 0, 1)";
+    ctx.fillRect(0, 0, halfWidth, halfHeight);
+    ctx.fillStyle = "rgba(255, 0, 0, 0.1)";
+    ctx.fillRect(halfWidth, 0, halfWidth, halfHeight);
+    ctx.fillStyle = "rgba(0, 255, 0, 1)";
+    ctx.fillRect(0, halfHeight, halfWidth, halfHeight);
+    ctx.fillStyle = "rgba(0, 255, 0, 0.1)";
+    ctx.fillRect(halfWidth, halfHeight, halfWidth, halfHeight);
+}
+
+function generateTest()
+{
+    var bitmaps = [];
+
+    var canvas = document.getElementById("example");
+    gl = canvas.getContext("webgl");
+
+    gl.clearColor(0,0,0,1);
+    gl.clearDepth(1);
+
+    var testCanvas = document.createElement('canvas');
+    testCanvas.width = testCanvas.height = 2;
+    var offscreen = testCanvas.transferControlToOffscreen();
+    var ctx = offscreen.getContext('2d');
+    setCanvasToRedGreen(ctx);
+
+    var p1 = createImageBitmap(offscreen).then(function(imageBitmap) { bitmaps.defaultOption = imageBitmap });
+    var p2 = createImageBitmap(offscreen, {imageOrientation: "none", premultiplyAlpha: "premultiply"}).then(function(imageBitmap) { bitmaps.noFlipYPremul = imageBitmap });
+    var p3 = createImageBitmap(offscreen, {imageOrientation: "none", premultiplyAlpha: "default"}).then(function(imageBitmap) { bitmaps.noFlipYDefault = imageBitmap });
+    var p4 = createImageBitmap(offscreen, {imageOrientation: "none", premultiplyAlpha: "none"}).then(function(imageBitmap) { bitmaps.noFlipYUnpremul = imageBitmap });
+    var p5 = createImageBitmap(offscreen, {imageOrientation: "flipY", premultiplyAlpha: "premultiply"}).then(function(imageBitmap) { bitmaps.flipYPremul = imageBitmap });
+    var p6 = createImageBitmap(offscreen, {imageOrientation: "flipY", premultiplyAlpha: "default"}).then(function(imageBitmap) { bitmaps.flipYDefault = imageBitmap });
+    var p7 = createImageBitmap(offscreen, {imageOrientation: "flipY", premultiplyAlpha: "none"}).then(function(imageBitmap) { bitmaps.flipYUnpremul = imageBitmap });
+    var p8 = createImageBitmap(offscreen).then(function(imageBitmap) { bitmaps.colorSpaceDef = imageBitmap });
+    var p9 = createImageBitmap(offscreen, {colorSpaceConversion: "none"}).then(function(imageBitmap) { bitmaps.colorSpaceNone = imageBitmap });
+    var p10 = createImageBitmap(offscreen, {colorSpaceConversion: "default"}).then(function(imageBitmap) { bitmaps.colorSpaceDefault = imageBitmap });
+    Promise.all([p1, p2, p3, p4, p5, p6, p7, p8, p9, p10]).then(function() {
+        var alphaVal = 0.5;
+        var testPassed = runTest(bitmaps, alphaVal, false);
+        if (testPassed)
+            pass();
+        finishJSTest();
+    }, function() {
+        promiseRejected();
+        finishJSTest();
+    });
+}
+
+function init()
+{
+    if (window.testRunner) {
+        testRunner.overridePreference("WebKitWebGLEnabled", "1");
+        testRunner.dumpAsText();
+    }
+    generateTest();
+}
+</script>
+</head>
+<body onload="init()">
+<canvas id="texcanvas" width="2" height="2"></canvas>
+<canvas id="example" width="32" height="32"></canvas>
+<div id="results">Test <span style="color:red">FAILED</span></div>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-video-last-uploaded-metadata.html b/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-video-last-uploaded-metadata.html
index ee52540..e1c30a2 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-video-last-uploaded-metadata.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/webgl/texImage-video-last-uploaded-metadata.html
@@ -28,6 +28,9 @@
     passing &= texture.lastUploadedVideoHeight === 64;
     passing &= texture.lastUploadedVideoWidth === 64;
     passing &= Math.abs(texture.lastUploadedVideoTimestamp - 2.0) < 0.001;
+    passing &= texture.lastUploadedVideoFrameWasSkipped === false;
+    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, video);
+    passing &= texture.lastUploadedVideoFrameWasSkipped === true;
     if (passing) {
         pass();
     }
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/composited-layer-expected.txt b/third_party/WebKit/LayoutTests/fast/multicol/composited-layer-expected.txt
index 15b06e6d..dcd77964 100644
--- a/third_party/WebKit/LayoutTests/fast/multicol/composited-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/multicol/composited-layer-expected.txt
@@ -8,8 +8,19 @@
 },
 {
 "name": "LayoutBlockFlow DIV id='multicol'",
-"position": [8, 8],
-"bounds": [200, 100]
+"bounds": [200, 100],
+"transform": 1
+}
+],
+"transforms": [
+{
+"id": 1,
+"transform": [
+[1, 0, 0, 0],
+[0, 1, 0, 0],
+[0, 0, 1, 0],
+[8, 8, 0, 1]
+]
 }
 ]
 }
diff --git a/third_party/WebKit/LayoutTests/fast/sub-pixel/repaint-subpixel-layer-in-subpixel-composited-layer-expected.txt b/third_party/WebKit/LayoutTests/fast/sub-pixel/repaint-subpixel-layer-in-subpixel-composited-layer-expected.txt
index 997b531..a9051de 100644
--- a/third_party/WebKit/LayoutTests/fast/sub-pixel/repaint-subpixel-layer-in-subpixel-composited-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/sub-pixel/repaint-subpixel-layer-in-subpixel-composited-layer-expected.txt
@@ -9,7 +9,6 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='container'",
-      "position": [11, 0],
       "bounds": [100, 50],
       "paintInvalidations": [
         {
@@ -22,6 +21,18 @@
           "rect": [10, 0, 12, 12],
           "reason": "geometry"
         }
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [11, 0, 0, 1]
       ]
     }
   ],
@@ -43,7 +54,6 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='container'",
-      "position": [11, 0],
       "bounds": [100, 50],
       "paintInvalidations": [
         {
@@ -56,6 +66,18 @@
           "rect": [9, 0, 13, 12],
           "reason": "geometry"
         }
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [11, 0, 0, 1]
       ]
     }
   ],
@@ -77,7 +99,6 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='container'",
-      "position": [10, 0],
       "bounds": [100, 50],
       "paintInvalidations": [
         {
@@ -90,6 +111,18 @@
           "rect": [10, 0, 13, 12],
           "reason": "geometry"
         }
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 0, 0, 1]
       ]
     }
   ],
@@ -111,7 +144,6 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='container'",
-      "position": [11, 0],
       "bounds": [100, 50],
       "paintInvalidations": [
         {
@@ -124,6 +156,18 @@
           "rect": [10, 0, 13, 12],
           "reason": "geometry"
         }
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [11, 0, 0, 1]
       ]
     }
   ],
@@ -145,7 +189,6 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='container'",
-      "position": [10, 0],
       "bounds": [100, 50],
       "paintInvalidations": [
         {
@@ -158,6 +201,18 @@
           "rect": [10, 0, 13, 12],
           "reason": "geometry"
         }
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 0, 0, 1]
       ]
     }
   ],
diff --git a/third_party/WebKit/LayoutTests/fast/sub-pixel/should-not-repaint-subpixel-composited-layer-expected.txt b/third_party/WebKit/LayoutTests/fast/sub-pixel/should-not-repaint-subpixel-composited-layer-expected.txt
index f1025cfa..609b07b8 100644
--- a/third_party/WebKit/LayoutTests/fast/sub-pixel/should-not-repaint-subpixel-composited-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/sub-pixel/should-not-repaint-subpixel-composited-layer-expected.txt
@@ -9,9 +9,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [18, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -26,9 +37,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [18, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -43,9 +65,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [18, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -60,9 +93,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [18, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -77,9 +121,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [19, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [19, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -94,9 +149,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [19, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [19, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -111,9 +177,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [19, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [19, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -128,9 +205,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [19, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [19, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -145,9 +233,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [19, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [19, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -162,9 +261,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [19, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [19, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -179,9 +289,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [19, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [19, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -196,9 +317,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [19, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [19, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -213,9 +345,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [19, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [19, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -230,9 +373,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [19, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [19, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -247,9 +401,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [20, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -264,9 +429,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [20, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -281,9 +457,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [20, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -298,9 +485,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [20, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -315,9 +513,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [20, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -332,9 +541,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [20, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 8, 0, 1]
+      ]
     }
   ]
 }
@@ -349,9 +569,20 @@
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='layer'",
-      "position": [20, 8],
       "bounds": [100, 50],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 8, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/compositing/containing-block-added-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/compositing/containing-block-added-expected.txt
new file mode 100644
index 0000000..7a22eb1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/compositing/containing-block-added-expected.txt
@@ -0,0 +1,37 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV class='fixed'",
+          "rect": [50, 50, 75, 75],
+          "reason": "disappeared"
+        }
+      ]
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='container'",
+      "position": [200, 100],
+      "bounds": [125, 125],
+      "backgroundColor": "#0000FF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV class='fixed'",
+          "rect": [50, 50, 75, 75],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV class='fixed'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/compositing/containing-block-added-individual-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/compositing/containing-block-added-individual-expected.txt
new file mode 100644
index 0000000..7a22eb1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/compositing/containing-block-added-individual-expected.txt
@@ -0,0 +1,37 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV class='fixed'",
+          "rect": [50, 50, 75, 75],
+          "reason": "disappeared"
+        }
+      ]
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='container'",
+      "position": [200, 100],
+      "bounds": [125, 125],
+      "backgroundColor": "#0000FF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV class='fixed'",
+          "rect": [50, 50, 75, 75],
+          "reason": "appeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV class='fixed'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/compositing/containing-block-removed-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/compositing/containing-block-removed-expected.txt
new file mode 100644
index 0000000..bb7844a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/compositing/containing-block-removed-expected.txt
@@ -0,0 +1,39 @@
+CONSOLE MESSAGE: line 30: debug
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV class='fixed'",
+          "rect": [50, 50, 75, 75],
+          "reason": "appeared"
+        }
+      ]
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='container'",
+      "position": [200, 100],
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV class='fixed'",
+          "rect": [50, 50, 75, 75],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV class='fixed'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/compositing/containing-block-removed-individual-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/compositing/containing-block-removed-individual-expected.txt
new file mode 100644
index 0000000..bb7844a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/compositing/containing-block-removed-individual-expected.txt
@@ -0,0 +1,39 @@
+CONSOLE MESSAGE: line 30: debug
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV class='fixed'",
+          "rect": [50, 50, 75, 75],
+          "reason": "appeared"
+        }
+      ]
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV id='container'",
+      "position": [200, 100],
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV class='fixed'",
+          "rect": [50, 50, 75, 75],
+          "reason": "disappeared"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow (positioned) DIV class='fixed'",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/destroy-composited-scrollbar-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/destroy-composited-scrollbar-expected.txt
new file mode 100644
index 0000000..844765d9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/destroy-composited-scrollbar-expected.txt
@@ -0,0 +1,35 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV",
+      "position": [0, 100],
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow (positioned) DIV",
+          "rect": [0, 0, 200, 200],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll control"
+    },
+    {
+      "object": "LayoutBlockFlow (positioned) DIV",
+      "reason": "geometry"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/filter-repaint-on-accelerated-layer-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/filter-repaint-on-accelerated-layer-expected.txt
new file mode 100644
index 0000000..cefe909
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/filter-repaint-on-accelerated-layer-expected.txt
@@ -0,0 +1,42 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow DIV id='resize' class='accelerated'",
+      "bounds": [100, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "paintInvalidations": [
+        {
+          "object": "LayoutBlockFlow DIV id='resize' class='accelerated'",
+          "rect": [100, 0, 100, 200],
+          "reason": "incremental"
+        }
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutBlockFlow DIV id='resize' class='accelerated'",
+      "reason": "incremental"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/mask-clip-change-stacking-child-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/mask-clip-change-stacking-child-expected.txt
index 29f61d96..e7738583 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/mask-clip-change-stacking-child-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/mask-clip-change-stacking-child-expected.txt
@@ -10,6 +10,7 @@
       "name": "LayoutBlockFlow DIV",
       "position": [8, 8],
       "bounds": [500, 500],
+      "contentsOpaque": true,
       "backgroundColor": "#ADD8E6"
     },
     {
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/repaint-overlay/layers-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/repaint-overlay/layers-expected.txt
index 1576397..4b239356 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/repaint-overlay/layers-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/repaint-overlay/layers-expected.txt
@@ -30,6 +30,7 @@
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='transform'",
       "bounds": [200, 200],
+      "contentsOpaque": true,
       "backgroundColor": "#FFFF00",
       "paintInvalidations": [
         {
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/stacking-context-lost-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/stacking-context-lost-expected.txt
index c8b591a..038cbc65 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/stacking-context-lost-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/stacking-context-lost-expected.txt
@@ -10,6 +10,7 @@
       "name": "LayoutBlockFlow (relative positioned) DIV id='outer'",
       "position": [278, 278],
       "bounds": [100, 100],
+      "contentsOpaque": true,
       "backfaceVisibility": "hidden",
       "backgroundColor": "#008000",
       "paintInvalidations": [
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/video-mute-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/video-mute-repaint-expected.txt
index 64417ff..79ba6b0 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/video-mute-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/video-mute-repaint-expected.txt
@@ -10,6 +10,7 @@
       "name": "LayoutFlexibleBox (relative positioned) DIV",
       "position": [8, 501],
       "bounds": [700, 32],
+      "contentsOpaque": true,
       "backgroundColor": "#FAFAFA",
       "paintInvalidations": [
         {
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/video-unmute-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/video-unmute-repaint-expected.txt
index 93458f30..57b74141 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/video-unmute-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/video-unmute-repaint-expected.txt
@@ -10,6 +10,7 @@
       "name": "LayoutFlexibleBox (relative positioned) DIV",
       "position": [8, 501],
       "bounds": [700, 32],
+      "contentsOpaque": true,
       "backgroundColor": "#FAFAFA",
       "paintInvalidations": [
         {
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/child-of-sub-pixel-offset-composited-layer-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/child-of-sub-pixel-offset-composited-layer-expected.txt
index 496905b..f605ba59 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/child-of-sub-pixel-offset-composited-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/child-of-sub-pixel-offset-composited-layer-expected.txt
@@ -8,7 +8,6 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='container'",
-      "position": [101, 100],
       "bounds": [14, 14],
       "backgroundColor": "#FF0000",
       "paintInvalidations": [
@@ -17,6 +16,18 @@
           "rect": [-1, 0, 15, 14],
           "reason": "style change"
         }
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [101, 100, 0, 1]
       ]
     }
   ],
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/composited-iframe-scroll-repaint-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/composited-iframe-scroll-repaint-expected.txt
index 42d373c9..b89fd8f 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/composited-iframe-scroll-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/composited-iframe-scroll-repaint-expected.txt
@@ -8,9 +8,8 @@
     },
     {
       "name": "LayoutBlockFlow BODY",
-      "position": [8, 8],
       "bounds": [284, 500],
-      "transform": 1
+      "transform": 2
     }
   ],
   "transforms": [
@@ -22,6 +21,16 @@
         [0, 0, 1, 0],
         [0, -20, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/clipping-should-not-repaint-composited-descendants-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/clipping-should-not-repaint-composited-descendants-expected.txt
index dab48cf0..2d945a0 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/clipping-should-not-repaint-composited-descendants-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/clipping-should-not-repaint-composited-descendants-expected.txt
@@ -8,41 +8,81 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='clipping-container'",
-      "position": [108, 108],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Child Containment Layer",
-      "position": [108, 108],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV class='clipped-composited-child'",
-      "position": [8, 8],
       "bounds": [252, 252],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFF00"
+      "backgroundColor": "#FFFF00",
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='clipping-container with-initial-clipping'",
-      "position": [108, 408],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "Child Containment Layer",
-      "position": [108, 408],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
     },
     {
       "name": "LayoutBlockFlow DIV class='clipped-composited-child'",
-      "position": [8, 308],
       "bounds": [252, 252],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFF00"
+      "backgroundColor": "#FFFF00",
+      "transform": 4
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [108, 108, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [-100, -100, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [108, 408, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [-100, -100, 0, 1]
+      ]
     }
   ],
   "objectPaintInvalidations": [
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer-expected.txt
index 6d18f80..c1127fa 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer-expected.txt
@@ -14,11 +14,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='compositedBehind'",
-      "position": [8, 8],
       "bounds": [500, 500],
       "contentsOpaque": true,
       "backgroundColor": "#00FFFF",
-      "transform": 1
+      "transform": 2
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='containerOverlapsComposited')",
@@ -48,6 +47,17 @@
         [0, 0, 1, 0],
         [0, -100, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
     }
   ],
   "objectPaintInvalidations": [
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
similarity index 87%
rename from third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
rename to third_party/WebKit/LayoutTests/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
index c9da3313..fd7ab7b 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
@@ -12,10 +12,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='foo'",
-      "position": [8, 8],
       "bounds": [200, 1000],
       "contentsOpaque": true,
-      "backgroundColor": "#D3D3D3"
+      "backgroundColor": "#D3D3D3",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV)",
@@ -50,6 +50,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutView #document",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/invalidate-when-leaving-squashed-layer-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/invalidate-when-leaving-squashed-layer-expected.txt
index b358058..12e27da 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/invalidate-when-leaving-squashed-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/invalidate-when-leaving-squashed-layer-expected.txt
@@ -12,10 +12,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV",
-      "position": [8, 8],
       "bounds": [200, 200],
       "contentsOpaque": true,
-      "backgroundColor": "#D3D3D3"
+      "backgroundColor": "#D3D3D3",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV)",
@@ -31,7 +31,6 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='target'",
-      "position": [50, 50],
       "bounds": [200, 200],
       "contentsOpaque": true,
       "backgroundColor": "#ADD8E6",
@@ -46,6 +45,28 @@
           "rect": [0, 0, 200, 200],
           "reason": "subtree"
         }
+      ],
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [50, 50, 0, 1]
       ]
     }
   ],
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/invalidations-on-composited-layers-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/invalidations-on-composited-layers-expected.txt
index d40c3958..d90323ef 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/invalidations-on-composited-layers-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/invalidations-on-composited-layers-expected.txt
@@ -8,7 +8,6 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='parent'",
-      "position": [8, 8],
       "bounds": [400, 400],
       "contentsOpaque": true,
       "backgroundColor": "#008000",
@@ -18,11 +17,11 @@
           "rect": [0, 0, 400, 400],
           "reason": "style change"
         }
-      ]
+      ],
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='child'",
-      "position": [58, 58],
       "bounds": [75, 75],
       "contentsOpaque": true,
       "backgroundColor": "#0000FF",
@@ -32,6 +31,28 @@
           "rect": [0, 0, 75, 75],
           "reason": "style change"
         }
+      ],
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [50, 50, 0, 1]
       ]
     }
   ],
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/overlap-test-with-filter-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/overlap-test-with-filter-expected.txt
index a8342fdd2..eb29d66 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/overlap-test-with-filter-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/overlap-test-with-filter-expected.txt
@@ -36,16 +36,16 @@
     },
     {
       "name": "LayoutBlockFlow BODY",
-      "position": [8, 8],
       "bounds": [284, 84],
-      "backgroundColor": "#FFFF00"
+      "backgroundColor": "#FFFF00",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV",
       "bounds": [300, 100],
       "contentsOpaque": true,
       "backgroundColor": "#D3D3D3",
-      "transform": 1
+      "transform": 2
     }
   ],
   "transforms": [
@@ -55,6 +55,15 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
         [151, 0, 0, 1]
       ]
     }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/compositing/remove-squashed-layer-plus-move-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/remove-squashed-layer-plus-move-expected.txt
similarity index 91%
rename from third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/compositing/remove-squashed-layer-plus-move-expected.txt
rename to third_party/WebKit/LayoutTests/paint/invalidation/compositing/remove-squashed-layer-plus-move-expected.txt
index f4445d30..5f6802d80 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/compositing/remove-squashed-layer-plus-move-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/remove-squashed-layer-plus-move-expected.txt
@@ -29,9 +29,9 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV",
-      "position": [8, 8],
       "bounds": [1000, 1000],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (relative positioned) DIV class='mv-tile')",
@@ -66,6 +66,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (relative positioned) DIV id='foo' class='mv-tile'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/repaint-overflow-scrolled-squashed-content-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/repaint-overflow-scrolled-squashed-content-expected.txt
index af83aaf..161d11ac 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/repaint-overflow-scrolled-squashed-content-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/repaint-overflow-scrolled-squashed-content-expected.txt
@@ -14,10 +14,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='foo2'",
-      "position": [8, 8],
       "bounds": [150, 1000],
       "contentsOpaque": true,
-      "backgroundColor": "#ADD8E6"
+      "backgroundColor": "#ADD8E6",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='foo')",
@@ -32,6 +32,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='foo'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/repaint-squashed-layer-in-rect-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/repaint-squashed-layer-in-rect-expected.txt
index 1da0d1e..ceb4997 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/repaint-squashed-layer-in-rect-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/repaint-squashed-layer-in-rect-expected.txt
@@ -12,10 +12,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV",
-      "position": [8, 8],
       "bounds": [800, 800],
       "contentsOpaque": true,
-      "backgroundColor": "#D3D3D3"
+      "backgroundColor": "#D3D3D3",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV)",
@@ -30,6 +30,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow DIV id='imgElement'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/repaint-via-layout-offset-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/repaint-via-layout-offset-expected.txt
index 2eb7c4e..4878785 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/repaint-via-layout-offset-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/repaint-via-layout-offset-expected.txt
@@ -12,10 +12,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV",
-      "position": [8, 8],
       "bounds": [200, 200],
       "contentsOpaque": true,
-      "backgroundColor": "#ADD8E6"
+      "backgroundColor": "#ADD8E6",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV id='container')",
@@ -30,6 +30,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) SPAN class='child embiggen'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/resize-repaint-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/resize-repaint-expected.txt
index f74ddf59..9275c85 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/resize-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/resize-repaint-expected.txt
@@ -8,7 +8,6 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='resizing'",
-      "position": [8, 8],
       "bounds": [402, 207],
       "paintInvalidations": [
         {
@@ -26,6 +25,18 @@
           "rect": [2, 104, 398, 50],
           "reason": "appeared"
         }
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
       ]
     }
   ],
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/resize-squashing-layer-that-needs-full-repaint-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/resize-squashing-layer-that-needs-full-repaint-expected.txt
index 09596b1..bfe26f5 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/resize-squashing-layer-that-needs-full-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/resize-squashing-layer-that-needs-full-repaint-expected.txt
@@ -12,9 +12,9 @@
     },
     {
       "name": "LayoutBlockFlow DIV",
-      "position": [8, 8],
       "bounds": [500, 500],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV)",
@@ -39,6 +39,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='to-be-removed'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/squash-partial-repaint-inside-squashed-layer-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/squash-partial-repaint-inside-squashed-layer-expected.txt
index 5329cc980..a1d85da7 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/squash-partial-repaint-inside-squashed-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/squash-partial-repaint-inside-squashed-layer-expected.txt
@@ -12,10 +12,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited box behind'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='box middle')",
@@ -30,6 +30,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutImage (positioned) IMG id='repaintdiv' class='repaintdiv'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/tricky-element-removal-crash-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/tricky-element-removal-crash-expected.txt
index 5242e20..6bdc321c 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/compositing/tricky-element-removal-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/compositing/tricky-element-removal-crash-expected.txt
@@ -12,10 +12,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV",
-      "position": [8, 8],
       "bounds": [200, 200],
       "contentsOpaque": true,
-      "backgroundColor": "#FA8072"
+      "backgroundColor": "#FA8072",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV)",
@@ -23,6 +23,18 @@
       "bounds": [100, 100]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow DIV id='target'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/filter-invalidation-with-composited-container-change-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/filter-invalidation-with-composited-container-change-expected.txt
index e5ffd453..271d0215 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/filter-invalidation-with-composited-container-change-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/filter-invalidation-with-composited-container-change-expected.txt
@@ -15,7 +15,6 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='box' class='green box blurry'",
-      "position": [8, 8],
       "bounds": [200, 200],
       "contentsOpaque": true,
       "backgroundColor": "#008000",
@@ -30,6 +29,18 @@
           "rect": [0, 0, 200, 200],
           "reason": "subtree"
         }
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
       ]
     }
   ],
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/filter-repaint-accelerated-child-with-filter-child-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/filter-repaint-accelerated-child-with-filter-child-expected.txt
index 2ba8e7e4..dfa12fc 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/filter-repaint-accelerated-child-with-filter-child-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/filter-repaint-accelerated-child-with-filter-child-expected.txt
@@ -13,7 +13,6 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='accelerated'",
-      "position": [-4, -4],
       "bounds": [212, 257],
       "paintInvalidations": [
         {
@@ -21,6 +20,18 @@
           "rect": [-1, -1, 258, 258],
           "reason": "geometry"
         }
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [-4, -4, 0, 1]
       ]
     }
   ],
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/filter-repaint-accelerated-on-accelerated-filter-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/filter-repaint-accelerated-on-accelerated-filter-expected.txt
index 3d44275..f5d1d6cff 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/filter-repaint-accelerated-on-accelerated-filter-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/filter-repaint-accelerated-on-accelerated-filter-expected.txt
@@ -8,12 +8,11 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='blur accelerated'",
-      "position": [8, 8],
-      "bounds": [200, 200]
+      "bounds": [200, 200],
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV id='resize' class='drop-shadow accelerated'",
-      "position": [8, 8],
       "bounds": [100, 200],
       "contentsOpaque": true,
       "backgroundColor": "#008000",
@@ -23,6 +22,18 @@
           "rect": [0, 0, 200, 200],
           "reason": "geometry"
         }
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
       ]
     }
   ],
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/filter-repaint-on-accelerated-layer-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/filter-repaint-on-accelerated-layer-expected.txt
index 3ebaf92..f42b0493 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/filter-repaint-on-accelerated-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/filter-repaint-on-accelerated-layer-expected.txt
@@ -13,7 +13,6 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='resize' class='accelerated'",
-      "position": [8, 8],
       "bounds": [100, 200],
       "contentsOpaque": true,
       "backgroundColor": "#008000",
@@ -23,6 +22,18 @@
           "rect": [100, 0, 100, 200],
           "reason": "incremental"
         }
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
       ]
     }
   ],
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/fixed-element-repaint-after-compositing-update-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/fixed-element-repaint-after-compositing-update-expected.txt
similarity index 74%
rename from third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/fixed-element-repaint-after-compositing-update-expected.txt
rename to third_party/WebKit/LayoutTests/paint/invalidation/fixed-element-repaint-after-compositing-update-expected.txt
index 566cea4..7bd0e55 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/fixed-element-repaint-after-compositing-update-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/fixed-element-repaint-after-compositing-update-expected.txt
@@ -8,9 +8,9 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='fixedTransformed'",
-      "position": [8, 102],
       "contentsOpaque": true,
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='fixedOverlapping'",
@@ -25,6 +25,17 @@
       "contentsOpaque": true,
       "backgroundColor": "#008000"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 102, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/invalidate-paint-in-iframe-in-composited-layer-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/invalidate-paint-in-iframe-in-composited-layer-expected.txt
index 13fd44c..3a257a9 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/invalidate-paint-in-iframe-in-composited-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/invalidate-paint-in-iframe-in-composited-layer-expected.txt
@@ -8,7 +8,6 @@
     },
     {
       "name": "LayoutBlockFlow DIV",
-      "position": [8, 408],
       "bounds": [304, 200],
       "paintInvalidations": [
         {
@@ -16,6 +15,18 @@
           "rect": [10, 10, 50, 50],
           "reason": "style change"
         }
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 408, 0, 1]
       ]
     }
   ],
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/overflow-changed-on-child-of-composited-layer-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/overflow-changed-on-child-of-composited-layer-expected.txt
index a015fe8..780b46e 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/overflow-changed-on-child-of-composited-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/overflow-changed-on-child-of-composited-layer-expected.txt
@@ -8,7 +8,6 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='parent'",
-      "position": [27, 19],
       "bounds": [745, 102],
       "paintInvalidations": [
         {
@@ -21,6 +20,18 @@
           "rect": [0, 0, 102, 102],
           "reason": "style change"
         }
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [27, 19, 0, 1]
       ]
     }
   ],
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/overflow-scroll-composited-non-stacking-child-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/overflow-scroll-composited-non-stacking-child-expected.txt
index a871aa88..42cbfbf 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/overflow-scroll-composited-non-stacking-child-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/overflow-scroll-composited-non-stacking-child-expected.txt
@@ -30,10 +30,10 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='icon'",
-      "position": [200, 10],
       "bounds": [40, 40],
       "contentsOpaque": true,
-      "backgroundColor": "#FFDDBB"
+      "backgroundColor": "#FFDDBB",
+      "transform": 1
     },
     {
       "name": "Ancestor Clipping Layer",
@@ -53,6 +53,17 @@
       "bounds": [180, 250]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [200, 10, 0, 1]
+      ]
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow DIV class='scroller'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/repaint-composited-child-in-scrolled-container-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/repaint-composited-child-in-scrolled-container-expected.txt
index 6d26ce0..cb69375 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/repaint-composited-child-in-scrolled-container-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/repaint-composited-child-in-scrolled-container-expected.txt
@@ -21,7 +21,6 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='container'",
-      "position": [-307, 8],
       "bounds": [600, 600],
       "contentsOpaque": true,
       "backgroundColor": "#FF0000",
@@ -31,7 +30,20 @@
           "rect": [0, 0, 600, 600],
           "reason": "style change"
         }
-      ]
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [-307, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
     }
   ],
   "objectPaintInvalidations": [
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/svg/resize-svg-invalidate-children-2-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/svg/resize-svg-invalidate-children-2-expected.txt
index 89d6c4a..0237acd 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/svg/resize-svg-invalidate-children-2-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/svg/resize-svg-invalidate-children-2-expected.txt
@@ -15,7 +15,6 @@
     },
     {
       "name": "LayoutSVGRoot svg",
-      "position": [8, 8],
       "bounds": [600, 400],
       "paintInvalidations": [
         {
@@ -38,6 +37,18 @@
           "rect": [0, 120, 200, 160],
           "reason": "geometry"
         }
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
       ]
     }
   ],
diff --git a/third_party/WebKit/LayoutTests/paint/pagination/pagination-change-clip-crash-expected.txt b/third_party/WebKit/LayoutTests/paint/pagination/pagination-change-clip-crash-expected.txt
index 34e96286..37e7d59 100644
--- a/third_party/WebKit/LayoutTests/paint/pagination/pagination-change-clip-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/pagination/pagination-change-clip-crash-expected.txt
@@ -2,22 +2,33 @@
   "layers": [
     {
       "name": "LayoutView #document",
-      "bounds": [785, 736],
+      "bounds": [785, 734],
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
           "object": "LayoutView #document",
-          "rect": [0, 636, 785, 100],
+          "rect": [0, 634, 785, 100],
           "reason": "incremental"
         }
       ]
     },
     {
       "name": "LayoutBlockFlow DIV id='background'",
-      "position": [8, 428],
       "bounds": [769, 300],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 426, 0, 1]
+      ]
     }
   ],
   "objectPaintInvalidations": [
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/squashing/selection-repaint-with-gaps-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/compositing/squashing/selection-repaint-with-gaps-expected.txt
index 6d87a3a..99b58e41 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/compositing/squashing/selection-repaint-with-gaps-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/squashing/selection-repaint-with-gaps-expected.txt
@@ -13,9 +13,9 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='overlap'",
-      "position": [8, 8],
       "bounds": [300, 500],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='item')",
@@ -30,6 +30,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='item1' class='item'",
@@ -67,9 +79,9 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='overlap'",
-      "position": [8, 8],
       "bounds": [300, 500],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='item')",
@@ -89,6 +101,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='item1' class='item'",
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/canvas-text-alignment-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/canvas-text-alignment-expected.png
index 3057ebc..bc5c79f 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/canvas-text-alignment-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/canvas-text-alignment-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/fillrect_gradient-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/fillrect_gradient-expected.png
index 4737236..064078d 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/fillrect_gradient-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu/fast/canvas/fillrect_gradient-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/bounds-ignores-hidden-composited-descendant-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/bounds-ignores-hidden-composited-descendant-expected.txt
deleted file mode 100644
index 21b6fe2..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/bounds-ignores-hidden-composited-descendant-expected.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [10, 10],
-      "bounds": [590, 208]
-    },
-    {
-      "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [10, 260],
-      "bounds": [590, 208]
-    },
-    {
-      "name": "LayoutBlockFlow (relative positioned) DIV class='composited inner box'",
-      "position": [500, 368],
-      "bounds": [100, 100],
-      "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/composited-in-columns-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/composited-in-columns-expected.txt
deleted file mode 100644
index d648298..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/composited-in-columns-expected.txt
+++ /dev/null
@@ -1,37 +0,0 @@
- {
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "LayoutBlockFlow DIV class='composited block'",
-      "position": [14, 162],
-      "bounds": [210, 60],
-      "backgroundColor": "#0000FF"
-    },
-    {
-      "name": "LayoutBlockFlow DIV class='composited box'",
-      "position": [19, 167],
-      "bounds": [50, 50],
-      "contentsOpaque": true,
-      "backgroundColor": "#008000"
-    },
-    {
-      "name": "LayoutBlockFlow DIV class='composited block'",
-      "position": [272, 88],
-      "bounds": [210, 60],
-      "backgroundColor": "#0000FF"
-    },
-    {
-      "name": "LayoutBlockFlow DIV class='composited box'",
-      "position": [277, 93],
-      "bounds": [50, 50],
-      "contentsOpaque": true,
-      "backgroundColor": "#008000"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/rtl/rtl-absolute-overflow-scrolled-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/rtl/rtl-absolute-overflow-scrolled-expected.png
deleted file mode 100644
index 88451f9b..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/rtl/rtl-absolute-overflow-scrolled-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/squashing/selection-repaint-with-gaps-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/compositing/squashing/selection-repaint-with-gaps-expected.txt
index 6faa922..9079a51 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/squashing/selection-repaint-with-gaps-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/squashing/selection-repaint-with-gaps-expected.txt
@@ -13,9 +13,9 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='overlap'",
-      "position": [8, 8],
       "bounds": [300, 500],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='item')",
@@ -30,6 +30,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='item1' class='item'",
@@ -67,9 +79,9 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='overlap'",
-      "position": [8, 8],
       "bounds": [300, 500],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='item')",
@@ -89,6 +101,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='item1' class='item'",
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/tiled-layers-hidpi-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/compositing/tiled-layers-hidpi-expected.txt
deleted file mode 100644
index fbf9880..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/tiled-layers-hidpi-expected.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [1808, 585],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "LayoutBlockFlow DIV id='composited'",
-      "position": [8, 8],
-      "bounds": [1800, 10],
-      "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/visibility/visibility-image-layers-dynamic-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/compositing/visibility/visibility-image-layers-dynamic-expected.txt
deleted file mode 100644
index 6b0e3ea3..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/visibility/visibility-image-layers-dynamic-expected.txt
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-Initial
-
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [785, 626],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "LayoutBlockFlow DIV class='composited container'",
-      "position": [18, 10],
-      "bounds": [749, 144],
-      "drawsContent": false
-    },
-    {
-      "name": "LayoutBlockFlow DIV class='composited hidden container'",
-      "position": [14, 314],
-      "bounds": [757, 152],
-      "drawsContent": false,
-      "contentsVisible": false
-    },
-    {
-      "name": "LayoutImage IMG class='visible composited box'",
-      "position": [38, 338],
-      "bounds": [100, 100],
-      "drawsContent": false
-    }
-  ]
-}
-After step 1
-
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [785, 1081],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "LayoutBlockFlow DIV class='composited container'",
-      "position": [18, 10],
-      "bounds": [749, 144],
-      "drawsContent": false
-    },
-    {
-      "name": "LayoutImage IMG class='hidden composited box'",
-      "position": [38, 30],
-      "bounds": [100, 100],
-      "drawsContent": false
-    },
-    {
-      "name": "LayoutBlockFlow DIV class='composited hidden container'",
-      "position": [14, 314],
-      "bounds": [757, 152],
-      "drawsContent": false,
-      "contentsVisible": false
-    },
-    {
-      "name": "LayoutImage IMG class='visible composited box'",
-      "position": [38, 338],
-      "bounds": [100, 100],
-      "drawsContent": false
-    }
-  ]
-}
-After step 2
-
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [785, 1625],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "LayoutBlockFlow DIV class='composited container'",
-      "position": [18, 10],
-      "bounds": [749, 144],
-      "drawsContent": false
-    },
-    {
-      "name": "LayoutImage IMG class='hidden composited box'",
-      "position": [38, 30],
-      "bounds": [100, 100],
-      "drawsContent": false
-    },
-    {
-      "name": "LayoutBlockFlow DIV class='composited hidden container'",
-      "position": [14, 160],
-      "bounds": [757, 152]
-    },
-    {
-      "name": "LayoutImage IMG class='composited box'",
-      "position": [38, 184],
-      "bounds": [100, 100],
-      "drawsContent": false
-    },
-    {
-      "name": "LayoutBlockFlow DIV class='composited hidden container'",
-      "position": [14, 314],
-      "bounds": [757, 152],
-      "drawsContent": false,
-      "contentsVisible": false
-    },
-    {
-      "name": "LayoutImage IMG class='visible composited box'",
-      "position": [38, 338],
-      "bounds": [100, 100],
-      "drawsContent": false
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/mix-blend-mode-composited-layers-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/mix-blend-mode-composited-layers-expected.txt
index 329764f..2873ba9 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/css3/blending/mix-blend-mode-composited-layers-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/css3/blending/mix-blend-mode-composited-layers-expected.txt
@@ -21,10 +21,21 @@
     },
     {
       "name": "LayoutBlockFlow DIV",
-      "position": [8, 18],
       "bounds": [10, 10],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFF00"
+      "backgroundColor": "#FFFF00",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 18, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
index 4acc4827..5dae0ea 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -8,17 +8,28 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#FF0000"
+      "backgroundColor": "#FF0000",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
     }
   ]
 }
@@ -32,17 +43,28 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#FF0000"
+      "backgroundColor": "#FF0000",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/canvas-text-alignment-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/canvas-text-alignment-expected.png
index 1a7d12d..837d1a904 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/canvas-text-alignment-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/canvas-text-alignment-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/fillrect_gradient-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/fillrect_gradient-expected.png
index ea900fd..df0a3d6 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/fillrect_gradient-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu/fast/canvas/fillrect_gradient-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/threaded/compositing/visibility/visibility-image-layers-dynamic-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/threaded/compositing/visibility/visibility-image-layers-dynamic-expected.txt
index 6b0e3ea3..b436fe6 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/threaded/compositing/visibility/visibility-image-layers-dynamic-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/threaded/compositing/visibility/visibility-image-layers-dynamic-expected.txt
@@ -13,22 +13,52 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='composited container'",
-      "position": [18, 10],
       "bounds": [749, 144],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV class='composited hidden container'",
-      "position": [14, 314],
       "bounds": [757, 152],
       "drawsContent": false,
-      "contentsVisible": false
+      "contentsVisible": false,
+      "transform": 2
     },
     {
       "name": "LayoutImage IMG class='visible composited box'",
-      "position": [38, 338],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 314, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [24, 24, 0, 1]
+      ]
     }
   ]
 }
@@ -38,34 +68,74 @@
   "layers": [
     {
       "name": "LayoutView #document",
-      "bounds": [785, 1081],
+      "bounds": [785, 1531],
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF"
     },
     {
       "name": "LayoutBlockFlow DIV class='composited container'",
-      "position": [18, 10],
       "bounds": [749, 144],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutImage IMG class='hidden composited box'",
-      "position": [38, 30],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow DIV class='composited hidden container'",
-      "position": [14, 314],
       "bounds": [757, 152],
       "drawsContent": false,
-      "contentsVisible": false
+      "contentsVisible": false,
+      "transform": 3
     },
     {
       "name": "LayoutImage IMG class='visible composited box'",
-      "position": [38, 338],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 4
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 20, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 314, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [24, 24, 0, 1]
+      ]
     }
   ]
 }
@@ -75,45 +145,104 @@
   "layers": [
     {
       "name": "LayoutView #document",
-      "bounds": [785, 1625],
+      "bounds": [785, 2675],
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF"
     },
     {
       "name": "LayoutBlockFlow DIV class='composited container'",
-      "position": [18, 10],
       "bounds": [749, 144],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutImage IMG class='hidden composited box'",
-      "position": [38, 30],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow DIV class='composited hidden container'",
-      "position": [14, 160],
-      "bounds": [757, 152]
+      "bounds": [757, 152],
+      "transform": 3
     },
     {
       "name": "LayoutImage IMG class='composited box'",
-      "position": [38, 184],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 4
     },
     {
       "name": "LayoutBlockFlow DIV class='composited hidden container'",
-      "position": [14, 314],
       "bounds": [757, 152],
       "drawsContent": false,
-      "contentsVisible": false
+      "contentsVisible": false,
+      "transform": 5
     },
     {
       "name": "LayoutImage IMG class='visible composited box'",
-      "position": [38, 338],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 6
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 20, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 160, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [24, 24, 0, 1]
+      ]
+    },
+    {
+      "id": 5,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 314, 0, 1]
+      ]
+    },
+    {
+      "id": 6,
+      "parent": 5,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [24, 24, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/bounds-ignores-hidden-composited-descendant-expected.txt b/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/bounds-ignores-hidden-composited-descendant-expected.txt
index cde43368..1a7deb31 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/bounds-ignores-hidden-composited-descendant-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/bounds-ignores-hidden-composited-descendant-expected.txt
@@ -10,20 +10,50 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [10, 10],
-      "bounds": [590, 210]
+      "bounds": [590, 210],
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='composited'",
-      "position": [10, 260],
-      "bounds": [590, 210]
+      "bounds": [590, 210],
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV class='composited inner box'",
-      "position": [500, 370],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 3
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 260, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [490, 110, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/composited-in-columns-expected.txt b/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/composited-in-columns-expected.txt
index 622e9fe..62560f4 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/composited-in-columns-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/composited-in-columns-expected.txt
@@ -8,29 +8,69 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='composited block'",
-      "position": [14, 164],
       "bounds": [210, 60],
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV class='composited box'",
-      "position": [19, 169],
       "bounds": [50, 50],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow DIV class='composited block'",
-      "position": [272, 89],
       "bounds": [210, 60],
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 3
     },
     {
       "name": "LayoutBlockFlow DIV class='composited box'",
-      "position": [277, 94],
       "bounds": [50, 50],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 4
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 164, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [5, 5, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [272, 89, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [5, 5, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/limit-layer-bounds-overflow-root-expected.txt b/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/limit-layer-bounds-overflow-root-expected.txt
index 563a9d7..80e1755 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/limit-layer-bounds-overflow-root-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/limit-layer-bounds-overflow-root-expected.txt
@@ -12,14 +12,26 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='compositing'",
-      "position": [21, 21],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='indicator')",
       "bounds": [216, 142]
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [21, 21, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/iframes/invisible-nested-iframe-show-expected.txt b/third_party/WebKit/LayoutTests/platform/win/compositing/iframes/invisible-nested-iframe-show-expected.txt
index e29a9465..5a13a85ad 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/iframes/invisible-nested-iframe-show-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/iframes/invisible-nested-iframe-show-expected.txt
@@ -76,10 +76,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='iframe-content' class='box'",
-      "position": [50, 42],
       "bounds": [210, 210],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
     },
     {
       "name": "Frame Vertical Scrollbar Layer",
@@ -92,7 +92,7 @@
       "bounds": [210, 210],
       "contentsOpaque": true,
       "backgroundColor": "#0000FF",
-      "transform": 2
+      "transform": 3
     }
   ],
   "transforms": [
@@ -102,12 +102,21 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
-        [18, 203, 0, 1]
+        [50, 42, 0, 1]
       ]
     },
     {
       "id": 2,
-      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 203, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
       "transform": [
         [1, 0, 0, 0],
         [0, 1, 0, 0],
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/rtl/rtl-absolute-overflow-scrolled-expected.png b/third_party/WebKit/LayoutTests/platform/win/compositing/rtl/rtl-absolute-overflow-scrolled-expected.png
deleted file mode 100644
index cf23535..0000000
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/rtl/rtl-absolute-overflow-scrolled-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/squashing/no-squashing-into-another-clip-layer-expected.txt b/third_party/WebKit/LayoutTests/platform/win/compositing/squashing/no-squashing-into-another-clip-layer-expected.txt
index f97b2fc5..90edfaf1 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/squashing/no-squashing-into-another-clip-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/squashing/no-squashing-into-another-clip-layer-expected.txt
@@ -8,23 +8,23 @@
     },
     {
       "name": "LayoutBlockFlow DIV",
-      "position": [8, 8],
       "bounds": [784, 10],
       "contentsOpaque": true,
-      "backgroundColor": "#ADD8E6"
+      "backgroundColor": "#ADD8E6",
+      "transform": 1
     },
     {
       "name": "Child Containment Layer",
-      "position": [8, 8],
       "bounds": [784, 10],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV id='inner'",
-      "position": [8, 8],
       "bounds": [784, 10],
       "contentsOpaque": true,
-      "backgroundColor": "#F5F5F5"
+      "backgroundColor": "#F5F5F5",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='hoverable'",
@@ -32,6 +32,17 @@
       "bounds": [216, 100],
       "backgroundColor": "#90EE90"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/squashing/selection-repaint-with-gaps-expected.txt b/third_party/WebKit/LayoutTests/platform/win/compositing/squashing/selection-repaint-with-gaps-expected.txt
index c8fdd2c..77391f23 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/squashing/selection-repaint-with-gaps-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/squashing/selection-repaint-with-gaps-expected.txt
@@ -13,9 +13,9 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='overlap'",
-      "position": [8, 8],
       "bounds": [300, 500],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='item')",
@@ -30,6 +30,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='item1' class='item'",
@@ -67,9 +79,9 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='overlap'",
-      "position": [8, 8],
       "bounds": [300, 500],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV class='item')",
@@ -89,6 +101,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (positioned) DIV id='item1' class='item'",
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/tiled-layers-hidpi-expected.txt b/third_party/WebKit/LayoutTests/platform/win/compositing/tiled-layers-hidpi-expected.txt
index 965baf2..5d09eb2 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/tiled-layers-hidpi-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/tiled-layers-hidpi-expected.txt
@@ -8,10 +8,21 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='composited'",
-      "position": [16, 16],
       "bounds": [3600, 20],
       "contentsOpaque": true,
-      "backgroundColor": "#0000FF"
+      "backgroundColor": "#0000FF",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [16, 16, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/visibility/visibility-image-layers-dynamic-expected.txt b/third_party/WebKit/LayoutTests/platform/win/compositing/visibility/visibility-image-layers-dynamic-expected.txt
index b982f95e..b69e862f 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/visibility/visibility-image-layers-dynamic-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/visibility/visibility-image-layers-dynamic-expected.txt
@@ -13,22 +13,52 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='composited container'",
-      "position": [18, 10],
       "bounds": [749, 145],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV class='composited hidden container'",
-      "position": [14, 316],
       "bounds": [757, 153],
       "drawsContent": false,
-      "contentsVisible": false
+      "contentsVisible": false,
+      "transform": 2
     },
     {
       "name": "LayoutImage IMG class='visible composited box'",
-      "position": [38, 340],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 316, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [24, 24, 0, 1]
+      ]
     }
   ]
 }
@@ -38,34 +68,74 @@
   "layers": [
     {
       "name": "LayoutView #document",
-      "bounds": [785, 1110],
+      "bounds": [785, 1590],
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF"
     },
     {
       "name": "LayoutBlockFlow DIV class='composited container'",
-      "position": [18, 10],
       "bounds": [749, 145],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutImage IMG class='hidden composited box'",
-      "position": [38, 30],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow DIV class='composited hidden container'",
-      "position": [14, 316],
       "bounds": [757, 153],
       "drawsContent": false,
-      "contentsVisible": false
+      "contentsVisible": false,
+      "transform": 3
     },
     {
       "name": "LayoutImage IMG class='visible composited box'",
-      "position": [38, 340],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 4
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 20, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 316, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [24, 24, 0, 1]
+      ]
     }
   ]
 }
@@ -75,45 +145,104 @@
   "layers": [
     {
       "name": "LayoutView #document",
-      "bounds": [785, 1689],
+      "bounds": [785, 2809],
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF"
     },
     {
       "name": "LayoutBlockFlow DIV class='composited container'",
-      "position": [18, 10],
       "bounds": [749, 145],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutImage IMG class='hidden composited box'",
-      "position": [38, 30],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow DIV class='composited hidden container'",
-      "position": [14, 161],
-      "bounds": [757, 153]
+      "bounds": [757, 153],
+      "transform": 3
     },
     {
       "name": "LayoutImage IMG class='composited box'",
-      "position": [38, 185],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 4
     },
     {
       "name": "LayoutBlockFlow DIV class='composited hidden container'",
-      "position": [14, 316],
       "bounds": [757, 153],
       "drawsContent": false,
-      "contentsVisible": false
+      "contentsVisible": false,
+      "transform": 5
     },
     {
       "name": "LayoutImage IMG class='visible composited box'",
-      "position": [38, 340],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 6
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 20, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 161, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [24, 24, 0, 1]
+      ]
+    },
+    {
+      "id": 5,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 316, 0, 1]
+      ]
+    },
+    {
+      "id": 6,
+      "parent": 5,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [24, 24, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/blending/mix-blend-mode-composited-layers-expected.txt b/third_party/WebKit/LayoutTests/platform/win/css3/blending/mix-blend-mode-composited-layers-expected.txt
index 27fc730..58ee9b76 100644
--- a/third_party/WebKit/LayoutTests/platform/win/css3/blending/mix-blend-mode-composited-layers-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/css3/blending/mix-blend-mode-composited-layers-expected.txt
@@ -21,10 +21,21 @@
     },
     {
       "name": "LayoutBlockFlow DIV",
-      "position": [8, 18],
       "bounds": [10, 10],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFF00"
+      "backgroundColor": "#FFFF00",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 18, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/WebKit/LayoutTests/platform/win/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
index 1cafcecd..0c38bd6b 100644
--- a/third_party/WebKit/LayoutTests/platform/win/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -8,17 +8,28 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#FF0000"
+      "backgroundColor": "#FF0000",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
     }
   ]
 }
@@ -32,17 +43,28 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#FF0000"
+      "backgroundColor": "#FF0000",
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'",
-      "position": [100, 100],
       "bounds": [100, 100],
       "contentsOpaque": true,
-      "backgroundColor": "#008000"
+      "backgroundColor": "#008000",
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [100, 100, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
index 9ec34e7..26ed3442 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
@@ -12,10 +12,10 @@
     },
     {
       "name": "LayoutBlockFlow DIV id='foo'",
-      "position": [8, 8],
       "bounds": [200, 1000],
       "contentsOpaque": true,
-      "backgroundColor": "#D3D3D3"
+      "backgroundColor": "#D3D3D3",
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (positioned) DIV)",
@@ -50,6 +50,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutView #document",
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/compositing/remove-squashed-layer-plus-move-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/compositing/remove-squashed-layer-plus-move-expected.txt
index 15acdb4..32391bf3 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/compositing/remove-squashed-layer-plus-move-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/compositing/remove-squashed-layer-plus-move-expected.txt
@@ -29,9 +29,9 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV",
-      "position": [8, 8],
       "bounds": [1000, 1000],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "Squashing Layer (first squashed layer: LayoutBlockFlow (relative positioned) DIV class='mv-tile')",
@@ -66,6 +66,18 @@
       ]
     }
   ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ],
+      "flattenInheritedTransform": false
+    }
+  ],
   "objectPaintInvalidations": [
     {
       "object": "LayoutBlockFlow (relative positioned) DIV id='foo' class='mv-tile'",
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/fixed-element-repaint-after-compositing-update-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/fixed-element-repaint-after-compositing-update-expected.txt
index b758cd7..e94a6ef 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/fixed-element-repaint-after-compositing-update-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/fixed-element-repaint-after-compositing-update-expected.txt
@@ -8,9 +8,9 @@
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='fixedTransformed'",
-      "position": [8, 88],
       "contentsOpaque": true,
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV id='fixedOverlapping'",
@@ -25,6 +25,17 @@
       "contentsOpaque": true,
       "backgroundColor": "#008000"
     }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 88, 0, 1]
+      ]
+    }
   ]
 }
 
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/pagination/pagination-change-clip-crash-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/pagination/pagination-change-clip-crash-expected.txt
similarity index 67%
rename from third_party/WebKit/LayoutTests/platform/mac/paint/pagination/pagination-change-clip-crash-expected.txt
rename to third_party/WebKit/LayoutTests/platform/win/paint/pagination/pagination-change-clip-crash-expected.txt
index c6aaae6..43a161f 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/pagination/pagination-change-clip-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/pagination/pagination-change-clip-crash-expected.txt
@@ -2,22 +2,33 @@
   "layers": [
     {
       "name": "LayoutView #document",
-      "bounds": [785, 734],
+      "bounds": [785, 736],
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
           "object": "LayoutView #document",
-          "rect": [0, 634, 785, 100],
+          "rect": [0, 636, 785, 100],
           "reason": "incremental"
         }
       ]
     },
     {
       "name": "LayoutBlockFlow DIV id='background'",
-      "position": [8, 426],
       "bounds": [769, 300],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 428, 0, 1]
+      ]
     }
   ],
   "objectPaintInvalidations": [
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/canvas-text-alignment-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/canvas-text-alignment-expected.png
index 47930d3..6d383b83 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/canvas-text-alignment-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/canvas-text-alignment-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/fillrect_gradient-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/fillrect_gradient-expected.png
index 3d549f6..da911f8 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/fillrect_gradient-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu/fast/canvas/fillrect_gradient-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/threaded/compositing/visibility/visibility-image-layers-dynamic-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/threaded/compositing/visibility/visibility-image-layers-dynamic-expected.txt
index b982f95e..b69e862f 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/threaded/compositing/visibility/visibility-image-layers-dynamic-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/threaded/compositing/visibility/visibility-image-layers-dynamic-expected.txt
@@ -13,22 +13,52 @@
     },
     {
       "name": "LayoutBlockFlow DIV class='composited container'",
-      "position": [18, 10],
       "bounds": [749, 145],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutBlockFlow DIV class='composited hidden container'",
-      "position": [14, 316],
       "bounds": [757, 153],
       "drawsContent": false,
-      "contentsVisible": false
+      "contentsVisible": false,
+      "transform": 2
     },
     {
       "name": "LayoutImage IMG class='visible composited box'",
-      "position": [38, 340],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 3
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 316, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [24, 24, 0, 1]
+      ]
     }
   ]
 }
@@ -38,34 +68,74 @@
   "layers": [
     {
       "name": "LayoutView #document",
-      "bounds": [785, 1110],
+      "bounds": [785, 1590],
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF"
     },
     {
       "name": "LayoutBlockFlow DIV class='composited container'",
-      "position": [18, 10],
       "bounds": [749, 145],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutImage IMG class='hidden composited box'",
-      "position": [38, 30],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow DIV class='composited hidden container'",
-      "position": [14, 316],
       "bounds": [757, 153],
       "drawsContent": false,
-      "contentsVisible": false
+      "contentsVisible": false,
+      "transform": 3
     },
     {
       "name": "LayoutImage IMG class='visible composited box'",
-      "position": [38, 340],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 4
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 20, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 316, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [24, 24, 0, 1]
+      ]
     }
   ]
 }
@@ -75,45 +145,104 @@
   "layers": [
     {
       "name": "LayoutView #document",
-      "bounds": [785, 1689],
+      "bounds": [785, 2809],
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF"
     },
     {
       "name": "LayoutBlockFlow DIV class='composited container'",
-      "position": [18, 10],
       "bounds": [749, 145],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 1
     },
     {
       "name": "LayoutImage IMG class='hidden composited box'",
-      "position": [38, 30],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow DIV class='composited hidden container'",
-      "position": [14, 161],
-      "bounds": [757, 153]
+      "bounds": [757, 153],
+      "transform": 3
     },
     {
       "name": "LayoutImage IMG class='composited box'",
-      "position": [38, 185],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 4
     },
     {
       "name": "LayoutBlockFlow DIV class='composited hidden container'",
-      "position": [14, 316],
       "bounds": [757, 153],
       "drawsContent": false,
-      "contentsVisible": false
+      "contentsVisible": false,
+      "transform": 5
     },
     {
       "name": "LayoutImage IMG class='visible composited box'",
-      "position": [38, 340],
       "bounds": [100, 100],
-      "drawsContent": false
+      "drawsContent": false,
+      "transform": 6
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [18, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [20, 20, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 161, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [24, 24, 0, 1]
+      ]
+    },
+    {
+      "id": 5,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [14, 316, 0, 1]
+      ]
+    },
+    {
+      "id": 6,
+      "parent": 5,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [24, 24, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/virtual/gpu/fast/canvas/gradient-add-second-start-end-stop-expected.png b/third_party/WebKit/LayoutTests/virtual/gpu/fast/canvas/gradient-add-second-start-end-stop-expected.png
index 11d81d4..019a9aaa 100644
--- a/third_party/WebKit/LayoutTests/virtual/gpu/fast/canvas/gradient-add-second-start-end-stop-expected.png
+++ b/third_party/WebKit/LayoutTests/virtual/gpu/fast/canvas/gradient-add-second-start-end-stop-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index ae4658c..7dccbc2 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -8572,6 +8572,7 @@
     method constructor
 interface WebGLTexture
     attribute @@toStringTag
+    getter lastUploadedVideoFrameWasSkipped
     getter lastUploadedVideoHeight
     getter lastUploadedVideoTimestamp
     getter lastUploadedVideoWidth
diff --git a/third_party/WebKit/Source/core/dom/events/EventPath.cpp b/third_party/WebKit/Source/core/dom/events/EventPath.cpp
index 546f589..0c20f55 100644
--- a/third_party/WebKit/Source/core/dom/events/EventPath.cpp
+++ b/third_party/WebKit/Source/core/dom/events/EventPath.cpp
@@ -151,12 +151,6 @@
   //   - TreeScopes in m_treeScopeEventContexts must be *connected* in the same
   //     composed tree.
   //   - The root tree must be included.
-  HeapHashMap<Member<const TreeScope>, Member<TreeScopeEventContext>>
-      tree_scope_event_context_map;
-  for (const auto& tree_scope_event_context : tree_scope_event_contexts_)
-    tree_scope_event_context_map.insert(
-        &tree_scope_event_context->GetTreeScope(),
-        tree_scope_event_context.Get());
   TreeScopeEventContext* root_tree = nullptr;
   for (const auto& tree_scope_event_context : tree_scope_event_contexts_) {
     // Use olderShadowRootOrParentTreeScope here for parent-child relationships.
@@ -170,37 +164,41 @@
       root_tree = tree_scope_event_context.Get();
       continue;
     }
-    DCHECK_NE(tree_scope_event_context_map.find(parent),
-              tree_scope_event_context_map.end());
-    tree_scope_event_context_map.find(parent)->value->AddChild(
-        *tree_scope_event_context.Get());
+    TreeScopeEventContext* parent_tree_scope_event_context =
+        GetTreeScopeEventContext(parent);
+    DCHECK(parent_tree_scope_event_context);
+    parent_tree_scope_event_context->AddChild(*tree_scope_event_context.Get());
   }
   DCHECK(root_tree);
   root_tree->CalculateTreeOrderAndSetNearestAncestorClosedTree(0, nullptr);
 }
 
+TreeScopeEventContext* EventPath::GetTreeScopeEventContext(
+    TreeScope* tree_scope) {
+  DCHECK(tree_scope);
+  for (TreeScopeEventContext* tree_scope_event_context :
+       tree_scope_event_contexts_) {
+    if (tree_scope_event_context->GetTreeScope() == tree_scope) {
+      return tree_scope_event_context;
+    }
+  }
+  return nullptr;
+}
+
 TreeScopeEventContext* EventPath::EnsureTreeScopeEventContext(
     Node* current_target,
-    TreeScope* tree_scope,
-    TreeScopeEventContextMap& tree_scope_event_context_map) {
+    TreeScope* tree_scope) {
   if (!tree_scope)
     return nullptr;
-  TreeScopeEventContext* tree_scope_event_context;
-  bool is_new_entry;
-  {
-    TreeScopeEventContextMap::AddResult add_result =
-        tree_scope_event_context_map.insert(tree_scope, nullptr);
-    is_new_entry = add_result.is_new_entry;
-    if (is_new_entry)
-      add_result.stored_value->value =
-          TreeScopeEventContext::Create(*tree_scope);
-    tree_scope_event_context = add_result.stored_value->value.Get();
-  }
-  if (is_new_entry) {
+  TreeScopeEventContext* tree_scope_event_context =
+      GetTreeScopeEventContext(tree_scope);
+  if (!tree_scope_event_context) {
+    tree_scope_event_context = TreeScopeEventContext::Create(*tree_scope);
+    tree_scope_event_contexts_.push_back(tree_scope_event_context);
+
     TreeScopeEventContext* parent_tree_scope_event_context =
         EnsureTreeScopeEventContext(
-            0, tree_scope->OlderShadowRootOrParentTreeScope(),
-            tree_scope_event_context_map);
+            0, tree_scope->OlderShadowRootOrParentTreeScope());
     if (parent_tree_scope_event_context &&
         parent_tree_scope_event_context->Target()) {
       tree_scope_event_context->SetTarget(
@@ -218,24 +216,19 @@
 
 void EventPath::CalculateAdjustedTargets() {
   const TreeScope* last_tree_scope = nullptr;
-
-  TreeScopeEventContextMap tree_scope_event_context_map;
   TreeScopeEventContext* last_tree_scope_event_context = nullptr;
 
   for (auto& context : node_event_contexts_) {
     Node* current_node = context.GetNode();
     TreeScope& current_tree_scope = current_node->GetTreeScope();
     if (last_tree_scope != &current_tree_scope) {
-      last_tree_scope_event_context = EnsureTreeScopeEventContext(
-          current_node, &current_tree_scope, tree_scope_event_context_map);
+      last_tree_scope_event_context =
+          EnsureTreeScopeEventContext(current_node, &current_tree_scope);
     }
     DCHECK(last_tree_scope_event_context);
     context.SetTreeScopeEventContext(last_tree_scope_event_context);
     last_tree_scope = &current_tree_scope;
   }
-  tree_scope_event_contexts_.AppendRange(
-      tree_scope_event_context_map.Values().begin(),
-      tree_scope_event_context_map.Values().end());
 }
 
 void EventPath::BuildRelatedNodeMap(const Node& related_node,
diff --git a/third_party/WebKit/Source/core/dom/events/EventPath.h b/third_party/WebKit/Source/core/dom/events/EventPath.h
index d277c84..16af362 100644
--- a/third_party/WebKit/Source/core/dom/events/EventPath.h
+++ b/third_party/WebKit/Source/core/dom/events/EventPath.h
@@ -44,7 +44,8 @@
 class TouchList;
 class TreeScope;
 
-class CORE_EXPORT EventPath final : public GarbageCollected<EventPath> {
+class CORE_EXPORT EventPath final
+    : public GarbageCollectedFinalized<EventPath> {
   WTF_MAKE_NONCOPYABLE(EventPath);
 
  public:
@@ -116,11 +117,9 @@
                        HeapVector<Member<TouchList>> adjusted_touch_list,
                        const HeapVector<Member<TreeScope>>& tree_scopes);
 
-  using TreeScopeEventContextMap =
-      HeapHashMap<Member<TreeScope>, Member<TreeScopeEventContext>>;
+  TreeScopeEventContext* GetTreeScopeEventContext(TreeScope*);
   TreeScopeEventContext* EnsureTreeScopeEventContext(Node* current_target,
-                                                     TreeScope*,
-                                                     TreeScopeEventContextMap&);
+                                                     TreeScope*);
 
   using RelatedTargetMap = HeapHashMap<Member<TreeScope>, Member<EventTarget>>;
 
@@ -134,7 +133,7 @@
   HeapVector<NodeEventContext> node_event_contexts_;
   Member<Node> node_;
   Member<Event> event_;
-  HeapVector<Member<TreeScopeEventContext>> tree_scope_event_contexts_;
+  HeapVector<Member<TreeScopeEventContext>, 8> tree_scope_event_contexts_;
   Member<WindowEventContext> window_event_context_;
 };
 
diff --git a/third_party/WebKit/Source/core/editing/spellcheck/SpellCheckerTest.cpp b/third_party/WebKit/Source/core/editing/spellcheck/SpellCheckerTest.cpp
index 5551bc4..1d475fe 100644
--- a/third_party/WebKit/Source/core/editing/spellcheck/SpellCheckerTest.cpp
+++ b/third_party/WebKit/Source/core/editing/spellcheck/SpellCheckerTest.cpp
@@ -79,8 +79,6 @@
   input->focus();
   input->setValue("Hello, input field");
   GetDocument().UpdateStyleAndLayout();
-  VisibleSelection old_selection =
-      GetDocument().GetFrame()->Selection().ComputeVisibleSelectionInDOMTree();
 
   Position new_position(input->InnerEditorElement()->firstChild(), 3);
   GetDocument().GetFrame()->Selection().SetSelection(
diff --git a/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp b/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp
index 0ea16cb..023ae5f 100644
--- a/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp
@@ -312,9 +312,12 @@
     SetDisplayMode(kPoster);
 }
 
-void HTMLVideoElement::PaintCurrentFrame(PaintCanvas* canvas,
-                                         const IntRect& dest_rect,
-                                         const PaintFlags* flags) const {
+void HTMLVideoElement::PaintCurrentFrame(
+    PaintCanvas* canvas,
+    const IntRect& dest_rect,
+    const PaintFlags* flags,
+    int already_uploaded_id,
+    WebMediaPlayer::VideoFrameUploadMetadata* out_metadata) const {
   if (!GetWebMediaPlayer())
     return;
 
@@ -326,7 +329,8 @@
     media_flags.setFilterQuality(kLow_SkFilterQuality);
   }
 
-  GetWebMediaPlayer()->Paint(canvas, dest_rect, media_flags);
+  GetWebMediaPlayer()->Paint(canvas, dest_rect, media_flags,
+                             already_uploaded_id, out_metadata);
 }
 
 bool HTMLVideoElement::CopyVideoTextureToPlatformTexture(
@@ -338,13 +342,15 @@
     GLenum type,
     GLint level,
     bool premultiply_alpha,
-    bool flip_y) {
+    bool flip_y,
+    int already_uploaded_id,
+    WebMediaPlayer::VideoFrameUploadMetadata* out_metadata) {
   if (!GetWebMediaPlayer())
     return false;
 
   return GetWebMediaPlayer()->CopyVideoTextureToPlatformTexture(
       gl, target, texture, internal_format, format, type, level,
-      premultiply_alpha, flip_y);
+      premultiply_alpha, flip_y, already_uploaded_id, out_metadata);
 }
 
 bool HTMLVideoElement::TexImageImpl(
diff --git a/third_party/WebKit/Source/core/html/HTMLVideoElement.h b/third_party/WebKit/Source/core/html/HTMLVideoElement.h
index 40f90b7..68a1b7a 100644
--- a/third_party/WebKit/Source/core/html/HTMLVideoElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLVideoElement.h
@@ -78,18 +78,26 @@
   unsigned webkitDroppedFrameCount() const;
 
   // Used by canvas to gain raw pixel access
-  void PaintCurrentFrame(PaintCanvas*, const IntRect&, const PaintFlags*) const;
+  void PaintCurrentFrame(
+      PaintCanvas*,
+      const IntRect&,
+      const PaintFlags*,
+      int already_uploaded_id = -1,
+      WebMediaPlayer::VideoFrameUploadMetadata* = nullptr) const;
 
   // Used by WebGL to do GPU-GPU textures copy if possible.
-  bool CopyVideoTextureToPlatformTexture(gpu::gles2::GLES2Interface*,
-                                         GLenum target,
-                                         GLuint texture,
-                                         GLenum internal_format,
-                                         GLenum format,
-                                         GLenum type,
-                                         GLint level,
-                                         bool premultiply_alpha,
-                                         bool flip_y);
+  bool CopyVideoTextureToPlatformTexture(
+      gpu::gles2::GLES2Interface*,
+      GLenum target,
+      GLuint texture,
+      GLenum internal_format,
+      GLenum format,
+      GLenum type,
+      GLint level,
+      bool premultiply_alpha,
+      bool flip_y,
+      int already_uploaded_id = -1,
+      WebMediaPlayer::VideoFrameUploadMetadata* out_metadata = nullptr);
 
   // Used by WebGL to do CPU-GPU texture upload if possible.
   bool TexImageImpl(WebMediaPlayer::TexImageFunctionID,
diff --git a/third_party/WebKit/Source/core/paint/BoxPainter.cpp b/third_party/WebKit/Source/core/paint/BoxPainter.cpp
index 421e4ba4..3b35a7e 100644
--- a/third_party/WebKit/Source/core/paint/BoxPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/BoxPainter.cpp
@@ -123,6 +123,12 @@
   BoxDecorationData box_decoration_data(layout_box_);
   GraphicsContextStateSaver state_saver(paint_info.context, false);
 
+  if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled() &&
+      LayoutRect(EnclosingIntRect(paint_rect)) == paint_rect &&
+      layout_box_.BackgroundIsKnownToBeOpaqueInRect(
+          BoundsForDrawingRecorder(paint_info, LayoutPoint())))
+    recorder.SetKnownToBeOpaque();
+
   if (!painting_overflow_contents) {
     // FIXME: Should eventually give the theme control over whether the box
     // shadow should paint, since controls could have custom shadows of their
diff --git a/third_party/WebKit/Source/modules/battery/BatteryDispatcher.cpp b/third_party/WebKit/Source/modules/battery/BatteryDispatcher.cpp
index 885c0a3a..80930a1 100644
--- a/third_party/WebKit/Source/modules/battery/BatteryDispatcher.cpp
+++ b/third_party/WebKit/Source/modules/battery/BatteryDispatcher.cpp
@@ -6,9 +6,9 @@
 
 #include "platform/mojo/MojoHelper.h"
 #include "platform/wtf/Assertions.h"
+#include "public/platform/InterfaceProvider.h"
 #include "public/platform/Platform.h"
 #include "services/device/public/interfaces/constants.mojom-blink.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 namespace blink {
 
@@ -45,8 +45,8 @@
 
 void BatteryDispatcher::StartListening() {
   DCHECK(!monitor_.is_bound());
-  Platform::Current()->GetConnector()->BindInterface(
-      device::mojom::blink::kServiceName, mojo::MakeRequest(&monitor_));
+  Platform::Current()->GetInterfaceProvider()->GetInterface(
+      mojo::MakeRequest(&monitor_));
   QueryNextStatus();
 }
 
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
index ac129037..26c91f82 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
@@ -5157,7 +5157,9 @@
 }
 
 PassRefPtr<Image> WebGLRenderingContextBase::VideoFrameToImage(
-    HTMLVideoElement* video) {
+    HTMLVideoElement* video,
+    int already_uploaded_id,
+    WebMediaPlayer::VideoFrameUploadMetadata* out_metadata) {
   const IntSize& visible_size = video->videoVisibleSize();
   if (visible_size.IsEmpty()) {
     SynthesizeGLError(GL_INVALID_VALUE, "tex(Sub)Image2D",
@@ -5170,7 +5172,8 @@
     return nullptr;
   }
   IntRect dest_rect(0, 0, visible_size.Width(), visible_size.Height());
-  video->PaintCurrentFrame(buf->Canvas(), dest_rect, nullptr);
+  video->PaintCurrentFrame(buf->Canvas(), dest_rect, nullptr,
+                           already_uploaded_id, out_metadata);
   return buf->NewImageSnapshot();
 }
 
@@ -5212,6 +5215,15 @@
                        yoffset, zoffset))
     return;
 
+  // For WebGL last-uploaded-frame-metadata API. https://crbug.com/639174
+  WebMediaPlayer::VideoFrameUploadMetadata frame_metadata = {};
+  int already_uploaded_id = -1;
+  WebMediaPlayer::VideoFrameUploadMetadata* frame_metadata_ptr = nullptr;
+  if (RuntimeEnabledFeatures::ExperimentalCanvasFeaturesEnabled()) {
+    already_uploaded_id = texture->GetLastUploadedVideoFrameId();
+    frame_metadata_ptr = &frame_metadata;
+  }
+
   bool source_image_rect_is_default =
       source_image_rect == SentinelEmptyRect() ||
       source_image_rect ==
@@ -5235,8 +5247,9 @@
 
     if (video->CopyVideoTextureToPlatformTexture(
             ContextGL(), target, texture->Object(), internalformat, format,
-            type, level, unpack_premultiply_alpha_, unpack_flip_y_)) {
-      texture->UpdateLastUploadedVideo(video->GetWebMediaPlayer());
+            type, level, unpack_premultiply_alpha_, unpack_flip_y_,
+            already_uploaded_id, frame_metadata_ptr)) {
+      texture->UpdateLastUploadedFrame(frame_metadata);
       return;
     }
   }
@@ -5253,7 +5266,7 @@
             xoffset, yoffset, zoffset, unpack_flip_y_,
             unpack_premultiply_alpha_ &&
                 unpack_colorspace_conversion_ == GL_NONE)) {
-      texture->UpdateLastUploadedVideo(video->GetWebMediaPlayer());
+      texture->ClearLastUploadedFrame();
       return;
     }
   }
@@ -5274,7 +5287,8 @@
         // (though it may still do a CPU conversion and upload the results).
         video->PaintCurrentFrame(
             image_buffer->Canvas(),
-            IntRect(0, 0, video->videoWidth(), video->videoHeight()), nullptr);
+            IntRect(0, 0, video->videoWidth(), video->videoHeight()), nullptr,
+            already_uploaded_id, frame_metadata_ptr);
 
         // This is a straight GPU-GPU copy, any necessary color space conversion
         // was handled in the paintCurrentFrameInContext() call.
@@ -5289,14 +5303,15 @@
                 texture->Object(), unpack_premultiply_alpha_, unpack_flip_y_,
                 IntPoint(0, 0),
                 IntRect(0, 0, video->videoWidth(), video->videoHeight()))) {
-          texture->UpdateLastUploadedVideo(video->GetWebMediaPlayer());
+          texture->UpdateLastUploadedFrame(frame_metadata);
           return;
         }
       }
     }
   }
 
-  RefPtr<Image> image = VideoFrameToImage(video);
+  RefPtr<Image> image =
+      VideoFrameToImage(video, already_uploaded_id, frame_metadata_ptr);
   if (!image)
     return;
   TexImageImpl(function_id, target, level, internalformat, xoffset, yoffset,
@@ -5304,7 +5319,7 @@
                WebGLImageConversion::kHtmlDomVideo, unpack_flip_y_,
                unpack_premultiply_alpha_, source_image_rect, depth,
                unpack_image_height);
-  texture->UpdateLastUploadedVideo(video->GetWebMediaPlayer());
+  texture->UpdateLastUploadedFrame(frame_metadata);
 }
 
 void WebGLRenderingContextBase::TexImageBitmapByGPU(
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
index 5076744..2968661 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
@@ -681,7 +681,10 @@
                                         int height,
                                         const char* function_name);
 
-  PassRefPtr<Image> VideoFrameToImage(HTMLVideoElement*);
+  PassRefPtr<Image> VideoFrameToImage(
+      HTMLVideoElement*,
+      int already_uploaded_id,
+      WebMediaPlayer::VideoFrameUploadMetadata* out_metadata);
 
   // Structure for rendering to a DrawingBuffer, instead of directly
   // to the back-buffer of m_context.
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLTexture.cpp b/third_party/WebKit/Source/modules/webgl/WebGLTexture.cpp
index f61d7fc..7b188f92 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLTexture.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLTexture.cpp
@@ -109,17 +109,4 @@
   return log + 1;
 }
 
-void WebGLTexture::UpdateLastUploadedVideo(WebMediaPlayer* player) {
-  if (player && player->GetLastUploadedFrameInfo(
-                    &last_uploaded_video_width_, &last_uploaded_video_height_,
-                    &last_uploaded_video_timestamp_)) {
-    return;
-  }
-
-  // getCurrentFrameInfo was unavailable or failed
-  last_uploaded_video_width_ = 0;
-  last_uploaded_video_height_ = 0;
-  last_uploaded_video_timestamp_ = 0.0;
-}
-
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLTexture.h b/third_party/WebKit/Source/modules/webgl/WebGLTexture.h
index 3d5d4b1..06217e7 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLTexture.h
+++ b/third_party/WebKit/Source/modules/webgl/WebGLTexture.h
@@ -47,13 +47,28 @@
 
   static GLint ComputeLevelCount(GLsizei width, GLsizei height, GLsizei depth);
 
-  void UpdateLastUploadedVideo(WebMediaPlayer*);
-  unsigned lastUploadedVideoWidth() const { return last_uploaded_video_width_; }
+  int GetLastUploadedVideoFrameId() const {
+    return last_uploaded_video_frame_metadata_.frame_id;
+  }
+
+  void UpdateLastUploadedFrame(
+      blink::WebMediaPlayer::VideoFrameUploadMetadata frame_metadata) {
+    last_uploaded_video_frame_metadata_ = frame_metadata;
+  }
+
+  void ClearLastUploadedFrame() { last_uploaded_video_frame_metadata_ = {}; }
+
+  unsigned lastUploadedVideoWidth() const {
+    return last_uploaded_video_frame_metadata_.visible_rect.width();
+  }
   unsigned lastUploadedVideoHeight() const {
-    return last_uploaded_video_height_;
+    return last_uploaded_video_frame_metadata_.visible_rect.height();
   }
   double lastUploadedVideoTimestamp() const {
-    return last_uploaded_video_timestamp_;
+    return last_uploaded_video_frame_metadata_.timestamp.InSecondsF();
+  }
+  bool lastUploadedVideoFrameWasSkipped() const {
+    return last_uploaded_video_frame_metadata_.skipped;
   }
 
  private:
@@ -67,9 +82,8 @@
 
   GLenum target_;
 
-  unsigned last_uploaded_video_width_ = 0;
-  unsigned last_uploaded_video_height_ = 0;
-  double last_uploaded_video_timestamp_ = 0.0;
+  blink::WebMediaPlayer::VideoFrameUploadMetadata
+      last_uploaded_video_frame_metadata_ = {};
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLTexture.idl b/third_party/WebKit/Source/modules/webgl/WebGLTexture.idl
index 6e81df8..f08f1c4 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLTexture.idl
+++ b/third_party/WebKit/Source/modules/webgl/WebGLTexture.idl
@@ -31,4 +31,5 @@
     [RuntimeEnabled=ExperimentalCanvasFeatures] readonly attribute unsigned long lastUploadedVideoWidth;
     [RuntimeEnabled=ExperimentalCanvasFeatures] readonly attribute unsigned long lastUploadedVideoHeight;
     [RuntimeEnabled=ExperimentalCanvasFeatures] readonly attribute double lastUploadedVideoTimestamp;
+    [RuntimeEnabled=ExperimentalCanvasFeatures] readonly attribute boolean lastUploadedVideoFrameWasSkipped;
 };
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.cpp b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.cpp
index 3994c1b4..7a9ae407 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.cpp
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResult.cpp
@@ -317,8 +317,7 @@
 void ShapeResult::ApplySpacingImpl(
     ShapeResultSpacing<TextContainerType>& spacing,
     int text_start_offset) {
-  float offset_x, offset_y;
-  float& offset = spacing.IsVerticalOffset() ? offset_y : offset_x;
+  float offset = 0;
   float total_space = 0;
   for (auto& run : runs_) {
     if (!run)
@@ -332,15 +331,25 @@
       if (i + 1 < run->glyph_data_.size() &&
           glyph_data.character_index ==
               run->glyph_data_[i + 1].character_index) {
-      } else {
-        offset_x = offset_y = 0;
-        float space = spacing.ComputeSpacing(
-            run_start_index + glyph_data.character_index, offset);
-        glyph_data.advance += space;
-        total_space_for_run += space;
-        glyph_data.offset.Expand(offset_x, offset_y);
+        continue;
       }
-      has_vertical_offsets_ |= (glyph_data.offset.Height() != 0);
+
+      float space = spacing.ComputeSpacing(
+          run_start_index + glyph_data.character_index, offset);
+      glyph_data.advance += space;
+      total_space_for_run += space;
+
+      // |offset| is non-zero only when justifying CJK characters that follow
+      // non-CJK characters.
+      if (UNLIKELY(offset)) {
+        if (run->IsHorizontal()) {
+          glyph_data.offset.SetWidth(glyph_data.offset.Width() + offset);
+        } else {
+          glyph_data.offset.SetHeight(glyph_data.offset.Height() + offset);
+          has_vertical_offsets_ = true;
+        }
+        offset = 0;
+      }
     }
     run->width_ += total_space_for_run;
     total_space += total_space_for_run;
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.cpp b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.cpp
index b9cca07..6ea81fb 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.cpp
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.cpp
@@ -22,8 +22,7 @@
       has_spacing_(false),
       normalize_space_(false),
       allow_tabs_(false),
-      is_after_expansion_(false),
-      is_vertical_offset_(false) {}
+      is_after_expansion_(false) {}
 
 template <typename TextContainerType>
 bool ShapeResultSpacing<TextContainerType>::SetSpacing(
@@ -35,7 +34,6 @@
 
   letter_spacing_ = font_description.LetterSpacing();
   word_spacing_ = font_description.WordSpacing();
-  is_vertical_offset_ = font_description.IsVerticalAnyUpright();
   DCHECK(!normalize_space_);
   allow_tabs_ = true;
   has_spacing_ = true;
@@ -73,7 +71,6 @@
   if (!has_spacing_)
     return;
 
-  is_vertical_offset_ = font_description.IsVerticalAnyUpright();
   normalize_space_ = text_.NormalizeSpace();
   allow_tabs_ = text_.AllowTabs();
 
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.h b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.h
index bd0cf74..1518a977 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.h
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultSpacing.h
@@ -26,7 +26,6 @@
   float LetterSpacing() const { return letter_spacing_; }
   bool HasSpacing() const { return has_spacing_; }
   bool HasExpansion() const { return expansion_opportunity_count_; }
-  bool IsVerticalOffset() const { return is_vertical_offset_; }
 
   // Set letter-spacing and word-spacing.
   bool SetSpacing(const FontDescription&);
@@ -69,7 +68,6 @@
   bool normalize_space_;
   bool allow_tabs_;
   bool is_after_expansion_;
-  bool is_vertical_offset_;
 };
 
 // Forward declare so no implicit instantiations happen before the
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
index e21546f..f2ab221 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
@@ -597,7 +597,8 @@
       layer.AddFlattenInheritedTransformJSON(*scroll_translate_json);
     }
 
-    if (!layer.transform_.IsIdentity() || layer.rendering_context3d_) {
+    if (!layer.transform_.IsIdentity() || layer.rendering_context3d_ ||
+        layer.GetCompositingReasons() & kCompositingReason3DTransform) {
       if (position != FloatPoint()) {
         // Output position offset as a transform.
         auto* position_translate_json = AddTransformJSON(transform_id);
@@ -609,11 +610,14 @@
           position_translate_json->SetBoolean("flattenInheritedTransform",
                                               false);
         }
+        position = FloatPoint();
       }
 
-      auto* transform_json = AddTransformJSON(transform_id);
-      layer.AddTransformJSONProperties(*transform_json, rendering_context_map_);
-      position = FloatPoint();
+      if (!layer.transform_.IsIdentity() || layer.rendering_context3d_) {
+        auto* transform_json = AddTransformJSON(transform_id);
+        layer.AddTransformJSONProperties(*transform_json,
+                                         rendering_context_map_);
+      }
     }
 
     auto json =
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
index 00dd30d..5467cb6 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
@@ -715,7 +715,10 @@
     layer->SetClipTreeIndex(clip_id);
     layer->SetEffectTreeIndex(effect_id);
 
-    layer->SetContentsOpaque(pending_layer.known_to_be_opaque);
+    layer->SetContentsOpaque(
+        // Don't set opaque if the pending_layer's bounds are at subpixels.
+        EnclosingIntRect(pending_layer.bounds) == pending_layer.bounds &&
+        pending_layer.known_to_be_opaque);
     layer->SetDoubleSided(!pending_layer.backface_hidden);
     layer->SetShouldCheckBackfaceVisibility(pending_layer.backface_hidden);
   }
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
index 7747bc64..93acb672 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
@@ -3061,4 +3061,81 @@
   EXPECT_EQ(0u, ContentLayerCount());
 }
 
+TEST_F(PaintArtifactCompositorTestWithPropertyTrees, ContentsNonOpaque) {
+  TestPaintArtifact artifact;
+  artifact.Chunk(DefaultPaintChunkProperties())
+      .RectDrawing(FloatRect(100, 100, 200, 200), Color::kBlack);
+  Update(artifact.Build());
+  ASSERT_EQ(1u, ContentLayerCount());
+  EXPECT_FALSE(ContentLayerAt(0)->contents_opaque());
+}
+
+TEST_F(PaintArtifactCompositorTestWithPropertyTrees, ContentsOpaque) {
+  TestPaintArtifact artifact;
+  artifact.Chunk(DefaultPaintChunkProperties())
+      .RectDrawing(FloatRect(100, 100, 200, 200), Color::kBlack)
+      .KnownToBeOpaque();
+  Update(artifact.Build());
+  ASSERT_EQ(1u, ContentLayerCount());
+  EXPECT_TRUE(ContentLayerAt(0)->contents_opaque());
+}
+
+TEST_F(PaintArtifactCompositorTestWithPropertyTrees, ContentsOpaqueSubpixel) {
+  TestPaintArtifact artifact;
+  artifact.Chunk(DefaultPaintChunkProperties())
+      .RectDrawing(FloatRect(100.5, 100.5, 200, 200), Color::kBlack)
+      .KnownToBeOpaque();
+  Update(artifact.Build());
+  ASSERT_EQ(1u, ContentLayerCount());
+  EXPECT_EQ(gfx::Size(201, 201), ContentLayerAt(0)->bounds());
+  EXPECT_FALSE(ContentLayerAt(0)->contents_opaque());
+}
+
+TEST_F(PaintArtifactCompositorTestWithPropertyTrees,
+       ContentsOpaqueUnitedNonOpaque) {
+  TestPaintArtifact artifact;
+  artifact.Chunk(DefaultPaintChunkProperties())
+      .RectDrawing(FloatRect(100, 100, 200, 200), Color::kBlack)
+      .KnownToBeOpaque()
+      .Chunk(DefaultPaintChunkProperties())
+      .RectDrawing(FloatRect(200, 200, 200, 200), Color::kBlack)
+      .KnownToBeOpaque();
+  Update(artifact.Build());
+  ASSERT_EQ(1u, ContentLayerCount());
+  EXPECT_EQ(gfx::Size(300, 300), ContentLayerAt(0)->bounds());
+  EXPECT_FALSE(ContentLayerAt(0)->contents_opaque());
+}
+
+TEST_F(PaintArtifactCompositorTestWithPropertyTrees,
+       ContentsOpaqueUnitedOpaque1) {
+  TestPaintArtifact artifact;
+  artifact.Chunk(DefaultPaintChunkProperties())
+      .RectDrawing(FloatRect(100, 100, 300, 300), Color::kBlack)
+      .KnownToBeOpaque()
+      .Chunk(DefaultPaintChunkProperties())
+      .RectDrawing(FloatRect(200, 200, 200, 200), Color::kBlack)
+      .KnownToBeOpaque();
+  Update(artifact.Build());
+  ASSERT_EQ(1u, ContentLayerCount());
+  EXPECT_EQ(gfx::Size(300, 300), ContentLayerAt(0)->bounds());
+  EXPECT_TRUE(ContentLayerAt(0)->contents_opaque());
+}
+
+TEST_F(PaintArtifactCompositorTestWithPropertyTrees,
+       ContentsOpaqueUnitedOpaque2) {
+  TestPaintArtifact artifact;
+  artifact.Chunk(DefaultPaintChunkProperties())
+      .RectDrawing(FloatRect(100, 100, 200, 200), Color::kBlack)
+      .KnownToBeOpaque()
+      .Chunk(DefaultPaintChunkProperties())
+      .RectDrawing(FloatRect(100, 100, 300, 300), Color::kBlack)
+      .KnownToBeOpaque();
+  Update(artifact.Build());
+  ASSERT_EQ(1u, ContentLayerCount());
+  EXPECT_EQ(gfx::Size(300, 300), ContentLayerAt(0)->bounds());
+  // TODO(crbug.com/701991): Upgrade GeometryMapper to make this test pass with
+  // the following EXPECT_FALSE changed to EXPECT_TRUE.
+  EXPECT_FALSE(ContentLayerAt(0)->contents_opaque());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/testing/EmptyWebMediaPlayer.h b/third_party/WebKit/Source/platform/testing/EmptyWebMediaPlayer.h
index bef5284f..979762d6 100644
--- a/third_party/WebKit/Source/platform/testing/EmptyWebMediaPlayer.h
+++ b/third_party/WebKit/Source/platform/testing/EmptyWebMediaPlayer.h
@@ -49,7 +49,11 @@
   unsigned DroppedFrameCount() const override { return 0; }
   size_t AudioDecodedByteCount() const override { return 0; }
   size_t VideoDecodedByteCount() const override { return 0; }
-  void Paint(WebCanvas*, const WebRect&, cc::PaintFlags&) override {}
+  void Paint(WebCanvas*,
+             const WebRect&,
+             cc::PaintFlags&,
+             int already_uploaded_id,
+             VideoFrameUploadMetadata*) override {}
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/testing/TestPaintArtifact.cpp b/third_party/WebKit/Source/platform/testing/TestPaintArtifact.cpp
index 7339d5e..37bf3211 100644
--- a/third_party/WebKit/Source/platform/testing/TestPaintArtifact.cpp
+++ b/third_party/WebKit/Source/platform/testing/TestPaintArtifact.cpp
@@ -100,6 +100,11 @@
   return *this;
 }
 
+TestPaintArtifact& TestPaintArtifact::KnownToBeOpaque() {
+  paint_chunks_.back().known_to_be_opaque = true;
+  return *this;
+}
+
 const PaintArtifact& TestPaintArtifact::Build() {
   if (built_)
     return paint_artifact_;
diff --git a/third_party/WebKit/Source/platform/testing/TestPaintArtifact.h b/third_party/WebKit/Source/platform/testing/TestPaintArtifact.h
index 1bceebd..9afc83c 100644
--- a/third_party/WebKit/Source/platform/testing/TestPaintArtifact.h
+++ b/third_party/WebKit/Source/platform/testing/TestPaintArtifact.h
@@ -57,6 +57,7 @@
                                   scoped_refptr<cc::Layer>);
   TestPaintArtifact& ScrollHitTest(
       PassRefPtr<const TransformPaintPropertyNode> scroll_offset);
+  TestPaintArtifact& KnownToBeOpaque();
 
   // Can't add more things once this is called.
   const PaintArtifact& Build();
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_exportable_commits.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_exportable_commits.py
index 10bd021..eae2f90 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_exportable_commits.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_exportable_commits.py
@@ -11,7 +11,8 @@
 
 
 def exportable_commits_over_last_n_commits(
-        host, local_wpt, wpt_github, number=DEFAULT_COMMIT_HISTORY_WINDOW, require_clean=True):
+        host, local_wpt, wpt_github, number=DEFAULT_COMMIT_HISTORY_WINDOW,
+        require_clean=True, verify_merged_pr=False):
     """Lists exportable commits after a certain point.
 
     Exportable commits contain changes in the wpt directory and have not been
@@ -29,6 +30,12 @@
             to and including HEAD.
         require_clean: Whether to only return exportable commits that can be
             applied cleanly and produce non-empty diff when tested individually.
+        verify_merged_pr: Whether to verify merged PRs can be found in the local
+            WPT repo. If this argument is True, for each Chromium commit with a
+            corresponding merged PR, we also check the local WPT repo, and still
+            consider the commit exportable if it cannot be found in local WPT.
+            (Note: Chromium commits that have closed but not merged PRs are
+            always considered exported regardless of this argument.)
 
     Returns:
         (exportable_commits, errors) where exportable_commits is a list of
@@ -37,10 +44,11 @@
         cleanly, both in chronological order.
     """
     start_commit = 'HEAD~{}'.format(number + 1)
-    return _exportable_commits_since(start_commit, host, local_wpt, wpt_github, require_clean)
+    return _exportable_commits_since(start_commit, host, local_wpt, wpt_github, require_clean, verify_merged_pr)
 
 
-def _exportable_commits_since(chromium_commit_hash, host, local_wpt, wpt_github, require_clean=True):
+def _exportable_commits_since(chromium_commit_hash, host, local_wpt, wpt_github,
+                              require_clean=True, verify_merged_pr=False):
     """Lists exportable commits after the given commit.
 
     Args:
@@ -62,7 +70,7 @@
     exportable_commits = []
     errors = []
     for commit in chromium_commits:
-        state, error = get_commit_export_state(commit, local_wpt, wpt_github)
+        state, error = get_commit_export_state(commit, local_wpt, wpt_github, verify_merged_pr)
         if require_clean:
             success = state == CommitExportState.EXPORTABLE_CLEAN
         else:
@@ -75,9 +83,17 @@
     return exportable_commits, errors
 
 
-def get_commit_export_state(chromium_commit, local_wpt, wpt_github):
+def get_commit_export_state(chromium_commit, local_wpt, wpt_github, verify_merged_pr=False):
     """Determines the exportability state of a Chromium commit.
 
+    Args:
+        verify_merged_pr: Whether to verify merged PRs can be found in the local
+            WPT repo. If this argument is True, for each Chromium commit with a
+            corresponding merged PR, we also check the local WPT repo, and still
+            consider the commit exportable if it cannot be found in local WPT.
+            (Note: Chromium commits that have closed but not merged PRs are
+            always considered exported regardless of this argument.)
+
     Returns:
         (state, error): state is one of the members of CommitExportState;
         error is a string of error messages if an exportable patch fails to
@@ -91,17 +107,39 @@
     if not patch:
         return CommitExportState.NO_PATCH, ''
 
-    # If there's a corresponding closed PR, then this commit should not
-    # be considered exportable; the PR might have been merged and reverted,
-    # or it might have been closed manually without merging.
-    pull_request = wpt_github.pr_for_chromium_commit(chromium_commit)
-    if pull_request and pull_request.state == 'closed':
+    if _is_commit_exported(chromium_commit, local_wpt, wpt_github, verify_merged_pr):
         return CommitExportState.EXPORTED, ''
 
     success, error = local_wpt.test_patch(patch)
     return (CommitExportState.EXPORTABLE_CLEAN, '') if success else (CommitExportState.EXPORTABLE_DIRTY, error)
 
 
+def _is_commit_exported(chromium_commit, local_wpt, wpt_github, verify_merged_pr):
+    pull_request = wpt_github.pr_for_chromium_commit(chromium_commit)
+    if not pull_request or pull_request.state != 'closed':
+        return False
+
+    # A closed PR can either be merged or abandoned:
+    # * Merged PR might not be present in local WPT as the checkout may be
+    #   stale. If verify_merged_pr=True, we further search the git log of local
+    #   WPT the commit to prevent clobbering during import. (crbug.com/756428)
+    # * Abandoned PRs are expected to be reverted in Chromium by importer, so
+    #   they are always considered "exported".
+    if not verify_merged_pr:
+        # If no verification is needed, all closed PRs are deemed exported.
+        return True
+
+    if not wpt_github.is_pr_merged(pull_request.number):
+        # PR is abandoned.
+        return True
+
+    # PR is merged, and we need to verify that local WPT contains the commit.
+    change_id = chromium_commit.change_id()
+    found_in_upstream = (local_wpt.seek_change_id(change_id) if change_id
+                         else local_wpt.seek_commit_position(chromium_commit.position))
+    return found_in_upstream
+
+
 class CommitExportState(object):
     """An enum class for exportability states of a Chromium commit."""
     # pylint: disable=pointless-string-statement
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_exportable_commits_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_exportable_commits_unittest.py
index 1608d58a..8439266 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_exportable_commits_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/chromium_exportable_commits_unittest.py
@@ -13,37 +13,11 @@
     get_commit_export_state,
     CommitExportState
 )
+from webkitpy.w3c.local_wpt_mock import MockLocalWPT
 from webkitpy.w3c.wpt_github import PullRequest
 from webkitpy.w3c.wpt_github_mock import MockWPTGitHub
 
 
-class MockLocalWPT(object):
-
-    def __init__(self, patch_success=True, patch_error=''):
-        self.patch_success = patch_success
-        self.patch_error = patch_error
-
-    def test_patch(self, _):
-        return self.patch_success, self.patch_error
-
-
-class MultiResponseMockLocalWPT(object):
-
-    def __init__(self, test_results):
-        """A mock LocalWPT with canned responses of test_patch.
-
-        Args:
-            test_results: a list of (success, error).
-        """
-        self.test_results = test_results
-        self.count = 0
-
-    def test_patch(self, _):
-        success, error = self.test_results[self.count]
-        self.count += 1
-        return success, error
-
-
 class ChromiumExportableCommitsTest(unittest.TestCase):
 
     # TODO(qyearsley): Add a test for exportable_commits_over_last_n_commits.
@@ -63,7 +37,7 @@
         }, strict=True)
 
         commits, _ = _exportable_commits_since(
-            'beefcafe', host, MockLocalWPT(), MockWPTGitHub(pull_requests=[]))
+            'beefcafe', host, MockLocalWPT(test_patch=[(True, '')]), MockWPTGitHub(pull_requests=[]))
         self.assertEqual(len(commits), 1)
         self.assertIsInstance(commits[0], ChromiumCommit)
         self.assertEqual(host.executive.calls, [
@@ -88,7 +62,7 @@
                         'add087a97844f4b9e307d9a216940582d96db307\n'
                         'add087a97844f4b9e307d9a216940582d96db308\n'
         })
-        local_wpt = MultiResponseMockLocalWPT([
+        local_wpt = MockLocalWPT(test_patch=[
             (True, ''),
             (False, 'patch failure'),
             (True, ''),
@@ -98,7 +72,7 @@
             'beefcafe', host, local_wpt, MockWPTGitHub(pull_requests=[]))
         self.assertEqual(len(commits), 2)
 
-    def test_exportable_commits_since_without_require_clean(self):
+    def test_exportable_commits_since_not_require_clean(self):
         host = MockHost()
         host.executive = mock_git_commands({
             'diff-tree': 'third_party/WebKit/LayoutTests/external/wpt/some_files',
@@ -108,7 +82,7 @@
                         'add087a97844f4b9e307d9a216940582d96db307\n'
                         'add087a97844f4b9e307d9a216940582d96db308\n'
         })
-        local_wpt = MultiResponseMockLocalWPT([
+        local_wpt = MockLocalWPT(test_patch=[
             (True, ''),
             (False, 'patch failure'),
             (True, ''),
@@ -121,9 +95,12 @@
     def test_get_commit_export_state(self):
         commit = MockChromiumCommit(MockHost())
         github = MockWPTGitHub(pull_requests=[])
-        self.assertEqual(get_commit_export_state(commit, MockLocalWPT(), github), (CommitExportState.EXPORTABLE_CLEAN, ''))
+        self.assertEqual(get_commit_export_state(commit, MockLocalWPT(test_patch=[(True, '')]), github),
+                         (CommitExportState.EXPORTABLE_CLEAN, ''))
 
     def test_commit_with_noexport_is_not_exportable(self):
+        # Patch is not tested if the commit is ignored based on the message, hence empty MockLocalWPT.
+
         commit = MockChromiumCommit(MockHost(), body='Message\nNo-Export: true')
         github = MockWPTGitHub(pull_requests=[])
         self.assertEqual(get_commit_export_state(commit, MockLocalWPT(), github), (CommitExportState.IGNORED, ''))
@@ -146,26 +123,51 @@
     def test_commit_that_has_open_pr_is_exportable(self):
         commit = MockChromiumCommit(MockHost(), change_id='I00decade')
         github = MockWPTGitHub(pull_requests=[
-            PullRequest('PR1', 1, 'body\nChange-Id: I00c0ffee', 'closed', []),
             PullRequest('PR2', 2, 'body\nChange-Id: I00decade', 'open', []),
         ])
-        self.assertEqual(get_commit_export_state(commit, MockLocalWPT(), github), (CommitExportState.EXPORTABLE_CLEAN, ''))
+        self.assertEqual(get_commit_export_state(commit, MockLocalWPT(test_patch=[(True, '')]), github),
+                         (CommitExportState.EXPORTABLE_CLEAN, ''))
 
-    def test_commit_that_has_closed_pr_is_not_exportable(self):
+    def test_commit_that_has_closed_but_not_merged_pr(self):
         commit = MockChromiumCommit(MockHost(), change_id='I00decade')
         github = MockWPTGitHub(pull_requests=[
-            PullRequest('PR1', 1, 'body\nChange-Id: I00c0ffee', 'closed', []),
             PullRequest('PR2', 2, 'body\nChange-Id: I00decade', 'closed', []),
         ])
-        self.assertEqual(get_commit_export_state(commit, MockLocalWPT(), github), (CommitExportState.EXPORTED, ''))
+        # Regardless of verify_merged_pr, abandoned PRs are always exported.
+        self.assertEqual(get_commit_export_state(commit, MockLocalWPT(), github, verify_merged_pr=False),
+                         (CommitExportState.EXPORTED, ''))
+        self.assertEqual(get_commit_export_state(commit, MockLocalWPT(), github, verify_merged_pr=True),
+                         (CommitExportState.EXPORTED, ''))
+
+    def test_commit_that_has_merged_pr_and_found_locally(self):
+        commit = MockChromiumCommit(MockHost(), change_id='I00decade')
+        github = MockWPTGitHub(pull_requests=[
+            PullRequest('PR2', 2, 'body\nChange-Id: I00decade', 'closed', []),
+        ], merged_index=0)
+        self.assertEqual(get_commit_export_state(commit, MockLocalWPT(change_ids=['I00decade']), github, verify_merged_pr=False),
+                         (CommitExportState.EXPORTED, ''))
+        self.assertEqual(get_commit_export_state(commit, MockLocalWPT(change_ids=['I00decade']), github, verify_merged_pr=True),
+                         (CommitExportState.EXPORTED, ''))
+
+    def test_commit_that_has_merged_pr_but_not_found_locally(self):
+        commit = MockChromiumCommit(MockHost(), change_id='I00decade')
+        github = MockWPTGitHub(pull_requests=[
+            PullRequest('PR2', 2, 'body\nChange-Id: I00decade', 'closed', []),
+        ], merged_index=0)
+        # verify_merged_pr should be False by default.
+        self.assertEqual(get_commit_export_state(commit, MockLocalWPT(), github),
+                         (CommitExportState.EXPORTED, ''))
+        self.assertEqual(get_commit_export_state(commit, MockLocalWPT(test_patch=[(True, '')]), github, verify_merged_pr=True),
+                         (CommitExportState.EXPORTABLE_CLEAN, ''))
 
     def test_commit_that_produces_errors(self):
         commit = MockChromiumCommit(MockHost())
         github = MockWPTGitHub(pull_requests=[])
-        self.assertEqual(get_commit_export_state(commit, MockLocalWPT(False, 'error'), github),
+        self.assertEqual(get_commit_export_state(commit, MockLocalWPT(test_patch=[(False, 'error')]), github),
                          (CommitExportState.EXPORTABLE_DIRTY, 'error'))
 
     def test_commit_that_produces_empty_diff(self):
         commit = MockChromiumCommit(MockHost())
         github = MockWPTGitHub(pull_requests=[])
-        self.assertEqual(get_commit_export_state(commit, MockLocalWPT(False, ''), github), (CommitExportState.EXPORTABLE_DIRTY, ''))
+        self.assertEqual(get_commit_export_state(commit, MockLocalWPT(test_patch=[(False, '')]), github),
+                         (CommitExportState.EXPORTABLE_DIRTY, ''))
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/local_wpt.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/local_wpt.py
index 017212e..18f0bba 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/local_wpt.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/local_wpt.py
@@ -46,6 +46,7 @@
 
     def run(self, command, **kwargs):
         """Runs a command in the local WPT directory."""
+        # TODO(robertma): Migrate to webkitpy.common.checkout.Git. (crbug.com/676399)
         return self.host.executive.run_command(command, cwd=self.path, **kwargs)
 
     def clean(self):
@@ -148,3 +149,33 @@
         return len(self.run([
             'git', 'rev-list', '{}..origin/master'.format(commit)
         ]).splitlines())
+
+    def _most_recent_log_matching(self, grep_str):
+        """Finds the most recent commit whose message contains the given pattern.
+
+        Args:
+            grep_str: A regular expression. (git uses basic regexp by default!)
+
+        Returns:
+            A string containing the commit log of the first matched commit
+            (empty if not found).
+        """
+        return self.run(['git', 'log', '-1', '--grep', grep_str])
+
+    def seek_change_id(self, change_id):
+        """Finds the most recent commit with the given Chromium change ID.
+
+        Returns:
+            A string of the matched commit log, empty if not found.
+        """
+        # Note: anchors (^, $) are important so that quoted commit messages are not matched.
+        return self._most_recent_log_matching('^Change-Id: %s$' % change_id)
+
+    def seek_commit_position(self, commit_position):
+        """Finds the most recent commit with the given Chromium commit position.
+
+        Returns:
+            A string of the matched commit log, empty if not found.
+        """
+        # Note: anchors (^, $) are important so that quoted commit messages are not matched.
+        return self._most_recent_log_matching('^Cr-Commit-Position: %s$' % commit_position)
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/local_wpt_mock.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/local_wpt_mock.py
new file mode 100644
index 0000000..90cc3de
--- /dev/null
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/local_wpt_mock.py
@@ -0,0 +1,40 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""A mock LocalWPT class with canned responses."""
+
+
+class MockLocalWPT(object):
+
+    def __init__(self, test_patch=None, apply_patch=None, change_ids=None, commit_positions=None):
+        """Initializes the mock with pre-populated responses.
+
+        Args:
+            test_patch: A list of (success, error), responses for test_patch.
+            apply_patch: A list of error strings, responses for apply_patch.
+            change_ids: A list of Change-Id footer values that can be found.
+            commit_positions: A list of Cr-Commit-Position footer values that can be found.
+        """
+        self.test_patch_responses = test_patch or []
+        self.apply_patch_responses = apply_patch or []
+        self.test_patch_called = 0
+        self.apply_patch_called = 0
+        self.change_ids = change_ids or []
+        self.commit_positions = commit_positions or []
+
+    def test_patch(self, _):
+        success, error = self.test_patch_responses[self.test_patch_called]
+        self.test_patch_called += 1
+        return success, error
+
+    def apply_patch(self, _):
+        error = self.apply_patch_responses[self.apply_patch_called]
+        self.apply_patch_called += 1
+        return error
+
+    def seek_change_id(self, change_id):
+        return change_id in self.change_ids
+
+    def seek_commit_position(self, commit_position):
+        return commit_position in self.commit_positions
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py
index d49c74e..86c9285 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py
@@ -295,7 +295,7 @@
         # irrelevant and ignored here, because it tests patches *individually*
         # while the importer tries to reapply these patches *cumulatively*.
         commits, _ = exportable_commits_over_last_n_commits(
-            self.host, local_wpt, self.wpt_github, require_clean=False)
+            self.host, local_wpt, self.wpt_github, require_clean=False, verify_merged_pr=True)
         return commits
 
     def _generate_manifest(self):
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer_unittest.py
index 73f8781c..dc490ba6 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer_unittest.py
@@ -15,6 +15,7 @@
 from webkitpy.layout_tests.builder_list import BuilderList
 from webkitpy.w3c.chromium_commit_mock import MockChromiumCommit
 from webkitpy.w3c.local_wpt import LocalWPT
+from webkitpy.w3c.local_wpt_mock import MockLocalWPT
 from webkitpy.w3c.test_importer import TestImporter
 from webkitpy.w3c.wpt_github_mock import MockWPTGitHub
 
@@ -153,6 +154,7 @@
             [['git', 'cl', 'try'], ['git', 'cl', 'set-close']])
 
     def test_apply_exportable_commits_locally(self):
+        # TODO(robertma): Consider using MockLocalWPT.
         host = MockHost()
         importer = TestImporter(host, wpt_github=MockWPTGitHub(pull_requests=[]))
         fake_commit = MockChromiumCommit(
@@ -193,8 +195,7 @@
         importer = TestImporter(host, wpt_github=wpt_github)
         commit = MockChromiumCommit(host, subject='My fake commit')
         importer.exportable_but_not_exported_commits = lambda _: [commit]
-        local_wpt = LocalWPT(host)
-        local_wpt.apply_patch = lambda _: 'Failed'  # Failure to apply patch.
+        local_wpt = MockLocalWPT(apply_patch=['Failed'])    # Failure to apply patch.
         applied = importer.apply_exportable_commits_locally(local_wpt)
         self.assertIsNone(applied)
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github.py
index e13df22..811784d9 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github.py
@@ -219,6 +219,24 @@
         else:
             raise Exception('Non-200 status code (%s): %s' % (response.status_code, response.data))
 
+    def is_pr_merged(self, pr_number):
+        path = '/repos/%s/%s/pulls/%d/merge' % (
+            WPT_GH_ORG,
+            WPT_GH_REPO_NAME,
+            pr_number
+        )
+        try:
+            response = self.request(path, method='GET')
+            if response.status_code == 204:
+                return True
+            else:
+                raise Exception('Unknown status code (%s): %s' % (response.status_code, response.data))
+        except urllib2.HTTPError as e:
+            if e.code == 404:
+                return False
+            else:
+                raise
+
     def merge_pull_request(self, pull_request_number):
         path = '/repos/%s/%s/pulls/%d/merge' % (
             WPT_GH_ORG,
@@ -226,8 +244,6 @@
             pull_request_number
         )
         body = {
-            # This currently will noop because the feature is in an opt-in beta.
-            # Once it leaves beta this will start working.
             'merge_method': 'rebase',
         }
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github_mock.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github_mock.py
index 380b222..1a4dd77 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github_mock.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github_mock.py
@@ -10,7 +10,18 @@
     # Some unused arguments may be included to match the real class's API.
     # pylint: disable=unused-argument
 
-    def __init__(self, pull_requests, unsuccessful_merge_index=-1, create_pr_fail_index=-1):
+    def __init__(self, pull_requests, unsuccessful_merge_index=-1, create_pr_fail_index=-1, merged_index=-1):
+        """Initializes a mock WPTGitHub.
+
+        Args:
+            pull_requests: A list of wpt_github.PullRequest.
+            unsuccessful_merge_index: The index to the PR in pull_requests that
+                cannot be merged. (-1 means all can be merged.)
+            create_pr_fail_index: The 0-based index of which PR creation request
+                will fail. (-1 means all will succeed.)
+            merged_index: The index to the PR in pull_requests that is already
+                merged. (-1 means none is merged.)
+        """
         self.pull_requests = pull_requests
         self.calls = []
         self.pull_requests_created = []
@@ -18,11 +29,18 @@
         self.unsuccessful_merge_index = unsuccessful_merge_index
         self.create_pr_index = 0
         self.create_pr_fail_index = create_pr_fail_index
+        self.merged_index = merged_index
 
     def all_pull_requests(self, limit=30):
         self.calls.append('all_pull_requests')
         return self.pull_requests
 
+    def is_pr_merged(self, number):
+        for index, pr in enumerate(self.pull_requests):
+            if pr.number == number:
+                return index == self.merged_index
+        return False
+
     def merge_pull_request(self, number):
         self.calls.append('merge_pull_request')
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github_unittest.py
index f9ad5790..a479e24 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/wpt_github_unittest.py
@@ -91,6 +91,18 @@
         with self.assertRaises(GitHubError):
             self.wpt_github.all_pull_requests()
 
+    def test_is_pr_merged_receives_204(self):
+        self.wpt_github.host.web.responses = [
+            {'status_code': 204},
+        ]
+        self.assertTrue(self.wpt_github.is_pr_merged(1234))
+
+    def test_is_pr_merged_receives_404(self):
+        self.wpt_github.host.web.responses = [
+            {'status_code': 404},
+        ]
+        self.assertFalse(self.wpt_github.is_pr_merged(1234))
+
     def test_merge_pull_request_throws_merge_error_on_405(self):
         self.wpt_github.host.web.responses = [
             {'status_code': 200},
diff --git a/third_party/WebKit/public/platform/WebMediaPlayer.h b/third_party/WebKit/public/platform/WebMediaPlayer.h
index 34f5a0f..d653658 100644
--- a/third_party/WebKit/public/platform/WebMediaPlayer.h
+++ b/third_party/WebKit/public/platform/WebMediaPlayer.h
@@ -108,6 +108,14 @@
     kTexSubImage3D
   };
 
+  // For last-uploaded-frame-metadata API. https://crbug.com/639174
+  struct VideoFrameUploadMetadata {
+    int frame_id = -1;
+    gfx::Rect visible_rect = {};
+    base::TimeDelta timestamp = {};
+    bool skipped = false;
+  };
+
   virtual ~WebMediaPlayer() {}
 
   virtual void Load(LoadType, const WebMediaPlayerSource&, CORSMode) = 0;
@@ -180,21 +188,45 @@
   virtual size_t AudioDecodedByteCount() const = 0;
   virtual size_t VideoDecodedByteCount() const = 0;
 
-  virtual void Paint(WebCanvas*, const WebRect&, cc::PaintFlags&) = 0;
+  // |out_metadata|, if set, is used to return metadata about the frame
+  //   that is uploaded during this call.
+  // |already_uploaded_id| indicates the unique_id of the frame last uploaded
+  //   to this destination. It should only be set by the caller if the contents
+  //   of the destination are known not to have changed since that upload.
+  //   - If |out_metadata| is not null, |already_uploaded_id| is compared with
+  //     the unique_id of the frame being uploaded. If it's the same, the
+  //     upload may be skipped and considered to be successful.
+  virtual void Paint(WebCanvas*,
+                     const WebRect&,
+                     cc::PaintFlags&,
+                     int already_uploaded_id = -1,
+                     VideoFrameUploadMetadata* out_metadata = nullptr) = 0;
 
   // Do a GPU-GPU texture copy of the current video frame to |texture|,
   // reallocating |texture| at the appropriate size with given internal
   // format, format, and type if necessary. If the copy is impossible
   // or fails, it returns false.
-  virtual bool CopyVideoTextureToPlatformTexture(gpu::gles2::GLES2Interface*,
-                                                 unsigned target,
-                                                 unsigned texture,
-                                                 unsigned internal_format,
-                                                 unsigned format,
-                                                 unsigned type,
-                                                 int level,
-                                                 bool premultiply_alpha,
-                                                 bool flip_y) {
+  //
+  // |out_metadata|, if set, is used to return metadata about the frame
+  //   that is uploaded during this call.
+  // |already_uploaded_id| indicates the unique_id of the frame last uploaded
+  //   to this destination. It should only be set by the caller if the contents
+  //   of the destination are known not to have changed since that upload.
+  //   - If |out_metadata| is not null, |already_uploaded_id| is compared with
+  //     the unique_id of the frame being uploaded. If it's the same, the
+  //     upload may be skipped and considered to be successful.
+  virtual bool CopyVideoTextureToPlatformTexture(
+      gpu::gles2::GLES2Interface*,
+      unsigned target,
+      unsigned texture,
+      unsigned internal_format,
+      unsigned format,
+      unsigned type,
+      int level,
+      bool premultiply_alpha,
+      bool flip_y,
+      int already_uploaded_id = -1,
+      VideoFrameUploadMetadata* out_metadata = nullptr) {
     return false;
   }
 
@@ -276,15 +308,6 @@
   // |selected_track_id| is null if no track is selected.
   virtual void SelectedVideoTrackChanged(TrackId* selected_track_id) {}
 
-  // TODO(kainino): This is for a prototype implementation for getting the
-  // width, height, and timestamp of the last frame uploaded to a WebGL
-  // texture. https://crbug.com/639174
-  virtual bool GetLastUploadedFrameInfo(unsigned* width,
-                                        unsigned* height,
-                                        double* timestamp) {
-    return false;
-  }
-
   // Callback called whenever the media element may have received or last native
   // controls. It might be called twice with the same value: the caller has to
   // check if the value have changed if it only wants to handle this case.
diff --git a/third_party/harfbuzz-ng/README.chromium b/third_party/harfbuzz-ng/README.chromium
index f580a85d..87d0dab0 100644
--- a/third_party/harfbuzz-ng/README.chromium
+++ b/third_party/harfbuzz-ng/README.chromium
@@ -1,8 +1,8 @@
 Name: harfbuzz-ng
 Short Name: harfbuzz-ng
 URL: http://harfbuzz.org
-Version: 1.5.0
-Date: 20170823
+Version: 1.5.1
+Date: 20170905
 Security Critical: yes
 License: MIT
 License File: COPYING
diff --git a/third_party/harfbuzz-ng/src/hb-buffer.cc b/third_party/harfbuzz-ng/src/hb-buffer.cc
index a45062a..171d101 100644
--- a/third_party/harfbuzz-ng/src/hb-buffer.cc
+++ b/third_party/harfbuzz-ng/src/hb-buffer.cc
@@ -31,10 +31,6 @@
 #include "hb-utf-private.hh"
 
 
-#ifndef HB_DEBUG_BUFFER
-#define HB_DEBUG_BUFFER (HB_DEBUG+0)
-#endif
-
 /**
  * SECTION: hb-buffer
  * @title: Buffers
@@ -1885,6 +1881,9 @@
 /**
  * hb_buffer_diff:
  *
+ * If dottedcircle_glyph is (hb_codepoint_t) -1 then %HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT
+ * and %HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT are never returned.  This should be used by most
+ * callers if just comparing two buffers is needed.
  *
  * Since: 1.5.0
  **/
@@ -1894,10 +1893,11 @@
 		hb_codepoint_t dottedcircle_glyph,
 		unsigned int position_fuzz)
 {
-  if (buffer->content_type != reference->content_type)
+  if (buffer->content_type != reference->content_type && buffer->len && reference->len)
     return HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH;
 
   hb_buffer_diff_flags_t result = HB_BUFFER_DIFF_FLAG_EQUAL;
+  bool contains = dottedcircle_glyph != (hb_codepoint_t) -1;
 
   unsigned int count = reference->len;
 
@@ -1911,9 +1911,9 @@
     unsigned int i;
     for (i = 0; i < count; i++)
     {
-      if (info[i].codepoint == dottedcircle_glyph)
+      if (contains && info[i].codepoint == dottedcircle_glyph)
         result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT;
-      else if (info[i].codepoint == 0)
+      if (contains && info[i].codepoint == 0)
         result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT;
     }
     result |= HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH;
@@ -1933,9 +1933,9 @@
       result |= HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH;
     if ((buf_info->mask & HB_GLYPH_FLAG_DEFINED) != (ref_info->mask & HB_GLYPH_FLAG_DEFINED))
       result |= HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH;
-    if (ref_info->codepoint == dottedcircle_glyph)
+    if (contains && ref_info->codepoint == dottedcircle_glyph)
       result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT;
-    else if (ref_info->codepoint == 0)
+    if (contains && ref_info->codepoint == 0)
       result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT;
     buf_info++;
     ref_info++;
diff --git a/third_party/harfbuzz-ng/src/hb-buffer.h b/third_party/harfbuzz-ng/src/hb-buffer.h
index ffbf66e..1d633f7 100644
--- a/third_party/harfbuzz-ng/src/hb-buffer.h
+++ b/third_party/harfbuzz-ng/src/hb-buffer.h
@@ -481,12 +481,12 @@
 
   /* Buffers with different content_type cannot be meaningfully compared
    * in any further detail. */
-  HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH	= 0X0001,
+  HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH	= 0x0001,
 
   /* For buffers with differing length, the per-glyph comparison is not
    * attempted, though we do still scan reference for dottedcircle / .notdef
    * glyphs. */
-  HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH		= 0X0002,
+  HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH		= 0x0002,
 
   /* We want to know if dottedcircle / .notdef glyphs are present in the
    * reference, as we may not care so much about other differences in this
diff --git a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-indic-private.hh b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-indic-private.hh
index 58be422..c880311 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-indic-private.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-indic-private.hh
@@ -121,7 +121,7 @@
   INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED		= OT_X, /* Don't care. */
   INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED		= OT_CM,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA	= OT_N,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER	= OT_Repha, /* TODO */
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER	= OT_C,
   INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK		= OT_SM,
   INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER		= OT_Coeng,
   INDIC_SYLLABIC_CATEGORY_JOINER			= OT_ZWJ,
diff --git a/third_party/harfbuzz-ng/src/hb-ot-shape.cc b/third_party/harfbuzz-ng/src/hb-ot-shape.cc
index bc536696..8cd8fcc 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-shape.cc
+++ b/third_party/harfbuzz-ng/src/hb-ot-shape.cc
@@ -275,8 +275,7 @@
 static void
 hb_form_clusters (hb_buffer_t *buffer)
 {
-  if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) ||
-      buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+  if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII))
     return;
 
   /* Loop duplicated in hb_ensure_native_direction(), and in _hb-coretext.cc */
@@ -288,11 +287,17 @@
     if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])) &&
 		!_hb_glyph_info_is_joiner (&info[i])))
     {
-      buffer->merge_clusters (base, i);
+      if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+	buffer->merge_clusters (base, i);
+      else
+	buffer->unsafe_to_break (base, i);
       base = i;
     }
   }
-  buffer->merge_clusters (base, count);
+  if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+    buffer->merge_clusters (base, count);
+  else
+    buffer->unsafe_to_break (base, count);
 }
 
 static void
@@ -394,6 +399,8 @@
 	     HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
         end++;
 
+      buffer->unsafe_to_break (start, end);
+
       for (unsigned int j = start; j < i; j++)
         info[j].mask |= pre_mask;
       info[i].mask |= c->plan->frac_mask;
@@ -578,8 +585,6 @@
 {
   hb_buffer_t *buffer = c->buffer;
 
-  hb_ot_shape_initialize_masks (c);
-
   hb_ot_mirror_chars (c);
 
   HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index);
@@ -834,8 +839,10 @@
 
   c->buffer->clear_output ();
 
+  hb_ot_shape_initialize_masks (c);
   hb_set_unicode_props (c->buffer);
   hb_insert_dotted_circle (c->buffer, c->font);
+
   hb_form_clusters (c->buffer);
 
   hb_ensure_native_direction (c->buffer);
diff --git a/third_party/harfbuzz-ng/src/hb-version.h b/third_party/harfbuzz-ng/src/hb-version.h
index 6aead3d4..457f5b7b 100644
--- a/third_party/harfbuzz-ng/src/hb-version.h
+++ b/third_party/harfbuzz-ng/src/hb-version.h
@@ -38,9 +38,9 @@
 
 #define HB_VERSION_MAJOR 1
 #define HB_VERSION_MINOR 5
-#define HB_VERSION_MICRO 0
+#define HB_VERSION_MICRO 1
 
-#define HB_VERSION_STRING "1.5.0"
+#define HB_VERSION_STRING "1.5.1"
 
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
diff --git a/third_party/leveldatabase/env_chromium.cc b/third_party/leveldatabase/env_chromium.cc
index 074d13e..0935350 100644
--- a/third_party/leveldatabase/env_chromium.cc
+++ b/third_party/leveldatabase/env_chromium.cc
@@ -1260,32 +1260,9 @@
     auto db_visitor = [](const base::trace_event::MemoryDumpArgs& args,
                          base::trace_event::ProcessMemoryDump* pmd,
                          TrackedDB* db) {
-      std::string db_dump_name = DBTracker::GetMemoryDumpName(db);
-      auto* db_dump = pmd->CreateAllocatorDump(db_dump_name.c_str());
-
-      uint64_t db_memory_usage = 0;
-      {
-        std::string usage_string;
-        bool success = db->GetProperty("leveldb.approximate-memory-usage",
-                                       &usage_string) &&
-                       base::StringToUint64(usage_string, &db_memory_usage);
-        DCHECK(success);
-      }
-      db_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
-                         base::trace_event::MemoryAllocatorDump::kUnitsBytes,
-                         db_memory_usage);
-
-      if (args.level_of_detail !=
-          base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND) {
-        db_dump->AddString("name", "", db->name());
-      }
-
-      const char* system_allocator_name =
-          base::trace_event::MemoryDumpManager::GetInstance()
-              ->system_allocator_pool_name();
-      if (system_allocator_name) {
-        pmd->AddSuballocation(db_dump->guid(), system_allocator_name);
-      }
+      auto* dump = DBTracker::GetOrCreateAllocatorDump(pmd, db);
+      // TODO(ssid): Do not add string attribute in background mode.
+      dump->AddString("name", "", db->name());
     };
 
     DBTracker::GetInstance()->VisitDatabases(
@@ -1308,9 +1285,37 @@
   return instance;
 }
 
-std::string DBTracker::GetMemoryDumpName(leveldb::DB* tracked_db) {
-  return base::StringPrintf("leveldatabase/0x%" PRIXPTR,
-                            reinterpret_cast<uintptr_t>(tracked_db));
+// static
+base::trace_event::MemoryAllocatorDump* DBTracker::GetOrCreateAllocatorDump(
+    base::trace_event::ProcessMemoryDump* pmd,
+    leveldb::DB* tracked_db) {
+  if (pmd->dump_args().level_of_detail ==
+      base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND) {
+    return nullptr;
+  }
+  std::string dump_name = base::StringPrintf(
+      "leveldatabase/0x%" PRIXPTR, reinterpret_cast<uintptr_t>(tracked_db));
+  auto* dump = pmd->GetAllocatorDump(dump_name);
+  if (dump)
+    return dump;
+  dump = pmd->CreateAllocatorDump(dump_name);
+
+  uint64_t memory_usage = 0;
+  std::string usage_string;
+  bool success = tracked_db->GetProperty("leveldb.approximate-memory-usage",
+                                         &usage_string) &&
+                 base::StringToUint64(usage_string, &memory_usage);
+  DCHECK(success);
+  dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+                  base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+                  memory_usage);
+
+  const char* system_allocator_name =
+      base::trace_event::MemoryDumpManager::GetInstance()
+          ->system_allocator_pool_name();
+  if (system_allocator_name)
+    pmd->AddSuballocation(dump->guid(), system_allocator_name);
+  return dump;
 }
 
 leveldb::Status DBTracker::OpenDatabase(const leveldb::Options& options,
diff --git a/third_party/leveldatabase/env_chromium.h b/third_party/leveldatabase/env_chromium.h
index 843538d..feb6001 100644
--- a/third_party/leveldatabase/env_chromium.h
+++ b/third_party/leveldatabase/env_chromium.h
@@ -22,6 +22,13 @@
 #include "port/port_chromium.h"
 #include "util/mutexlock.h"
 
+namespace base {
+namespace trace_event {
+class MemoryAllocatorDump;
+class ProcessMemoryDump;
+}  // namespace trace_event
+}  // namespace base
+
 namespace leveldb_env {
 
 // These entries map to values in tools/metrics/histograms/histograms.xml. New
@@ -248,12 +255,14 @@
   // DBTracker singleton instance.
   static DBTracker* GetInstance();
 
-  // Returns name of memory-infra dump for |tracked_db|. Can be used to attach
+  // Returns the memory-infra dump for |tracked_db|. Can be used to attach
   // additional info to the database dump, or to properly attribute memory
   // usage in memory dump providers that also dump |tracked_db|.
   // Note that |tracked_db| should be a live database instance produced by
   // OpenDatabase() method or leveldb_env::OpenDB() function.
-  static std::string GetMemoryDumpName(leveldb::DB* tracked_db);
+  static base::trace_event::MemoryAllocatorDump* GetOrCreateAllocatorDump(
+      base::trace_event::ProcessMemoryDump* pmd,
+      leveldb::DB* tracked_db);
 
   // Provides extra information about a tracked database.
   class TrackedDB : public leveldb::DB {
diff --git a/third_party/wayland-protocols/include/protocol/remote-shell-unstable-v1-client-protocol.h b/third_party/wayland-protocols/include/protocol/remote-shell-unstable-v1-client-protocol.h
index df883d0d..05caad9 100644
--- a/third_party/wayland-protocols/include/protocol/remote-shell-unstable-v1-client-protocol.h
+++ b/third_party/wayland-protocols/include/protocol/remote-shell-unstable-v1-client-protocol.h
@@ -297,7 +297,7 @@
 	 * initialize scale configuration
 	 *
 	 * Sends the default device scale factor.
-	 * @param scale DP to pixels ratio, in 16.16 fixed point format
+	 * @param scale DP to pixels ratio, in 8.24 fixed point format
 	 * @since 8
 	 */
 	void (*default_device_scale_factor)(void *data,
diff --git a/third_party/wayland-protocols/include/protocol/remote-shell-unstable-v1-server-protocol.h b/third_party/wayland-protocols/include/protocol/remote-shell-unstable-v1-server-protocol.h
index 08b09e5..3972d7a 100644
--- a/third_party/wayland-protocols/include/protocol/remote-shell-unstable-v1-server-protocol.h
+++ b/third_party/wayland-protocols/include/protocol/remote-shell-unstable-v1-server-protocol.h
@@ -368,7 +368,7 @@
  * @ingroup iface_zcr_remote_shell_v1
  * Sends an default_device_scale_factor event to the client owning the resource.
  * @param resource_ The client's resource
- * @param scale DP to pixels ratio, in 16.16 fixed point format
+ * @param scale DP to pixels ratio, in 8.24 fixed point format
  */
 static inline void
 zcr_remote_shell_v1_send_default_device_scale_factor(struct wl_resource *resource_, int32_t scale)
diff --git a/third_party/wayland-protocols/unstable/remote-shell/remote-shell-unstable-v1.xml b/third_party/wayland-protocols/unstable/remote-shell/remote-shell-unstable-v1.xml
index 3fa25840..a2b11fe 100644
--- a/third_party/wayland-protocols/unstable/remote-shell/remote-shell-unstable-v1.xml
+++ b/third_party/wayland-protocols/unstable/remote-shell/remote-shell-unstable-v1.xml
@@ -176,7 +176,7 @@
       <description summary="initialize scale configuration">
 	Sends the default device scale factor.
       </description>
-      <arg name="scale" type="int" summary="DP to pixels ratio, in 16.16 fixed point format"/>
+      <arg name="scale" type="int" summary="DP to pixels ratio, in 8.24 fixed point format"/>
     </event>
   </interface>
 
diff --git a/third_party/win_build_output/midl/google_update/x64/google_update_idl.h b/third_party/win_build_output/midl/google_update/x64/google_update_idl.h
index aff56ae8a..e40ed08 100644
--- a/third_party/win_build_output/midl/google_update/x64/google_update_idl.h
+++ b/third_party/win_build_output/midl/google_update/x64/google_update_idl.h
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the definitions for the interfaces */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../google_update/google_update_idl.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/google_update/x64/google_update_idl.tlb b/third_party/win_build_output/midl/google_update/x64/google_update_idl.tlb
index da8590c1..2fb7e2f 100644
--- a/third_party/win_build_output/midl/google_update/x64/google_update_idl.tlb
+++ b/third_party/win_build_output/midl/google_update/x64/google_update_idl.tlb
Binary files differ
diff --git a/third_party/win_build_output/midl/google_update/x64/google_update_idl_i.c b/third_party/win_build_output/midl/google_update/x64/google_update_idl_i.c
index 2d5c757..972a43cb 100644
--- a/third_party/win_build_output/midl/google_update/x64/google_update_idl_i.c
+++ b/third_party/win_build_output/midl/google_update/x64/google_update_idl_i.c
@@ -5,11 +5,11 @@
 /* link this file in with the server and any clients */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../google_update/google_update_idl.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/google_update/x64/google_update_idl_p.c b/third_party/win_build_output/midl/google_update/x64/google_update_idl_p.c
index 2fba451..0d2f709 100644
--- a/third_party/win_build_output/midl/google_update/x64/google_update_idl_p.c
+++ b/third_party/win_build_output/midl/google_update/x64/google_update_idl_p.c
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the proxy stub code */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../google_update/google_update_idl.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/google_update/x86/google_update_idl.h b/third_party/win_build_output/midl/google_update/x86/google_update_idl.h
index 931f4bb..2066810 100644
--- a/third_party/win_build_output/midl/google_update/x86/google_update_idl.h
+++ b/third_party/win_build_output/midl/google_update/x86/google_update_idl.h
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the definitions for the interfaces */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../google_update/google_update_idl.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/google_update/x86/google_update_idl.tlb b/third_party/win_build_output/midl/google_update/x86/google_update_idl.tlb
index 5deb697..5dcf862b 100644
--- a/third_party/win_build_output/midl/google_update/x86/google_update_idl.tlb
+++ b/third_party/win_build_output/midl/google_update/x86/google_update_idl.tlb
Binary files differ
diff --git a/third_party/win_build_output/midl/google_update/x86/google_update_idl_i.c b/third_party/win_build_output/midl/google_update/x86/google_update_idl_i.c
index 3ed313a..d0bdcafa 100644
--- a/third_party/win_build_output/midl/google_update/x86/google_update_idl_i.c
+++ b/third_party/win_build_output/midl/google_update/x86/google_update_idl_i.c
@@ -5,11 +5,11 @@
 /* link this file in with the server and any clients */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../google_update/google_update_idl.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/google_update/x86/google_update_idl_p.c b/third_party/win_build_output/midl/google_update/x86/google_update_idl_p.c
index 3c7da91a..9f11f889 100644
--- a/third_party/win_build_output/midl/google_update/x86/google_update_idl_p.c
+++ b/third_party/win_build_output/midl/google_update/x86/google_update_idl_p.c
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the proxy stub code */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../google_update/google_update_idl.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/remoting/host/win/x64/chromoting_lib.h b/third_party/win_build_output/midl/remoting/host/win/x64/chromoting_lib.h
index ca2b507..d71b022 100644
--- a/third_party/win_build_output/midl/remoting/host/win/x64/chromoting_lib.h
+++ b/third_party/win_build_output/midl/remoting/host/win/x64/chromoting_lib.h
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the definitions for the interfaces */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for gen/remoting/host/win/chromoting_lib.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

@@ -304,7 +304,7 @@
 

 #ifdef __cplusplus

 

-class DECLSPEC_UUID("d2265286-2cc6-53cb-b6ef-909034b95730")

+class DECLSPEC_UUID("6741fd0a-6a8a-5838-a35e-8088697e2088")

 RdpDesktopSession;

 #endif

 #endif /* __ChromotingLib_LIBRARY_DEFINED__ */

diff --git a/third_party/win_build_output/midl/remoting/host/win/x64/chromoting_lib.tlb b/third_party/win_build_output/midl/remoting/host/win/x64/chromoting_lib.tlb
index 33de87e..01bf6f0 100644
--- a/third_party/win_build_output/midl/remoting/host/win/x64/chromoting_lib.tlb
+++ b/third_party/win_build_output/midl/remoting/host/win/x64/chromoting_lib.tlb
Binary files differ
diff --git a/third_party/win_build_output/midl/remoting/host/win/x64/chromoting_lib_i.c b/third_party/win_build_output/midl/remoting/host/win/x64/chromoting_lib_i.c
index 6422392..09a7f62 100644
--- a/third_party/win_build_output/midl/remoting/host/win/x64/chromoting_lib_i.c
+++ b/third_party/win_build_output/midl/remoting/host/win/x64/chromoting_lib_i.c
@@ -5,11 +5,11 @@
 /* link this file in with the server and any clients */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for gen/remoting/host/win/chromoting_lib.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

@@ -76,7 +76,7 @@
 MIDL_DEFINE_GUID(IID, LIBID_ChromotingLib,0xb6396c45,0xb0cc,0x456b,0x9f,0x49,0xf1,0x29,0x64,0xee,0x6d,0xf4);

 

 

-MIDL_DEFINE_GUID(CLSID, CLSID_RdpDesktopSession,0xd2265286,0x2cc6,0x53cb,0xb6,0xef,0x90,0x90,0x34,0xb9,0x57,0x30);

+MIDL_DEFINE_GUID(CLSID, CLSID_RdpDesktopSession,0x6741fd0a,0x6a8a,0x5838,0xa3,0x5e,0x80,0x88,0x69,0x7e,0x20,0x88);

 

 #undef MIDL_DEFINE_GUID

 

diff --git a/third_party/win_build_output/midl/remoting/host/win/x64/chromoting_lib_p.c b/third_party/win_build_output/midl/remoting/host/win/x64/chromoting_lib_p.c
index d2bde94..ccb6c1e0 100644
--- a/third_party/win_build_output/midl/remoting/host/win/x64/chromoting_lib_p.c
+++ b/third_party/win_build_output/midl/remoting/host/win/x64/chromoting_lib_p.c
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the proxy stub code */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for gen/remoting/host/win/chromoting_lib.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/remoting/host/win/x86/chromoting_lib.h b/third_party/win_build_output/midl/remoting/host/win/x86/chromoting_lib.h
index 02931fb..4173dae 100644
--- a/third_party/win_build_output/midl/remoting/host/win/x86/chromoting_lib.h
+++ b/third_party/win_build_output/midl/remoting/host/win/x86/chromoting_lib.h
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the definitions for the interfaces */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for gen/remoting/host/win/chromoting_lib.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

@@ -304,7 +304,7 @@
 

 #ifdef __cplusplus

 

-class DECLSPEC_UUID("406f7429-b57f-535a-9c79-6ae803e3aeaa")

+class DECLSPEC_UUID("6741fd0a-6a8a-5838-a35e-8088697e2088")

 RdpDesktopSession;

 #endif

 #endif /* __ChromotingLib_LIBRARY_DEFINED__ */

diff --git a/third_party/win_build_output/midl/remoting/host/win/x86/chromoting_lib.tlb b/third_party/win_build_output/midl/remoting/host/win/x86/chromoting_lib.tlb
index 63a3a8d9..42ae987 100644
--- a/third_party/win_build_output/midl/remoting/host/win/x86/chromoting_lib.tlb
+++ b/third_party/win_build_output/midl/remoting/host/win/x86/chromoting_lib.tlb
Binary files differ
diff --git a/third_party/win_build_output/midl/remoting/host/win/x86/chromoting_lib_i.c b/third_party/win_build_output/midl/remoting/host/win/x86/chromoting_lib_i.c
index 7188a8f..2780084 100644
--- a/third_party/win_build_output/midl/remoting/host/win/x86/chromoting_lib_i.c
+++ b/third_party/win_build_output/midl/remoting/host/win/x86/chromoting_lib_i.c
@@ -5,11 +5,11 @@
 /* link this file in with the server and any clients */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for gen/remoting/host/win/chromoting_lib.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

@@ -76,7 +76,7 @@
 MIDL_DEFINE_GUID(IID, LIBID_ChromotingLib,0xb6396c45,0xb0cc,0x456b,0x9f,0x49,0xf1,0x29,0x64,0xee,0x6d,0xf4);

 

 

-MIDL_DEFINE_GUID(CLSID, CLSID_RdpDesktopSession,0x406f7429,0xb57f,0x535a,0x9c,0x79,0x6a,0xe8,0x03,0xe3,0xae,0xaa);

+MIDL_DEFINE_GUID(CLSID, CLSID_RdpDesktopSession,0x6741fd0a,0x6a8a,0x5838,0xa3,0x5e,0x80,0x88,0x69,0x7e,0x20,0x88);

 

 #undef MIDL_DEFINE_GUID

 

diff --git a/third_party/win_build_output/midl/remoting/host/win/x86/chromoting_lib_p.c b/third_party/win_build_output/midl/remoting/host/win/x86/chromoting_lib_p.c
index 0ff8df5..34712abc 100644
--- a/third_party/win_build_output/midl/remoting/host/win/x86/chromoting_lib_p.c
+++ b/third_party/win_build_output/midl/remoting/host/win/x86/chromoting_lib_p.c
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the proxy stub code */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for gen/remoting/host/win/chromoting_lib.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all.h b/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all.h
index 34715e2..53f8657 100644
--- a/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all.h
+++ b/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all.h
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the definitions for the interfaces */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/iaccessible2/ia2_api_all.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all.tlb b/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all.tlb
index a839f361..9dff4f9 100644
--- a/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all.tlb
+++ b/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all.tlb
Binary files differ
diff --git a/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all_i.c b/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all_i.c
index 3b407b4..942f0276 100644
--- a/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all_i.c
+++ b/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all_i.c
@@ -5,11 +5,11 @@
 /* link this file in with the server and any clients */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/iaccessible2/ia2_api_all.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all_p.c b/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all_p.c
index 9173b8e..b006e1b 100644
--- a/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all_p.c
+++ b/third_party/win_build_output/midl/third_party/iaccessible2/x64/ia2_api_all_p.c
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the proxy stub code */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/iaccessible2/ia2_api_all.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all.h b/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all.h
index a4863a8..0811355 100644
--- a/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all.h
+++ b/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all.h
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the definitions for the interfaces */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/iaccessible2/ia2_api_all.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all.tlb b/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all.tlb
index e7ad0c68..e8cb50a 100644
--- a/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all.tlb
+++ b/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all.tlb
Binary files differ
diff --git a/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all_i.c b/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all_i.c
index 36ffefded..fdaad5c 100644
--- a/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all_i.c
+++ b/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all_i.c
@@ -5,11 +5,11 @@
 /* link this file in with the server and any clients */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/iaccessible2/ia2_api_all.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all_p.c b/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all_p.c
index 1a0d4d5..6f76dda 100644
--- a/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all_p.c
+++ b/third_party/win_build_output/midl/third_party/iaccessible2/x86/ia2_api_all_p.c
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the proxy stub code */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/iaccessible2/ia2_api_all.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x64/ISimpleDOMDocument.h b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x64/ISimpleDOMDocument.h
index c562450..c77a908 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x64/ISimpleDOMDocument.h
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x64/ISimpleDOMDocument.h
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the definitions for the interfaces */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMDocument.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x64/ISimpleDOMDocument_i.c b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x64/ISimpleDOMDocument_i.c
index d868c60c..36aed1f 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x64/ISimpleDOMDocument_i.c
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x64/ISimpleDOMDocument_i.c
@@ -5,11 +5,11 @@
 /* link this file in with the server and any clients */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMDocument.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x64/ISimpleDOMDocument_p.c b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x64/ISimpleDOMDocument_p.c
index 393a99d..0dda988 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x64/ISimpleDOMDocument_p.c
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x64/ISimpleDOMDocument_p.c
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the proxy stub code */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMDocument.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x86/ISimpleDOMDocument.h b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x86/ISimpleDOMDocument.h
index f9ecc2d..933bf5f1 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x86/ISimpleDOMDocument.h
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x86/ISimpleDOMDocument.h
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the definitions for the interfaces */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMDocument.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x86/ISimpleDOMDocument_i.c b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x86/ISimpleDOMDocument_i.c
index 202c77ed..aa47a63d 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x86/ISimpleDOMDocument_i.c
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x86/ISimpleDOMDocument_i.c
@@ -5,11 +5,11 @@
 /* link this file in with the server and any clients */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMDocument.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x86/ISimpleDOMDocument_p.c b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x86/ISimpleDOMDocument_p.c
index d563a190..051e27fc 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x86/ISimpleDOMDocument_p.c
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMDocument.idl/x86/ISimpleDOMDocument_p.c
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the proxy stub code */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMDocument.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x64/ISimpleDOMNode.h b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x64/ISimpleDOMNode.h
index 807accf..ff7500d 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x64/ISimpleDOMNode.h
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x64/ISimpleDOMNode.h
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the definitions for the interfaces */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMNode.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x64/ISimpleDOMNode_i.c b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x64/ISimpleDOMNode_i.c
index 44551bd..1132dd1 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x64/ISimpleDOMNode_i.c
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x64/ISimpleDOMNode_i.c
@@ -5,11 +5,11 @@
 /* link this file in with the server and any clients */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMNode.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x64/ISimpleDOMNode_p.c b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x64/ISimpleDOMNode_p.c
index 0800d741..503ba38 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x64/ISimpleDOMNode_p.c
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x64/ISimpleDOMNode_p.c
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the proxy stub code */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMNode.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x86/ISimpleDOMNode.h b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x86/ISimpleDOMNode.h
index 8e078f0..2dca526 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x86/ISimpleDOMNode.h
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x86/ISimpleDOMNode.h
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the definitions for the interfaces */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMNode.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x86/ISimpleDOMNode_i.c b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x86/ISimpleDOMNode_i.c
index 9497bb2..62d171e 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x86/ISimpleDOMNode_i.c
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x86/ISimpleDOMNode_i.c
@@ -5,11 +5,11 @@
 /* link this file in with the server and any clients */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMNode.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x86/ISimpleDOMNode_p.c b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x86/ISimpleDOMNode_p.c
index d90f708..b255a15 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x86/ISimpleDOMNode_p.c
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMNode.idl/x86/ISimpleDOMNode_p.c
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the proxy stub code */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMNode.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x64/ISimpleDOMText.h b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x64/ISimpleDOMText.h
index dd1de6fc..f2038fc 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x64/ISimpleDOMText.h
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x64/ISimpleDOMText.h
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the definitions for the interfaces */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMText.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x64/ISimpleDOMText_i.c b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x64/ISimpleDOMText_i.c
index f73288e..7d7b15e4 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x64/ISimpleDOMText_i.c
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x64/ISimpleDOMText_i.c
@@ -5,11 +5,11 @@
 /* link this file in with the server and any clients */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMText.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x64/ISimpleDOMText_p.c b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x64/ISimpleDOMText_p.c
index a6799fc..15e050cd 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x64/ISimpleDOMText_p.c
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x64/ISimpleDOMText_p.c
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the proxy stub code */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMText.idl:

-    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 

+    Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x86/ISimpleDOMText.h b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x86/ISimpleDOMText.h
index 7922549a..ce9ceeb7 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x86/ISimpleDOMText.h
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x86/ISimpleDOMText.h
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the definitions for the interfaces */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMText.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x86/ISimpleDOMText_i.c b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x86/ISimpleDOMText_i.c
index 38d66b37..1db9b74 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x86/ISimpleDOMText_i.c
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x86/ISimpleDOMText_i.c
@@ -5,11 +5,11 @@
 /* link this file in with the server and any clients */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMText.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x86/ISimpleDOMText_p.c b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x86/ISimpleDOMText_p.c
index dbd877c7..40a85ed 100644
--- a/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x86/ISimpleDOMText_p.c
+++ b/third_party/win_build_output/midl/third_party/isimpledom/ISimpleDOMText.idl/x86/ISimpleDOMText_p.c
@@ -3,11 +3,11 @@
 /* this ALWAYS GENERATED file contains the proxy stub code */

 

 

- /* File created by MIDL compiler version 8.01.0622 */

+ /* File created by MIDL compiler version 8.xx.xxxx */

 /* at a redacted point in time

  */

 /* Compiler settings for ../../third_party/isimpledom/ISimpleDOMText.idl:

-    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622 

+    Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.xx.xxxx 

     protocol : dce , ms_ext, c_ext, robust

     error checks: allocation ref bounds_check enum stub_data 

     VC __declspec() decoration level: 

diff --git a/tools/gn/bootstrap/bootstrap.py b/tools/gn/bootstrap/bootstrap.py
index 10efa68..9ff3579 100755
--- a/tools/gn/bootstrap/bootstrap.py
+++ b/tools/gn/bootstrap/bootstrap.py
@@ -610,7 +610,6 @@
         'base/synchronization/condition_variable_posix.cc',
         'base/synchronization/lock_impl_posix.cc',
         'base/synchronization/read_write_lock_posix.cc',
-        'base/synchronization/waitable_event_posix.cc',
         'base/sys_info_posix.cc',
         'base/task_scheduler/task_tracker_posix.cc',
         'base/threading/platform_thread_internal_posix.cc',
@@ -618,8 +617,6 @@
         'base/threading/thread_local_storage_posix.cc',
         'base/threading/worker_pool_posix.cc',
         'base/time/time_conversion_posix.cc',
-        'base/time/time_exploded_posix.cc',
-        'base/time/time_now_posix.cc',
         'base/trace_event/heap_profiler_allocation_register_posix.cc',
     ])
     static_libraries['libevent'] = {
@@ -664,7 +661,10 @@
         'base/process/process_linux.cc',
         'base/process/process_metrics_linux.cc',
         'base/strings/sys_string_conversions_posix.cc',
+        'base/synchronization/waitable_event_posix.cc',
         'base/sys_info_linux.cc',
+        'base/time/time_exploded_posix.cc',
+        'base/time/time_now_posix.cc',
         'base/threading/platform_thread_linux.cc',
     ])
     if is_linux:
@@ -711,6 +711,7 @@
         'base/process/process_iterator_mac.cc',
         'base/process/process_metrics_mac.cc',
         'base/strings/sys_string_conversions_mac.mm',
+        'base/synchronization/waitable_event_mac.cc',
         'base/sys_info_mac.mm',
         'base/time/time_mac.cc',
         'base/threading/platform_thread_mac.mm',
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 12fd86439..a4fb33d7 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -23666,6 +23666,7 @@
   <int value="-76631048" label="disable-offline-auto-reload-visible-only"/>
   <int value="-72455054" label="WebVrAutopresent:disabled"/>
   <int value="-70595606" label="ash-enable-unified-desktop"/>
+  <int value="-69427025" label="OfflinePagesPrefetchingUI:enabled"/>
   <int value="-68877684" label="BackgroundVideoTrackOptimization:enabled"/>
   <int value="-68225452" label="enable-translate-new-ux"/>
   <int value="-59401847" label="ContentSuggestionsLargeThumbnail:disabled"/>
@@ -24355,6 +24356,7 @@
   <int value="2014629801" label="view-passwords:disabled"/>
   <int value="2020107447" label="AndroidPayIntegrationV1:enabled"/>
   <int value="2037756154" label="enable-impl-side-painting"/>
+  <int value="2043321329" label="OfflinePagesPrefetchingUI:disabled"/>
   <int value="2056572020" label="EnableUsernameCorrection:disabled"/>
   <int value="2059322877" label="new-avatar-menu"/>
   <int value="2063091429" label="OfflinePagesSharing:enabled"/>
@@ -39638,6 +39640,8 @@
   <int value="8" label="File access permission was suppressed in VR"/>
   <int value="9" label="Password manager was disabled in VR"/>
   <int value="10" label="Autofill was disabled in VR"/>
+  <int value="11" label="USB chooser dialog was suppressed in VR"/>
+  <int value="12" label="SSL client certificate selector was suppressed in VR"/>
 </enum>
 
 <enum name="VRUnsupportedMode">
diff --git a/tools/metrics/rappor/rappor.xml b/tools/metrics/rappor/rappor.xml
index 6cd0fd0..0036c5d 100644
--- a/tools/metrics/rappor/rappor.xml
+++ b/tools/metrics/rappor/rappor.xml
@@ -1521,6 +1521,9 @@
 </rappor-metric>
 
 <rappor-metric name="Net.ErrAborted.Fast" type="ETLD_PLUS_ONE">
+  <obsolete>
+    Deprecated Aug 2017, metric just showed popular sites.
+  </obsolete>
   <owner>csharrison@chromium.org</owner>
   <summary>
     The domain and registry of the URL that leads to a main frame ERR_ABORTED
@@ -1529,6 +1532,9 @@
 </rappor-metric>
 
 <rappor-metric name="Net.ErrAborted.Slow" type="ETLD_PLUS_ONE">
+  <obsolete>
+    Deprecated Aug 2017, metric just showed popular sites.
+  </obsolete>
   <owner>csharrison@chromium.org</owner>
   <summary>
     The domain and registry of the URL that leads to a main frame ERR_ABORTED
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index be3ca32..68b8c10d 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -31,6 +31,10 @@
 from core.sharding_map_generator import load_benchmark_sharding_map
 
 
+_UNSCHEDULED_TELEMETRY_BENCHMARKS = set([
+  ])
+
+
 # TODO(rnephew): Remove when no tests disable using
 # expectations.PermanentlyDisableBenchmark()
 ANDROID_BOT_TO_DEVICE_TYPE_MAP = {
@@ -775,12 +779,13 @@
       device = None
       sharding_map = benchmark_sharding_map.get(name, None)
       device = sharding_map.get(benchmark.Name(), None)
-      if device is None:
-        raise ValueError('No sharding map for benchmark %r found. Please'
-                         ' disable the benchmark with @Disabled(\'all\'), and'
-                         ' file a bug with Speed>Benchmarks>Waterfall'
-                         ' component and cc martiniss@ and nednguyen@ to'
-                         ' execute the benchmark on the waterfall.' % (
+      if not device:
+        raise ValueError('No sharding map for benchmark %r found. Please '
+                         'add the benchmark to '
+                         '_UNSCHEDULED_TELEMETRY_BENCHMARKS list, '
+                         'then file a bug with Speed>Benchmarks>Waterfall '
+                         'component and assign to eyaich@ or martiniss@ to '
+                         'schedule the benchmark on the perf waterfall.' % (
                              benchmark.Name()))
       swarming_dimensions.append(get_swarming_dimension(
           dimension, device))
@@ -838,9 +843,13 @@
       path_util.GetChromiumSrcDir(), 'tools', 'perf', 'benchmarks')
   top_level_dir = os.path.dirname(benchmarks_dir)
 
-  all_benchmarks = discover.DiscoverClasses(
+  all_benchmarks = []
+
+  for b in discover.DiscoverClasses(
       benchmarks_dir, top_level_dir, benchmark_module.Benchmark,
-      index_by_class_name=True).values()
+      index_by_class_name=True).values():
+    if not b.Name() in _UNSCHEDULED_TELEMETRY_BENCHMARKS:
+      all_benchmarks.append(b)
 
   return sorted(all_benchmarks, key=lambda b: b.Name())
 
diff --git a/tools/perf/page_sets/static_top_25/.gitignore b/tools/perf/page_sets/static_top_25/.gitignore
new file mode 100644
index 0000000..2d19fc76
--- /dev/null
+++ b/tools/perf/page_sets/static_top_25/.gitignore
@@ -0,0 +1 @@
+*.html
diff --git a/tools/perf/page_sets/static_top_25_pages.py b/tools/perf/page_sets/static_top_25_pages.py
new file mode 100644
index 0000000..f6dc9a0
--- /dev/null
+++ b/tools/perf/page_sets/static_top_25_pages.py
@@ -0,0 +1,53 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from telemetry.page import page
+from telemetry.page import shared_page_state
+from telemetry import story
+
+STATIC_TOP_25_DIR = 'static_top_25'
+
+class StaticTop25PageSet(story.StorySet):
+
+  """ Page set consists of static top 25 pages. """
+
+  def __init__(self):
+    super(StaticTop25PageSet, self).__init__(
+        serving_dirs=[STATIC_TOP_25_DIR],
+        cloud_storage_bucket=story.PARTNER_BUCKET)
+
+    shared_desktop_state = shared_page_state.SharedDesktopPageState
+    files = [
+        'amazon.html',
+        'blogger.html',
+        'booking.html',
+        'cnn.html',
+        'ebay.html',
+        'espn.html',
+        'facebook.html',
+        'gmail.html',
+        'googlecalendar.html',
+        'googledocs.html',
+        'google.html',
+        'googleimagesearch.html',
+        'googleplus.html',
+        'linkedin.html',
+        'pinterest.html',
+        'techcrunch.html',
+        'twitter.html',
+        'weather.html',
+        'wikipedia.html',
+        'wordpress.html',
+        'yahooanswers.html',
+        'yahoogames.html',
+        'yahoonews.html',
+        'yahoosports.html',
+        'youtube.html'
+        ]
+    for f in files:
+        url = "file://%s/%s" % (STATIC_TOP_25_DIR, f)
+        self.AddStory(
+            page.Page(url, self, self.base_dir,
+                      shared_page_state_class=shared_desktop_state,
+                      name=url))
diff --git a/ui/android/delegated_frame_host_android.cc b/ui/android/delegated_frame_host_android.cc
index 7cc00f54..b1e2944 100644
--- a/ui/android/delegated_frame_host_android.cc
+++ b/ui/android/delegated_frame_host_android.cc
@@ -124,7 +124,8 @@
   readback_layer->SetHideLayerAndSubtree(true);
   compositor->AttachLayerForReadback(readback_layer);
   std::unique_ptr<viz::CopyOutputRequest> copy_output_request =
-      viz::CopyOutputRequest::CreateRequest(
+      std::make_unique<viz::CopyOutputRequest>(
+          viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
           base::BindOnce(&CopyOutputRequestCallback, readback_layer,
                          std::move(result_callback)));
 
diff --git a/ui/android/delegated_frame_host_android.h b/ui/android/delegated_frame_host_android.h
index 00a03ae..636381a3 100644
--- a/ui/android/delegated_frame_host_android.h
+++ b/ui/android/delegated_frame_host_android.h
@@ -64,7 +64,9 @@
 
   viz::FrameSinkId GetFrameSinkId() const;
 
-  // Should only be called when the host has a content layer.
+  // Should only be called when the host has a content layer. Use this for one-
+  // off screen capture, not for video. Always provides RGBA_BITMAP
+  // CopyOutputResults.
   void RequestCopyOfSurface(
       WindowAndroidCompositor* compositor,
       const gfx::Rect& src_subrect_in_pixel,
diff --git a/ui/android/view_android.cc b/ui/android/view_android.cc
index dd7bf1b..7ee3d01 100644
--- a/ui/android/view_android.cc
+++ b/ui/android/view_android.cc
@@ -5,6 +5,7 @@
 #include "ui/android/view_android.h"
 
 #include <algorithm>
+#include <cmath>
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
@@ -140,6 +141,15 @@
   // accidentally overwrite the valid ones in the children.
   if (!physical_size_.IsEmpty())
     child->OnPhysicalBackingSizeChanged(physical_size_);
+
+  // Empty view size also need not propagating down in order to prevent
+  // spurious events with empty size from being sent down.
+  if (child->layout_params_.match_parent && layout_params_.width != 0 &&
+      layout_params_.height != 0) {
+    child->OnSizeChangedInternal(layout_params_.width, layout_params_.height);
+    DispatchOnSizeChanged();
+  }
+
   if (GetWindowAndroid())
     child->OnAttachedToWindow();
 }
@@ -304,10 +314,6 @@
   layer_ = layer;
 }
 
-void ViewAndroid::SetLayout(ViewAndroid::LayoutParams params) {
-  layout_params_ = params;
-}
-
 bool ViewAndroid::StartDragAndDrop(const JavaRef<jstring>& jtext,
                                    const JavaRef<jobject>& jimage) {
   ScopedJavaLocalRef<jobject> delegate(GetViewAndroidDelegate());
@@ -376,6 +382,36 @@
   return Java_ViewAndroidDelegate_getSystemWindowInsetBottom(env, delegate);
 }
 
+void ViewAndroid::OnSizeChanged(int width, int height) {
+  // TODO(jinsukkim): Assert match-parent here. Match-parent view should keep
+  // its size in sync with its parent, ignoring the incoming size change event.
+  float scale = GetDipScale();
+  OnSizeChangedInternal(std::ceil(width / scale), std::ceil(height / scale));
+
+  // Signal resize event after all the views in the tree get the updated size.
+  DispatchOnSizeChanged();
+}
+
+void ViewAndroid::OnSizeChangedInternal(int width, int height) {
+  if (layout_params_.width == width && layout_params_.height == height)
+    return;
+
+  layout_params_.width = width;
+  layout_params_.height = height;
+  for (auto* child : children_) {
+    if (child->layout_params_.match_parent)
+      child->OnSizeChangedInternal(width, height);
+  }
+}
+
+void ViewAndroid::DispatchOnSizeChanged() {
+  client_->OnSizeChanged();
+  for (auto* child : children_) {
+    if (child->layout_params_.match_parent)
+      child->DispatchOnSizeChanged();
+  }
+}
+
 void ViewAndroid::OnPhysicalBackingSizeChanged(const gfx::Size& size) {
   if (physical_size_ == size)
     return;
diff --git a/ui/android/view_android.h b/ui/android/view_android.h
index 5dc92530..0cc1a9a 100644
--- a/ui/android/view_android.h
+++ b/ui/android/view_android.h
@@ -123,6 +123,9 @@
   // if disconnected.
   virtual WindowAndroid* GetWindowAndroid() const;
 
+  // Virtual for testing.
+  virtual float GetDipScale();
+
   // Used to return and set the layer for this view. May be |null|.
   cc::Layer* GetLayer() const;
   void SetLayer(scoped_refptr<cc::Layer> layer);
@@ -147,13 +150,12 @@
   bool HasFocus();
   void RequestFocus();
 
-  // Sets the layout relative to parent. Used to do hit testing against events.
-  void SetLayout(LayoutParams params);
-
   bool StartDragAndDrop(const base::android::JavaRef<jstring>& jtext,
                         const base::android::JavaRef<jobject>& jimage);
 
   gfx::Size GetPhysicalBackingSize();
+
+  void OnSizeChanged(int width, int height);
   void OnPhysicalBackingSizeChanged(const gfx::Size& size);
   void OnCursorChanged(int type,
                        const SkBitmap& custom_image,
@@ -179,12 +181,17 @@
   void AddObserver(ViewAndroidObserver* observer);
   void RemoveObserver(ViewAndroidObserver* observer);
 
-  float GetDipScale();
-
  protected:
   ViewAndroid* parent_;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(ViewAndroidBoundsTest, MatchesViewInFront);
+  FRIEND_TEST_ALL_PREFIXES(ViewAndroidBoundsTest, MatchesViewArea);
+  FRIEND_TEST_ALL_PREFIXES(ViewAndroidBoundsTest, MatchesViewAfterMove);
+  FRIEND_TEST_ALL_PREFIXES(ViewAndroidBoundsTest,
+                           MatchesViewSizeOfkMatchParent);
+  FRIEND_TEST_ALL_PREFIXES(ViewAndroidBoundsTest, MatchesViewsWithOffset);
+  FRIEND_TEST_ALL_PREFIXES(ViewAndroidBoundsTest, OnSizeChanged);
   friend class EventForwarder;
   friend class ViewAndroidBoundsTest;
 
@@ -229,6 +236,9 @@
   // each leaf of subtree.
   static bool SubtreeHasEventForwarder(ViewAndroid* view);
 
+  void OnSizeChangedInternal(int width, int height);
+  void DispatchOnSizeChanged();
+
   // Returns the Java delegate for this view. This is used to delegate work
   // up to the embedding view (or the embedder that can deal with the
   // implementation details).
diff --git a/ui/android/view_android_unittests.cc b/ui/android/view_android_unittests.cc
index 2205668..e267dd04 100644
--- a/ui/android/view_android_unittests.cc
+++ b/ui/android/view_android_unittests.cc
@@ -15,23 +15,36 @@
 
 using base::android::JavaParamRef;
 
+class TestViewAndroid : public ViewAndroid {
+ public:
+  explicit TestViewAndroid(ViewClient* client) : ViewAndroid(client) {}
+
+  float GetDipScale() override { return 1.f; }
+};
+
 class TestViewClient : public ViewClient {
  public:
-  TestViewClient() : handle_event_(true), called_(false) {}
+  TestViewClient() {}
 
-  void SetHandleEvent(bool handle_event) { handle_event_ = handle_event; }
   bool OnTouchEvent(const MotionEventAndroid& event) override {
-    called_ = true;
+    touch_called_ = true;
     return handle_event_;
   }
+  void OnSizeChanged() override { onsize_called_ = true; }
 
-  bool EventHandled() { return called_ && handle_event_; }
-  bool EventCalled() { return called_; }
-  void Reset() { called_ = false; }
+  void SetHandleEvent(bool handle_event) { handle_event_ = handle_event; }
+  bool TouchEventHandled() { return touch_called_ && handle_event_; }
+  bool TouchEventCalled() { return touch_called_; }
+  bool OnSizeCalled() { return onsize_called_; }
+  void Reset() {
+    touch_called_ = false;
+    onsize_called_ = false;
+  }
 
  private:
-  bool handle_event_;  // Marks as event was consumed. True by default.
-  bool called_;
+  bool handle_event_{true};  // Marks as event was consumed. True by default.
+  bool touch_called_{false};
+  bool onsize_called_{false};
 };
 
 class ViewAndroidBoundsTest : public testing::Test {
@@ -42,7 +55,7 @@
         view2_(&client2_),
         view3_(&client3_) {
     root_.GetEventForwarder();
-    root_.SetLayout(ViewAndroid::LayoutParams::MatchParent());
+    root_.layout_params_ = ViewAndroid::LayoutParams::MatchParent();
   }
 
   void Reset() {
@@ -64,25 +77,26 @@
     TestViewClient* clients[3] = {&client1_, &client2_, &client3_};
     for (auto* client : clients) {
       if (&hitClient == client)
-        EXPECT_TRUE(client->EventHandled());
+        EXPECT_TRUE(client->TouchEventHandled());
       else
-        EXPECT_FALSE(client->EventHandled());
+        EXPECT_FALSE(client->TouchEventHandled());
     }
     Reset();
   }
 
-  ViewAndroid root_;
-  ViewAndroid view1_;
-  ViewAndroid view2_;
-  ViewAndroid view3_;
+  TestViewAndroid root_;
+  TestViewAndroid view1_;
+  TestViewAndroid view2_;
+  TestViewAndroid view3_;
   TestViewClient client1_;
   TestViewClient client2_;
   TestViewClient client3_;
 };
 
 TEST_F(ViewAndroidBoundsTest, MatchesViewInFront) {
-  view1_.SetLayout(ViewAndroid::LayoutParams::Normal(50, 50, 400, 600));
-  view2_.SetLayout(ViewAndroid::LayoutParams::Normal(50, 50, 400, 600));
+  view1_.layout_params_ = ViewAndroid::LayoutParams::Normal(50, 50, 400, 600);
+  view1_.layout_params_ = ViewAndroid::LayoutParams::Normal(50, 50, 400, 600);
+  view2_.layout_params_ = ViewAndroid::LayoutParams::Normal(50, 50, 400, 600);
   root_.AddChild(&view2_);
   root_.AddChild(&view1_);
 
@@ -96,8 +110,8 @@
 }
 
 TEST_F(ViewAndroidBoundsTest, MatchesViewArea) {
-  view1_.SetLayout(ViewAndroid::LayoutParams::Normal(50, 50, 200, 200));
-  view2_.SetLayout(ViewAndroid::LayoutParams::Normal(20, 20, 400, 600));
+  view1_.layout_params_ = ViewAndroid::LayoutParams::Normal(50, 50, 200, 200);
+  view2_.layout_params_ = ViewAndroid::LayoutParams::Normal(20, 20, 400, 600);
 
   root_.AddChild(&view2_);
   root_.AddChild(&view1_);
@@ -112,23 +126,23 @@
 }
 
 TEST_F(ViewAndroidBoundsTest, MatchesViewAfterMove) {
-  view1_.SetLayout(ViewAndroid::LayoutParams::Normal(50, 50, 200, 200));
-  view2_.SetLayout(ViewAndroid::LayoutParams::Normal(20, 20, 400, 600));
+  view1_.layout_params_ = ViewAndroid::LayoutParams::Normal(50, 50, 200, 200);
+  view2_.layout_params_ = ViewAndroid::LayoutParams::Normal(20, 20, 400, 600);
   root_.AddChild(&view2_);
   root_.AddChild(&view1_);
 
   GenerateTouchEventAt(100.f, 100.f);
   ExpectHit(client1_);
 
-  view1_.SetLayout(ViewAndroid::LayoutParams::Normal(150, 150, 200, 200));
+  view1_.layout_params_ = ViewAndroid::LayoutParams::Normal(150, 150, 200, 200);
   GenerateTouchEventAt(100.f, 100.f);
   ExpectHit(client2_);
 }
 
 TEST_F(ViewAndroidBoundsTest, MatchesViewSizeOfkMatchParent) {
-  view1_.SetLayout(ViewAndroid::LayoutParams::Normal(20, 20, 400, 600));
-  view3_.SetLayout(ViewAndroid::LayoutParams::MatchParent());
-  view2_.SetLayout(ViewAndroid::LayoutParams::Normal(50, 50, 200, 200));
+  view1_.layout_params_ = ViewAndroid::LayoutParams::Normal(20, 20, 400, 600);
+  view3_.layout_params_ = ViewAndroid::LayoutParams::MatchParent();
+  view2_.layout_params_ = ViewAndroid::LayoutParams::Normal(50, 50, 200, 200);
 
   root_.AddChild(&view1_);
   root_.AddChild(&view2_);
@@ -142,14 +156,14 @@
 
   client1_.SetHandleEvent(false);
   GenerateTouchEventAt(300.f, 400.f);
-  EXPECT_TRUE(client1_.EventCalled());
+  EXPECT_TRUE(client1_.TouchEventCalled());
   ExpectHit(client3_);
 }
 
 TEST_F(ViewAndroidBoundsTest, MatchesViewsWithOffset) {
-  view1_.SetLayout(ViewAndroid::LayoutParams::Normal(10, 20, 150, 100));
-  view2_.SetLayout(ViewAndroid::LayoutParams::Normal(20, 30, 40, 30));
-  view3_.SetLayout(ViewAndroid::LayoutParams::Normal(70, 30, 40, 30));
+  view1_.layout_params_ = ViewAndroid::LayoutParams::Normal(10, 20, 150, 100);
+  view2_.layout_params_ = ViewAndroid::LayoutParams::Normal(20, 30, 40, 30);
+  view3_.layout_params_ = ViewAndroid::LayoutParams::Normal(70, 30, 40, 30);
 
   root_.AddChild(&view1_);
   view1_.AddChild(&view2_);
@@ -160,14 +174,51 @@
 
   client1_.SetHandleEvent(false);
   GenerateTouchEventAt(40, 60);
-  EXPECT_TRUE(client1_.EventCalled());
+  EXPECT_TRUE(client1_.TouchEventCalled());
   ExpectHit(client2_);
 
   GenerateTouchEventAt(100, 70);
-  EXPECT_TRUE(client1_.EventCalled());
+  EXPECT_TRUE(client1_.TouchEventCalled());
   ExpectHit(client3_);
 }
 
+TEST_F(ViewAndroidBoundsTest, OnSizeChanged) {
+  root_.AddChild(&view1_);
+  view1_.AddChild(&view2_);
+  view1_.AddChild(&view3_);
+
+  view1_.layout_params_ = ViewAndroid::LayoutParams::Normal(0, 0, 0, 0);
+  view2_.layout_params_ = ViewAndroid::LayoutParams::MatchParent();
+  view3_.layout_params_ = ViewAndroid::LayoutParams::Normal(0, 0, 0, 0);
+
+  // Size event propagates to non-match-parent children only.
+  view1_.OnSizeChanged(100, 100);
+  EXPECT_TRUE(client1_.OnSizeCalled());
+  EXPECT_TRUE(client2_.OnSizeCalled());
+  EXPECT_FALSE(client3_.OnSizeCalled());
+
+  Reset();
+
+  // TODO(jinsukkim): Enable following test once the top view can be
+  //     set to have match-parent property.
+
+  // Match-parent view should ignore the size event.
+  // view2_.OnSizeChanged(100, 200);
+  // EXPECT_FALSE(client2_.OnSizeCalled());
+  // EXPECT_FALSE(client3_.OnSizeCalled());
+
+  Reset();
+
+  view2_.RemoveFromParent();
+  view1_.OnSizeChanged(100, 100);
+
+  // Size event is generated for a newly added, match-parent child view.
+  EXPECT_FALSE(client2_.OnSizeCalled());
+  view1_.AddChild(&view2_);
+  EXPECT_TRUE(client2_.OnSizeCalled());
+  EXPECT_FALSE(client3_.OnSizeCalled());
+}
+
 TEST(ViewAndroidTest, ChecksMultipleEventForwarders) {
   ViewAndroid parent;
   ViewAndroid child;
diff --git a/ui/android/view_client.cc b/ui/android/view_client.cc
index 007ff62d..a9c8438 100644
--- a/ui/android/view_client.cc
+++ b/ui/android/view_client.cc
@@ -22,6 +22,8 @@
   return false;
 }
 
+void ViewClient::OnSizeChanged() {}
+
 void ViewClient::OnPhysicalBackingSizeChanged() {}
 
 }  // namespace ui
diff --git a/ui/android/view_client.h b/ui/android/view_client.h
index 6a01dff1..d04098c4 100644
--- a/ui/android/view_client.h
+++ b/ui/android/view_client.h
@@ -23,6 +23,7 @@
   virtual bool OnMouseEvent(const MotionEventAndroid& event);
   virtual bool OnMouseWheelEvent(const MotionEventAndroid& event);
   virtual bool OnDragEvent(const DragEventAndroid& event);
+  virtual void OnSizeChanged();
   virtual void OnPhysicalBackingSizeChanged();
 };
 
diff --git a/ui/app_list/presenter/app_list.cc b/ui/app_list/presenter/app_list.cc
index 0f4a6ef..a1de43b 100644
--- a/ui/app_list/presenter/app_list.cc
+++ b/ui/app_list/presenter/app_list.cc
@@ -9,6 +9,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/presenter/app_list_delegate.h"
+#include "ui/events/event.h"
 
 namespace app_list {
 
@@ -45,6 +46,11 @@
     presenter_->EndDragFromShelf(app_list_state);
 }
 
+void AppList::ProcessMouseWheelEvent(const ui::MouseWheelEvent& event) {
+  if (presenter_)
+    presenter_->ProcessMouseWheelOffset(event.offset().y());
+}
+
 void AppList::Dismiss() {
   if (presenter_)
     presenter_->Dismiss();
diff --git a/ui/app_list/presenter/app_list.h b/ui/app_list/presenter/app_list.h
index 11bfc3e..106e115 100644
--- a/ui/app_list/presenter/app_list.h
+++ b/ui/app_list/presenter/app_list.h
@@ -10,6 +10,10 @@
 #include "ui/app_list/presenter/app_list_presenter.mojom.h"
 #include "ui/app_list/presenter/app_list_presenter_export.h"
 
+namespace ui {
+class MouseWheelEvent;
+}
+
 namespace app_list {
 
 class AppListDelegate;
@@ -31,6 +35,8 @@
   void UpdateYPositionAndOpacity(int y_position_in_screen,
                                  float background_opacity);
   void EndDragFromShelf(mojom::AppListState app_list_state);
+  void ProcessMouseWheelEvent(const ui::MouseWheelEvent& event);
+
   void Dismiss();
   void ToggleAppList(int64_t display_id, AppListShowSource show_source);
   void StartVoiceInteractionSession();
diff --git a/ui/app_list/presenter/app_list_presenter.mojom b/ui/app_list/presenter/app_list_presenter.mojom
index 46481e9..2b54979 100644
--- a/ui/app_list/presenter/app_list_presenter.mojom
+++ b/ui/app_list/presenter/app_list_presenter.mojom
@@ -64,4 +64,8 @@
 
   // Ends the drag of app list from shelf.
   EndDragFromShelf(AppListState app_list_state);
+
+  // Passes MouseWheelEvents from the Shelf to the AppListView.
+  ProcessMouseWheelOffset(int32 y_scroll_offset);
+
 };
diff --git a/ui/app_list/presenter/app_list_presenter_impl.cc b/ui/app_list/presenter/app_list_presenter_impl.cc
index 7664b54..ac3a933 100644
--- a/ui/app_list/presenter/app_list_presenter_impl.cc
+++ b/ui/app_list/presenter/app_list_presenter_impl.cc
@@ -150,6 +150,11 @@
   }
 }
 
+void AppListPresenterImpl::ProcessMouseWheelOffset(int y_scroll_offset) {
+  if (view_)
+    view_->HandleScroll(y_scroll_offset, ui::ET_MOUSEWHEEL);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // AppListPresenterImpl, private:
 
diff --git a/ui/app_list/presenter/app_list_presenter_impl.h b/ui/app_list/presenter/app_list_presenter_impl.h
index 27aecd5..8ceb731 100644
--- a/ui/app_list/presenter/app_list_presenter_impl.h
+++ b/ui/app_list/presenter/app_list_presenter_impl.h
@@ -79,6 +79,9 @@
   // Ends the drag of app list from shelf.
   void EndDragFromShelf(mojom::AppListState app_list_state);
 
+  // Passes a MouseWheelEvent from the shelf to the AppListView.
+  void ProcessMouseWheelOffset(int y_scroll_offset);
+
  private:
   friend class test::AppListPresenterImplTestApi;
 
diff --git a/ui/app_list/presenter/test/test_app_list_presenter.cc b/ui/app_list/presenter/test/test_app_list_presenter.cc
index 03da9e5..c446ad7f 100644
--- a/ui/app_list/presenter/test/test_app_list_presenter.cc
+++ b/ui/app_list/presenter/test/test_app_list_presenter.cc
@@ -47,5 +47,9 @@
   app_list_state_ = app_list_state;
 }
 
+void TestAppListPresenter::ProcessMouseWheelOffset(int y_scroll_offset) {
+  process_mouse_wheel_offset_count_++;
+}
+
 }  // namespace test
 }  // namespace app_list
diff --git a/ui/app_list/presenter/test/test_app_list_presenter.h b/ui/app_list/presenter/test/test_app_list_presenter.h
index 95ef76d..6216d9f 100644
--- a/ui/app_list/presenter/test/test_app_list_presenter.h
+++ b/ui/app_list/presenter/test/test_app_list_presenter.h
@@ -30,6 +30,7 @@
   void UpdateYPositionAndOpacity(int y_position_in_screen,
                                  float background_opacity) override;
   void EndDragFromShelf(mojom::AppListState app_list_state) override;
+  void ProcessMouseWheelOffset(int y_scroll_offset) override;
 
   size_t show_count() const { return show_count_; }
   size_t dismiss_count() const { return dismiss_count_; }
@@ -39,6 +40,9 @@
     return voice_session_toggle_count_;
   }
   size_t set_y_position_count() const { return set_y_position_count_; }
+  size_t process_mouse_wheel_offset_count() const {
+    return process_mouse_wheel_offset_count_;
+  }
   mojom::AppListState app_list_state() const { return app_list_state_; }
 
  private:
@@ -48,6 +52,7 @@
   size_t voice_session_count_ = 0u;
   size_t voice_session_toggle_count_ = 0u;
   size_t set_y_position_count_ = 0u;
+  size_t process_mouse_wheel_offset_count_ = 0u;
   mojom::AppListState app_list_state_ = mojom::AppListState::CLOSED;
 
   mojo::Binding<mojom::AppListPresenter> binding_;
diff --git a/ui/app_list/views/app_list_view.cc b/ui/app_list/views/app_list_view.cc
index 035e20d..c8df24c 100644
--- a/ui/app_list/views/app_list_view.cc
+++ b/ui/app_list/views/app_list_view.cc
@@ -9,7 +9,6 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "components/wallpaper/wallpaper_color_profile.h"
@@ -408,12 +407,6 @@
 };
 
 void AppListView::InitContents(gfx::NativeView parent, int initial_apps_page) {
-  // TODO(vadimt): Remove ScopedTracker below once crbug.com/440224 and
-  // crbug.com/441028 are fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "440224, 441028 AppListView::InitContents"));
-
   if (is_fullscreen_app_list_enabled_) {
     // The shield view that colors/blurs the background of the app list and
     // makes it transparent.
@@ -441,22 +434,10 @@
   search_box_view_->layer()->SetFillsBoundsOpaquely(false);
   search_box_view_->layer()->SetMasksToBounds(true);
 
-  // TODO(vadimt): Remove ScopedTracker below once crbug.com/440224 and
-  // crbug.com/441028 are fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "440224, 441028 AppListView::InitContents1"));
-
   app_list_main_view_->Init(
       parent, is_fullscreen_app_list_enabled_ ? 0 : initial_apps_page,
       search_box_view_);
 
-  // TODO(vadimt): Remove ScopedTracker below once crbug.com/440224 and
-  // crbug.com/441028 are fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "440224, 441028 AppListView::InitContents2"));
-
   // Speech recognition is available only when the start page exists.
   if (delegate_ && delegate_->IsSpeechRecognitionEnabled()) {
     speech_view_ = new SpeechView(delegate_);
@@ -765,7 +746,6 @@
   switch (app_list_state_) {
     case CLOSED:
       // CLOSED->X transitions are not useful for UMA.
-      NOTREACHED();
       return kMaxAppListStateTransition;
     case PEEKING:
       switch (target_state) {
@@ -882,7 +862,7 @@
   if (event->type() == ui::ET_SCROLL_FLING_CANCEL)
     return;
 
-  if (!HandleScroll(event))
+  if (!HandleScroll(event->y_offset(), event->type()))
     return;
 
   event->SetHandled();
@@ -899,7 +879,8 @@
       HandleClickOrTap(event);
       break;
     case ui::ET_MOUSEWHEEL:
-      if (HandleScroll(event))
+      if (HandleScroll(event->AsMouseWheelEvent()->offset().y(),
+                       ui::ET_MOUSEWHEEL))
         event->SetHandled();
       break;
     default:
@@ -948,7 +929,8 @@
       event->SetHandled();
       break;
     case ui::ET_MOUSEWHEEL: {
-      if (HandleScroll(event))
+      if (HandleScroll(event->AsMouseWheelEvent()->offset().y(),
+                       ui::ET_MOUSEWHEEL))
         event->SetHandled();
       break;
     }
@@ -1045,14 +1027,13 @@
   }
 }
 
-bool AppListView::HandleScroll(const ui::Event* event) {
+bool AppListView::HandleScroll(int offset, ui::EventType type) {
   if (app_list_state_ != PEEKING)
     return false;
 
-  switch (event->type()) {
+  switch (type) {
     case ui::ET_MOUSEWHEEL:
-      SetState(event->AsMouseWheelEvent()->y_offset() < 0 ? FULLSCREEN_ALL_APPS
-                                                          : CLOSED);
+      SetState(offset < 0 ? FULLSCREEN_ALL_APPS : CLOSED);
       if (app_list_state_ == FULLSCREEN_ALL_APPS) {
         UMA_HISTOGRAM_ENUMERATION(kAppListPeekingToFullscreenHistogram,
                                   kMousewheelScroll, kMaxPeekingToFullscreen);
@@ -1062,10 +1043,8 @@
       return true;
     case ui::ET_SCROLL:
     case ui::ET_SCROLL_FLING_START: {
-      if (fabs(event->AsScrollEvent()->y_offset()) >
-          kAppListMinScrollToSwitchStates) {
-        SetState(event->AsScrollEvent()->y_offset() < 0 ? FULLSCREEN_ALL_APPS
-                                                        : CLOSED);
+      if (fabs(offset) > kAppListMinScrollToSwitchStates) {
+        SetState(offset < 0 ? FULLSCREEN_ALL_APPS : CLOSED);
         if (app_list_state_ == FULLSCREEN_ALL_APPS) {
           UMA_HISTOGRAM_ENUMERATION(kAppListPeekingToFullscreenHistogram,
                                     kMousepadScroll, kMaxPeekingToFullscreen);
diff --git a/ui/app_list/views/app_list_view.h b/ui/app_list/views/app_list_view.h
index 4b336f6..52113d52 100644
--- a/ui/app_list/views/app_list_view.h
+++ b/ui/app_list/views/app_list_view.h
@@ -146,8 +146,8 @@
   // Called when tablet mode starts and ends.
   void OnTabletModeChanged(bool started);
 
-  // Changes |app_list_state_| from |PEEKING| to |FULLSCREEN_ALL_APPS|.
-  bool HandleScroll(const ui::Event* event);
+  // Handles scroll events from various sources.
+  bool HandleScroll(int offset, ui::EventType type);
 
   // Changes the app list state.
   void SetState(AppListState new_state);
diff --git a/ui/app_list/views/page_switcher_vertical.cc b/ui/app_list/views/page_switcher_vertical.cc
index cfeb1214..cb7730e 100644
--- a/ui/app_list/views/page_switcher_vertical.cc
+++ b/ui/app_list/views/page_switcher_vertical.cc
@@ -6,16 +6,19 @@
 
 #include <algorithm>
 
+#include "base/i18n/number_formatting.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "third_party/skia/include/core/SkPath.h"
 #include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/pagination_model.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/animation/throb_animation.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/skia_util.h"
+#include "ui/strings/grit/ui_strings.h"
 #include "ui/views/animation/flood_fill_ink_drop_ripple.h"
 #include "ui/views/animation/ink_drop_highlight.h"
 #include "ui/views/animation/ink_drop_impl.h"
@@ -61,6 +64,8 @@
 
     selected_ = selected;
     SchedulePaint();
+    if (selected)
+      NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
   }
 
   // Overridden from views::View:
@@ -257,6 +262,9 @@
   buttons_->RemoveAllChildViews(true);
   for (int i = 0; i < model_->total_pages(); ++i) {
     PageSwitcherButton* button = new PageSwitcherButton(this);
+    button->SetAccessibleName(l10n_util::GetStringFUTF16(
+        IDS_APP_LIST_PAGE_SWITCHER, base::FormatNumber(i + 1),
+        base::FormatNumber(model_->total_pages())));
     button->SetSelected(i == model_->selected_page() ? true : false);
     buttons_->AddChildView(button);
   }
diff --git a/ui/app_list/views/search_box_view.cc b/ui/app_list/views/search_box_view.cc
index 66c7bc6f..a4f3552 100644
--- a/ui/app_list/views/search_box_view.cc
+++ b/ui/app_list/views/search_box_view.cc
@@ -576,8 +576,11 @@
     return;
 
   if (located_event->type() == ui::ET_MOUSEWHEEL) {
-    if (!app_list_view_->HandleScroll(located_event))
+    if (!app_list_view_->HandleScroll(
+            located_event->AsMouseWheelEvent()->offset().y(),
+            ui::ET_MOUSEWHEEL)) {
       return;
+    }
 
   } else if (located_event->type() == ui::ET_MOUSE_PRESSED ||
              located_event->type() == ui::ET_GESTURE_TAP) {
@@ -846,7 +849,8 @@
     return false;
 
   if (mouse_event.type() == ui::ET_MOUSEWHEEL) {
-    return app_list_view_->HandleScroll(&mouse_event);
+    return app_list_view_->HandleScroll(
+        (&mouse_event)->AsMouseWheelEvent()->offset().y(), ui::ET_MOUSEWHEEL);
   } else {
     return OnTextfieldEvent();
   }
diff --git a/ui/compositor/layer_unittest.cc b/ui/compositor/layer_unittest.cc
index dcb2cf9e..94072f6a 100644
--- a/ui/compositor/layer_unittest.cc
+++ b/ui/compositor/layer_unittest.cc
@@ -199,7 +199,8 @@
   void ReadPixels(SkBitmap* bitmap, gfx::Rect source_rect) {
     scoped_refptr<ReadbackHolder> holder(new ReadbackHolder);
     std::unique_ptr<viz::CopyOutputRequest> request =
-        viz::CopyOutputRequest::CreateBitmapRequest(
+        std::make_unique<viz::CopyOutputRequest>(
+            viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
             base::BindOnce(&ReadbackHolder::OutputRequestCallback, holder));
     request->set_area(source_rect);
 
@@ -247,7 +248,10 @@
     ReadbackHolder() : run_loop_(new base::RunLoop) {}
 
     void OutputRequestCallback(std::unique_ptr<viz::CopyOutputResult> result) {
-      result_ = result->TakeBitmap();
+      if (result->IsEmpty())
+        result_.reset();
+      else
+        result_ = std::make_unique<SkBitmap>(result->AsSkBitmap());
       run_loop_->Quit();
     }
 
diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc
index e79247fd..43ff3fa 100644
--- a/ui/events/blink/input_handler_proxy.cc
+++ b/ui/events/blink/input_handler_proxy.cc
@@ -276,7 +276,8 @@
     bool is_scroll_end_from_wheel =
         gesture_event.source_device == blink::kWebGestureDeviceTouchpad &&
         gesture_event.GetType() == blink::WebGestureEvent::kGestureScrollEnd;
-    if (is_from_set_non_blocking_touch || is_scroll_end_from_wheel) {
+    if (is_from_set_non_blocking_touch || is_scroll_end_from_wheel ||
+        synchronous_input_handler_) {
       // Gesture events was already delayed by blocking events in rAF aligned
       // queue. We want to avoid additional one frame delay by flushing the
       // VSync queue immediately.
diff --git a/ui/gfx/font_fallback_win.cc b/ui/gfx/font_fallback_win.cc
index cf3c417..17ac788 100644
--- a/ui/gfx/font_fallback_win.cc
+++ b/ui/gfx/font_fallback_win.cc
@@ -15,7 +15,6 @@
 #include "base/macros.h"
 #include "base/memory/singleton.h"
 #include "base/message_loop/message_loop.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -155,11 +154,6 @@
   cached_linked_fonts_[font_name] = std::vector<Font>();
   std::vector<Font>* linked_fonts = &cached_linked_fonts_[font_name];
 
-  // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "441028 QueryLinkedFontsFromRegistry()"));
-
   QueryLinkedFontsFromRegistry(font, &cached_system_fonts_, linked_fonts);
   return linked_fonts;
 }
diff --git a/ui/gfx/render_text_harfbuzz.cc b/ui/gfx/render_text_harfbuzz.cc
index 07a4bbd..ffbb3b1 100644
--- a/ui/gfx/render_text_harfbuzz.cc
+++ b/ui/gfx/render_text_harfbuzz.cc
@@ -14,7 +14,6 @@
 #include "base/i18n/char_iterator.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -1230,13 +1229,7 @@
 
     if (!display_text.empty()) {
       TRACE_EVENT0("ui", "RenderTextHarfBuzz:EnsureLayout1");
-
       ItemizeTextToRuns(display_text, display_run_list_.get());
-
-      // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is
-      // fixed.
-      tracked_objects::ScopedTracker tracking_profile(
-          FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 ShapeRunList() 1"));
       ShapeRunList(display_text, display_run_list_.get());
     }
     update_display_run_list_ = false;
@@ -1245,12 +1238,6 @@
   }
 
   if (lines().empty()) {
-    // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is
-    // fixed.
-    std::unique_ptr<tracked_objects::ScopedTracker> tracking_profile(
-        new tracked_objects::ScopedTracker(
-            FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 HarfBuzzLineBreaker")));
-
     internal::TextRunList* run_list = GetRunList();
     const int height = std::max(font_list().GetHeight(), min_line_height());
     HarfBuzzLineBreaker line_breaker(
@@ -1259,8 +1246,6 @@
         word_wrap_behavior(), GetDisplayText(),
         multiline() ? &GetLineBreaks() : nullptr, *run_list);
 
-    tracking_profile.reset();
-
     if (multiline())
       line_breaker.ConstructMultiLines();
     else
@@ -1625,15 +1610,8 @@
   // TODO(ckocagil): Should we determine the actual language?
   hb_buffer_set_language(buffer, hb_language_get_default());
 
-  {
-    // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is
-    // fixed.
-    tracked_objects::ScopedTracker tracking_profile(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 hb_shape()"));
-
-    // Shape the text.
-    hb_shape(harfbuzz_font, buffer, NULL, 0);
-  }
+  // Shape the text.
+  hb_shape(harfbuzz_font, buffer, NULL, 0);
 
   // Populate the run fields with the resulting glyph data in the buffer.
   unsigned int glyph_count = 0;
@@ -1676,11 +1654,6 @@
     if (!text.empty()) {
       TRACE_EVENT0("ui", "RenderTextHarfBuzz:EnsureLayoutRunList");
       ItemizeTextToRuns(text, &layout_run_list_);
-
-      // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is
-      // fixed.
-      tracked_objects::ScopedTracker tracking_profile(
-          FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 ShapeRunList() 2"));
       ShapeRunList(text, &layout_run_list_);
     }
 
diff --git a/ui/message_center/views/message_popup_collection_unittest.cc b/ui/message_center/views/message_popup_collection_unittest.cc
index bcbccf2..9b9e0ea 100644
--- a/ui/message_center/views/message_popup_collection_unittest.cc
+++ b/ui/message_center/views/message_popup_collection_unittest.cc
@@ -709,13 +709,14 @@
   };
   std::vector<TestCase> updates = {
       {"shrinking", ""},
-      {"enlarging", "abc\ndef\nghk\n"},
-      {"restoring", "abc\ndef\n"},
+      {"enlarging", "abc\ndef\nghi\n"},
+      {"restoring", "abc\n"},
   };
 
   std::vector<std::string> notification_ids;
   // adding notifications
   {
+    // adding popup notifications
     constexpr int max_visible_popup_notifications = 3;
     notification_ids.reserve(max_visible_popup_notifications);
     for (int i = 0; i < max_visible_popup_notifications; ++i) {
@@ -728,6 +729,10 @@
 
   WaitForTransitionsDone();
 
+  // Confirms that there are 2 toasts of 3 notifications.
+  EXPECT_EQ(3u, GetToastCounts());
+  EXPECT_EQ(3u, MessageCenter::Get()->NotificationCount());
+
   // updating notifications one by one
   for (const std::string& id : notification_ids) {
     for (const auto& update : updates) {
diff --git a/ui/message_center/views/notification_view_md.cc b/ui/message_center/views/notification_view_md.cc
index 69e15176..25642d9 100644
--- a/ui/message_center/views/notification_view_md.cc
+++ b/ui/message_center/views/notification_view_md.cc
@@ -47,7 +47,7 @@
 namespace {
 
 // Dimensions.
-constexpr gfx::Insets kContentRowPadding(2, 12, 12, 12);
+constexpr gfx::Insets kContentRowPadding(0, 12, 16, 12);
 constexpr gfx::Insets kActionsRowPadding(8, 8, 8, 8);
 constexpr int kActionsRowHorizontalSpacing = 8;
 constexpr gfx::Insets kActionButtonPadding(0, 12, 0, 12);
@@ -59,8 +59,8 @@
 constexpr gfx::Insets kLargeImageContainerPadding(0, 12, 12, 12);
 constexpr gfx::Size kLargeImageMinSize(328, 0);
 constexpr gfx::Size kLargeImageMaxSize(328, 218);
-constexpr gfx::Insets kLeftContentPadding(0, 4, 0, 4);
-constexpr gfx::Insets kLeftContentPaddingWithIcon(0, 4, 0, 12);
+constexpr gfx::Insets kLeftContentPadding(2, 4, 0, 4);
+constexpr gfx::Insets kLeftContentPaddingWithIcon(2, 4, 0, 12);
 
 // Background of inline actions area.
 const SkColor kActionsRowBackgroundColor = SkColorSetRGB(0xee, 0xee, 0xee);
diff --git a/ui/snapshot/snapshot_android.cc b/ui/snapshot/snapshot_android.cc
index c209fa5..2f46639b 100644
--- a/ui/snapshot/snapshot_android.cc
+++ b/ui/snapshot/snapshot_android.cc
@@ -39,8 +39,9 @@
     const gfx::Rect& source_rect,
     viz::CopyOutputRequest::CopyOutputRequestCallback callback) {
   std::unique_ptr<viz::CopyOutputRequest> request =
-      viz::CopyOutputRequest::CreateBitmapRequest(std::move(callback));
-
+      std::make_unique<viz::CopyOutputRequest>(
+          viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+          std::move(callback));
   float scale = ui::GetScaleFactorForNativeView(window);
   request->set_area(gfx::ScaleToEnclosingRect(source_rect, scale));
   window->GetCompositor()->RequestCopyOfOutputOnRootLayer(std::move(request));
diff --git a/ui/snapshot/snapshot_async.cc b/ui/snapshot/snapshot_async.cc
index 1eedc3c..0299d7a 100644
--- a/ui/snapshot/snapshot_async.cc
+++ b/ui/snapshot/snapshot_async.cc
@@ -37,7 +37,8 @@
     const GrabWindowSnapshotAsyncCallback& callback,
     const gfx::Size& target_size,
     std::unique_ptr<viz::CopyOutputResult> result) {
-  if (result->IsEmpty()) {
+  const SkBitmap bitmap = result->AsSkBitmap();
+  if (!bitmap.readyToDraw()) {
     callback.Run(gfx::Image());
     return;
   }
@@ -48,19 +49,19 @@
   // somewhere so that it can be reused here.
   base::PostTaskWithTraitsAndReplyWithResult(
       FROM_HERE, {base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-      base::Bind(ScaleBitmap, *result->TakeBitmap(), target_size),
+      base::Bind(ScaleBitmap, bitmap, target_size),
       base::Bind(&OnFrameScalingFinished, callback));
 }
 
 void SnapshotAsync::RunCallbackWithCopyOutputResult(
     const GrabWindowSnapshotAsyncCallback& callback,
     std::unique_ptr<viz::CopyOutputResult> result) {
-  if (result->IsEmpty()) {
+  const SkBitmap bitmap = result->AsSkBitmap();
+  if (!bitmap.readyToDraw()) {
     callback.Run(gfx::Image());
     return;
   }
-
-  callback.Run(gfx::Image::CreateFrom1xBitmap(*result->TakeBitmap()));
+  callback.Run(gfx::Image::CreateFrom1xBitmap(bitmap));
 }
 
 }  // namespace ui
diff --git a/ui/snapshot/snapshot_aura.cc b/ui/snapshot/snapshot_aura.cc
index 40b59a11..ccfa4d32 100644
--- a/ui/snapshot/snapshot_aura.cc
+++ b/ui/snapshot/snapshot_aura.cc
@@ -34,7 +34,9 @@
     const gfx::Rect& source_rect,
     viz::CopyOutputRequest::CopyOutputRequestCallback callback) {
   std::unique_ptr<viz::CopyOutputRequest> request =
-      viz::CopyOutputRequest::CreateBitmapRequest(std::move(callback));
+      std::make_unique<viz::CopyOutputRequest>(
+          viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
+          std::move(callback));
   request->set_area(source_rect);
   layer->RequestCopyOfOutput(std::move(request));
 }
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index 8a51d75..e745459 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -762,6 +762,9 @@
       <message name="IDS_APP_LIST_CLEAR_SEARCHBOX" desc="Tooltip for the button that clears all text from the search box in the app list.">
         Clear searchbox text
       </message>
+      <message name="IDS_APP_LIST_PAGE_SWITCHER" desc="Tooltip for page switcher for each page in fullscreen view which shows all apps.">
+        Page <ph name="selected_page">$1<ex>1</ex></ph> of <ph name="total_page_num">$2<ex>3</ex></ph>
+      </message>
 
       <!-- Strings describing the touch calibration UX -->
       <message name="IDS_DISPLAY_TOUCH_CALIBRATION_EXIT_LABEL" desc="A message to notify the user about using the escape key to exit the calibration mode.">
diff --git a/ui/views/bubble/bubble_border.cc b/ui/views/bubble/bubble_border.cc
index d946ebc4..95fcc169 100644
--- a/ui/views/bubble/bubble_border.cc
+++ b/ui/views/bubble/bubble_border.cc
@@ -310,7 +310,7 @@
 }
 
 int BubbleBorder::GetBorderCornerRadius() const {
-  return UseMd() ? 3 : images_->corner_radius;
+  return UseMd() ? 2 : images_->corner_radius;
 }
 
 int BubbleBorder::GetArrowOffset(const gfx::Size& border_size) const {
diff --git a/ui/views/controls/label.cc b/ui/views/controls/label.cc
index 16aec515..c5bbcdc7 100644
--- a/ui/views/controls/label.cc
+++ b/ui/views/controls/label.cc
@@ -15,7 +15,6 @@
 #include "base/i18n/rtl.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_node_data.h"
@@ -516,11 +515,6 @@
 void Label::OnPaint(gfx::Canvas* canvas) {
   View::OnPaint(canvas);
   if (is_first_paint_text_) {
-    // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is
-    // fixed.
-    tracked_objects::ScopedTracker tracking_profile(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 First PaintText()"));
-
     is_first_paint_text_ = false;
     PaintText(canvas);
   } else {
diff --git a/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html b/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html
index a494cbe..2d5528b3 100644
--- a/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html
+++ b/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html
@@ -13,14 +13,13 @@
         --scroll-border: 1px solid var(--paper-grey-300);
         border: 0;
         border-radius: 2px;
+        bottom: 50%;
         box-shadow: 0 0 16px rgba(0, 0, 0, 0.12),
                     0 16px 16px rgba(0, 0, 0, 0.24);
         color: inherit;
-        left: 50%;
-        margin: 0;
+        overflow-y: hidden;
         padding: 0;
         top: 50%;
-        transform: translate(-50%, -50%);
         width: 512px;
       }
 
diff --git a/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.js b/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.js
index 836f7b3..59ee2f6 100644
--- a/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.js
+++ b/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.js
@@ -212,10 +212,10 @@
 
     this.animate(
         [
-          {transform: 'translate(-50%, -50%) scale(1)', offset: 0},
-          {transform: 'translate(-50%, -50%) scale(1.02)', offset: 0.4},
-          {transform: 'translate(-50%, -50%) scale(1.02)', offset: 0.6},
-          {transform: 'translate(-50%, -50%) scale(1)', offset: 1},
+          {transform: 'scale(1)', offset: 0},
+          {transform: 'scale(1.02)', offset: 0.4},
+          {transform: 'scale(1.02)', offset: 0.6},
+          {transform: 'scale(1)', offset: 1},
         ],
         /** @type {!KeyframeEffectOptions} */ ({
           duration: 180,